diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/UserResource.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/UserResource.java index 68b3d33..5639074 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/UserResource.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/UserResource.java @@ -120,7 +120,8 @@ public interface UserResource { @ResponseBody @Operation(summary = "Activate/deactivate a user profile", description = "None") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to activate/deactivate profile")}) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to activate/deactivate profile"), + @ApiResponse(responseCode = "403", description = "Cannot activate/deactivate users with higher rank roles")}) @PostMapping(value = ACTIVATE_USER_PROFILE_PATH + USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE) User activateProfile(@PathVariable(USER_ID) String userId, @RequestParam(IS_ACTIVE_PARAM) boolean isActive); diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java index ea95832..e891839 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java @@ -438,6 +438,12 @@ public class UserService { public User activateProfile(String userId, boolean isActive) { + var executionValid = isExecutionRankValid(KeycloakSecurity.getUserId(), userId); + + if (!executionValid) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, "It is not allowed to activate/deactivate a user with higher ranking roles"); + } + var user = this.getUserResource(userId); var userRepresentation = user.toRepresentation(); @@ -445,6 +451,7 @@ public class UserService { user.update(userRepresentation); var currentRoles = getRoles(userId); + if (isActive && currentRoles.isEmpty()) { // add RED_USER role setRoles(userId, tenantUserManagementProperties.getKcRoleMapping().getDefaultRoles()); } diff --git a/src/test/java/com/knecon/fforesight/tenantusermanagement/tests/UserTest.java b/src/test/java/com/knecon/fforesight/tenantusermanagement/tests/UserTest.java index e7349b3..4b4d250 100644 --- a/src/test/java/com/knecon/fforesight/tenantusermanagement/tests/UserTest.java +++ b/src/test/java/com/knecon/fforesight/tenantusermanagement/tests/UserTest.java @@ -3,27 +3,29 @@ package com.knecon.fforesight.tenantusermanagement.tests; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.springframework.beans.factory.annotation.Autowired; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; -import lombok.SneakyThrows; -import feign.FeignException; - +import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantusermanagement.AbstractTenantUserManagementIntegrationTest; import com.knecon.fforesight.tenantusermanagement.feigntestclients.external.UserClient; -import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantusermanagement.model.CreateUserRequest; import com.knecon.fforesight.tenantusermanagement.model.ResetPasswordRequest; import com.knecon.fforesight.tenantusermanagement.model.UpdateMyProfileRequest; import com.knecon.fforesight.tenantusermanagement.model.UpdateProfileRequest; import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties; +import feign.FeignException; +import lombok.SneakyThrows; + public class UserTest extends AbstractTenantUserManagementIntegrationTest { @Autowired @@ -37,6 +39,7 @@ public class UserTest extends AbstractTenantUserManagementIntegrationTest { public void testUsers() { TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID); + tokenService.setUser("test@fforesight.com", "secret"); var allUsers = userClient.getAllUsers(true); var testUserFound = allUsers.stream().anyMatch(u -> u.getEmail().equalsIgnoreCase("test@fforesight.com")); @@ -93,6 +96,8 @@ public class UserTest extends AbstractTenantUserManagementIntegrationTest { allUsers = userClient.getAllUsers(true); assertThat(allUsers).hasSize(1); + + TenantContext.clear(); } @@ -138,6 +143,123 @@ public class UserTest extends AbstractTenantUserManagementIntegrationTest { assertThat(e.status()).isEqualTo(403); } + TenantContext.clear(); + } + + + @Test + @SneakyThrows + public void testActivateUserWithLowerRole() { + + TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID); + tokenService.setUser("test@fforesight.com", "secret"); + + var redAdminUserRequest = new CreateUserRequest(); + redAdminUserRequest.setEmail("red-admin@knecon.com"); + redAdminUserRequest.setUsername(redAdminUserRequest.getEmail()); + redAdminUserRequest.setRoles(Set.of("SUPER_USER")); + var redAdmin = userClient.createUser(redAdminUserRequest); + + var redUserAdminUserRequest = new CreateUserRequest(); + redUserAdminUserRequest.setEmail("red-user-admin@knecon.com"); + redUserAdminUserRequest.setUsername(redUserAdminUserRequest.getEmail()); + redUserAdminUserRequest.setRoles(Set.of("LESS_SUPER_USER")); + var redUserAdmin = userClient.createUser(redUserAdminUserRequest); + + userClient.resetPassword(redAdmin.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build()); + userClient.resetPassword(redUserAdmin.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build()); + + // authenticate as red-admin + tokenService.setUser("red-admin@knecon.com", "Secret@secured!23"); + + // activate red-user-admin should work since the user has a lower rank + var user = userClient.activateProfile(redUserAdmin.getUserId(), true); + assertTrue(user.isActive()); + + // deactivate red-user-admin should work since the user has a lower rank + user = userClient.activateProfile(redUserAdmin.getUserId(), false); + assertFalse(user.isActive()); + + tokenService.setUser("test@fforesight.com", "secret"); + userClient.deleteUser(redUserAdmin.getUserId()); + userClient.deleteUser(redAdmin.getUserId()); + TenantContext.clear(); + } + + + @Test + @SneakyThrows + public void testActivateUserWithHigherRole() { + + TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID); + tokenService.setUser("test@fforesight.com", "secret"); + + var redAdminUserRequest = new CreateUserRequest(); + redAdminUserRequest.setEmail("red-admin@knecon.com"); + redAdminUserRequest.setUsername(redAdminUserRequest.getEmail()); + redAdminUserRequest.setRoles(Set.of("SUPER_USER")); + var redAdmin = userClient.createUser(redAdminUserRequest); + + var redUserAdminUserRequest = new CreateUserRequest(); + redUserAdminUserRequest.setEmail("red-user-admin@knecon.com"); + redUserAdminUserRequest.setUsername(redUserAdminUserRequest.getEmail()); + redUserAdminUserRequest.setRoles(Set.of("LESS_SUPER_USER")); + var redUserAdmin = userClient.createUser(redUserAdminUserRequest); + + userClient.resetPassword(redAdmin.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build()); + userClient.resetPassword(redUserAdmin.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build()); + + // authenticate as red-user-admin + tokenService.setUser("red-user-admin@knecon.com", "Secret@secured!23"); + + // activate red-admin should not work since the user has a higher rank + FeignException feignException = assertThrows(FeignException.class, () -> userClient.activateProfile(redAdmin.getUserId(), true)); + assertEquals(feignException.status(), 403); + assertTrue(feignException.getMessage().contains("It is not allowed to activate/deactivate a user with higher ranking role")); + + tokenService.setUser("test@fforesight.com", "secret"); + userClient.deleteUser(redUserAdmin.getUserId()); + userClient.deleteUser(redAdmin.getUserId()); + TenantContext.clear(); + } + + @Test + @SneakyThrows + public void testActivateUserWithSameRole() { + + TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID); + tokenService.setUser("test@fforesight.com", "secret"); + + var redAdminUserRequest = new CreateUserRequest(); + redAdminUserRequest.setEmail("red-admin@knecon.com"); + redAdminUserRequest.setUsername(redAdminUserRequest.getEmail()); + redAdminUserRequest.setRoles(Set.of("SUPER_USER")); + var redAdmin = userClient.createUser(redAdminUserRequest); + + var redUserAdminUserRequest = new CreateUserRequest(); + redUserAdminUserRequest.setEmail("red-user-admin@knecon.com"); + redUserAdminUserRequest.setUsername(redUserAdminUserRequest.getEmail()); + redUserAdminUserRequest.setRoles(Set.of("SUPER_USER")); + var redUserAdmin = userClient.createUser(redUserAdminUserRequest); + + userClient.resetPassword(redAdmin.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build()); + userClient.resetPassword(redUserAdmin.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build()); + + // authenticate as red-user-admin + tokenService.setUser("red-admin@knecon.com", "Secret@secured!23"); + + // activate red-user-admin should work since the user has the same rank + var user = userClient.activateProfile(redUserAdmin.getUserId(), true); + assertTrue(user.isActive()); + + // deactivate red-user-admin should work since the user has the same rank + user = userClient.activateProfile(redUserAdmin.getUserId(), false); + assertFalse(user.isActive()); + + tokenService.setUser("test@fforesight.com", "secret"); + userClient.deleteUser(redUserAdmin.getUserId()); + userClient.deleteUser(redAdmin.getUserId()); + TenantContext.clear(); }