import Controller from "wms-core/Controller"; import AuthComponent, {RequireAuthMiddleware, RequireGuestMiddleware} from "wms-core/auth/AuthComponent"; import {Request, Response} from "express"; import Validator, {InvalidFormatValidationError, ValidationBag} from "wms-core/db/Validator"; import UserPasswordComponent, {PasswordAuthProof} from "../models/UserPasswordComponent"; import UserNameComponent, {USERNAME_REGEXP} from "../models/UserNameComponent"; import _AuthController from "wms-core/auth/AuthController"; import {NotFoundHttpError, ServerError} from "wms-core/HttpError"; import {AuthError, PendingApprovalAuthError, RegisterCallback} from "wms-core/auth/AuthGuard"; import User from "wms-core/auth/models/User"; import Throttler from "wms-core/Throttler"; export default class AuthController extends _AuthController { public routes(): void { this.get('/login', this.getLogin, 'auth', RequireGuestMiddleware); this.post('/login', this.postLogin, 'auth', RequireGuestMiddleware); this.get('/register', this.getRegister, 'register', RequireGuestMiddleware); this.post('/register', this.postRegister, 'register', RequireGuestMiddleware); this.post('/logout', this.postLogout, 'logout', RequireAuthMiddleware); } protected async getLogin(req: Request, res: Response): Promise { res.render('login'); } protected async postLogin(req: Request, res: Response): Promise { await this.validate({ username: new Validator().defined().exists(User, 'name'), password: new Validator().acceptUndefined(), }, req.body); const user = await User.select() .where('name', req.body.username) .first(); if (!user) throw new NotFoundHttpError(`Couldn't find a user with name ${req.body.username}`, req.url); if (!req.session) throw new ServerError('Session not initialized.'); const passwordAuthProof = PasswordAuthProof.createProofForLogin(req.session); passwordAuthProof.setResource(user); await passwordAuthProof.authorize(req.body.password); try { await this.getApp().as(AuthComponent).getAuthGuard().authenticateOrRegister(req.session, passwordAuthProof); } catch (e) { if (e instanceof AuthError) { Throttler.throttle('login_failed_attempts_user', 3, 180000, user.getOrFail('name'), 1000, 60000); Throttler.throttle('login_failed_attempts_ip', 5, 60000, req.ip, 1000, 60000); if (e instanceof PendingApprovalAuthError) { req.flash('error', 'Your account is still being reviewed.'); res.redirectBack(); return; } else { const bag = new ValidationBag(); const err = new InvalidFormatValidationError('Invalid password.'); err.thingName = 'password'; bag.addMessage(err); throw bag; } } else { throw e; } } req.flash('success', `Welcome, ${user.name}.`); res.redirect(Controller.route('home')); } protected async getRegister(req: Request, res: Response): Promise { res.render('register'); } protected async postRegister(req: Request, res: Response): Promise { Throttler.throttle('register_password', 10, 30000, req.ip); await this.validate({ username: new Validator().defined().between(3, 64).regexp(USERNAME_REGEXP).unique(User, 'name'), password: new Validator().defined().minLength(8), password_confirmation: new Validator().defined().sameAs('password', req.body.password), terms: new Validator().defined(), }, req.body); if(!req.session) throw new ServerError('Session not initialized.'); const passwordAuthProof = PasswordAuthProof.createAuthorizedProofForRegistration(req.session); try { await this.getApp().as(AuthComponent).getAuthGuard().authenticateOrRegister(req.session, passwordAuthProof, undefined, async (connection, user) => { passwordAuthProof.setResource(user); const callbacks: RegisterCallback[] = []; // Password await user.as(UserPasswordComponent).setPassword(req.body.password); // Username user.as(UserNameComponent).name = req.body.username; return callbacks; }); } catch (e) { if (e instanceof PendingApprovalAuthError) { req.flash('info', `Your account was successfully created and is pending review from an administrator.`); res.redirect(Controller.route('home')); return; } else { throw e; } } const user = await passwordAuthProof.getResource(); req.flash('success', `Your account was successfully created! Welcome, ${user?.as(UserNameComponent).name}.`); res.redirect(Controller.route('home')); } protected async getCheckAuth(): Promise { throw new ServerError('Not implemented.'); } protected async postAuth(): Promise { throw new ServerError('Not implemented.'); } }