From bdacb9bdb05c3e8e3d06642e4529969ef380b79a Mon Sep 17 00:00:00 2001 From: Lemonade255 Date: Wed, 25 Oct 2023 18:21:29 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(BE):=20=EC=82=AC=EC=9A=A9=EC=9E=90/?= =?UTF-8?q?=EB=A7=A4=EC=B9=AD=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B6=8C=ED=95=9C=20=EC=A0=9C=EC=96=B4=20BUS-203-A?= =?UTF-8?q?uthority-check=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/rest/MatchingController.java | 35 ++++++++++++++++-- .../user/adapter/in/rest/UserController.java | 9 +++++ .../out/persistence/UserMapperInterface.java | 2 ++ .../application/port/in/FindUserUsecase.java | 2 ++ .../com/example/api/user/dto/FindUserDto.java | 1 - .../api/user/dto/UserAuthorityCheckDto.java | 26 ++++++++++++++ .../example/api/user/service/UserService.java | 36 +++++++++++++------ 7 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/example/api/user/dto/UserAuthorityCheckDto.java diff --git a/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java b/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java index 299b3a8..13ed713 100644 --- a/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java +++ b/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java @@ -1,12 +1,17 @@ package com.example.api.matching.adapter.in.rest; +import com.example.api.auth.domain.SecurityUser; import com.example.api.chatroom.domain.ChatRoom; import com.example.api.common.type.ApplicationStateEnum; +import com.example.api.common.utils.AuthenticationUtils; import com.example.api.matching.adapter.out.persistence.MatchingApplicationPK; import com.example.api.matching.application.port.in.*; import com.example.api.matching.domain.MatchingApplication; import com.example.api.matching.dto.*; +import com.example.api.user.application.port.in.FindUserUsecase; import com.example.api.user.dto.FindUserDto; +import com.example.api.user.dto.UserAuthorityCheckDto; +import com.example.api.user.type.UserRoleEnum; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -23,6 +28,7 @@ @Slf4j @Tag(name = "Matching", description = "Matching API") public class MatchingController { + private final FindUserUsecase findUserUsecase; private final SaveMatchingUsecase saveMatchingUsecase; private final FindMatchingUsecase findMatchingUsecase; private final DeleteMatchingUsecase deleteMatchingUsecase; @@ -48,10 +54,22 @@ public FindMatchingDto createMatching(@RequestBody SaveMatchingDto matchingDto) @Operation(summary = "Create matching application", description = "새로운 매칭 신청을 생성한다.") @PostMapping("/matching/application") public ChatRoom createMatchingApplication(@RequestBody SaveMatchingApplicationDto matchingApplicationDto) { - if (findMatchingUsecase.getMatchingById(matchingApplicationDto.getMatchingId()).isEmpty()) { - log.error("MatchingController::createMatchingApplication: no such matching"); + Optional matchingDto = findMatchingUsecase.getMatchingById(matchingApplicationDto.getMatchingId()); + if (matchingDto.isEmpty()) { + log.error("MatchingController::createMatchingApplication: No such matching."); return ChatRoom.builder().build(); } + + SecurityUser securityUser = AuthenticationUtils.getCurrentUserAuthentication(); + if (securityUser == null) { + log.error("MatchingController::createMatchingApplication: Authentication is needed."); + return ChatRoom.builder().build(); + } + if (securityUser.getUserId().equals(matchingApplicationDto.getUserId())) { + log.error("MatchingController::createMatchingApplication: WriterId equals to applicantId."); + return ChatRoom.builder().build(); + } + MatchingApplication matchingApplication = matchingApplicationUsecase.createMatchingApplicationData(matchingApplicationDto); ChatRoom chatRoom = matchingApplicationUsecase.createMatchingChatRoom(matchingApplication); return matchingApplicationUsecase.setupMatchingChatRoom(matchingApplication, chatRoom); @@ -166,6 +184,10 @@ public void processMatchingApplication(SaveMatchingApplicationDto matchingApplic @Operation(summary = "Delete all matching", description = "모든 매칭을 삭제한다.") @DeleteMapping("/matching") public void deleteAll() { + if (!(findUserUsecase.getUser().getRole().equals(UserRoleEnum.Admin))) { + log.error("MatchingController::deleteAll: Admin authority is needed."); + return; + } deleteMatchingUsecase.deleteAll(); } @@ -176,6 +198,15 @@ public void deleteAll() { @Operation(summary = "Delete matching", description = "ID가 matchingId인 매칭을 삭제한다.") @DeleteMapping("/matching/{matchingId}") public void deleteMatching(@PathVariable Long matchingId) { + UserAuthorityCheckDto userDto = findUserUsecase.getAuthorityUser(); + Optional matchingDto = findMatchingUsecase.getMatchingById(matchingId); + if (matchingDto.isEmpty()) { + log.error("MatchingController::deleteMatching: No such matching."); + return; + } + if (!(userDto.getRole().equals(UserRoleEnum.Admin)) && !(userDto.getUserId().equals(matchingDto.get().getWriterId()))) { + log.error("MatchingController::deleteMatching: Admin or owner authority is needed."); + } deleteMatchingUsecase.deleteMatching(matchingId); } } \ No newline at end of file diff --git a/src/main/java/com/example/api/user/adapter/in/rest/UserController.java b/src/main/java/com/example/api/user/adapter/in/rest/UserController.java index dc6af2b..2b8471b 100644 --- a/src/main/java/com/example/api/user/adapter/in/rest/UserController.java +++ b/src/main/java/com/example/api/user/adapter/in/rest/UserController.java @@ -8,11 +8,13 @@ import com.example.api.user.dto.CreateUserDto; import com.example.api.user.dto.FindUserDto; import com.example.api.user.dto.UpdateUserDto; +import com.example.api.user.type.UserRoleEnum; import com.example.api.user.validator.CreateGenderValidator; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.WebDataBinder; @@ -25,6 +27,7 @@ @RequiredArgsConstructor @EnableWebMvc @Validated +@Slf4j @Tag(name = "User", description = "User API") public class UserController { private final SaveUserUsecase saveUserUsecase; @@ -126,6 +129,9 @@ public FindUserDto updateUser(@RequestBody UpdateUserDto userDto) { @Operation(summary = "Delete all users", description = "모든 사용자를 삭제한다.") @DeleteMapping("/user/all") public void deleteAll() { + if (!(findUserUsecase.getUser().getRole().equals(UserRoleEnum.Admin))) { + log.error("UserController::deleteAll: Admin authority is needed."); + } deleteUserUsecase.deleteAll(); } @@ -135,6 +141,9 @@ public void deleteAll() { @Operation(summary = "Delete user", description = "ID가 userId인 사용자를 삭제한다.") @DeleteMapping("/user") public void deleteUser() { + if (!(findUserUsecase.getUser().getRole().equals(UserRoleEnum.Admin))) { + log.error("UserController::deleteUser: Admin authority is needed."); + } deleteUserUsecase.deleteUser(); } } \ No newline at end of file diff --git a/src/main/java/com/example/api/user/adapter/out/persistence/UserMapperInterface.java b/src/main/java/com/example/api/user/adapter/out/persistence/UserMapperInterface.java index 4047d7b..d273a96 100644 --- a/src/main/java/com/example/api/user/adapter/out/persistence/UserMapperInterface.java +++ b/src/main/java/com/example/api/user/adapter/out/persistence/UserMapperInterface.java @@ -5,6 +5,7 @@ import com.example.api.user.dto.CreateUserDto; import com.example.api.user.dto.FindUserDto; import com.example.api.user.dto.UpdateUserDto; +import com.example.api.user.dto.UserAuthorityCheckDto; import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -23,5 +24,6 @@ public interface UserMapperInterface { User toDomain(UserEntity userEntity); FindUserDto toDto(UserEntity userEntity); FindUserDto toDto(User user); + UserAuthorityCheckDto toAuthorityDto(UserEntity userEntity); FindUserDto toDto(UpdateUserDto updateUserDto); } \ No newline at end of file diff --git a/src/main/java/com/example/api/user/application/port/in/FindUserUsecase.java b/src/main/java/com/example/api/user/application/port/in/FindUserUsecase.java index 23569c4..bd8ed81 100644 --- a/src/main/java/com/example/api/user/application/port/in/FindUserUsecase.java +++ b/src/main/java/com/example/api/user/application/port/in/FindUserUsecase.java @@ -1,10 +1,12 @@ package com.example.api.user.application.port.in; import com.example.api.user.dto.FindUserDto; +import com.example.api.user.dto.UserAuthorityCheckDto; import java.util.List; public interface FindUserUsecase { List getAll(); FindUserDto getUser(); + UserAuthorityCheckDto getAuthorityUser(); } \ No newline at end of file diff --git a/src/main/java/com/example/api/user/dto/FindUserDto.java b/src/main/java/com/example/api/user/dto/FindUserDto.java index e0090f8..f367476 100644 --- a/src/main/java/com/example/api/user/dto/FindUserDto.java +++ b/src/main/java/com/example/api/user/dto/FindUserDto.java @@ -8,7 +8,6 @@ import java.time.LocalDateTime; @Getter -@Setter @Builder @ToString @NoArgsConstructor diff --git a/src/main/java/com/example/api/user/dto/UserAuthorityCheckDto.java b/src/main/java/com/example/api/user/dto/UserAuthorityCheckDto.java new file mode 100644 index 0000000..56fd519 --- /dev/null +++ b/src/main/java/com/example/api/user/dto/UserAuthorityCheckDto.java @@ -0,0 +1,26 @@ +package com.example.api.user.dto; + +import com.example.api.user.type.UserRoleEnum; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +import java.util.UUID; + +@Getter +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class UserAuthorityCheckDto { + @NotNull + private UUID userId; + + @NotNull + private UserRoleEnum role; + + @NotNull + private Boolean blacklist; + + @NotNull + private Boolean isActive; +} \ No newline at end of file diff --git a/src/main/java/com/example/api/user/service/UserService.java b/src/main/java/com/example/api/user/service/UserService.java index d3ff48a..28981ea 100644 --- a/src/main/java/com/example/api/user/service/UserService.java +++ b/src/main/java/com/example/api/user/service/UserService.java @@ -19,6 +19,7 @@ import com.example.api.user.application.port.in.SaveUserUsecase; import com.example.api.user.dto.CreateUserDto; import com.example.api.user.dto.UpdateUserDto; +import com.example.api.user.dto.UserAuthorityCheckDto; import com.example.api.user.type.UserGenderEnum; import com.example.api.user.type.UserRoleEnum; import lombok.RequiredArgsConstructor; @@ -60,6 +61,15 @@ public FindUserDto getDefaultUser() { .isActive(false) .build(); } + + public UserAuthorityCheckDto getDefaultAuthorityUser() { + return UserAuthorityCheckDto.builder() + .userId(UUID.fromString("00000000-0000-0000-0000-000000000000")) + .role(UserRoleEnum.User) + .blacklist(false) + .isActive(false) + .build(); + } @Override @Transactional @@ -84,12 +94,23 @@ public FindUserDto getUser() { log.error("UserService::getUser: Authentication is needed"); return this.getDefaultUser(); } - log.info("UserID: {}", securityUser.getUserId()); return findUserPort.getByUserId(securityUser.getUserId()) .map(userMapper::toDto) .orElse(this.getDefaultUser()); } + @Override + public UserAuthorityCheckDto getAuthorityUser() { + SecurityUser securityUser = AuthenticationUtils.getCurrentUserAuthentication(); + if (securityUser == null) { + log.error("UserService::getAuthorityUser: Authentication is needed"); + return this.getDefaultAuthorityUser(); + } + return findUserPort.getByUserId(securityUser.getUserId()) + .map(userMapper::toAuthorityDto) + .orElse(this.getDefaultAuthorityUser()); + } + @Override @Transactional public FindUserDto updateUser(UpdateUserDto userDto) { @@ -112,22 +133,17 @@ public void deleteAll() { @Transactional public void deleteUser() { SecurityUser securityUser = AuthenticationUtils.getCurrentUserAuthentication(); - try { - if (securityUser == null) { - log.error("UserService::deleteUser: Authentication is needed."); - throw new Exception(); - } - deleteUserPort.deleteByUserId(securityUser.getUserId()); - } catch (Exception e) { - e.printStackTrace(); + if (securityUser == null) { + log.error("UserService::deleteUser: Authentication is needed."); + return; } + deleteUserPort.deleteByUserId(securityUser.getUserId()); } // Social public SecurityUser findSocialUser(String id, String provider) { User user = userMapper.toDomain(findUserPort.findSocialUser(id, provider).orElseThrow(IllegalStateException::new)); - return SecurityUser.builder() .userId(user.getUserId()) .naverId(user.getSocialId().getNaverId()) From f829608ebcfdd2e3196bf3c051e9b78205be0e7c Mon Sep 17 00:00:00 2001 From: Lemonade255 Date: Wed, 25 Oct 2023 19:41:22 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat(BE):=20Custom=20exception=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20BUS-203-Authority-check=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/CustomExceptionHandler.java | 4 ++-- .../api/common/type/ErrorCodeEnum.java | 20 ++++++++++++++----- .../adapter/in/rest/MatchingController.java | 13 +++++++----- .../user/adapter/in/rest/UserController.java | 4 ++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/api/common/handler/CustomExceptionHandler.java b/src/main/java/com/example/api/common/handler/CustomExceptionHandler.java index 31f3d93..267d3ec 100644 --- a/src/main/java/com/example/api/common/handler/CustomExceptionHandler.java +++ b/src/main/java/com/example/api/common/handler/CustomExceptionHandler.java @@ -21,7 +21,7 @@ public ResponseEntity exceptionHandler(CustomException e) { return ResponseEntity.status(e.getErrorCodeEnum().getHttpStatus()) .body(new ExceptionDto(e.getErrorCodeEnum())); } - + @ExceptionHandler(value = MethodArgumentNotValidException.class) public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); @@ -32,7 +32,7 @@ public ResponseEntity constraintViolationException(ConstraintViolationExcepti log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); } - + @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String runtimeExceptionHandler(RuntimeException e) { diff --git a/src/main/java/com/example/api/common/type/ErrorCodeEnum.java b/src/main/java/com/example/api/common/type/ErrorCodeEnum.java index 2cee961..4a10c50 100644 --- a/src/main/java/com/example/api/common/type/ErrorCodeEnum.java +++ b/src/main/java/com/example/api/common/type/ErrorCodeEnum.java @@ -7,12 +7,22 @@ @Getter @AllArgsConstructor public enum ErrorCodeEnum { - LOGIN_IS_NOT_DONE(HttpStatus.UNAUTHORIZED, "로그인 정보가 없습니다"), - INVALID_PERMISSION(HttpStatus.UNAUTHORIZED, "권한이 없습니다"), - DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "데이터베이스 오류"), - CODE_IS_NOT_VALID(HttpStatus.BAD_REQUEST, "잘못된 인증번호입니다"), + // 200 OK + SUCCESS(HttpStatus.OK, "정상 처리되었습니다"), + // 201 Created + CREATED(HttpStatus.CREATED, "생성되었습니다"), + // 400 Bad Request USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "유저 정보가 없습니다"), - CODE_IS_EXPIRED(HttpStatus.BAD_REQUEST, "휴대전화를 인증해주세요"); + CODE_IS_EXPIRED(HttpStatus.BAD_REQUEST, "휴대전화를 인증해주세요"), + CODE_IS_NOT_VALID(HttpStatus.BAD_REQUEST, "잘못된 인증번호입니다"), + MATCHING_NOT_FOUND(HttpStatus.BAD_REQUEST, "매칭 정보가 없습니다"), + // 401 Unauthorized + LOGIN_IS_NOT_DONE(HttpStatus.UNAUTHORIZED, "로그인 정보가 없습니다"), + // 403 Forbidden + INVALID_PERMISSION(HttpStatus.FORBIDDEN, "권한이 없습니다"), + // 500 Internal Server Error + DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "데이터베이스 오류"); + private final HttpStatus httpStatus; private final String message; } \ No newline at end of file diff --git a/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java b/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java index 13ed713..c2542d4 100644 --- a/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java +++ b/src/main/java/com/example/api/matching/adapter/in/rest/MatchingController.java @@ -2,7 +2,9 @@ import com.example.api.auth.domain.SecurityUser; import com.example.api.chatroom.domain.ChatRoom; +import com.example.api.common.exception.CustomException; import com.example.api.common.type.ApplicationStateEnum; +import com.example.api.common.type.ErrorCodeEnum; import com.example.api.common.utils.AuthenticationUtils; import com.example.api.matching.adapter.out.persistence.MatchingApplicationPK; import com.example.api.matching.application.port.in.*; @@ -57,17 +59,17 @@ public ChatRoom createMatchingApplication(@RequestBody SaveMatchingApplicationDt Optional matchingDto = findMatchingUsecase.getMatchingById(matchingApplicationDto.getMatchingId()); if (matchingDto.isEmpty()) { log.error("MatchingController::createMatchingApplication: No such matching."); - return ChatRoom.builder().build(); + throw new CustomException(ErrorCodeEnum.MATCHING_NOT_FOUND); } SecurityUser securityUser = AuthenticationUtils.getCurrentUserAuthentication(); if (securityUser == null) { log.error("MatchingController::createMatchingApplication: Authentication is needed."); - return ChatRoom.builder().build(); + throw new CustomException(ErrorCodeEnum.LOGIN_IS_NOT_DONE); } if (securityUser.getUserId().equals(matchingApplicationDto.getUserId())) { log.error("MatchingController::createMatchingApplication: WriterId equals to applicantId."); - return ChatRoom.builder().build(); + throw new CustomException(ErrorCodeEnum.INVALID_PERMISSION); } MatchingApplication matchingApplication = matchingApplicationUsecase.createMatchingApplicationData(matchingApplicationDto); @@ -186,7 +188,7 @@ public void processMatchingApplication(SaveMatchingApplicationDto matchingApplic public void deleteAll() { if (!(findUserUsecase.getUser().getRole().equals(UserRoleEnum.Admin))) { log.error("MatchingController::deleteAll: Admin authority is needed."); - return; + throw new CustomException(ErrorCodeEnum.INVALID_PERMISSION); } deleteMatchingUsecase.deleteAll(); } @@ -202,10 +204,11 @@ public void deleteMatching(@PathVariable Long matchingId) { Optional matchingDto = findMatchingUsecase.getMatchingById(matchingId); if (matchingDto.isEmpty()) { log.error("MatchingController::deleteMatching: No such matching."); - return; + throw new CustomException(ErrorCodeEnum.MATCHING_NOT_FOUND); } if (!(userDto.getRole().equals(UserRoleEnum.Admin)) && !(userDto.getUserId().equals(matchingDto.get().getWriterId()))) { log.error("MatchingController::deleteMatching: Admin or owner authority is needed."); + throw new CustomException(ErrorCodeEnum.INVALID_PERMISSION); } deleteMatchingUsecase.deleteMatching(matchingId); } diff --git a/src/main/java/com/example/api/user/adapter/in/rest/UserController.java b/src/main/java/com/example/api/user/adapter/in/rest/UserController.java index 2b8471b..07bc7ce 100644 --- a/src/main/java/com/example/api/user/adapter/in/rest/UserController.java +++ b/src/main/java/com/example/api/user/adapter/in/rest/UserController.java @@ -1,6 +1,8 @@ package com.example.api.user.adapter.in.rest; +import com.example.api.common.exception.CustomException; import com.example.api.common.type.ApplicationStateEnum; +import com.example.api.common.type.ErrorCodeEnum; import com.example.api.matching.application.port.in.FindMatchingUsecase; import com.example.api.matching.application.port.in.MatchingApplicationUsecase; import com.example.api.matching.dto.FindMatchingDto; @@ -131,6 +133,7 @@ public FindUserDto updateUser(@RequestBody UpdateUserDto userDto) { public void deleteAll() { if (!(findUserUsecase.getUser().getRole().equals(UserRoleEnum.Admin))) { log.error("UserController::deleteAll: Admin authority is needed."); + throw new CustomException(ErrorCodeEnum.INVALID_PERMISSION); } deleteUserUsecase.deleteAll(); } @@ -143,6 +146,7 @@ public void deleteAll() { public void deleteUser() { if (!(findUserUsecase.getUser().getRole().equals(UserRoleEnum.Admin))) { log.error("UserController::deleteUser: Admin authority is needed."); + throw new CustomException(ErrorCodeEnum.INVALID_PERMISSION); } deleteUserUsecase.deleteUser(); }