chore(quarkus-327): bump to Quarkus 3.27.3 LTS, make pom autonomous, rename deprecated config keys
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user