ws2801-webserver
Version:
A ready-to-use webserver for the WS2801-Pi package.
190 lines (189 loc) • 7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthService = void 0;
const ip_1 = __importDefault(require("ip"));
const logger_1 = require("./logger");
const persister_1 = require("./persister");
const webserver_1 = require("./webserver");
class AuthService {
constructor(config, webserver) {
this.resolveFunctionsForRegistrationConfirmation = [];
this.config = config;
this.webserver = webserver;
this.logger = new logger_1.Logger('Auth Service');
this.persister = new persister_1.Persister();
this.confirmationWebserver = new webserver_1.Webserver(this.config.confirmationPort, this.config.logRequests === true);
}
start() {
this.loadUsers();
this.addAuthMiddleware();
this.addAuthRoutes();
this.confirmationWebserver.start();
}
stop() {
this.confirmationWebserver.stop();
}
registerUser(name, apiKey) {
const userExists = this.users.some((user) => user.name === name);
if (userExists) {
throw new Error(`User with name '${name}' already exists.`);
}
const newUser = this.generateUser(name, apiKey);
this.users.push(newUser);
this.saveUsers();
return newUser;
}
// private setUserIsAllowed(name: string, allowed: boolean): void {
// this.users.find((user: User): boolean => user.name === name).allowed = allowed;
// }
getUserByName(name) {
return this.users.find((user) => user.name === name);
}
getUserByApiKey(apiKey) {
return this.users.find((user) => user.apiKey === apiKey);
}
generateUser(name, apiKey) {
const user = {
name: name,
allowed: true,
apiKey: apiKey,
};
return user;
}
loadUsers() {
const usersAsString = this.persister.loadData('webserver-api-keys.json');
if (!usersAsString) {
this.users = [];
this.saveUsers();
return;
}
this.users = JSON.parse(usersAsString);
}
saveUsers() {
this.persister.saveData('webserver-api-keys.json', JSON.stringify(this.users, null, 2));
}
addAuthMiddleware() {
this.webserver.addMiddleware(this.authMiddleware.bind(this));
}
addAuthRoutes() {
this.webserver.addPostRoute('/register', this.register.bind(this));
this.webserver.addPostRoute('/login', this.login.bind(this));
this.confirmationWebserver.addGetRoute('/confirm-registration', this.confirmRegistration.bind(this));
}
async waitForConfirmation(name) {
const confirmationForUserExists = this.resolveFunctionsForRegistrationConfirmation
.some((resolveFunction) => {
return resolveFunction.name === name;
});
if (confirmationForUserExists) {
throw new Error(`The user "${name}" is already waiting for confirmation.`);
}
const resolvePromise = new Promise((resolve) => {
this.resolveFunctionsForRegistrationConfirmation.push({
name: name,
resolveFunction: resolve,
});
this.logger.log(`User '${name}' would like to register.
Click this link to confirm: http://${ip_1.default.address()}:${this.config.confirmationPort}/confirm-registration?name=${name.replace(/ /g, '%20').replace(/ /g, '%C2%A0')}`);
});
return resolvePromise;
}
// Middleware functions
authMiddleware(request, response, next) {
if (request.path === '/login-required'
|| request.path === '/register'
|| request.path === '/login') {
next();
return;
}
const apiKey = request.query.apiKey;
if (!apiKey) {
response.status(401).send('Please register first.');
return;
}
const user = this.getUserByApiKey(apiKey);
if (!user) {
response.status(401).send('Please register first.');
return;
}
if (!user.allowed) {
response.status(403).send(`You are not allowed to do that.`);
return;
}
next();
}
// Route functions
async register(request, response) {
const name = request.body.name;
const apiKey = request.body.apiKey;
if (!name) {
response.status(400).send(`Request body must contain a 'name'.`);
return;
}
if (!apiKey) {
response.status(400).send(`Request body must contain a 'apiKey'.`);
return;
}
const userAlreadyExists = this.getUserByName(name) !== undefined;
if (userAlreadyExists) {
response.status(403).send(`User '${name}' already exists.`);
return;
}
try {
await this.waitForConfirmation(name);
}
catch (error) {
response.status(403).send(error.message);
return;
}
try {
this.registerUser(name, apiKey);
response.status(200).json({ apiKey: apiKey });
}
catch (error) {
response.status(403).send(error.message);
}
}
confirmRegistration(request, response) {
const nameInQuery = request.query.name;
if (!nameInQuery) {
response.status(400).send(`Request must contain name as query param.`);
return;
}
const resolveFunctionIndex = this.resolveFunctionsForRegistrationConfirmation
.findIndex((resolveFunctionWithName) => {
return resolveFunctionWithName.name === nameInQuery;
});
if (resolveFunctionIndex === -1) {
response.status(400).send(`Could not confirm registration.`);
return;
}
this.resolveFunctionsForRegistrationConfirmation[resolveFunctionIndex].resolveFunction();
this.resolveFunctionsForRegistrationConfirmation.splice(resolveFunctionIndex);
response.status(200).send(`User '${nameInQuery}' was successfully registered.`);
}
async login(request, response) {
const apiKey = request.query.apiKey;
if (!apiKey) {
response.status(400).send(`Request must contain 'apiKey' as query parameter.`);
return;
}
try {
const user = this.getUserByApiKey(apiKey);
if (!user) {
throw new Error('User is not registered. Try to register again.');
}
if (!user.allowed) {
throw new Error('You are not allowed to login.');
}
response.status(200).json({ loggedIn: true });
}
catch (error) {
response.status(403).send(error.message);
}
}
}
exports.AuthService = AuthService;