UNPKG

ws2801-webserver

Version:

A ready-to-use webserver for the WS2801-Pi package.

190 lines (189 loc) 7 kB
"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;