- 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
179 lines
6.3 KiB
Java
179 lines
6.3 KiB
Java
package dev.lions.user.manager.security;
|
|
|
|
import jakarta.ws.rs.container.ContainerRequestContext;
|
|
import jakarta.ws.rs.core.SecurityContext;
|
|
import jakarta.ws.rs.core.UriInfo;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.extension.ExtendWith;
|
|
import org.mockito.ArgumentCaptor;
|
|
import org.mockito.Mock;
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
/**
|
|
* Tests unitaires pour DevSecurityContextProducer
|
|
*/
|
|
@ExtendWith(MockitoExtension.class)
|
|
class DevSecurityContextProducerTest {
|
|
|
|
@Mock
|
|
private ContainerRequestContext requestContext;
|
|
|
|
@Mock
|
|
private UriInfo uriInfo;
|
|
|
|
@Mock
|
|
private SecurityContext originalSecurityContext;
|
|
|
|
private DevSecurityContextProducer producer;
|
|
|
|
@BeforeEach
|
|
void setUp() throws Exception {
|
|
producer = new DevSecurityContextProducer();
|
|
|
|
// Injecter les propriétés via reflection
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", false);
|
|
}
|
|
|
|
private void setField(String fieldName, Object value) throws Exception {
|
|
Field field = DevSecurityContextProducer.class.getDeclaredField(fieldName);
|
|
field.setAccessible(true);
|
|
field.set(producer, value);
|
|
}
|
|
|
|
@Test
|
|
void testFilter_DevMode() throws Exception {
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", true);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/users");
|
|
when(requestContext.getSecurityContext()).thenReturn(originalSecurityContext);
|
|
|
|
producer.filter(requestContext);
|
|
|
|
verify(requestContext, times(1)).setSecurityContext(any(SecurityContext.class));
|
|
}
|
|
|
|
@Test
|
|
void testFilter_ProdMode() throws Exception {
|
|
setField("profile", "prod");
|
|
setField("oidcEnabled", true);
|
|
|
|
// En mode prod, on n'a pas besoin de mocker getUriInfo car le code ne l'utilise pas
|
|
producer.filter(requestContext);
|
|
|
|
verify(requestContext, never()).setSecurityContext(any(SecurityContext.class));
|
|
}
|
|
|
|
@Test
|
|
void testFilter_OidcDisabled() throws Exception {
|
|
setField("profile", "prod");
|
|
setField("oidcEnabled", false);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/users");
|
|
when(requestContext.getSecurityContext()).thenReturn(originalSecurityContext);
|
|
|
|
producer.filter(requestContext);
|
|
|
|
verify(requestContext, times(1)).setSecurityContext(any(SecurityContext.class));
|
|
}
|
|
|
|
@Test
|
|
void testDevSecurityContext_GetUserPrincipal() throws Exception {
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", false);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/test");
|
|
when(requestContext.getSecurityContext()).thenReturn(originalSecurityContext);
|
|
|
|
ArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);
|
|
producer.filter(requestContext);
|
|
verify(requestContext).setSecurityContext(captor.capture());
|
|
|
|
SecurityContext devCtx = captor.getValue();
|
|
assertNotNull(devCtx.getUserPrincipal());
|
|
assertEquals("dev-user", devCtx.getUserPrincipal().getName());
|
|
}
|
|
|
|
@Test
|
|
void testDevSecurityContext_IsUserInRole() throws Exception {
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", false);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/test");
|
|
when(requestContext.getSecurityContext()).thenReturn(originalSecurityContext);
|
|
|
|
ArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);
|
|
producer.filter(requestContext);
|
|
verify(requestContext).setSecurityContext(captor.capture());
|
|
|
|
SecurityContext devCtx = captor.getValue();
|
|
assertTrue(devCtx.isUserInRole("admin"));
|
|
assertTrue(devCtx.isUserInRole("user_manager"));
|
|
assertTrue(devCtx.isUserInRole("any_role"));
|
|
}
|
|
|
|
@Test
|
|
void testDevSecurityContext_IsSecure_WithOriginal() throws Exception {
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", false);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/test");
|
|
when(requestContext.getSecurityContext()).thenReturn(originalSecurityContext);
|
|
when(originalSecurityContext.isSecure()).thenReturn(true);
|
|
|
|
ArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);
|
|
producer.filter(requestContext);
|
|
verify(requestContext).setSecurityContext(captor.capture());
|
|
|
|
SecurityContext devCtx = captor.getValue();
|
|
assertTrue(devCtx.isSecure()); // delegates to original which returns true
|
|
}
|
|
|
|
@Test
|
|
void testDevSecurityContext_IsSecure_WithNullOriginal() throws Exception {
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", false);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/test");
|
|
when(requestContext.getSecurityContext()).thenReturn(null); // null original
|
|
|
|
ArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);
|
|
producer.filter(requestContext);
|
|
verify(requestContext).setSecurityContext(captor.capture());
|
|
|
|
SecurityContext devCtx = captor.getValue();
|
|
assertFalse(devCtx.isSecure()); // original is null → returns false
|
|
}
|
|
|
|
@Test
|
|
void testDevSecurityContext_GetAuthenticationScheme() throws Exception {
|
|
setField("profile", "dev");
|
|
setField("oidcEnabled", false);
|
|
|
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
|
when(uriInfo.getPath()).thenReturn("/api/test");
|
|
when(requestContext.getSecurityContext()).thenReturn(originalSecurityContext);
|
|
|
|
ArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);
|
|
producer.filter(requestContext);
|
|
verify(requestContext).setSecurityContext(captor.capture());
|
|
|
|
SecurityContext devCtx = captor.getValue();
|
|
assertEquals("DEV", devCtx.getAuthenticationScheme());
|
|
}
|
|
}
|
|
|