feat: Implémentation sécurité @RolesAllowed et corrections diverses
- Ajout DevSecurityContextProducer pour @RolesAllowed en dev - Correction UserSearchCriteriaDTO (searchTerm au lieu de searchText) - Mise à jour version quarkus-primefaces à 3.15.1 - Corrections expressions EL dans composants audit et role-assignment
This commit is contained in:
@@ -50,10 +50,10 @@
|
|||||||
|
|
||||||
<div class="field #{colSize}">
|
<div class="field #{colSize}">
|
||||||
<c:choose>
|
<c:choose>
|
||||||
<c:when test="#{clickable}">
|
<c:when test="#{clickable and not empty clickAction}">
|
||||||
<p:commandLink
|
<p:commandLink
|
||||||
styleClass="card-link"
|
styleClass="card-link"
|
||||||
action="#{not empty clickAction ? clickAction : null}">
|
action="#{clickAction}">
|
||||||
<div class="card surface-0 hover:surface-100 border-round-lg transition-all transition-duration-200 p-4">
|
<div class="card surface-0 hover:surface-100 border-round-lg transition-all transition-duration-200 p-4">
|
||||||
<div class="flex align-items-center justify-content-between mb-3">
|
<div class="flex align-items-center justify-content-between mb-3">
|
||||||
<span class="block text-600 font-medium text-sm">#{title}</span>
|
<span class="block text-600 font-medium text-sm">#{title}</span>
|
||||||
|
|||||||
@@ -16,8 +16,7 @@
|
|||||||
- user: UserDTO (requis) - L'utilisateur concerné
|
- user: UserDTO (requis) - L'utilisateur concerné
|
||||||
- availableRoles: List<RoleDTO> (requis) - Liste des rôles disponibles
|
- availableRoles: List<RoleDTO> (requis) - Liste des rôles disponibles
|
||||||
- userRoles: List<RoleDTO> (requis) - Liste des rôles de l'utilisateur
|
- userRoles: List<RoleDTO> (requis) - Liste des rôles de l'utilisateur
|
||||||
- assignAction: String (requis) - Action pour attribuer un rôle
|
- roleBean: String (optionnel) - Nom du bean pour les actions (défaut: "roleGestionBean")
|
||||||
- revokeAction: String (requis) - Action pour révoquer un rôle
|
|
||||||
- update: String (défaut: "@form") - Composants à mettre à jour
|
- update: String (défaut: "@form") - Composants à mettre à jour
|
||||||
- showRealmRoles: Boolean (défaut: true) - Afficher les rôles Realm
|
- showRealmRoles: Boolean (défaut: true) - Afficher les rôles Realm
|
||||||
- showClientRoles: Boolean (défaut: true) - Afficher les rôles Client
|
- showClientRoles: Boolean (défaut: true) - Afficher les rôles Client
|
||||||
@@ -39,6 +38,7 @@
|
|||||||
<c:set var="update" value="#{empty update ? '@form' : update}" />
|
<c:set var="update" value="#{empty update ? '@form' : update}" />
|
||||||
<c:set var="showRealmRoles" value="#{empty showRealmRoles ? true : showRealmRoles}" />
|
<c:set var="showRealmRoles" value="#{empty showRealmRoles ? true : showRealmRoles}" />
|
||||||
<c:set var="showClientRoles" value="#{empty showClientRoles ? true : showClientRoles}" />
|
<c:set var="showClientRoles" value="#{empty showClientRoles ? true : showClientRoles}" />
|
||||||
|
<c:set var="roleBeanName" value="#{empty roleBean ? 'roleGestionBean' : roleBean}" />
|
||||||
|
|
||||||
<h:form id="#{formId}">
|
<h:form id="#{formId}">
|
||||||
<p:panel header="Attribution de rôles - #{user.username}" styleClass="w-full">
|
<p:panel header="Attribution de rôles - #{user.username}" styleClass="w-full">
|
||||||
@@ -54,11 +54,11 @@
|
|||||||
<p:commandButton
|
<p:commandButton
|
||||||
icon="pi pi-times"
|
icon="pi pi-times"
|
||||||
styleClass="p-button-text p-button-sm p-button-danger ml-2"
|
styleClass="p-button-text p-button-sm p-button-danger ml-2"
|
||||||
action="#{revokeAction}"
|
action="#{roleGestionBean.revokeRoleFromParams}"
|
||||||
update="#{update}"
|
update="#{update}"
|
||||||
title="Révoquer le rôle">
|
title="Révoquer le rôle">
|
||||||
<f:param name="userId" value="#{user.id}" />
|
<f:param name="userId" value="#{user.id}" />
|
||||||
<f:param name="roleId" value="#{userRole.id}" />
|
<f:param name="roleName" value="#{userRole.name}" />
|
||||||
</p:commandButton>
|
</p:commandButton>
|
||||||
</p:tag>
|
</p:tag>
|
||||||
</c:forEach>
|
</c:forEach>
|
||||||
@@ -100,11 +100,11 @@
|
|||||||
<p:commandButton
|
<p:commandButton
|
||||||
icon="pi pi-plus"
|
icon="pi pi-plus"
|
||||||
styleClass="p-button-text p-button-sm p-button-success ml-2"
|
styleClass="p-button-text p-button-sm p-button-success ml-2"
|
||||||
action="#{assignAction}"
|
action="#{roleGestionBean.assignRoleFromParams}"
|
||||||
update="#{update}"
|
update="#{update}"
|
||||||
title="Attribuer le rôle">
|
title="Attribuer le rôle">
|
||||||
<f:param name="userId" value="#{user.id}" />
|
<f:param name="userId" value="#{user.id}" />
|
||||||
<f:param name="roleId" value="#{role.id}" />
|
<f:param name="roleName" value="#{role.name}" />
|
||||||
</p:commandButton>
|
</p:commandButton>
|
||||||
</p:tag>
|
</p:tag>
|
||||||
</c:otherwise>
|
</c:otherwise>
|
||||||
@@ -143,11 +143,11 @@
|
|||||||
<p:commandButton
|
<p:commandButton
|
||||||
icon="pi pi-plus"
|
icon="pi pi-plus"
|
||||||
styleClass="p-button-text p-button-sm p-button-success ml-2"
|
styleClass="p-button-text p-button-sm p-button-success ml-2"
|
||||||
action="#{assignAction}"
|
action="#{roleGestionBean.assignRoleFromParams}"
|
||||||
update="#{update}"
|
update="#{update}"
|
||||||
title="Attribuer le rôle">
|
title="Attribuer le rôle">
|
||||||
<f:param name="userId" value="#{user.id}" />
|
<f:param name="userId" value="#{user.id}" />
|
||||||
<f:param name="roleId" value="#{role.id}" />
|
<f:param name="roleName" value="#{role.name}" />
|
||||||
</p:commandButton>
|
</p:commandButton>
|
||||||
</p:tag>
|
</p:tag>
|
||||||
</c:otherwise>
|
</c:otherwise>
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
<h3>Rechercher un rôle</h3>
|
<h3>Rechercher un rôle</h3>
|
||||||
<div class="flex gap-2 mb-3">
|
<div class="flex gap-2 mb-3">
|
||||||
<p:inputText
|
<p:inputText
|
||||||
value="#{roleBean.roleSearchText}"
|
value="#{roleGestionBean.roleSearchText}"
|
||||||
placeholder="Rechercher un rôle..."
|
placeholder="Rechercher un rôle..."
|
||||||
styleClass="flex-1">
|
styleClass="flex-1">
|
||||||
<p:ajax event="keyup"
|
<p:ajax event="keyup"
|
||||||
|
|||||||
@@ -79,10 +79,10 @@
|
|||||||
|
|
||||||
<c:choose>
|
<c:choose>
|
||||||
<!-- Badge cliquable -->
|
<!-- Badge cliquable -->
|
||||||
<c:when test="#{clickable}">
|
<c:when test="#{clickable and not empty clickAction}">
|
||||||
<p:commandLink
|
<p:commandLink
|
||||||
styleClass="role-badge-link"
|
styleClass="role-badge-link"
|
||||||
action="#{not empty clickAction ? clickAction : null}">
|
action="#{clickAction}">
|
||||||
<p:tag
|
<p:tag
|
||||||
value="#{roleName}"
|
value="#{roleName}"
|
||||||
severity="#{severity}"
|
severity="#{severity}"
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ public class UserSearchCriteriaDTO implements Serializable {
|
|||||||
@Schema(description = "Nom de famille", example = "Dupont")
|
@Schema(description = "Nom de famille", example = "Dupont")
|
||||||
private String nom;
|
private String nom;
|
||||||
|
|
||||||
|
@Schema(description = "Numéro de téléphone", example = "+225 01 02 03 04 05")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
// Filtres de statut
|
// Filtres de statut
|
||||||
@Schema(description = "Statut de l'utilisateur", example = "ACTIF")
|
@Schema(description = "Statut de l'utilisateur", example = "ACTIF")
|
||||||
private StatutUser statut;
|
private StatutUser statut;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package dev.lions.user.manager.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.quarkus.jackson.ObjectMapperCustomizer;
|
||||||
|
import jakarta.inject.Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration Jackson pour ignorer les propriétés inconnues
|
||||||
|
* Nécessaire pour la compatibilité avec les versions récentes de Keycloak
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class JacksonConfig implements ObjectMapperCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ObjectMapper objectMapper) {
|
||||||
|
// Ignorer les propriétés inconnues pour compatibilité Keycloak
|
||||||
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package dev.lions.user.manager.security;
|
||||||
|
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.Priorities;
|
||||||
|
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||||
|
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import jakarta.ws.rs.core.SecurityContext;
|
||||||
|
import jakarta.ws.rs.ext.Provider;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filtre JAX-RS pour remplacer le SecurityContext en mode développement
|
||||||
|
* En dev, remplace le SecurityContext par un mock qui autorise tous les rôles
|
||||||
|
* En prod, laisse le SecurityContext réel de Quarkus
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
@Priority(Priorities.AUTHENTICATION - 1) // S'exécute avant l'authentification
|
||||||
|
public class DevSecurityContextProducer implements ContainerRequestFilter {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(DevSecurityContextProducer.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@ConfigProperty(name = "quarkus.profile", defaultValue = "prod")
|
||||||
|
String profile;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext) {
|
||||||
|
// En dev, remplacer le SecurityContext par un mock
|
||||||
|
if ("dev".equals(profile) || "development".equals(profile)) {
|
||||||
|
LOG.debug("Mode dev: remplacement du SecurityContext par un mock avec tous les rôles");
|
||||||
|
SecurityContext original = requestContext.getSecurityContext();
|
||||||
|
requestContext.setSecurityContext(new DevSecurityContext(original));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecurityContext mock pour le mode développement
|
||||||
|
* Simule un utilisateur avec tous les rôles nécessaires
|
||||||
|
*/
|
||||||
|
private static class DevSecurityContext implements SecurityContext {
|
||||||
|
|
||||||
|
private final SecurityContext original;
|
||||||
|
private final Principal principal = new Principal() {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "dev-user";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public DevSecurityContext(SecurityContext original) {
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getUserPrincipal() {
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserInRole(String role) {
|
||||||
|
// En dev, autoriser tous les rôles
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecure() {
|
||||||
|
return original != null ? original.isSecure() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthenticationScheme() {
|
||||||
|
return "DEV";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1
pom.xml
1
pom.xml
@@ -22,6 +22,7 @@
|
|||||||
<quarkus.version>3.15.1</quarkus.version>
|
<quarkus.version>3.15.1</quarkus.version>
|
||||||
<quarkus-primefaces.version>3.15.1</quarkus-primefaces.version>
|
<quarkus-primefaces.version>3.15.1</quarkus-primefaces.version>
|
||||||
<primefaces.version>14.0.5</primefaces.version>
|
<primefaces.version>14.0.5</primefaces.version>
|
||||||
|
<keycloak.version>26.0.4</keycloak.version>
|
||||||
<lombok.version>1.18.30</lombok.version>
|
<lombok.version>1.18.30</lombok.version>
|
||||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user