chore(quarkus-327): bump to Quarkus 3.27.3 LTS, make pom autonomous, rename deprecated config keys

This commit is contained in:
2026-04-23 14:46:48 +00:00
parent 401a72458b
commit b0d23a0003
196 changed files with 41220 additions and 41207 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,179 +1,179 @@
package dev.lions.unionflow.client.bean;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.jboss.logging.Logger;
import java.io.IOException;
/**
* Bean centralisé pour la sécurisation des pages basée sur les rôles.
* Fournit des méthodes réutilisables pour vérifier l'accès et rediriger si nécessaire.
*
* <p>Principe DRY/WOU : Une seule implémentation de la logique de sécurité,
* réutilisée par toutes les pages via un composant Facelet.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-02
*/
@Named("pageSecurityBean")
@ApplicationScoped
public class PageSecurityBean {
private static final Logger LOG = Logger.getLogger(PageSecurityBean.class);
private static final String ACCESS_DENIED_PAGE = "/pages/secure/access-denied.xhtml";
@Inject
SecurityIdentity securityIdentity;
@Inject
MenuBean menuBean;
/**
* Vérifie si l'utilisateur a le droit d'accéder à une page donnée.
* Si non autorisé, redirige vers la page access-denied.
*
* @param allowedRoles Rôles autorisés séparés par des virgules (ex: "ADMIN,TRESORIER")
* @return true si autorisé, false sinon (après redirection)
*/
public boolean checkAccessOrRedirect(String allowedRoles) {
if (allowedRoles == null || allowedRoles.trim().isEmpty()) {
// Aucune restriction = accès autorisé pour tous les utilisateurs authentifiés
return !securityIdentity.isAnonymous();
}
String[] roles = allowedRoles.split(",");
boolean hasAccess = false;
for (String role : roles) {
String trimmedRole = role.trim();
if (hasRole(trimmedRole)) {
hasAccess = true;
break;
}
}
if (!hasAccess) {
LOG.warnf("Accès refusé pour l'utilisateur %s à une page nécessitant les rôles: %s",
securityIdentity.getPrincipal().getName(), allowedRoles);
redirectToAccessDenied();
return false;
}
return true;
}
/**
* Vérifie si l'utilisateur possède un rôle spécifique.
*
* @param role Le rôle à vérifier
* @return true si l'utilisateur a ce rôle
*/
private boolean hasRole(String role) {
return switch (role) {
case "SUPER_ADMIN" -> menuBean.isSuperAdmin();
case "ADMIN_ORGANISATION", "ADMIN" -> menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "TRESORIER" -> menuBean.isTresorier() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "SECRETAIRE" -> menuBean.isSecretaire() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "RESPONSABLE_SOCIAL" -> menuBean.isResponsableSocial() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "RESPONSABLE_EVENEMENTS" -> menuBean.isResponsableEvenements() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "RESPONSABLE_CREDIT" -> menuBean.isResponsableCredit() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "MEMBRE_BUREAU" -> menuBean.isMembreBureau() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "MEMBRE_ACTIF" -> menuBean.isMembreActif() || menuBean.isMembreBureau() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "MEMBRE_SIMPLE" -> menuBean.isMembreSimple() || menuBean.isMembreActif() || menuBean.isMembreBureau() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "ALL" -> !securityIdentity.isAnonymous(); // Tous les utilisateurs authentifiés
default -> {
LOG.warnf("Rôle inconnu: %s", role);
yield false;
}
};
}
/**
* Redirige vers la page d'accès refusé.
*/
private void redirectToAccessDenied() {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx != null && !ctx.getResponseComplete()) {
String contextPath = ctx.getExternalContext().getRequestContextPath();
ctx.getExternalContext().redirect(contextPath + ACCESS_DENIED_PAGE);
ctx.responseComplete();
}
} catch (IOException e) {
LOG.error("Erreur lors de la redirection vers access-denied", e);
}
}
// ═══════════════════════════════════════════════════════════════════════
// Méthodes helper pour vérifications rapides (utilisées dans les pages)
// ═══════════════════════════════════════════════════════════════════════
/**
* Vérifie si l'utilisateur peut gérer les membres.
* @return true si SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageMembers() {
return hasRole("SECRETAIRE");
}
/**
* Vérifie si l'utilisateur peut gérer les finances.
* @return true si TRESORIER, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageFinances() {
return hasRole("TRESORIER");
}
/**
* Vérifie si l'utilisateur peut gérer les événements.
* @return true si RESPONSABLE_EVENEMENTS, SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageEvents() {
return hasRole("RESPONSABLE_EVENEMENTS");
}
/**
* Vérifie si l'utilisateur peut gérer les aides sociales.
* @return true si RESPONSABLE_SOCIAL, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageSocialAid() {
return hasRole("RESPONSABLE_SOCIAL");
}
/**
* Vérifie si l'utilisateur peut voir les rapports financiers.
* @return true si TRESORIER, SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canViewFinancialReports() {
return hasRole("TRESORIER") || hasRole("SECRETAIRE");
}
/**
* Vérifie si l'utilisateur peut exporter des données.
* @return true si TRESORIER, SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canExportData() {
return hasRole("TRESORIER") || hasRole("SECRETAIRE");
}
/**
* Vérifie si l'utilisateur est un simple membre (MEMBRE_ACTIF uniquement).
* @return true si MEMBRE_ACTIF mais pas d'autre rôle administratif
*/
public boolean isSimpleMember() {
return menuBean.isMembreActif() &&
!menuBean.isSecretaire() &&
!menuBean.isTresorier() &&
!menuBean.isResponsableSocial() &&
!menuBean.isResponsableEvenements() &&
!menuBean.isResponsableCredit() &&
!menuBean.isMembreBureau() &&
!menuBean.isAdminOrganisation() &&
!menuBean.isSuperAdmin();
}
}
package dev.lions.unionflow.client.bean;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.jboss.logging.Logger;
import java.io.IOException;
/**
* Bean centralisé pour la sécurisation des pages basée sur les rôles.
* Fournit des méthodes réutilisables pour vérifier l'accès et rediriger si nécessaire.
*
* <p>Principe DRY/WOU : Une seule implémentation de la logique de sécurité,
* réutilisée par toutes les pages via un composant Facelet.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-02
*/
@Named("pageSecurityBean")
@ApplicationScoped
public class PageSecurityBean {
private static final Logger LOG = Logger.getLogger(PageSecurityBean.class);
private static final String ACCESS_DENIED_PAGE = "/pages/secure/access-denied.xhtml";
@Inject
SecurityIdentity securityIdentity;
@Inject
MenuBean menuBean;
/**
* Vérifie si l'utilisateur a le droit d'accéder à une page donnée.
* Si non autorisé, redirige vers la page access-denied.
*
* @param allowedRoles Rôles autorisés séparés par des virgules (ex: "ADMIN,TRESORIER")
* @return true si autorisé, false sinon (après redirection)
*/
public boolean checkAccessOrRedirect(String allowedRoles) {
if (allowedRoles == null || allowedRoles.trim().isEmpty()) {
// Aucune restriction = accès autorisé pour tous les utilisateurs authentifiés
return !securityIdentity.isAnonymous();
}
String[] roles = allowedRoles.split(",");
boolean hasAccess = false;
for (String role : roles) {
String trimmedRole = role.trim();
if (hasRole(trimmedRole)) {
hasAccess = true;
break;
}
}
if (!hasAccess) {
LOG.warnf("Accès refusé pour l'utilisateur %s à une page nécessitant les rôles: %s",
securityIdentity.getPrincipal().getName(), allowedRoles);
redirectToAccessDenied();
return false;
}
return true;
}
/**
* Vérifie si l'utilisateur possède un rôle spécifique.
*
* @param role Le rôle à vérifier
* @return true si l'utilisateur a ce rôle
*/
private boolean hasRole(String role) {
return switch (role) {
case "SUPER_ADMIN" -> menuBean.isSuperAdmin();
case "ADMIN_ORGANISATION", "ADMIN" -> menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "TRESORIER" -> menuBean.isTresorier() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "SECRETAIRE" -> menuBean.isSecretaire() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "RESPONSABLE_SOCIAL" -> menuBean.isResponsableSocial() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "RESPONSABLE_EVENEMENTS" -> menuBean.isResponsableEvenements() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "RESPONSABLE_CREDIT" -> menuBean.isResponsableCredit() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "MEMBRE_BUREAU" -> menuBean.isMembreBureau() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "MEMBRE_ACTIF" -> menuBean.isMembreActif() || menuBean.isMembreBureau() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "MEMBRE_SIMPLE" -> menuBean.isMembreSimple() || menuBean.isMembreActif() || menuBean.isMembreBureau() || menuBean.isAdminOrganisation() || menuBean.isSuperAdmin();
case "ALL" -> !securityIdentity.isAnonymous(); // Tous les utilisateurs authentifiés
default -> {
LOG.warnf("Rôle inconnu: %s", role);
yield false;
}
};
}
/**
* Redirige vers la page d'accès refusé.
*/
private void redirectToAccessDenied() {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx != null && !ctx.getResponseComplete()) {
String contextPath = ctx.getExternalContext().getRequestContextPath();
ctx.getExternalContext().redirect(contextPath + ACCESS_DENIED_PAGE);
ctx.responseComplete();
}
} catch (IOException e) {
LOG.error("Erreur lors de la redirection vers access-denied", e);
}
}
// ═══════════════════════════════════════════════════════════════════════
// Méthodes helper pour vérifications rapides (utilisées dans les pages)
// ═══════════════════════════════════════════════════════════════════════
/**
* Vérifie si l'utilisateur peut gérer les membres.
* @return true si SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageMembers() {
return hasRole("SECRETAIRE");
}
/**
* Vérifie si l'utilisateur peut gérer les finances.
* @return true si TRESORIER, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageFinances() {
return hasRole("TRESORIER");
}
/**
* Vérifie si l'utilisateur peut gérer les événements.
* @return true si RESPONSABLE_EVENEMENTS, SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageEvents() {
return hasRole("RESPONSABLE_EVENEMENTS");
}
/**
* Vérifie si l'utilisateur peut gérer les aides sociales.
* @return true si RESPONSABLE_SOCIAL, ADMIN, ou SUPER_ADMIN
*/
public boolean canManageSocialAid() {
return hasRole("RESPONSABLE_SOCIAL");
}
/**
* Vérifie si l'utilisateur peut voir les rapports financiers.
* @return true si TRESORIER, SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canViewFinancialReports() {
return hasRole("TRESORIER") || hasRole("SECRETAIRE");
}
/**
* Vérifie si l'utilisateur peut exporter des données.
* @return true si TRESORIER, SECRETAIRE, ADMIN, ou SUPER_ADMIN
*/
public boolean canExportData() {
return hasRole("TRESORIER") || hasRole("SECRETAIRE");
}
/**
* Vérifie si l'utilisateur est un simple membre (MEMBRE_ACTIF uniquement).
* @return true si MEMBRE_ACTIF mais pas d'autre rôle administratif
*/
public boolean isSimpleMember() {
return menuBean.isMembreActif() &&
!menuBean.isSecretaire() &&
!menuBean.isTresorier() &&
!menuBean.isResponsableSocial() &&
!menuBean.isResponsableEvenements() &&
!menuBean.isResponsableCredit() &&
!menuBean.isMembreBureau() &&
!menuBean.isAdminOrganisation() &&
!menuBean.isSuperAdmin();
}
}

View File

@@ -1,37 +1,37 @@
package dev.lions.unionflow.client.converter;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.convert.ConverterException;
import jakarta.faces.convert.FacesConverter;
import java.util.UUID;
/**
* Convertisseur JSF pour les paramètres de vue et champs liés à {@link UUID}.
* Permet la conversion String ↔ UUID dans les f:viewParam et composants d'entrée.
*/
@FacesConverter(value = "uuidConverter", managed = true)
public class UuidConverter implements Converter<UUID> {
@Override
public UUID getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isBlank()) {
return null;
}
try {
return UUID.fromString(value.trim());
} catch (IllegalArgumentException e) {
throw new ConverterException("Identifiant invalide : " + value, e);
}
}
@Override
public String getAsString(FacesContext context, UIComponent component, UUID value) {
if (value == null) {
return "";
}
return value.toString();
}
}
package dev.lions.unionflow.client.converter;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.convert.ConverterException;
import jakarta.faces.convert.FacesConverter;
import java.util.UUID;
/**
* Convertisseur JSF pour les paramètres de vue et champs liés à {@link UUID}.
* Permet la conversion String ↔ UUID dans les f:viewParam et composants d'entrée.
*/
@FacesConverter(value = "uuidConverter", managed = true)
public class UuidConverter implements Converter<UUID> {
@Override
public UUID getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isBlank()) {
return null;
}
try {
return UUID.fromString(value.trim());
} catch (IllegalArgumentException e) {
throw new ConverterException("Identifiant invalide : " + value, e);
}
}
@Override
public String getAsString(FacesContext context, UIComponent component, UUID value) {
if (value == null) {
return "";
}
return value.toString();
}
}

View File

@@ -1,23 +1,23 @@
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.api.dto.MembreDashboardResponse;
import dev.lions.unionflow.client.security.AuthHeaderFactory;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(configKey = "unionflow-api")
@RegisterClientHeaders(AuthHeaderFactory.class)
@Path("/api/dashboard/membre")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface MembreDashboardRestClient {
@GET
@Path("/me")
MembreDashboardResponse getMonDashboard();
}
package dev.lions.unionflow.client.service;
import dev.lions.unionflow.client.api.dto.MembreDashboardResponse;
import dev.lions.unionflow.client.security.AuthHeaderFactory;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(configKey = "unionflow-api")
@RegisterClientHeaders(AuthHeaderFactory.class)
@Path("/api/dashboard/membre")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface MembreDashboardRestClient {
@GET
@Path("/me")
MembreDashboardResponse getMonDashboard();
}