Added Backend for User Profile
This commit is contained in:
		@@ -34,3 +34,13 @@ export class TfaSetup extends BaseRequest {
 | 
				
			|||||||
	@IsBoolean()
 | 
						@IsBoolean()
 | 
				
			||||||
	mail: boolean;
 | 
						mail: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ChangePasswordRequest extends BaseRequest {
 | 
				
			||||||
 | 
						@IsNotEmpty()
 | 
				
			||||||
 | 
						@IsString()
 | 
				
			||||||
 | 
						oldPassword: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@IsNotEmpty()
 | 
				
			||||||
 | 
						@IsString()
 | 
				
			||||||
 | 
						newPassword: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,4 +35,5 @@ export class RemoveTfaResponse extends SuccessResponse {}
 | 
				
			|||||||
export class RequestEmailTfaResponse extends SuccessResponse {}
 | 
					export class RequestEmailTfaResponse extends SuccessResponse {}
 | 
				
			||||||
export class TfaCompletedResponse extends SuccessResponse {}
 | 
					export class TfaCompletedResponse extends SuccessResponse {}
 | 
				
			||||||
export class SignupResponse extends SuccessResponse {}
 | 
					export class SignupResponse extends SuccessResponse {}
 | 
				
			||||||
 | 
					export class ChangePasswordResponse extends SuccessResponse {}
 | 
				
			||||||
export class RefreshResponse extends LoginResponse {}
 | 
					export class RefreshResponse extends LoginResponse {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
export * from './base';
 | 
					export * from './base';
 | 
				
			||||||
export * as Auth from './auth';
 | 
					export * as Auth from './auth';
 | 
				
			||||||
export * as FS from './fs';
 | 
					export * as FS from './fs';
 | 
				
			||||||
 | 
					export * as User from './user';
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								dto/src/responses/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								dto/src/responses/user.ts
									
									
									
									
									
										Normal 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 {}
 | 
				
			||||||
@@ -18,7 +18,8 @@
 | 
				
			|||||||
		"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
 | 
							"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",
 | 
							"test:e2e": "jest --config ./test/jest-e2e.json",
 | 
				
			||||||
		"webpack": "webpack --config webpack.config.ts",
 | 
							"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": {
 | 
						"dependencies": {
 | 
				
			||||||
		"@fastify/multipart": "^7.1.0",
 | 
							"@fastify/multipart": "^7.1.0",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,4 +134,17 @@ export default class AuthController {
 | 
				
			|||||||
			url: `/set_token?token=${token}`
 | 
								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
									
								
							
							
						
						
									
										27
									
								
								src/controller/user.ts
									
									
									
									
									
										Normal 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();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,8 +6,9 @@ import {
 | 
				
			|||||||
	AuthLocalService,
 | 
						AuthLocalService,
 | 
				
			||||||
	AuthJwtService
 | 
						AuthJwtService
 | 
				
			||||||
} from '../services/auth';
 | 
					} from '../services/auth';
 | 
				
			||||||
import AuthController from '../controller/auth';
 | 
					 | 
				
			||||||
import FileSystemService from '../services/filesystem';
 | 
					import FileSystemService from '../services/filesystem';
 | 
				
			||||||
 | 
					import AuthController from '../controller/auth';
 | 
				
			||||||
 | 
					import UserController from '../controller/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Module({
 | 
					@Module({
 | 
				
			||||||
	imports: [TypeOrmModule.forFeature([User, INode, JWTToken])],
 | 
						imports: [TypeOrmModule.forFeature([User, INode, JWTToken])],
 | 
				
			||||||
@@ -17,6 +18,6 @@ import FileSystemService from '../services/filesystem';
 | 
				
			|||||||
		AuthJwtService,
 | 
							AuthJwtService,
 | 
				
			||||||
		FileSystemService
 | 
							FileSystemService
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
	controllers: [AuthController]
 | 
						controllers: [AuthController, UserController]
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export default class AuthModule {}
 | 
					export default class AuthModule {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
	BadRequestException,
 | 
						BadRequestException,
 | 
				
			||||||
 | 
						ForbiddenException,
 | 
				
			||||||
	Injectable,
 | 
						Injectable,
 | 
				
			||||||
	UnauthorizedException
 | 
						UnauthorizedException
 | 
				
			||||||
} from '@nestjs/common';
 | 
					} from '@nestjs/common';
 | 
				
			||||||
@@ -312,6 +313,23 @@ export class AuthService {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return info && info.username == user.name;
 | 
							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()
 | 
					@Injectable()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,12 +51,12 @@ export default class FileSystemService {
 | 
				
			|||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async delete(node: INode): Promise<void> {
 | 
						async delete(node: INode, force = false): Promise<void> {
 | 
				
			||||||
		if (node.parentId == null)
 | 
							if (node.parentId == null || force)
 | 
				
			||||||
			throw new BadRequestException("Can't delete root");
 | 
								throw new BadRequestException("Can't delete root");
 | 
				
			||||||
		if (!node.isFile)
 | 
							if (!node.isFile)
 | 
				
			||||||
			await Promise.all(
 | 
								await Promise.all(
 | 
				
			||||||
				(await node.children).map((child) => this.delete(child))
 | 
									(await node.children).map((child) => this.delete(child, force))
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			unlink(`files/${node.id}`, (err) => {
 | 
								unlink(`files/${node.id}`, (err) => {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user