From e23ed3f451940e863e4aec45c6b06f08bf3f5c1f Mon Sep 17 00:00:00 2001 From: dahoud Date: Sun, 9 Nov 2025 00:06:05 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Impl=C3=A9mentation=20compl=C3=A8te=20p?= =?UTF-8?q?age=20Profil=20utilisateur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProfileView.java: Bean avec données OIDC/Keycloak - Extraction: nom, email, rôles, dernière connexion, expiration - Interface complète: infos personnelles, rôles, sécurité - Bouton redirection vers compte Keycloak - Design moderne avec PrimeFaces + Flex 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../dev/lions/btpxpress/view/ProfileView.java | 218 ++++++++++++++++++ .../META-INF/resources/profile.xhtml | 199 +++++++++++++++- 2 files changed, 411 insertions(+), 6 deletions(-) create mode 100644 src/main/java/dev/lions/btpxpress/view/ProfileView.java diff --git a/src/main/java/dev/lions/btpxpress/view/ProfileView.java b/src/main/java/dev/lions/btpxpress/view/ProfileView.java new file mode 100644 index 0000000..4545e40 --- /dev/null +++ b/src/main/java/dev/lions/btpxpress/view/ProfileView.java @@ -0,0 +1,218 @@ +package dev.lions.btpxpress.view; + +import io.quarkus.oidc.IdToken; +import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.RequestScoped; +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.jwt.JsonWebToken; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Bean pour gérer la page de profil utilisateur. + * + * @author BTP Xpress Team + * @version 1.0 + */ +@Named("profileView") +@RequestScoped +@Getter +@Setter +@Slf4j +public class ProfileView { + + @Inject + SecurityIdentity securityIdentity; + + @Inject + @IdToken + JsonWebToken idToken; + + private String nomComplet; + private String prenom; + private String nom; + private String email; + private String username; + private String telephone; + private String organisation; + private List roles; + private String derniereConnexion; + private String tokenExpiration; + + @PostConstruct + public void init() { + try { + log.info("Initialisation du profil utilisateur"); + + if (securityIdentity != null && securityIdentity.getPrincipal() != null && idToken != null) { + // Nom complet + nomComplet = idToken.getClaim("name"); + if (nomComplet == null || nomComplet.trim().isEmpty()) { + nomComplet = idToken.getClaim("preferred_username"); + } + if (nomComplet == null || nomComplet.trim().isEmpty()) { + nomComplet = securityIdentity.getPrincipal().getName(); + } + + // Prénom et nom + prenom = idToken.getClaim("given_name"); + nom = idToken.getClaim("family_name"); + + // Email + email = idToken.getClaim("email"); + + // Username + username = idToken.getClaim("preferred_username"); + if (username == null || username.trim().isEmpty()) { + username = securityIdentity.getPrincipal().getName(); + } + + // Téléphone + telephone = idToken.getClaim("phone_number"); + + // Organisation + organisation = idToken.getClaim("organization"); + + // Rôles + roles = new ArrayList<>(); + Set userRoles = securityIdentity.getRoles(); + if (userRoles != null) { + for (String role : userRoles) { + // Formatage des rôles pour affichage + String formattedRole = role.replace("_", " ").replace("-", " "); + formattedRole = capitalizeWords(formattedRole); + roles.add(formattedRole); + } + } + + // Dernière connexion (auth_time claim) + Long authTime = idToken.getClaim("auth_time"); + if (authTime != null) { + LocalDateTime dateTime = LocalDateTime.ofInstant( + Instant.ofEpochSecond(authTime), + ZoneId.systemDefault() + ); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); + derniereConnexion = dateTime.format(formatter); + } else { + derniereConnexion = "Non disponible"; + } + + // Expiration du token + Long exp = idToken.getExpirationTime(); + if (exp != null) { + LocalDateTime dateTime = LocalDateTime.ofInstant( + Instant.ofEpochSecond(exp), + ZoneId.systemDefault() + ); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); + tokenExpiration = dateTime.format(formatter); + } else { + tokenExpiration = "Non disponible"; + } + + log.info("Profil chargé pour: {}", nomComplet); + + } else { + log.warn("SecurityIdentity ou IdToken non disponible"); + setDefaultValues(); + } + + } catch (Exception e) { + log.error("Erreur lors de l'initialisation du profil", e); + setDefaultValues(); + } + } + + /** + * Valeurs par défaut si les données ne sont pas disponibles. + */ + private void setDefaultValues() { + nomComplet = "Utilisateur"; + email = "utilisateur@btpxpress.com"; + username = "utilisateur"; + roles = new ArrayList<>(); + roles.add("Utilisateur"); + derniereConnexion = "Non disponible"; + tokenExpiration = "Non disponible"; + } + + /** + * Capitalize first letter of each word. + */ + private String capitalizeWords(String str) { + if (str == null || str.isEmpty()) { + return str; + } + String[] words = str.toLowerCase().split(" "); + StringBuilder result = new StringBuilder(); + for (String word : words) { + if (!word.isEmpty()) { + result.append(Character.toUpperCase(word.charAt(0))) + .append(word.substring(1)) + .append(" "); + } + } + return result.toString().trim(); + } + + /** + * Redirige vers la page de changement de mot de passe Keycloak. + */ + public void changerMotDePasse() { + try { + FacesContext facesContext = FacesContext.getCurrentInstance(); + jakarta.faces.context.ExternalContext externalContext = facesContext.getExternalContext(); + + // URL de la page account de Keycloak + String keycloakAccountUrl = "https://security.lions.dev/realms/btpxpress/account/"; + + externalContext.redirect(keycloakAccountUrl); + facesContext.responseComplete(); + + } catch (Exception e) { + log.error("Erreur lors de la redirection vers Keycloak account", e); + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, + "Erreur", "Impossible de rediriger vers la page de gestion du compte")); + } + } + + /** + * Retourne les initiales de l'utilisateur pour l'avatar. + */ + public String getInitiales() { + if (nomComplet == null || nomComplet.trim().isEmpty()) { + return "U"; + } + + String[] parts = nomComplet.trim().split("\\s+"); + if (parts.length >= 2) { + return String.valueOf(parts[0].charAt(0)).toUpperCase() + + String.valueOf(parts[1].charAt(0)).toUpperCase(); + } else if (parts.length == 1) { + return parts[0].substring(0, Math.min(2, parts[0].length())).toUpperCase(); + } + return "U"; + } + + /** + * Retourne le nombre total de rôles. + */ + public int getNombreRoles() { + return roles != null ? roles.size() : 0; + } +} diff --git a/src/main/resources/META-INF/resources/profile.xhtml b/src/main/resources/META-INF/resources/profile.xhtml index a56bc61..8a064ad 100644 --- a/src/main/resources/META-INF/resources/profile.xhtml +++ b/src/main/resources/META-INF/resources/profile.xhtml @@ -1,8 +1,8 @@ - Mon Profil - BTP Xpress @@ -10,14 +10,201 @@
+
-

Mon Profil

-

Module en cours de développement...

+
+
+ #{profileView.initiales} +
+
+

#{profileView.nomComplet}

+

+ + + #{role} + +

+
+
+
+
+ + +
+
+
+

+ + Informations Personnelles +

+
+ + + +
+
+ +
+ + #{profileView.username} +
+
+ +
+ +
+ + #{profileView.email} +
+
+ +
+ +
+ + #{profileView.prenom} +
+
+ +
+ +
+ + #{profileView.nom} +
+
+ +
+ +
+ + #{profileView.telephone} +
+
+ +
+ +
+ + #{profileView.organisation} +
+
+
+
+
+ + +
+ +
+

+ + Rôles et Permissions +

+ + + +
+
+ Rôles attribués + +
+ +
+ + + +
+
+
+ + +
+

+ + Sécurité +

+ + + +
+
+ +
+ + #{profileView.derniereConnexion} +
+
+ +
+ +
+ + #{profileView.tokenExpiration} +
+
+ +
+ + + +
+
+
+
+ + +
+
+

+ + Activité Récente +

+ + + +
+
+
+
+ +
+
Projets actifs
+
-
+
+
+ +
+
+
+ +
+
Tâches complétées
+
-
+
+
+ +
+
+
+ +
+
Notifications
+
-
+
+
+
+ +
-