import Controller from "../Controller"; import {RequireAuthMiddleware} from "./AuthComponent"; import {Request, Response} from "express"; import {BadRequestError, ForbiddenHttpError, NotFoundHttpError} from "../HttpError"; import config from "config"; import Validator, {EMAIL_REGEX, InvalidFormatValidationError} from "../db/Validator"; import UserPasswordComponent from "./password/UserPasswordComponent"; import User from "./models/User"; import ModelFactory from "../db/ModelFactory"; import UserEmail from "./models/UserEmail"; import MagicLinkController from "./magic_link/MagicLinkController"; import {MailTemplate} from "../mail/Mail"; import {ADD_EMAIL_MAIL_TEMPLATE} from "../Mails"; import AuthMagicLinkActionType from "./magic_link/AuthMagicLinkActionType"; export default class AccountController extends Controller { private readonly addEmailMailTemplate: MailTemplate; public constructor(addEmailMailTemplate: MailTemplate = ADD_EMAIL_MAIL_TEMPLATE) { super(); this.addEmailMailTemplate = addEmailMailTemplate; } public getRoutesPrefix(): string { return '/account'; } public routes(): void { this.get('/', this.getAccount, 'account', RequireAuthMiddleware); if (ModelFactory.get(User).hasComponent(UserPasswordComponent)) { this.post('/change-password', this.postChangePassword, 'change-password', RequireAuthMiddleware); } this.post('/add-email', this.addEmail, 'add-email', RequireAuthMiddleware); this.post('/set-main-email', this.postSetMainEmail, 'set-main-email', RequireAuthMiddleware); this.post('/remove-email', this.postRemoveEmail, 'remove-email', RequireAuthMiddleware); } protected async getAccount(req: Request, res: Response): Promise { const user = req.as(RequireAuthMiddleware).getUser(); res.render('auth/account', { main_email: await user.mainEmail.get(), emails: await user.emails.get(), display_email_warning: config.get('app.display_email_warning'), has_password: user.asOptional(UserPasswordComponent)?.hasPassword(), }); } protected async postChangePassword(req: Request, res: Response): Promise { const validationMap = { 'new_password': new Validator().defined(), 'new_password_confirmation': new Validator().sameAs('new_password', req.body.new_password), }; await Validator.validate(validationMap, req.body); const user = req.as(RequireAuthMiddleware).getUser(); const passwordComponent = user.as(UserPasswordComponent); if (passwordComponent.hasPassword() && !await passwordComponent.verifyPassword(req.body.current_password)) { req.flash('error', 'Invalid current password.'); res.redirectBack(Controller.route('account')); return; } await passwordComponent.setPassword(req.body.new_password, 'new_password'); await user.save(); req.flash('success', 'Password changed successfully.'); res.redirectBack(Controller.route('account')); } protected async addEmail(req: Request, res: Response): Promise { await Validator.validate({ email: new Validator().defined().regexp(EMAIL_REGEX), }, req.body); const email = req.body.email; // Existing email if (await UserEmail.select().where('email', email).first()) { const error = new InvalidFormatValidationError('You already have this email.'); error.thingName = 'email'; throw error; } await MagicLinkController.sendMagicLink( this.getApp(), req.getSession().id, AuthMagicLinkActionType.ADD_EMAIL, Controller.route('account'), email, this.addEmailMailTemplate, { email: email, }, ); res.redirect(Controller.route('magic_link_lobby', undefined, { redirect_uri: Controller.route('account'), })); } protected async postSetMainEmail(req: Request, res: Response): Promise { if (!req.body.id) throw new BadRequestError('Missing id field', 'Check form parameters.', req.url); const user = req.as(RequireAuthMiddleware).getUser(); const userEmail = await UserEmail.getById(req.body.id); if (!userEmail) throw new NotFoundHttpError('email', req.url); if (userEmail.user_id !== user.id) throw new ForbiddenHttpError('email', req.url); if (userEmail.id === user.main_email_id) throw new BadRequestError('This address is already your main address', 'Try refreshing the account page.', req.url); user.main_email_id = userEmail.id; await user.save(); req.flash('success', 'This email was successfully set as your main address.'); res.redirectBack(); } protected async postRemoveEmail(req: Request, res: Response): Promise { if (!req.body.id) throw new BadRequestError('Missing id field', 'Check form parameters.', req.url); const user = req.as(RequireAuthMiddleware).getUser(); const userEmail = await UserEmail.getById(req.body.id); if (!userEmail) throw new NotFoundHttpError('email', req.url); if (userEmail.user_id !== user.id) throw new ForbiddenHttpError('email', req.url); if (userEmail.id === user.main_email_id) throw new BadRequestError('Cannot remove main email address', 'Try refreshing the account page.', req.url); await userEmail.delete(); req.flash('success', 'This email was successfully removed from your account.'); res.redirectBack(); } }