security settings

This commit is contained in:
Evert Prants 2022-03-20 19:51:42 +02:00
parent abb0e24d99
commit bcc3b86b4d
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
5 changed files with 126 additions and 17 deletions

View File

@ -1,23 +1,10 @@
import { Controller, Get, Req, Res, Session } from '@nestjs/common'; import { Controller, Get, Redirect } from '@nestjs/common';
import { Request, Response } from 'express';
import { SessionData } from 'express-session';
import { AppService } from './app.service';
@Controller() @Controller()
export class AppController { export class AppController {
constructor(private readonly appService: AppService) {}
@Get() @Get()
getHello( @Redirect('/account/general')
@Session() session: SessionData, getHello() {
@Res() res: Response, return;
@Req() req: Request,
): Record<string, any> {
if (!session.user) {
res.redirect('/login');
return;
}
res.render('index', { user: req.user });
} }
} }

View File

@ -8,6 +8,9 @@
outline: 0px solid var(--focus-outline); outline: 0px solid var(--focus-outline);
background-color: var(--btn-background); background-color: var(--btn-background);
color: var(--btn-color); color: var(--btn-color);
text-shadow: none;
text-decoration: none;
text-align: center;
min-width: 120px; min-width: 120px;
transition: background-color 0.35s linear, outline 0.15s linear; transition: background-color 0.35s linear, outline 0.15s linear;

View File

@ -7,6 +7,9 @@
.align-self-center { .align-self-center {
align-self: center; align-self: center;
} }
.align-items-start {
align-items: flex-start;
}
.d-flex { .d-flex {
display: flex; display: flex;
} }

View File

@ -18,6 +18,7 @@ import { Request, Response } from 'express';
import { unlink } from 'fs/promises'; import { unlink } from 'fs/promises';
import { OAuth2ClientService } from 'src/modules/objects/oauth2-client/oauth2-client.service'; import { OAuth2ClientService } from 'src/modules/objects/oauth2-client/oauth2-client.service';
import { UploadService } from 'src/modules/objects/upload/upload.service'; import { UploadService } from 'src/modules/objects/upload/upload.service';
import { UserTOTPService } from 'src/modules/objects/user-token/user-totp-token.service';
import { UserService } from 'src/modules/objects/user/user.service'; import { UserService } from 'src/modules/objects/user/user.service';
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service'; import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
import { TokenService } from 'src/modules/utility/services/token.service'; import { TokenService } from 'src/modules/utility/services/token.service';
@ -31,6 +32,7 @@ export class SettingsController {
private readonly _upload: UploadService, private readonly _upload: UploadService,
private readonly _token: TokenService, private readonly _token: TokenService,
private readonly _user: UserService, private readonly _user: UserService,
private readonly _totp: UserTOTPService,
private readonly _client: OAuth2ClientService, private readonly _client: OAuth2ClientService,
) {} ) {}
@ -163,4 +165,116 @@ export class SettingsController {
} }
res.redirect('/account/oauth2'); res.redirect('/account/oauth2');
} }
@Get('security')
@Render('settings/security')
public async security(@Req() req: Request) {
const mailSplit = req.user.email.split('@');
const emailHint = `${mailSplit[0].substring(0, 1)}***@${mailSplit[1]}`;
const twofactor = await this._totp.userHasTOTP(req.user);
return this._form.populateTemplate(req, {
user: req.user,
emailHint,
twofactor,
});
}
@Post('security/password')
public async setPassword(
@Req() req: Request,
@Res() res: Response,
@Body()
body: {
password: string;
new_password: string;
password_repeat: string;
},
) {
const { password, new_password, password_repeat } = body;
try {
if (!password || !new_password || !password_repeat) {
throw new Error('Please fill out all of the fields.');
}
if (!(await this._user.comparePasswords(req.user.password, password))) {
throw new Error('Current password is invalid.');
}
if (!new_password.match(this._form.passwordRegex)) {
throw new Error(
'Password must be at least 8 characters long, contain a capital and lowercase letter and a number',
);
}
if (new_password !== password_repeat) {
throw new Error('The passwords do not match.');
}
} catch (e: any) {
req.flash('message', {
error: true,
text: e.message,
});
res.redirect('/account/security');
return;
}
const newPassword = await this._user.hashPassword(new_password);
req.user.password = newPassword;
await this._user.updateUser(req.user);
req.flash('message', {
error: false,
text: 'Password changed successfully.',
});
res.redirect('/account/security');
}
@Post('security/email')
public async setEmail(
@Req() req: Request,
@Res() res: Response,
@Body()
body: {
current_email: string;
email: string;
},
) {
const { current_email, email } = body;
try {
if (!current_email || !email) {
throw new Error('Please fill out all of the fields.');
}
if (current_email !== req.user.email) {
throw new Error('The current email address is invalid.');
}
if (!email.match(this._form.emailRegex)) {
throw new Error('The new email address is invalid.');
}
const existing = await this._user.getByEmail(email);
if (existing) {
throw new Error(
'There is already an existing user with this email address.',
);
}
} catch (e: any) {
req.flash('message', {
error: true,
text: e.message,
});
res.redirect('/account/security');
return;
}
req.user.email = email;
await this._user.updateUser(req.user);
req.flash('message', {
error: false,
text: 'Email address changed successfully.',
});
res.redirect('/account/security');
}
} }

View File

@ -19,6 +19,7 @@ import { OAuth2Module } from '../oauth2/oauth2.module';
import { SettingsController } from './settings.controller'; import { SettingsController } from './settings.controller';
import { SettingsService } from './settings.service'; import { SettingsService } from './settings.service';
import { OAuth2ClientModule } from 'src/modules/objects/oauth2-client/oauth2-client.module'; import { OAuth2ClientModule } from 'src/modules/objects/oauth2-client/oauth2-client.module';
import { UserTokenModule } from 'src/modules/objects/user-token/user-token.module';
@Module({ @Module({
controllers: [SettingsController], controllers: [SettingsController],
@ -26,6 +27,7 @@ import { OAuth2ClientModule } from 'src/modules/objects/oauth2-client/oauth2-cli
ConfigurationModule, ConfigurationModule,
UploadModule, UploadModule,
UserModule, UserModule,
UserTokenModule,
OAuth2Module, OAuth2Module,
OAuth2ClientModule, OAuth2ClientModule,
MulterModule.registerAsync({ MulterModule.registerAsync({