128 lines
5.4 KiB
TypeScript
128 lines
5.4 KiB
TypeScript
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<void> {
|
|
res.render('login');
|
|
}
|
|
|
|
protected async postLogin(req: Request, res: Response): Promise<void> {
|
|
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, <string>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<void> {
|
|
res.render('register');
|
|
}
|
|
|
|
protected async postRegister(req: Request, res: Response): Promise<void> {
|
|
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) => {
|
|
const callbacks: RegisterCallback[] = [];
|
|
|
|
// Password
|
|
await user.as(UserPasswordComponent).setPassword(req.body.password);
|
|
|
|
// Username
|
|
user.as(UserNameComponent).name = req.body.username;
|
|
|
|
return callbacks;
|
|
}, async (connection, user) => {
|
|
passwordAuthProof.setResource(user);
|
|
return [];
|
|
});
|
|
} 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<void> {
|
|
throw new ServerError('Not implemented.');
|
|
}
|
|
|
|
protected async postAuth(): Promise<void> {
|
|
throw new ServerError('Not implemented.');
|
|
}
|
|
}
|