feat(lum): KeycloakRealmSetupService + rôles RBAC UnionFlow + Jacoco 100%
- Ajoute KeycloakRealmSetupService : auto-initialisation des rôles realm (admin, user_manager, user_viewer, role_manager...) et assignation du rôle user_manager au service account unionflow-server au démarrage (idempotent, retries, thread séparé pour ne pas bloquer le démarrage) → Corrige le 403 sur resetPassword / changement de mot de passe premier login - UserResource : étend les @RolesAllowed avec ADMIN/SUPER_ADMIN/USER pour permettre aux appels inter-services unionflow-server d'accéder aux endpoints sans être bloqués par le RBAC LUM ; corrige sendVerificationEmail (retourne Response) - application-dev.properties : service-accounts.user-manager-clients=unionflow-server - application-prod.properties : client-id, credentials.secret, token.audience, auto-setup - application-test.properties : H2 in-memory (plus besoin de Docker pour les tests) - pom.xml : H2 scope test, Jacoco 100% enforcement (exclusions MapStruct/repos/setup), annotation processors MapStruct+Lombok explicites - .gitignore + .env ajouté (.env exclu du commit) - script/docker/.env.example : variables KEYCLOAK_ADMIN_USERNAME/PASSWORD documentées
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package dev.lions.user.manager.client;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -16,6 +18,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -36,6 +41,9 @@ class KeycloakAdminClientImplCompleteTest {
|
||||
@InjectMocks
|
||||
KeycloakAdminClientImpl client;
|
||||
|
||||
private HttpServer localServer;
|
||||
private int localPort;
|
||||
|
||||
private void setField(String fieldName, Object value) throws Exception {
|
||||
Field field = KeycloakAdminClientImpl.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
@@ -50,6 +58,26 @@ class KeycloakAdminClientImplCompleteTest {
|
||||
setField("adminUsername", "admin");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (localServer != null) {
|
||||
localServer.stop(0);
|
||||
localServer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private int startLocalServer(String path, String responseBody, int statusCode) throws Exception {
|
||||
localServer = HttpServer.create(new InetSocketAddress(0), 0);
|
||||
localServer.createContext(path, exchange -> {
|
||||
byte[] bytes = responseBody.getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(statusCode, bytes.length);
|
||||
exchange.getResponseBody().write(bytes);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
localServer.start();
|
||||
return localServer.getAddress().getPort();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstance() {
|
||||
Keycloak result = client.getInstance();
|
||||
@@ -162,4 +190,76 @@ class KeycloakAdminClientImplCompleteTest {
|
||||
void testReconnect() {
|
||||
assertDoesNotThrow(() -> client.reconnect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInit() throws Exception {
|
||||
// init() est appelé @PostConstruct — l'appeler via réflexion pour couvrir la méthode
|
||||
Method initMethod = KeycloakAdminClientImpl.class.getDeclaredMethod("init");
|
||||
initMethod.setAccessible(true);
|
||||
assertDoesNotThrow(() -> initMethod.invoke(client));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllRealms_Success() throws Exception {
|
||||
// Démarrer un serveur HTTP local qui retourne une liste de realms JSON
|
||||
String realmsJson = "[{\"realm\":\"master\",\"id\":\"1\"},{\"realm\":\"lions\",\"id\":\"2\"}]";
|
||||
localPort = startLocalServer("/admin/realms", realmsJson, 200);
|
||||
setField("serverUrl", "http://localhost:" + localPort);
|
||||
|
||||
when(mockKeycloak.tokenManager()).thenReturn(mockTokenManager);
|
||||
when(mockTokenManager.getAccessTokenString()).thenReturn("fake-token");
|
||||
|
||||
List<String> realms = client.getAllRealms();
|
||||
|
||||
assertNotNull(realms);
|
||||
assertEquals(2, realms.size());
|
||||
assertTrue(realms.contains("master"));
|
||||
assertTrue(realms.contains("lions"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllRealms_NonOkResponse() throws Exception {
|
||||
localPort = startLocalServer("/admin/realms", "Forbidden", 403);
|
||||
setField("serverUrl", "http://localhost:" + localPort);
|
||||
|
||||
when(mockKeycloak.tokenManager()).thenReturn(mockTokenManager);
|
||||
when(mockTokenManager.getAccessTokenString()).thenReturn("fake-token");
|
||||
|
||||
assertThrows(RuntimeException.class, () -> client.getAllRealms());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRealmClients_Success() throws Exception {
|
||||
String clientsJson = "[{\"clientId\":\"admin-cli\",\"id\":\"1\"},{\"clientId\":\"account\",\"id\":\"2\"}]";
|
||||
localPort = startLocalServer("/admin/realms/master/clients", clientsJson, 200);
|
||||
setField("serverUrl", "http://localhost:" + localPort);
|
||||
|
||||
when(mockKeycloak.tokenManager()).thenReturn(mockTokenManager);
|
||||
when(mockTokenManager.getAccessTokenString()).thenReturn("fake-token");
|
||||
|
||||
List<String> clients = client.getRealmClients("master");
|
||||
|
||||
assertNotNull(clients);
|
||||
assertEquals(2, clients.size());
|
||||
assertTrue(clients.contains("admin-cli"));
|
||||
assertTrue(clients.contains("account"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRealmClients_NonOkResponse() throws Exception {
|
||||
localPort = startLocalServer("/admin/realms/bad/clients", "Not Found", 404);
|
||||
setField("serverUrl", "http://localhost:" + localPort);
|
||||
|
||||
when(mockKeycloak.tokenManager()).thenReturn(mockTokenManager);
|
||||
when(mockTokenManager.getAccessTokenString()).thenReturn("fake-token");
|
||||
|
||||
assertThrows(RuntimeException.class, () -> client.getRealmClients("bad"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRealmClients_TokenError() {
|
||||
when(mockKeycloak.tokenManager()).thenThrow(new RuntimeException("Token error"));
|
||||
|
||||
assertThrows(RuntimeException.class, () -> client.getRealmClients("master"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user