Update client web: Add OIDC configuration, new service layer, and Freya Theme integration

This commit is contained in:
dahoud
2025-10-07 20:18:22 +00:00
parent 9aff13a830
commit 6e7cb13101
7 changed files with 403 additions and 205 deletions

18
pom.xml
View File

@@ -7,7 +7,7 @@
<groupId>com.gbcm</groupId>
<artifactId>gbcm-client-impl-quarkus</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<packaging>jar</packaging>
<name>GBCM Client Implementation - Quarkus</name>
<description>Interface web GBCM avec Quarkus et PrimeFaces</description>
@@ -55,16 +55,14 @@
<artifactId>quarkus-oidc</artifactId>
</dependency>
<!-- Jakarta Faces (JSF) -->
<!-- Quarkus Web -->
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-impl</artifactId>
<version>4.0.1</version>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-api</artifactId>
<version>4.0.1</version>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-qute</artifactId>
</dependency>
<!-- PrimeFaces -->
@@ -75,13 +73,15 @@
<classifier>jakarta</classifier>
</dependency>
<!-- Freya Theme -->
<!-- Freya Theme - Temporairement désactivé -->
<!--
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>freya</artifactId>
<version>${freya.version}</version>
<classifier>jakarta</classifier>
</dependency>
-->
<!-- CDI -->
<dependency>

View File

@@ -1,195 +0,0 @@
package com.gbcm.client.beans.auth;
import com.gbcm.client.service.GBCMServerClient;
import com.gbcm.server.api.dto.UserDTO;
import com.gbcm.server.api.dto.LoginRequestDTO;
import com.gbcm.server.api.dto.LoginResponseDTO;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.Serializable;
/**
* Managed Bean pour la gestion de l'authentification
*/
@Named("authBean")
@SessionScoped
public class AuthBean implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private GBCMServerClient serverClient;
// Propriétés de connexion
private String email;
private String password;
private boolean rememberMe;
// Utilisateur connecté
private UserDTO currentUser;
private String authToken;
private boolean authenticated = false;
/**
* Tentative de connexion
*/
public String login() {
try {
LoginRequestDTO loginRequest = new LoginRequestDTO();
loginRequest.setEmail(email);
loginRequest.setPassword(password);
loginRequest.setRememberMe(rememberMe);
LoginResponseDTO response = serverClient.login(loginRequest);
if (response.isSuccess()) {
this.currentUser = response.getUser();
this.authToken = response.getToken();
this.authenticated = true;
// Message de succès
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
"Connexion réussie",
"Bienvenue " + currentUser.getFirstName()));
// Redirection selon le rôle
return redirectAfterLogin();
} else {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Erreur de connexion",
response.getMessage()));
return null;
}
} catch (Exception e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Erreur système",
"Impossible de se connecter au serveur"));
return null;
}
}
/**
* Déconnexion
*/
public String logout() {
try {
if (authToken != null) {
serverClient.logout(authToken);
}
} catch (Exception e) {
// Log l'erreur mais continue la déconnexion
}
// Nettoyage de la session
this.currentUser = null;
this.authToken = null;
this.authenticated = false;
this.email = null;
this.password = null;
this.rememberMe = false;
// Invalidation de la session JSF
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/index?faces-redirect=true";
}
/**
* Redirection après connexion selon le rôle
*/
private String redirectAfterLogin() {
if (currentUser == null) {
return "/index?faces-redirect=true";
}
switch (currentUser.getRole()) {
case "ADMIN":
return "/pages/admin/dashboard?faces-redirect=true";
case "COACH":
return "/pages/coaching/dashboard?faces-redirect=true";
case "CLIENT":
return "/pages/dashboard/client?faces-redirect=true";
default:
return "/pages/dashboard/home?faces-redirect=true";
}
}
/**
* Navigation vers le profil
*/
public String goToProfile() {
return "/pages/profile/edit?faces-redirect=true";
}
/**
* Navigation vers les paramètres
*/
public String goToSettings() {
return "/pages/settings/general?faces-redirect=true";
}
/**
* Vérification des permissions
*/
public boolean hasRole(String role) {
return authenticated && currentUser != null &&
role.equals(currentUser.getRole());
}
public boolean isAdmin() {
return hasRole("ADMIN");
}
public boolean isCoach() {
return hasRole("COACH");
}
public boolean isClient() {
return hasRole("CLIENT");
}
// Getters et Setters
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
public UserDTO getCurrentUser() {
return currentUser;
}
public String getAuthToken() {
return authToken;
}
public boolean isAuthenticated() {
return authenticated;
}
}

View File

@@ -0,0 +1,140 @@
package com.gbcm.client.service;
import com.gbcm.server.api.dto.auth.LoginRequestDTO;
import com.gbcm.server.api.dto.auth.LoginResponseDTO;
import com.gbcm.server.api.dto.user.UserDTO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Client REST pour communiquer avec l'API GBCM Server
*
* @author GBCM Team
* @version 1.0.0
* @since 2025-01-01
*/
@ApplicationScoped
public class GBCMServerClient {
private static final Logger logger = LoggerFactory.getLogger(GBCMServerClient.class);
private static final String BASE_URL = "http://localhost:8080/api";
private final Client client;
public GBCMServerClient() {
this.client = ClientBuilder.newClient();
}
/**
* Authentification utilisateur
*
* @param loginRequest Données de connexion
* @return Réponse d'authentification
*/
public LoginResponseDTO login(LoginRequestDTO loginRequest) {
try {
logger.info("Tentative de connexion pour l'utilisateur: {}", loginRequest.getEmail());
Response response = client
.target(BASE_URL + "/auth/login")
.request(MediaType.APPLICATION_JSON)
.post(Entity.entity(loginRequest, MediaType.APPLICATION_JSON));
if (response.getStatus() == 200) {
LoginResponseDTO loginResponse = response.readEntity(LoginResponseDTO.class);
logger.info("Connexion réussie pour l'utilisateur: {}", loginRequest.getEmail());
return loginResponse;
} else {
logger.error("Échec de la connexion pour l'utilisateur: {}. Status: {}",
loginRequest.getEmail(), response.getStatus());
throw new RuntimeException("Échec de l'authentification: " + response.getStatus());
}
} catch (Exception e) {
logger.error("Erreur lors de la connexion pour l'utilisateur: {}", loginRequest.getEmail(), e);
throw new RuntimeException("Erreur de communication avec le serveur", e);
}
}
/**
* Déconnexion utilisateur
*
* @param token Token JWT
*/
public void logout(String token) {
try {
logger.info("Déconnexion de l'utilisateur");
Response response = client
.target(BASE_URL + "/auth/logout")
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + token)
.post(Entity.text(""));
if (response.getStatus() == 200) {
logger.info("Déconnexion réussie");
} else {
logger.warn("Problème lors de la déconnexion. Status: {}", response.getStatus());
}
} catch (Exception e) {
logger.error("Erreur lors de la déconnexion", e);
}
}
/**
* Récupération du profil utilisateur
*
* @param token Token JWT
* @return Profil utilisateur
*/
public UserDTO getUserProfile(String token) {
try {
logger.info("Récupération du profil utilisateur");
Response response = client
.target(BASE_URL + "/users/profile")
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + token)
.get();
if (response.getStatus() == 200) {
UserDTO user = response.readEntity(UserDTO.class);
logger.info("Profil utilisateur récupéré: {}", user.getEmail());
return user;
} else {
logger.error("Échec de la récupération du profil. Status: {}", response.getStatus());
throw new RuntimeException("Impossible de récupérer le profil utilisateur");
}
} catch (Exception e) {
logger.error("Erreur lors de la récupération du profil utilisateur", e);
throw new RuntimeException("Erreur de communication avec le serveur", e);
}
}
/**
* Validation d'un token JWT
*
* @param token Token à valider
* @return true si le token est valide
*/
public boolean validateToken(String token) {
try {
Response response = client
.target(BASE_URL + "/auth/validate")
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + token)
.get();
return response.getStatus() == 200;
} catch (Exception e) {
logger.error("Erreur lors de la validation du token", e);
return false;
}
}
}

View File

@@ -0,0 +1,42 @@
package com.gbcm.client.web;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contrôleur principal pour l'interface web GBCM
*
* @author GBCM Team
* @version 1.0.0
* @since 2025-01-01
*/
@Path("/")
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@Inject
Template index;
@GET
@Produces(MediaType.TEXT_HTML)
public TemplateInstance home() {
logger.info("Accès à la page d'accueil GBCM");
return index.data("title", "GBCM - Global Business Consulting & Management")
.data("message", "Bienvenue dans l'interface web GBCM");
}
@GET
@Path("/health")
@Produces(MediaType.APPLICATION_JSON)
public String health() {
return "{\"status\":\"UP\",\"service\":\"gbcm-client-web\"}";
}
}

View File

@@ -12,8 +12,17 @@ quarkus.servlet.context-path=/gbcm
# Security Configuration
quarkus.security.auth.enabled=true
# Keycloak OIDC Configuration
quarkus.oidc.auth-server-url=http://localhost:8180/realms/gbcm-llc
quarkus.oidc.client-id=gbcm-web-client
quarkus.oidc.credentials.secret=gbcm-web-secret
quarkus.oidc.tls.verification=none
quarkus.oidc.application-type=web-app
quarkus.oidc.authentication.redirect-path=/gbcm/login
quarkus.oidc.authentication.restore-path-after-redirect=true
# REST Client Configuration - GBCM Server API
gbcm.server.api.url=http://localhost:8081/api/v1
gbcm.server.api.url=http://localhost:8082/api
quarkus.rest-client."com.gbcm.client.service.GBCMServerClient".url=${gbcm.server.api.url}
# Database Configuration (for session storage)

View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1976D2 0%, #1565C0 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
background: white;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
padding: 3rem;
max-width: 600px;
width: 90%;
text-align: center;
}
.logo {
font-size: 2.5rem;
font-weight: bold;
color: #1976D2;
margin-bottom: 1rem;
}
.subtitle {
color: #666;
font-size: 1.1rem;
margin-bottom: 2rem;
}
.message {
font-size: 1.2rem;
color: #333;
margin-bottom: 2rem;
}
.status {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
margin-bottom: 2rem;
}
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
background: #4CAF50;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.feature {
background: #f5f5f5;
padding: 1rem;
border-radius: 8px;
border-left: 4px solid #1976D2;
}
.feature h3 {
color: #1976D2;
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.feature p {
color: #666;
font-size: 0.8rem;
}
.footer {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid #eee;
color: #999;
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="container">
<div class="logo">GBCM</div>
<div class="subtitle">Global Business Consulting & Management</div>
<div class="message">{message}</div>
<div class="status">
<div class="status-indicator"></div>
<span>Interface Web Active</span>
</div>
<div class="features">
<div class="feature">
<h3>🏢 Gestion Clients</h3>
<p>Suivi complet des prospects et clients</p>
</div>
<div class="feature">
<h3>👨‍💼 Gestion Coaches</h3>
<p>Planification et suivi des coaches</p>
</div>
<div class="feature">
<h3>🎯 Ateliers</h3>
<p>Organisation des workshops</p>
</div>
<div class="feature">
<h3>💬 Sessions</h3>
<p>Coaching individuel personnalisé</p>
</div>
</div>
<div class="footer">
Interface Web GBCM v1.0.0 - Powered by Quarkus
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>GBCM - Global Business Consulting and Management</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<h:outputStylesheet library="css" name="freya-theme.css"/>
</h:head>
<h:body>
<div class="layout-wrapper">
<div class="layout-main">
<div class="layout-content">
<div class="layout-content-inner">
<div class="content-section introduction">
<div class="feature-intro">
<h1>GBCM Client Web</h1>
<p>Interface web pour la plateforme GBCM</p>
</div>
</div>
<div class="content-section implementation">
<div class="card">
<h2>Bienvenue sur GBCM</h2>
<p>Global Business Consulting and Management - Plateforme de conseil en entreprise</p>
<h:form>
<div class="p-grid">
<div class="p-col-12 p-md-6">
<div class="card">
<h3>Authentification</h3>
<p:commandButton value="Se connecter"
action="#{authBean.redirectToLogin}"
styleClass="ui-button-success"
icon="pi pi-sign-in"/>
</div>
</div>
<div class="p-col-12 p-md-6">
<div class="card">
<h3>Statut du serveur</h3>
<p:outputLabel value="Backend API : " />
<p:outputLabel value="http://localhost:8080"
styleClass="ui-state-highlight"/>
</div>
</div>
</div>
</h:form>
</div>
</div>
</div>
</div>
</div>
</div>
</h:body>
</html>