rainbox.email/src/controllers/AccountController.ts

179 lines
7.7 KiB
TypeScript
Raw Normal View History

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";
2020-07-27 16:02:09 +02:00
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 {
2020-07-27 16:02:09 +02:00
public getRoutesPrefix(): string {
return '/account';
}
routes(): void {
2020-07-27 16:02:09 +02:00
this.get('/', this.getAccount, 'account', REQUIRE_AUTH_MIDDLEWARE);
this.post('/add-recovery-email', this.addRecoveryEmail, 'add-recovery-email', REQUIRE_AUTH_MIDDLEWARE);
2020-07-27 16:02:09 +02:00
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<void> {
2020-07-27 16:02:09 +02:00
const user = req.models.user!;
const userMailIdentity = user.as(UserMailIdentityComponent);
res.render('account', {
2020-07-27 16:02:09 +02:00
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<void> {
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'),
}));
}
2020-07-27 16:02:09 +02:00
protected async postSetMainRecoveryEmail(req: Request, res: Response): Promise<void> {
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.');
2020-07-27 16:02:09 +02:00
res.redirectBack();
}
2020-07-27 16:02:09 +02:00
protected async postRemoveRecoveryEmail(req: Request, res: Response): Promise<void> {
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.');
2020-07-27 16:02:09 +02:00
res.redirectBack();
}
protected async postCreateMailIdentity(req: Request, res: Response): Promise<void> {
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<string>().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<void> {
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();
}
}