diff --git a/apps/backend/apps/client/src/user/dto/updateUser.dto.ts b/apps/backend/apps/client/src/user/dto/updateUser.dto.ts index aabe5f795b..5b78e87bb3 100644 --- a/apps/backend/apps/client/src/user/dto/updateUser.dto.ts +++ b/apps/backend/apps/client/src/user/dto/updateUser.dto.ts @@ -1,8 +1,9 @@ import { IsOptional, IsString, IsNumberString, Matches } from 'class-validator' export class UpdateUserDto { + @IsOptional() @IsString() - readonly password: string + readonly password?: string @IsOptional() @IsString() diff --git a/apps/backend/apps/client/src/user/user.controller.ts b/apps/backend/apps/client/src/user/user.controller.ts index 68f75ab13b..c4a63e2e40 100644 --- a/apps/backend/apps/client/src/user/user.controller.ts +++ b/apps/backend/apps/client/src/user/user.controller.ts @@ -8,10 +8,18 @@ import { Controller, Logger, Delete, - Query + Query, + NotFoundException, + InternalServerErrorException } from '@nestjs/common' +import { Prisma } from '@prisma/client' import { Request, type Response } from 'express' import { AuthenticatedRequest, AuthNotNeededIfOpenSpace } from '@libs/auth' +import { + EntityNotExistException, + UnidentifiedException, + UnprocessableDataException +} from '@libs/exception' import { DeleteUserDto } from './dto/deleteUser.dto' import { EmailAuthenticationPinDto } from './dto/email-auth-pin.dto' import { NewPasswordDto } from './dto/newPassword.dto' @@ -88,7 +96,24 @@ export class UserController { @Req() req: AuthenticatedRequest, @Body() updateUserDto: UpdateUserDto ) { - return await this.userService.updateUser(req, updateUserDto) + try { + return await this.userService.updateUser(req, updateUserDto) + } catch (error) { + if ( + error instanceof Prisma.PrismaClientKnownRequestError && + error.name == 'NotFoundError' + ) { + throw new NotFoundException(error.message) + } else if ( + error instanceof EntityNotExistException || + error instanceof UnprocessableDataException || + error instanceof UnidentifiedException + ) { + throw error.convert2HTTPException() + } + this.logger.error(error) + throw new InternalServerErrorException() + } } } diff --git a/apps/backend/apps/client/src/user/user.service.ts b/apps/backend/apps/client/src/user/user.service.ts index 58e8bf273e..3e1485b828 100644 --- a/apps/backend/apps/client/src/user/user.service.ts +++ b/apps/backend/apps/client/src/user/user.service.ts @@ -559,24 +559,29 @@ export class UserService { // update user field (password, studentId, major, realName) async updateUser(req: AuthenticatedRequest, updateUserDto: UpdateUserDto) { - const user = await this.getUserCredential(req.user.username) - if (!user) { - throw new EntityNotExistException('User') - } - - const isValidUser = await this.jwtAuthService.isValidUser( - user, - updateUserDto.password - ) - if (!isValidUser) { - throw new UnidentifiedException('password') - } - let encryptedNewPassword: string | undefined = undefined if (updateUserDto.newPassword) { + if (!updateUserDto.password) { + throw new UnprocessableDataException( + 'current password needed to change password' + ) + } + const user = await this.getUserCredential(req.user.username) + if (!user) { + throw new EntityNotExistException('User') + } + + const isValidUser = await this.jwtAuthService.isValidUser( + user, + updateUserDto.password + ) + if (!isValidUser) { + throw new UnidentifiedException('current password') + } + if (!this.isValidPassword(updateUserDto.newPassword)) { - throw new UnprocessableDataException('Bad password') + throw new UnprocessableDataException('Bad new password') } encryptedNewPassword = await hash( updateUserDto.newPassword, diff --git a/collection/client/User/Update User/Optionally update.bru b/collection/client/User/Update User/Optionally update.bru new file mode 100644 index 0000000000..018f3f81c0 --- /dev/null +++ b/collection/client/User/Update User/Optionally update.bru @@ -0,0 +1,39 @@ +meta { + name: Optionally Update User + type: http + seq: 1 +} + +patch { + url: {{baseUrl}}/user + body: json + auth: none +} + +body:json { + { + "password": "Useruser", + "newPassword": "Useruser", + // Don't change password in stage server please!! + "realName": "changedName", + "major": "changedMajor" + } +} + +script:pre-request { + await require("./login").loginUser(req); +} + +script:post-response { + // TODO: User Fields rollback +} + +docs { + # user 정보 수정 + - realname, studentId, major, 비밀번호를 선택적으로 수정할 수 있습니다. + 비밀번호를 변경하려면 기존 password(=password 필드)와 new password(=newPassword 필드)를 같이 입력으로 주어야 하며, + 이때 로그인한 유저의 DB정보를 통해 기존 password를 맞게 썼는지를 검사합니다. + - realname, studentId, major는 변경하려는 값으로만 입력해주시면 됩니다. + 비밀번호를 변경하지 않을 경우 기존 password를 입력하지 않아야 합니다. + - 변경하려는 값이 없는 경우에는 해당 필드를 input에 넣지 않아야 합니다. +} diff --git a/collection/client/User/Update User/Succeed.bru b/collection/client/User/Update User/Succeed.bru deleted file mode 100644 index b4910174ae..0000000000 --- a/collection/client/User/Update User/Succeed.bru +++ /dev/null @@ -1,33 +0,0 @@ -meta { - name: Succeed - type: http - seq: 1 -} - -patch { - url: {{baseUrl}}/user - body: json - auth: none -} - -body:json { - { - "password": "Useruser", - "newPassword": "Useruser", - // Don't change password in stage server please!! - "realName": "changedName" - } -} - -script:pre-request { - await require("./login").loginUser(req); -} - -script:post-response { - // TODO: User Fields rollback -} - -docs { - # user 정보 수정 - - realname, studentId, major, password를 선택적으로 수정할 수 있으며, 이를 수행하기 전 로그인한 유저의 password를 검사하여 올바른 request인지 validation합니다. -} diff --git a/collection/client/User/Update User/Update All Fields of User.bru b/collection/client/User/Update User/Update All Fields of User.bru new file mode 100644 index 0000000000..5dffb30fcf --- /dev/null +++ b/collection/client/User/Update User/Update All Fields of User.bru @@ -0,0 +1,39 @@ +meta { + name: Update All Fields of User + type: http + seq: 1 +} + +patch { + url: {{baseUrl}}/user + body: json + auth: none +} + +body:json { + { + "password": "Useruser", + "newPassword": "Useruser", + // Don't change password in stage server please!! + "realName": "changedName", + "major": "changedMajor" + } +} + +script:pre-request { + await require("./login").loginUser(req); +} + +script:post-response { + // TODO: User Fields rollback +} + +docs { + # user 정보 수정 + - realname, studentId, major, 비밀번호를 선택적으로 수정할 수 있습니다. + 비밀번호를 변경하려면 기존 password(=password 필드)와 new password(=newPassword 필드)를 같이 입력으로 주어야 하며, + 이때 로그인한 유저의 DB정보를 통해 기존 password를 맞게 썼는지를 검사합니다. + - realname, studentId, major는 변경하려는 값으로만 입력해주시면 됩니다. + 비밀번호를 변경하지 않을 경우 기존 password를 입력하지 않아야 합니다. + - 변경하려는 값이 없는 경우에는 해당 필드를 input에 넣지 않아야 합니다. +} diff --git a/collection/client/User/Update User/[401] Incorrect current password.bru b/collection/client/User/Update User/[401] Incorrect current password.bru new file mode 100644 index 0000000000..64ace95cbf --- /dev/null +++ b/collection/client/User/Update User/[401] Incorrect current password.bru @@ -0,0 +1,39 @@ +meta { + name: [401] Incorrect current password + type: http + seq: 1 +} + +patch { + url: {{baseUrl}}/user + body: json + auth: none +} + +body:json { + { + "password": "wrongpass", + "newPassword": "Useruser", + // Don't change password in stage server please!! + "realName": "changedName", + "major": "changedMajor" + } +} + +script:pre-request { + await require("./login").loginUser(req); +} + +script:post-response { + // TODO: User Fields rollback +} + +docs { + # user 정보 수정 + - realname, studentId, major, 비밀번호를 선택적으로 수정할 수 있습니다. + 비밀번호를 변경하려면 기존 password(=password 필드)와 new password(=newPassword 필드)를 같이 입력으로 주어야 하며, + 이때 로그인한 유저의 DB정보를 통해 기존 password를 맞게 썼는지를 검사합니다. + - realname, studentId, major는 변경하려는 값으로만 입력해주시면 됩니다. + 비밀번호를 변경하지 않을 경우 기존 password를 입력하지 않아야 합니다. + - 변경하려는 값이 없는 경우에는 해당 필드를 input에 넣지 않아야 합니다. +} diff --git a/collection/client/User/Update User/[422] current password needed to change password.bru b/collection/client/User/Update User/[422] current password needed to change password.bru new file mode 100644 index 0000000000..c5eef2df12 --- /dev/null +++ b/collection/client/User/Update User/[422] current password needed to change password.bru @@ -0,0 +1,38 @@ +meta { + name: [422] current password needed to change password + type: http + seq: 1 +} + +patch { + url: {{baseUrl}}/user + body: json + auth: none +} + +body:json { + { + "newPassword": "Useruser", + // Don't change password in stage server please!! + "realName": "changedName", + "major": "changedMajor" + } +} + +script:pre-request { + await require("./login").loginUser(req); +} + +script:post-response { + // TODO: User Fields rollback +} + +docs { + # user 정보 수정 + - realname, studentId, major, 비밀번호를 선택적으로 수정할 수 있습니다. + 비밀번호를 변경하려면 기존 password(=password 필드)와 new password(=newPassword 필드)를 같이 입력으로 주어야 하며, + 이때 로그인한 유저의 DB정보를 통해 기존 password를 맞게 썼는지를 검사합니다. + - realname, studentId, major는 변경하려는 값으로만 입력해주시면 됩니다. + 비밀번호를 변경하지 않을 경우 기존 password를 입력하지 않아야 합니다. + - 변경하려는 값이 없는 경우에는 해당 필드를 input에 넣지 않아야 합니다. +}