535 lines
19 KiB
Java
535 lines
19 KiB
Java
package dev.lions.unionflow.client.view;
|
|
|
|
import jakarta.enterprise.context.SessionScoped;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.inject.Named;
|
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import java.util.logging.Logger;
|
|
|
|
/**
|
|
* Gestion de la session utilisateur avec Keycloak OIDC
|
|
*
|
|
* @author UnionFlow Team
|
|
* @version 2.0
|
|
*/
|
|
@Named("userSession")
|
|
@SessionScoped
|
|
public class UserSession implements Serializable {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private static final Logger LOGGER = Logger.getLogger(UserSession.class.getName());
|
|
|
|
@Inject
|
|
private JsonWebToken jwt;
|
|
|
|
private String username;
|
|
private boolean authenticated = false;
|
|
private String typeCompte;
|
|
private List<String> roles;
|
|
private List<String> permissions;
|
|
private CurrentUser currentUser;
|
|
private EntiteInfo entite;
|
|
|
|
public UserSession() {
|
|
// Session par défaut non authentifiée
|
|
clearSession();
|
|
}
|
|
|
|
/**
|
|
* Initialise la session depuis le token OIDC Keycloak
|
|
* Appelé automatiquement après l'authentification
|
|
*/
|
|
public void initializeFromOidcToken() {
|
|
if (jwt != null && jwt.getName() != null) {
|
|
this.authenticated = true;
|
|
this.username = jwt.getClaim("preferred_username");
|
|
if (this.username == null) {
|
|
this.username = jwt.getName();
|
|
}
|
|
|
|
// Récupérer les informations du token
|
|
String email = jwt.getClaim("email");
|
|
String givenName = jwt.getClaim("given_name");
|
|
String familyName = jwt.getClaim("family_name");
|
|
|
|
// Récupérer les rôles depuis le token
|
|
this.roles = extractRolesFromToken();
|
|
LOGGER.info("Rôles assignés à this.roles: " + this.roles);
|
|
LOGGER.info("Vérification contains('SUPER_ADMIN'): " + (this.roles != null && this.roles.contains("SUPER_ADMIN")));
|
|
this.typeCompte = determineTypeCompte();
|
|
LOGGER.info("Type de compte déterminé: " + this.typeCompte);
|
|
|
|
// Mettre à jour les informations utilisateur
|
|
this.currentUser = new CurrentUser();
|
|
this.currentUser.setUsername(this.username);
|
|
this.currentUser.setEmail(email);
|
|
this.currentUser.setPrenom(givenName);
|
|
this.currentUser.setNom(familyName);
|
|
|
|
// Générer un ID depuis le subject du token
|
|
String subject = jwt.getSubject();
|
|
if (subject != null) {
|
|
try {
|
|
this.currentUser.setId(UUID.fromString(subject));
|
|
} catch (IllegalArgumentException e) {
|
|
// Si le subject n'est pas un UUID, générer un UUID déterministe
|
|
this.currentUser.setId(UUID.nameUUIDFromBytes(subject.getBytes()));
|
|
}
|
|
}
|
|
|
|
LOGGER.info("Session utilisateur initialisée depuis Keycloak pour: " + this.username +
|
|
" (Type: " + typeCompte + ")");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convertit un objet JSON en String de manière sécurisée
|
|
* Gère les cas où l'objet est un JsonStringImpl, String, ou autre type
|
|
*/
|
|
private String convertToString(Object obj) {
|
|
if (obj == null) {
|
|
return null;
|
|
}
|
|
if (obj instanceof String) {
|
|
String str = (String) obj;
|
|
// Nettoyer les guillemets qui pourraient être présents
|
|
str = str.trim();
|
|
if (str.startsWith("'") && str.endsWith("'") && str.length() > 1) {
|
|
str = str.substring(1, str.length() - 1);
|
|
}
|
|
if (str.startsWith("\"") && str.endsWith("\"") && str.length() > 1) {
|
|
str = str.substring(1, str.length() - 1);
|
|
}
|
|
return str.trim();
|
|
}
|
|
// Gérer JsonStringImpl et autres types JSON
|
|
String str = obj.toString();
|
|
// Nettoyer les guillemets qui pourraient être présents
|
|
str = str.trim();
|
|
if (str.startsWith("'") && str.endsWith("'") && str.length() > 1) {
|
|
str = str.substring(1, str.length() - 1);
|
|
}
|
|
if (str.startsWith("\"") && str.endsWith("\"") && str.length() > 1) {
|
|
str = str.substring(1, str.length() - 1);
|
|
}
|
|
return str.trim();
|
|
}
|
|
|
|
/**
|
|
* Extrait et convertit une liste de rôles depuis un objet JSON
|
|
*/
|
|
private List<String> extractRolesFromList(Object rolesObj) {
|
|
List<String> roles = new ArrayList<>();
|
|
if (rolesObj instanceof List) {
|
|
@SuppressWarnings("unchecked")
|
|
List<Object> rolesList = (List<Object>) rolesObj;
|
|
for (Object roleObj : rolesList) {
|
|
String role = convertToString(roleObj);
|
|
if (role != null && !role.isEmpty()) {
|
|
// S'assurer que c'est vraiment un String en créant une nouvelle instance
|
|
roles.add(new String(role));
|
|
LOGGER.fine("Rôle converti: '" + role + "' (type: " + role.getClass().getName() + ")");
|
|
}
|
|
}
|
|
}
|
|
return roles;
|
|
}
|
|
|
|
/**
|
|
* Extrait les rôles depuis le token JWT
|
|
*/
|
|
private List<String> extractRolesFromToken() {
|
|
List<String> extractedRoles = new ArrayList<>();
|
|
|
|
// Rôles dans "realm_access.roles"
|
|
try {
|
|
Object realmAccess = jwt.getClaim("realm_access");
|
|
if (realmAccess instanceof java.util.Map) {
|
|
@SuppressWarnings("unchecked")
|
|
java.util.Map<String, Object> realmMap = (java.util.Map<String, Object>) realmAccess;
|
|
Object rolesObj = realmMap.get("roles");
|
|
List<String> realmRoles = extractRolesFromList(rolesObj);
|
|
if (!realmRoles.isEmpty()) {
|
|
extractedRoles.addAll(realmRoles);
|
|
LOGGER.info("Rôles extraits depuis realm_access.roles: " + realmRoles);
|
|
}
|
|
} else if (realmAccess instanceof List) {
|
|
// Fallback: si realm_access est directement une liste de rôles
|
|
List<String> realmRoles = extractRolesFromList(realmAccess);
|
|
if (!realmRoles.isEmpty()) {
|
|
extractedRoles.addAll(realmRoles);
|
|
LOGGER.info("Rôles extraits depuis realm_access (liste): " + realmRoles);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
LOGGER.warning("Erreur lors de l'extraction des rôles realm: " + e.getMessage());
|
|
}
|
|
|
|
// Rôles dans "resource_access"
|
|
try {
|
|
Object resourceAccess = jwt.getClaim("resource_access");
|
|
if (resourceAccess instanceof java.util.Map) {
|
|
@SuppressWarnings("unchecked")
|
|
java.util.Map<String, Object> resourceMap = (java.util.Map<String, Object>) resourceAccess;
|
|
for (Object value : resourceMap.values()) {
|
|
if (value instanceof java.util.Map) {
|
|
@SuppressWarnings("unchecked")
|
|
java.util.Map<String, Object> clientMap = (java.util.Map<String, Object>) value;
|
|
Object rolesObj = clientMap.get("roles");
|
|
List<String> clientRoles = extractRolesFromList(rolesObj);
|
|
if (!clientRoles.isEmpty()) {
|
|
extractedRoles.addAll(clientRoles);
|
|
LOGGER.info("Rôles extraits depuis resource_access: " + clientRoles);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
LOGGER.warning("Erreur lors de l'extraction des rôles client: " + e.getMessage());
|
|
}
|
|
|
|
// Fallback: essayer d'extraire les rôles depuis le claim "roles" directement
|
|
if (extractedRoles.isEmpty()) {
|
|
try {
|
|
Object rolesClaim = jwt.getClaim("roles");
|
|
List<String> directRoles = extractRolesFromList(rolesClaim);
|
|
if (!directRoles.isEmpty()) {
|
|
extractedRoles.addAll(directRoles);
|
|
LOGGER.info("Rôles extraits depuis claim 'roles': " + directRoles);
|
|
}
|
|
} catch (Exception e) {
|
|
LOGGER.warning("Erreur lors de l'extraction des rôles depuis claim 'roles': " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
LOGGER.info("Total des rôles extraits: " + extractedRoles);
|
|
return extractedRoles;
|
|
}
|
|
|
|
/**
|
|
* Détermine le type de compte depuis les rôles
|
|
*/
|
|
private String determineTypeCompte() {
|
|
// Utiliser this.roles pour s'assurer qu'on utilise la bonne variable d'instance
|
|
List<String> rolesToCheck = this.roles;
|
|
|
|
if (rolesToCheck == null || rolesToCheck.isEmpty()) {
|
|
LOGGER.warning("Aucun rôle trouvé, type de compte par défaut: MEMBRE");
|
|
return "MEMBRE";
|
|
}
|
|
|
|
LOGGER.info("Détermination du type de compte depuis les rôles: " + rolesToCheck);
|
|
LOGGER.info("Nombre de rôles: " + rolesToCheck.size());
|
|
|
|
// Vérifier le type des éléments de la liste
|
|
if (!rolesToCheck.isEmpty()) {
|
|
Object firstRole = rolesToCheck.get(0);
|
|
LOGGER.info("Type du premier rôle: " + (firstRole != null ? firstRole.getClass().getName() : "null"));
|
|
LOGGER.info("Premier rôle (toString): '" + firstRole + "'");
|
|
LOGGER.info("Premier rôle (length): " + (firstRole != null ? firstRole.toString().length() : 0));
|
|
// Vérifier les caractères du premier rôle
|
|
if (firstRole != null) {
|
|
String firstRoleStr = firstRole.toString();
|
|
LOGGER.info("Premier rôle (bytes): " + java.util.Arrays.toString(firstRoleStr.getBytes()));
|
|
}
|
|
}
|
|
|
|
// Vérifier SUPER_ADMIN en parcourant la liste (plus robuste que contains)
|
|
for (String role : rolesToCheck) {
|
|
if (role != null) {
|
|
// Nettoyer la chaîne : retirer les guillemets et espaces
|
|
String roleStr = role.toString().trim();
|
|
// Retirer les guillemets simples et doubles au début et à la fin
|
|
if (roleStr.startsWith("'") && roleStr.endsWith("'")) {
|
|
roleStr = roleStr.substring(1, roleStr.length() - 1);
|
|
}
|
|
if (roleStr.startsWith("\"") && roleStr.endsWith("\"")) {
|
|
roleStr = roleStr.substring(1, roleStr.length() - 1);
|
|
}
|
|
roleStr = roleStr.trim();
|
|
|
|
LOGGER.info("Vérification du rôle: '" + roleStr + "' (longueur: " + roleStr.length() + ", original: '" + role + "')");
|
|
if ("SUPER_ADMIN".equals(roleStr) || "super-admin".equalsIgnoreCase(roleStr)) {
|
|
LOGGER.info("✅ Type de compte détecté: SUPER_ADMIN (rôle trouvé: '" + roleStr + "')");
|
|
return "SUPER_ADMIN";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: utiliser contains() pour compatibilité
|
|
boolean hasSuperAdmin = rolesToCheck.contains("SUPER_ADMIN");
|
|
boolean hasSuperAdminLower = rolesToCheck.contains("super-admin");
|
|
LOGGER.info("Contient 'SUPER_ADMIN' (contains): " + hasSuperAdmin);
|
|
LOGGER.info("Contient 'super-admin' (contains): " + hasSuperAdminLower);
|
|
|
|
if (hasSuperAdmin || hasSuperAdminLower) {
|
|
LOGGER.info("✅ Type de compte détecté: SUPER_ADMIN (via contains)");
|
|
return "SUPER_ADMIN";
|
|
}
|
|
|
|
// Vérifier ADMIN_ENTITE (mais pas si c'est juste "ADMIN" qui pourrait être ambigu)
|
|
if (rolesToCheck.contains("ADMIN_ENTITE")) {
|
|
LOGGER.info("Type de compte détecté: ADMIN_ENTITE");
|
|
return "ADMIN_ENTITE";
|
|
}
|
|
|
|
// Vérifier les autres rôles admin (avec précaution pour éviter les faux positifs)
|
|
for (String role : rolesToCheck) {
|
|
if (role != null && (role.equals("ADMIN") || role.equalsIgnoreCase("admin"))) {
|
|
LOGGER.info("Type de compte détecté: ADMIN_ENTITE (via rôle ADMIN)");
|
|
return "ADMIN_ENTITE";
|
|
}
|
|
}
|
|
|
|
LOGGER.warning("Aucun rôle admin trouvé, type de compte par défaut: MEMBRE");
|
|
return "MEMBRE";
|
|
}
|
|
|
|
public void clearSession() {
|
|
this.authenticated = false;
|
|
this.username = null;
|
|
this.typeCompte = null;
|
|
this.roles = null;
|
|
this.permissions = null;
|
|
this.currentUser = null;
|
|
this.entite = null;
|
|
|
|
LOGGER.info("Session utilisateur effacée");
|
|
}
|
|
|
|
// Méthodes de vérification des rôles et permissions
|
|
public boolean hasRole(String role) {
|
|
return roles != null && roles.contains(role);
|
|
}
|
|
|
|
public boolean hasPermission(String permission) {
|
|
return permissions != null && permissions.contains(permission);
|
|
}
|
|
|
|
public boolean isSuperAdmin() {
|
|
return "SUPER_ADMIN".equals(typeCompte) || hasRole("SUPER_ADMIN");
|
|
}
|
|
|
|
public boolean isAdmin() {
|
|
return isSuperAdmin() || "ADMIN_ENTITE".equals(typeCompte) || hasRole("ADMIN_ENTITE");
|
|
}
|
|
|
|
public boolean isMembre() {
|
|
return "MEMBRE".equals(typeCompte) || hasRole("MEMBRE");
|
|
}
|
|
|
|
// Méthode pour obtenir le rôle principal
|
|
public String getRole() {
|
|
if (isSuperAdmin()) {
|
|
return "SUPER_ADMIN";
|
|
}
|
|
if (isAdmin()) {
|
|
return "ADMIN";
|
|
}
|
|
if (typeCompte != null) {
|
|
return typeCompte;
|
|
}
|
|
if (roles != null && !roles.isEmpty()) {
|
|
return roles.get(0);
|
|
}
|
|
return "MEMBER";
|
|
}
|
|
|
|
// Getters et Setters
|
|
public String getUsername() {
|
|
return username;
|
|
}
|
|
|
|
public void setUsername(String username) {
|
|
this.username = username;
|
|
}
|
|
|
|
public boolean isAuthenticated() {
|
|
// Vérifier via JsonWebToken
|
|
if (jwt != null && jwt.getName() != null && !authenticated) {
|
|
initializeFromOidcToken();
|
|
}
|
|
return authenticated || (jwt != null && jwt.getName() != null);
|
|
}
|
|
|
|
public void setAuthenticated(boolean authenticated) {
|
|
this.authenticated = authenticated;
|
|
}
|
|
|
|
public String getTypeCompte() {
|
|
// Si le type de compte n'est pas encore déterminé, l'initialiser
|
|
if (typeCompte == null && jwt != null && jwt.getName() != null) {
|
|
LOGGER.info("getTypeCompte() appelé avant initialisation, initialisation en cours...");
|
|
initializeFromOidcToken();
|
|
}
|
|
return typeCompte;
|
|
}
|
|
|
|
public void setTypeCompte(String typeCompte) {
|
|
this.typeCompte = typeCompte;
|
|
}
|
|
|
|
public List<String> getRoles() {
|
|
return roles;
|
|
}
|
|
|
|
public void setRoles(List<String> roles) {
|
|
this.roles = roles;
|
|
}
|
|
|
|
public List<String> getPermissions() {
|
|
return permissions;
|
|
}
|
|
|
|
public void setPermissions(List<String> permissions) {
|
|
this.permissions = permissions;
|
|
}
|
|
|
|
public CurrentUser getCurrentUser() {
|
|
return currentUser;
|
|
}
|
|
|
|
public void setCurrentUser(CurrentUser currentUser) {
|
|
this.currentUser = currentUser;
|
|
}
|
|
|
|
public EntiteInfo getEntite() {
|
|
return entite;
|
|
}
|
|
|
|
public void setEntite(EntiteInfo entite) {
|
|
this.entite = entite;
|
|
}
|
|
|
|
// Classes internes
|
|
public static class CurrentUser implements Serializable {
|
|
private UUID id;
|
|
private String nom;
|
|
private String prenom;
|
|
private String email;
|
|
private String username;
|
|
|
|
public String getNomComplet() {
|
|
if (prenom != null && nom != null) {
|
|
return prenom + " " + nom;
|
|
}
|
|
return nom != null ? nom : username;
|
|
}
|
|
|
|
public String getInitiales() {
|
|
StringBuilder initiales = new StringBuilder();
|
|
if (prenom != null && !prenom.isEmpty()) {
|
|
initiales.append(prenom.charAt(0));
|
|
}
|
|
if (nom != null && !nom.isEmpty()) {
|
|
initiales.append(nom.charAt(0));
|
|
}
|
|
return initiales.toString().toUpperCase();
|
|
}
|
|
|
|
// Getters et Setters
|
|
public UUID getId() {
|
|
return id;
|
|
}
|
|
|
|
public void setId(UUID id) {
|
|
this.id = id;
|
|
}
|
|
|
|
public String getNom() {
|
|
return nom;
|
|
}
|
|
|
|
public void setNom(String nom) {
|
|
this.nom = nom;
|
|
}
|
|
|
|
public String getPrenom() {
|
|
return prenom;
|
|
}
|
|
|
|
public void setPrenom(String prenom) {
|
|
this.prenom = prenom;
|
|
}
|
|
|
|
public String getEmail() {
|
|
return email;
|
|
}
|
|
|
|
public void setEmail(String email) {
|
|
this.email = email;
|
|
}
|
|
|
|
public String getUsername() {
|
|
return username;
|
|
}
|
|
|
|
public void setUsername(String username) {
|
|
this.username = username;
|
|
}
|
|
}
|
|
|
|
public static class EntiteInfo implements Serializable {
|
|
private UUID id;
|
|
private String nom;
|
|
private String type;
|
|
private String pays;
|
|
private String ville;
|
|
|
|
public String getDescription() {
|
|
StringBuilder desc = new StringBuilder();
|
|
if (nom != null) {
|
|
desc.append(nom);
|
|
}
|
|
if (ville != null && pays != null) {
|
|
desc.append(" (").append(ville).append(", ").append(pays).append(")");
|
|
}
|
|
return desc.toString();
|
|
}
|
|
|
|
// Getters et Setters
|
|
public UUID getId() {
|
|
return id;
|
|
}
|
|
|
|
public void setId(UUID id) {
|
|
this.id = id;
|
|
}
|
|
|
|
public String getNom() {
|
|
return nom;
|
|
}
|
|
|
|
public void setNom(String nom) {
|
|
this.nom = nom;
|
|
}
|
|
|
|
public String getType() {
|
|
return type;
|
|
}
|
|
|
|
public void setType(String type) {
|
|
this.type = type;
|
|
}
|
|
|
|
public String getPays() {
|
|
return pays;
|
|
}
|
|
|
|
public void setPays(String pays) {
|
|
this.pays = pays;
|
|
}
|
|
|
|
public String getVille() {
|
|
return ville;
|
|
}
|
|
|
|
public void setVille(String ville) {
|
|
this.ville = ville;
|
|
}
|
|
}
|
|
} |