icynet-auth-server/src/modules/ssr-front-end/register/register.controller.ts

174 lines
5.1 KiB
TypeScript

import {
Body,
Controller,
Get,
Post,
Query,
Req,
Res,
UnauthorizedException,
UseGuards,
} from '@nestjs/common';
import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
import { Request, Response } from 'express';
import { LoginAntispamGuard } from 'src/guards/login-antispam.guard';
import { ConfigurationService } from 'src/modules/config/config.service';
import { AuditAction } from 'src/modules/objects/audit/audit.enum';
import { AuditService } from 'src/modules/objects/audit/audit.service';
import { UserService } from 'src/modules/objects/user/user.service';
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
import { RegisterDto } from './register.interfaces';
@Controller('/register')
@UseGuards(LoginAntispamGuard)
export class RegisterController {
constructor(
private readonly userService: UserService,
private readonly formUtil: FormUtilityService,
private readonly config: ConfigurationService,
private readonly audit: AuditService,
) {}
@Get()
public async registerView(
@Req() req: Request,
@Res() res: Response,
@Query('token') registrationToken: string,
@Query('redirectTo') redirectTo: string,
) {
let registrationAuthorized = this.config.get<boolean>('app.registrations');
if (registrationToken) {
const registrationEmail =
await this.userService.checkRegistrationToken(registrationToken);
if (!registrationEmail) {
req.flash('message', {
error: true,
text: `This registration token is invalid or expired.`,
});
return res.redirect(
'/login' + (redirectTo ? '?redirectTo=' + redirectTo : ''),
);
}
req.flash('form', { email: registrationEmail });
// bypass limitations
registrationAuthorized = true;
}
res.render(
'register',
this.formUtil.populateTemplate(req, {
registrationAuthorized,
}),
);
}
@Post()
@Throttle({ default: { limit: 3, ttl: 10000 } })
@UseGuards(ThrottlerGuard)
public async registerRequest(
@Req() req: Request,
@Res() res: Response,
@Body() body: RegisterDto,
@Query('token') registrationToken: string,
@Query('redirectTo') redirectTo?: string,
) {
const { username, display_name, email, password, password_repeat } =
this.formUtil.trimmed(body, ['username', 'display_name', 'email']);
let registrationAuthorized = this.config.get<boolean>('app.registrations');
let tokenEmail: string | undefined;
if (registrationToken) {
tokenEmail =
await this.userService.checkRegistrationToken(registrationToken);
if (!tokenEmail) {
req.flash('message', {
error: true,
text: `This registration token is invalid or expired.`,
});
return res.redirect(
'/login' + (redirectTo ? '?redirectTo=' + redirectTo : ''),
);
}
// bypass limitations
registrationAuthorized = true;
}
if (!registrationAuthorized) {
throw new UnauthorizedException(
'Registrations are disabled by administrator.',
);
}
try {
if (
!username ||
!display_name ||
!email ||
!password ||
!password_repeat
) {
throw new Error('Please fill out all of the fields!');
}
if (!username || !username.match(this.formUtil.usernameRegex)) {
throw new Error(
'Username must be alphanumeric and between 3 to 26 characters long (_, - and . are also allowed)',
);
}
if (display_name.length < 3 || display_name.length > 32) {
throw new Error(
'Display name must be between 3 and 32 characters long.',
);
}
// https://stackoverflow.com/a/21456918
if (!password.match(this.formUtil.passwordRegex)) {
throw new Error(
'Password must be at least 8 characters long, contain a capital and lowercase letter and a number',
);
}
if (!email.match(this.formUtil.emailRegex)) {
throw new Error('Invalid email address!');
}
if (password !== password_repeat) {
throw new Error('The passwords do not match!');
}
const sendActivationEmail = tokenEmail ? tokenEmail !== email : true;
const user = await this.userService.userRegistration(
body,
redirectTo,
!sendActivationEmail,
);
await this.audit.auditRequest(req, AuditAction.REGISTRATION, null, user);
if (tokenEmail) {
await this.userService.invalidateRegistrationToken(registrationToken);
}
req.flash('message', {
error: false,
text: sendActivationEmail
? `An activation email has been sent to ${email}!`
: `Welcome, we have been expecting you! You may now log in.`,
});
res.redirect('/login' + (redirectTo ? '?redirectTo=' + redirectTo : ''));
} catch (e: unknown) {
req.flash('message', { error: true, text: (e as Error).message });
req.flash('form', { ...body, password_repeat: undefined });
res.redirect(req.originalUrl);
}
}
}