diff --git a/starter/studio-platform-starter-user/README.md b/starter/studio-platform-starter-user/README.md index 7bf2ca5f..ae4c2e35 100644 --- a/starter/studio-platform-starter-user/README.md +++ b/starter/studio-platform-starter-user/README.md @@ -140,6 +140,9 @@ studio: - `/api/mgmt/roles` - `/api/self` +기본 사용자 관리 API에는 `DELETE /api/mgmt/users/{id}`가 포함된다. 이 엔드포인트는 +`features:user` admin 권한을 요구하며, 성공 시 `204 No Content`를 반환한다. + 기본 컨트롤러는 `ApplicationUserMapper`/`ApplicationUserService` 빈이 있을 때만 등록된다. 커스텀 컨트롤러를 제공할 때는 `UserMgmtApi`/`UserPublicApi`/`UserAuthPublicApi`/`UserMeApi` 인터페이스를 구현하면 기본 컨트롤러가 자동으로 비활성화된다. diff --git a/studio-platform-user-default/src/main/java/studio/one/base/user/service/impl/ApplicationUserServiceImpl.java b/studio-platform-user-default/src/main/java/studio/one/base/user/service/impl/ApplicationUserServiceImpl.java index 5ad4803a..bac4e43c 100644 --- a/studio-platform-user-default/src/main/java/studio/one/base/user/service/impl/ApplicationUserServiceImpl.java +++ b/studio-platform-user-default/src/main/java/studio/one/base/user/service/impl/ApplicationUserServiceImpl.java @@ -175,6 +175,10 @@ public ApplicationUser update(Long userId, Consumer mutator) { } @Transactional + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.User.BY_USER_ID, key = "#userId"), + @CacheEvict(cacheNames = CacheNames.User.BY_USERNAME, allEntries = true) + }) public void delete(Long userId) { ApplicationUser u = get(userId); userRepo.delete(u); diff --git a/studio-platform-user-default/src/main/java/studio/one/base/user/web/controller/UserMgmtController.java b/studio-platform-user-default/src/main/java/studio/one/base/user/web/controller/UserMgmtController.java index 0a259fbb..553cd812 100644 --- a/studio-platform-user-default/src/main/java/studio/one/base/user/web/controller/UserMgmtController.java +++ b/studio-platform-user-default/src/main/java/studio/one/base/user/web/controller/UserMgmtController.java @@ -176,6 +176,14 @@ public ResponseEntity> update(@PathVariable Long id, @Reque return ResponseEntity.ok(ApiResponse.ok(userMapper.toDto(updated))); } + @DeleteMapping("/{id}") + @PreAuthorize("@endpointAuthz.can('features:user','admin')") + @Override + public ResponseEntity delete(@PathVariable Long id) { + userService.delete(id); + return ResponseEntity.noContent().build(); + } + @GetMapping("/password-policy") @PreAuthorize("@endpointAuthz.can('features:user','admin')") @Override diff --git a/studio-platform-user-default/src/test/java/studio/one/base/user/web/controller/UserMgmtControllerAuthorizationTest.java b/studio-platform-user-default/src/test/java/studio/one/base/user/web/controller/UserMgmtControllerAuthorizationTest.java index 0ea0d192..72db2b81 100644 --- a/studio-platform-user-default/src/test/java/studio/one/base/user/web/controller/UserMgmtControllerAuthorizationTest.java +++ b/studio-platform-user-default/src/test/java/studio/one/base/user/web/controller/UserMgmtControllerAuthorizationTest.java @@ -86,6 +86,16 @@ void passwordResetDelegatesActorUsername() { verify(userService).resetPassword(eq(10L), eq("NextPassword123!"), eq("admin"), eq("test-reason")); } + @Test + void deleteDelegatesToUserService() { + UserMgmtController controller = controller(); + + var response = controller.delete(10L); + + assertEquals(204, response.getStatusCode().value()); + verify(userService).delete(10L); + } + @Test void updateUserRolesDelegatesDistinctRoleIds() { UserMgmtController controller = controller(); diff --git a/studio-platform-user/README.md b/studio-platform-user/README.md index a8fc0823..4ecc65c6 100644 --- a/studio-platform-user/README.md +++ b/studio-platform-user/README.md @@ -24,6 +24,7 @@ Response: `ApiResponse` Password policy is defined by configuration and enforced on: - **Self password change**: `PUT /api/self/password` - **Admin reset**: `POST /api/mgmt/users/{id}/password` +- **Admin delete**: `DELETE /api/mgmt/users/{id}` removes the user through the configured `ApplicationUserService` implementation. ### Policy Config (YAML) ```yaml @@ -117,6 +118,13 @@ Returns current password policy configuration for admin UI. Response: `ApiResponse` +### DELETE /api/mgmt/users/{id} +Deletes a user from the admin API. + +Auth: `features:user` admin permission + +Response: `204 No Content` + ## Example Requests ### PATCH diff --git a/studio-platform-user/src/main/java/studio/one/base/user/web/controller/UserMgmtApi.java b/studio-platform-user/src/main/java/studio/one/base/user/web/controller/UserMgmtApi.java index 95706b42..1f806f14 100644 --- a/studio-platform-user/src/main/java/studio/one/base/user/web/controller/UserMgmtApi.java +++ b/studio-platform-user/src/main/java/studio/one/base/user/web/controller/UserMgmtApi.java @@ -50,6 +50,10 @@ ResponseEntity>> find( ResponseEntity> update(Long id, @RequestBody UpdateUserRequest req); + default ResponseEntity delete(Long id) { + throw new UnsupportedOperationException("User delete endpoint is not implemented"); + } + ResponseEntity> passwordPolicy(); ResponseEntity passwordReset(Long id, @Valid @RequestBody ChangePasswordRequest req,