From 9c66909eff5b2460624096263d09d1409ecdade2 Mon Sep 17 00:00:00 2001 From: dahoud <41957584+DahoudG@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:07:11 +0000 Subject: [PATCH] Refactoring - Version stable Co-Authored-By: Claude Sonnet 4.6 --- .../server/client/UserServiceClient.java | 1 + .../service/MembreKeycloakSyncService.java | 47 +++++---- .../MembreKeycloakSyncServiceTest.java | 99 +++++++++++++++++-- 3 files changed, 118 insertions(+), 29 deletions(-) diff --git a/src/main/java/dev/lions/unionflow/server/client/UserServiceClient.java b/src/main/java/dev/lions/unionflow/server/client/UserServiceClient.java index 4112d44..fb1b5f2 100644 --- a/src/main/java/dev/lions/unionflow/server/client/UserServiceClient.java +++ b/src/main/java/dev/lions/unionflow/server/client/UserServiceClient.java @@ -73,4 +73,5 @@ public interface UserServiceClient { void sendVerificationEmail( @PathParam("userId") String userId, @QueryParam("realm") String realmName); + } diff --git a/src/main/java/dev/lions/unionflow/server/service/MembreKeycloakSyncService.java b/src/main/java/dev/lions/unionflow/server/service/MembreKeycloakSyncService.java index 190b48b..2963171 100644 --- a/src/main/java/dev/lions/unionflow/server/service/MembreKeycloakSyncService.java +++ b/src/main/java/dev/lions/unionflow/server/service/MembreKeycloakSyncService.java @@ -1,5 +1,6 @@ package dev.lions.unionflow.server.service; +import dev.lions.unionflow.server.client.RoleServiceClient; import dev.lions.unionflow.server.client.UserServiceClient; import dev.lions.unionflow.server.entity.Membre; import dev.lions.unionflow.server.repository.MembreRepository; @@ -53,6 +54,10 @@ public class MembreKeycloakSyncService { @RestClient UserServiceClient userServiceClient; + @Inject + @RestClient + RoleServiceClient roleServiceClient; + /** * Provisionne un compte Keycloak pour un Membre existant qui n'en a pas encore. * @@ -178,15 +183,12 @@ public class MembreKeycloakSyncService { userServiceClient.updateUser(keycloakUserId, user, DEFAULT_REALM); } - // Ajouter le rôle MEMBRE_ACTIF s'il n'est pas déjà présent - List roles = user.getRealmRoles() != null - ? new java.util.ArrayList<>(user.getRealmRoles()) - : new java.util.ArrayList<>(); - if (!roles.contains("MEMBRE_ACTIF")) { - roles.add("MEMBRE_ACTIF"); - user.setRealmRoles(roles); - userServiceClient.updateUser(keycloakUserId, user, DEFAULT_REALM); - } + // Assigner MEMBRE_ACTIF via l'endpoint dédié + roleServiceClient.assignRealmRoles( + keycloakUserId, + DEFAULT_REALM, + new RoleServiceClient.RoleNamesRequest(List.of("MEMBRE_ACTIF")) + ); LOGGER.info("✅ Rôle MEMBRE_ACTIF assigné dans Keycloak pour " + membre.getNomComplet()); @@ -230,19 +232,26 @@ public class MembreKeycloakSyncService { // S'assurer que le compte est activé if (Boolean.FALSE.equals(user.getEnabled())) { user.setEnabled(true); + userServiceClient.updateUser(keycloakUserId, user, DEFAULT_REALM); } - // Construire la liste de rôles : ajouter ADMIN_ORGANISATION, retirer MEMBRE et MEMBRE_ACTIF - List roles = user.getRealmRoles() != null - ? new java.util.ArrayList<>(user.getRealmRoles()) - : new java.util.ArrayList<>(); - roles.remove("MEMBRE"); - roles.remove("MEMBRE_ACTIF"); - if (!roles.contains("ADMIN_ORGANISATION")) { - roles.add("ADMIN_ORGANISATION"); + // Révoquer MEMBRE et MEMBRE_ACTIF (non bloquant — peuvent ne pas exister) + try { + roleServiceClient.revokeRealmRoles( + keycloakUserId, + DEFAULT_REALM, + new RoleServiceClient.RoleNamesRequest(List.of("MEMBRE", "MEMBRE_ACTIF")) + ); + } catch (Exception e) { + LOGGER.warning("⚠️ Révocation MEMBRE/MEMBRE_ACTIF non bloquante: " + e.getMessage()); } - user.setRealmRoles(roles); - userServiceClient.updateUser(keycloakUserId, user, DEFAULT_REALM); + + // Assigner ADMIN_ORGANISATION via l'endpoint dédié + roleServiceClient.assignRealmRoles( + keycloakUserId, + DEFAULT_REALM, + new RoleServiceClient.RoleNamesRequest(List.of("ADMIN_ORGANISATION")) + ); LOGGER.info("✅ Rôle ADMIN_ORGANISATION assigné dans Keycloak pour " + membre.getNomComplet()); diff --git a/src/test/java/dev/lions/unionflow/server/service/MembreKeycloakSyncServiceTest.java b/src/test/java/dev/lions/unionflow/server/service/MembreKeycloakSyncServiceTest.java index 869d38c..64e2e3a 100644 --- a/src/test/java/dev/lions/unionflow/server/service/MembreKeycloakSyncServiceTest.java +++ b/src/test/java/dev/lions/unionflow/server/service/MembreKeycloakSyncServiceTest.java @@ -6,6 +6,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; +import dev.lions.unionflow.server.client.RoleServiceClient; import dev.lions.unionflow.server.client.UserServiceClient; import dev.lions.unionflow.server.entity.Membre; import dev.lions.unionflow.server.repository.MembreRepository; @@ -36,6 +37,10 @@ class MembreKeycloakSyncServiceTest { @RestClient UserServiceClient userServiceClient; + @InjectMock + @RestClient + RoleServiceClient roleServiceClient; + // ========================================================================= // provisionKeycloakUser // ========================================================================= @@ -232,8 +237,8 @@ class MembreKeycloakSyncServiceTest { } @Test - @DisplayName("promouvoirAdminOrganisation assigne ADMIN_ORGANISATION et retire MEMBRE/MEMBRE_ACTIF") - void promouvoirAdminOrganisation_assignsAdminRoleAndRemovesMemberRoles() { + @DisplayName("promouvoirAdminOrganisation assigne ADMIN_ORGANISATION et révoque MEMBRE/MEMBRE_ACTIF") + void promouvoirAdminOrganisation_assignsAdminRoleAndRevokesMemberRoles() { UUID membreId = UUID.randomUUID(); UUID keycloakId = UUID.randomUUID(); @@ -249,16 +254,15 @@ class MembreKeycloakSyncServiceTest { UserDTO user = new UserDTO(); user.setId(keycloakId.toString()); user.setEnabled(true); - user.setRealmRoles(new java.util.ArrayList<>(java.util.List.of("MEMBRE", "MEMBRE_ACTIF"))); user.setRealmName("unionflow"); when(userServiceClient.getUserById(eq(keycloakId.toString()), eq("unionflow"))).thenReturn(user); - when(userServiceClient.updateUser(anyString(), any(UserDTO.class), anyString())).thenReturn(user); + doNothing().when(roleServiceClient).revokeRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); + doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); syncService.promouvoirAdminOrganisationDansKeycloak(membreId); - verify(userServiceClient).updateUser(eq(keycloakId.toString()), any(UserDTO.class), eq("unionflow")); - assertThat(user.getRealmRoles()).contains("ADMIN_ORGANISATION"); - assertThat(user.getRealmRoles()).doesNotContain("MEMBRE", "MEMBRE_ACTIF"); + verify(roleServiceClient).revokeRealmRoles(eq(keycloakId.toString()), eq("unionflow"), any(RoleServiceClient.RoleNamesRequest.class)); + verify(roleServiceClient).assignRealmRoles(eq(keycloakId.toString()), eq("unionflow"), any(RoleServiceClient.RoleNamesRequest.class)); } @Test @@ -278,11 +282,12 @@ class MembreKeycloakSyncServiceTest { UserDTO user = new UserDTO(); user.setId(keycloakId.toString()); - user.setEnabled(false); // désactivé - user.setRealmRoles(new java.util.ArrayList<>()); + user.setEnabled(false); user.setRealmName("unionflow"); when(userServiceClient.getUserById(anyString(), anyString())).thenReturn(user); when(userServiceClient.updateUser(anyString(), any(UserDTO.class), anyString())).thenReturn(user); + doNothing().when(roleServiceClient).revokeRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); + doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); syncService.promouvoirAdminOrganisationDansKeycloak(membreId); @@ -330,11 +335,13 @@ class MembreKeycloakSyncServiceTest { fetchedUser.setRealmName("unionflow"); when(userServiceClient.getUserById(eq(newKeycloakId.toString()), anyString())).thenReturn(fetchedUser); when(userServiceClient.updateUser(anyString(), any(UserDTO.class), anyString())).thenReturn(fetchedUser); + doNothing().when(roleServiceClient).revokeRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); + doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); syncService.promouvoirAdminOrganisationDansKeycloak(membreId); verify(userServiceClient).createUser(any(UserDTO.class), eq("unionflow")); - verify(userServiceClient).updateUser(eq(newKeycloakId.toString()), any(UserDTO.class), eq("unionflow")); + verify(roleServiceClient).assignRealmRoles(eq(newKeycloakId.toString()), eq("unionflow"), any(RoleServiceClient.RoleNamesRequest.class)); } @Test @@ -359,6 +366,78 @@ class MembreKeycloakSyncServiceTest { .hasMessageContaining("Impossible de promouvoir le compte Keycloak"); } + // ========================================================================= + // activerMembreDansKeycloak + // ========================================================================= + + @Test + @DisplayName("activerMembreDansKeycloak échoue si le membre n'existe pas") + void activerMembreDansKeycloak_failsIfMembreNotFound() { + UUID membreId = UUID.randomUUID(); + when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> syncService.activerMembreDansKeycloak(membreId)) + .isInstanceOf(NotFoundException.class); + } + + @Test + @DisplayName("activerMembreDansKeycloak assigne MEMBRE_ACTIF via roleServiceClient") + void activerMembreDansKeycloak_assignsMemberActifRole() { + UUID membreId = UUID.randomUUID(); + UUID keycloakId = UUID.randomUUID(); + + Membre membre = new Membre(); + membre.setId(membreId); + membre.setKeycloakId(keycloakId); + membre.setEmail("membre@unionflow.dev"); + membre.setNom("Membre"); + membre.setPrenom("Test"); + + when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre)); + + UserDTO user = new UserDTO(); + user.setId(keycloakId.toString()); + user.setEnabled(true); + user.setRealmName("unionflow"); + when(userServiceClient.getUserById(eq(keycloakId.toString()), eq("unionflow"))).thenReturn(user); + doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); + + syncService.activerMembreDansKeycloak(membreId); + + verify(roleServiceClient).assignRealmRoles( + eq(keycloakId.toString()), eq("unionflow"), any(RoleServiceClient.RoleNamesRequest.class)); + } + + @Test + @DisplayName("activerMembreDansKeycloak active le compte s'il est désactivé") + void activerMembreDansKeycloak_enablesDisabledAccount() { + UUID membreId = UUID.randomUUID(); + UUID keycloakId = UUID.randomUUID(); + + Membre membre = new Membre(); + membre.setId(membreId); + membre.setKeycloakId(keycloakId); + membre.setEmail("disabled@unionflow.dev"); + membre.setNom("Disabled"); + membre.setPrenom("Membre"); + + when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre)); + + UserDTO user = new UserDTO(); + user.setId(keycloakId.toString()); + user.setEnabled(false); + user.setRealmName("unionflow"); + when(userServiceClient.getUserById(anyString(), anyString())).thenReturn(user); + when(userServiceClient.updateUser(anyString(), any(UserDTO.class), anyString())).thenReturn(user); + doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); + + syncService.activerMembreDansKeycloak(membreId); + + assertThat(user.getEnabled()).isTrue(); + verify(userServiceClient).updateUser(anyString(), any(UserDTO.class), anyString()); + verify(roleServiceClient).assignRealmRoles(anyString(), anyString(), any(RoleServiceClient.RoleNamesRequest.class)); + } + // ========================================================================= // syncMembreToKeycloak // =========================================================================