import Controller from "wms-core/Controller"; import {REQUIRE_AUTH_MIDDLEWARE} from "wms-core/auth/AuthComponent"; import {NextFunction, Request, Response} from "express"; import {ADD_RECOVERY_EMAIL_MAIL_TEMPLATE} from "../Mails"; import Validator, {InvalidFormatValidationError, ValidationBag} from "wms-core/db/Validator"; import {EMAIL_REGEX} from "wms-core/db/Model"; import MagicLinkController from "./MagicLinkController"; import {MagicLinkActionType} from "./MagicLinkActionType"; import UserEmail from "wms-core/auth/models/UserEmail"; import {BadRequestError, ForbiddenHttpError, NotFoundHttpError} from "wms-core/HttpError"; import MailDomain from "../models/MailDomain"; import UserMailIdentityComponent from "../models/UserMailIdentityComponent"; import MailIdentity from "../models/MailIdentity"; import UserNameComponent from "../models/UserNameComponent"; import {WhereOperator, WhereTest} from "wms-core/db/ModelQuery"; export default class AccountController extends Controller { public getRoutesPrefix(): string { return '/account'; } routes(): void { this.get('/', this.getAccount, 'account', REQUIRE_AUTH_MIDDLEWARE); this.post('/add-recovery-email', this.addRecoveryEmail, 'add-recovery-email', REQUIRE_AUTH_MIDDLEWARE); this.post('/set-main-email', this.postSetMainRecoveryEmail, 'set-main-recovery-email', REQUIRE_AUTH_MIDDLEWARE); this.post('/remove-email', this.postRemoveRecoveryEmail, 'remove-recovery-email', REQUIRE_AUTH_MIDDLEWARE); this.post('/create-mail-identity', this.postCreateMailIdentity, 'create-mail-identity', REQUIRE_AUTH_MIDDLEWARE); this.post('/delete-mail-identity', this.postDeleteMailIdentity, 'delete-mail-identity', REQUIRE_AUTH_MIDDLEWARE); } protected async getAccount(req: Request, res: Response, next: NextFunction): Promise { const user = req.models.user!; const userMailIdentity = user.as(UserMailIdentityComponent); res.render('account', { emails: await user.emails.get(), mailboxIdentity: await (await userMailIdentity.mainMailIdentity.get())?.toEmail(), identities: await Promise.all((await userMailIdentity.mailIdentities.get()).map(async identity => ({ id: identity.id, email: await identity.toEmail(), }))), domains: (await MailDomain.select() .where('user_id', user.id!) .where('user_id', null, WhereTest.EQ, WhereOperator.OR) .sortBy('user_id', 'DESC') .get()) .map(d => ({ value: d.id, display: d.name, })), }); } protected async addRecoveryEmail(req: Request, res: Response, next: NextFunction): Promise { await this.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 bag = new ValidationBag(); const error = new InvalidFormatValidationError('You already have this email.'); error.thingName = 'email'; bag.addMessage(error); throw bag; } await MagicLinkController.sendMagicLink( req.sessionID!, MagicLinkActionType.ADD_RECOVERY_EMAIL, Controller.route('account'), email, ADD_RECOVERY_EMAIL_MAIL_TEMPLATE, {} ); res.redirect(Controller.route('magic_link_lobby', undefined, { redirect_uri: Controller.route('account'), })); } protected async postSetMainRecoveryEmail(req: Request, res: Response): Promise { if (!req.body.id) throw new BadRequestError('Missing id field', 'Check form parameters.', req.url); const userEmail = await UserEmail.getById(req.body.id); if (!userEmail) throw new NotFoundHttpError('email', req.url); if (userEmail.user_id !== req.models.user!.id) throw new ForbiddenHttpError('email', req.url); if (userEmail.id === req.models.user!.main_email_id) throw new BadRequestError('This address is already your main address', 'Try refreshing the account page.', req.url); req.models.user!.main_email_id = userEmail.id; await req.models.user!.save(); req.flash('success', 'This email was successfully set as your main address.'); res.redirectBack(); } protected async postRemoveRecoveryEmail(req: Request, res: Response): Promise { if (!req.body.id) throw new BadRequestError('Missing id field', 'Check form parameters.', req.url); const userEmail = await UserEmail.getById(req.body.id); if (!userEmail) throw new NotFoundHttpError('email', req.url); if (userEmail.user_id !== req.models.user!.id) throw new ForbiddenHttpError('email', req.url); if (userEmail.id === req.models.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(); } protected async postCreateMailIdentity(req: Request, res: Response): Promise { const domain = await MailDomain.getById(req.body.mail_domain_id); if (!domain) throw new NotFoundHttpError('domain', req.url); const user = req.models.user!; const mailIdentityComponent = user.as(UserMailIdentityComponent); const identity = MailIdentity.create({ user_id: user.id, name: req.body.name, mail_domain_id: req.body.mail_domain_id, }); // Check whether this identity can be created by this user if (domain.isPublic()) { await this.validate({ name: new Validator().defined().equals(user.as(UserNameComponent).name), }, req.body); if ((await mailIdentityComponent.getPublicAddressesCount()) >= mailIdentityComponent.getMaxPublicAddressesCount()) { req.flash('error', 'You have reached maximum public email addresses.'); res.redirectBack(); return; } } else { if (!domain.canCreateAddresses(user)) { throw new ForbiddenHttpError('domain', req.url); } } // Save identity await identity.save(); // Set main mail identity if not already set if (!mailIdentityComponent.main_mail_identity_id) { mailIdentityComponent.main_mail_identity_id = identity.id; await user.save(); req.flash('info', 'Congratulations! You just created your mailbox.'); } req.flash('success', 'Mail identity ' + (await identity.toEmail()) + ' successfully created.') res.redirectBack(); } protected async postDeleteMailIdentity(req: Request, res: Response): Promise { const identity = await MailIdentity.getById(req.body.id); if (!identity) throw new NotFoundHttpError('Mail identity', req.url); if (identity.user_id !== req.models.user!.id) throw new ForbiddenHttpError('mail identity', req.url); if (req.models.user!.as(UserMailIdentityComponent).main_mail_identity_id === identity.id) { req.flash('error', 'Cannot delete your mailbox identity.'); res.redirectBack(); return; } await identity.delete(); req.flash('success', 'Identity ' + (await identity.toEmail()) + ' successfully deleted.'); res.redirectBack(); } }