Added Backend for User Profile

This commit is contained in:
Mutzi 2022-08-25 13:27:47 +02:00
parent 1f714b5b50
commit 06fffbee2f
10 changed files with 105 additions and 6 deletions

View File

@ -34,3 +34,13 @@ export class TfaSetup extends BaseRequest {
@IsBoolean()
mail: boolean;
}
export class ChangePasswordRequest extends BaseRequest {
@IsNotEmpty()
@IsString()
oldPassword: string;
@IsNotEmpty()
@IsString()
newPassword: string;
}

View File

@ -35,4 +35,5 @@ export class RemoveTfaResponse extends SuccessResponse {}
export class RequestEmailTfaResponse extends SuccessResponse {}
export class TfaCompletedResponse extends SuccessResponse {}
export class SignupResponse extends SuccessResponse {}
export class ChangePasswordResponse extends SuccessResponse {}
export class RefreshResponse extends LoginResponse {}

View File

@ -1,3 +1,4 @@
export * from './base';
export * as Auth from './auth';
export * as FS from './fs';
export * as User from './user';

27
dto/src/responses/user.ts Normal file
View File

@ -0,0 +1,27 @@
import { SuccessResponse } from './base';
import { ValidateConstructor } from '../utils';
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator';
@ValidateConstructor
export class UserInfoResponse extends SuccessResponse {
constructor(name: string, gitlab: boolean, tfaEnabled: boolean) {
super();
this.name = name;
this.gitlab = gitlab;
this.tfaEnabled = tfaEnabled;
}
@IsNotEmpty()
@IsString()
name: string;
@IsBoolean()
gitlab: boolean;
@IsBoolean()
tfaEnabled: boolean;
}
export class DeleteUserResponse extends SuccessResponse {}
export class ChangePasswordResponse extends SuccessResponse {}
export class LogoutAllResponse extends SuccessResponse {}

View File

@ -18,7 +18,8 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"webpack": "webpack --config webpack.config.ts",
"genapi": "ts-node tools/apigen.ts"
"genapi": "ts-node tools/apigen.ts",
"updateDto": "cd dto && yarn build && cd .. && yarn add ./dto && cd frontend && yarn add ../dto"
},
"dependencies": {
"@fastify/multipart": "^7.1.0",

View File

@ -134,4 +134,17 @@ export default class AuthController {
url: `/set_token?token=${token}`
};
}
@Post('change_password')
async changePassword(
@Request() req,
@Body(new ValidationPipe()) data: Requests.Auth.ChangePasswordRequest
): Promise<Responses.Auth.ChangePasswordResponse> {
await this.authService.changePassword(
req.user,
data.oldPassword,
data.newPassword
);
return new Responses.Auth.ChangePasswordResponse();
}
}

27
src/controller/user.ts Normal file
View File

@ -0,0 +1,27 @@
import { Controller, Get, Post, Request } from '@nestjs/common';
import { AuthService } from '../services/auth';
import { Responses } from 'dto';
@Controller('api/user')
export default class UserController {
constructor(private authService: AuthService) {}
@Get('info')
async getUserInfo(
@Request() req
): Promise<Responses.User.UserInfoResponse> {
return new Responses.User.UserInfoResponse(
req.user.name,
req.user.isGitlabUser,
this.authService.requiresTfa(req.user)
);
}
@Post('delete')
async deleteUser(
@Request() req
): Promise<Responses.User.DeleteUserResponse> {
await this.authService.deleteUser(req.user);
return new Responses.User.DeleteUserResponse();
}
}

View File

@ -6,8 +6,9 @@ import {
AuthLocalService,
AuthJwtService
} from '../services/auth';
import AuthController from '../controller/auth';
import FileSystemService from '../services/filesystem';
import AuthController from '../controller/auth';
import UserController from '../controller/user';
@Module({
imports: [TypeOrmModule.forFeature([User, INode, JWTToken])],
@ -17,6 +18,6 @@ import FileSystemService from '../services/filesystem';
AuthJwtService,
FileSystemService
],
controllers: [AuthController]
controllers: [AuthController, UserController]
})
export default class AuthModule {}

View File

@ -1,5 +1,6 @@
import {
BadRequestException,
ForbiddenException,
Injectable,
UnauthorizedException
} from '@nestjs/common';
@ -312,6 +313,23 @@ export class AuthService {
}
return info && info.username == user.name;
}
async deleteUser(user: User): Promise<void> {
await this.revokeAll(user);
await this.fsService.delete(await user.root, true);
await this.userRepo.remove(user);
}
async changePassword(
user: User,
oldPW: string,
newPw: string
): Promise<void> {
if (!(await argon2.verify(user.password, oldPW)))
throw new ForbiddenException('Old password is wrong');
user.password = await argon2.hash(newPw);
await this.revokeAll(await this.userRepo.save(user));
}
}
@Injectable()

View File

@ -51,12 +51,12 @@ export default class FileSystemService {
);
}
async delete(node: INode): Promise<void> {
if (node.parentId == null)
async delete(node: INode, force = false): Promise<void> {
if (node.parentId == null || force)
throw new BadRequestException("Can't delete root");
if (!node.isFile)
await Promise.all(
(await node.children).map((child) => this.delete(child))
(await node.children).map((child) => this.delete(child, force))
);
else
unlink(`files/${node.id}`, (err) => {