diff --git a/pom.xml b/pom.xml index 874bd84..71aafee 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ io.quarkiverse.primefaces quarkus-primefaces - 3.13.3 + 3.15.1 @@ -58,6 +58,26 @@ jakarta + + + org.primefaces.themes + freya-theme-jakarta + 5.0.0 + + + + + io.quarkiverse.omnifaces + quarkus-omnifaces + 4.4.1 + + + + + io.quarkus + quarkus-undertow + + org.projectlombok diff --git a/src/main/java/dev/lions/user/manager/client/service/AuditServiceClient.java b/src/main/java/dev/lions/user/manager/client/service/AuditServiceClient.java new file mode 100644 index 0000000..947b933 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/service/AuditServiceClient.java @@ -0,0 +1,105 @@ +package dev.lions.user.manager.client.service; + +import dev.lions.user.manager.dto.audit.AuditLogDTO; +import dev.lions.user.manager.enums.audit.TypeActionAudit; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * REST Client pour le service d'audit + */ +@Path("/api/audit") +@RegisterRestClient(configKey = "lions-user-manager-api") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public interface AuditServiceClient { + + @POST + @Path("/search") + List searchLogs( + @QueryParam("acteur") String acteurUsername, + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin, + @QueryParam("typeAction") TypeActionAudit typeAction, + @QueryParam("ressourceType") String ressourceType, + @QueryParam("succes") Boolean succes, + @QueryParam("page") @DefaultValue("0") int page, + @QueryParam("pageSize") @DefaultValue("50") int pageSize + ); + + @GET + @Path("/acteur/{acteurUsername}") + List getLogsByActeur( + @PathParam("acteurUsername") String acteurUsername, + @QueryParam("limit") @DefaultValue("100") int limit + ); + + @GET + @Path("/realm/{realmName}") + List getLogsByRealm( + @PathParam("realmName") String realmName, + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin, + @QueryParam("page") @DefaultValue("0") int page, + @QueryParam("pageSize") @DefaultValue("50") int pageSize + ); + + @GET + @Path("/ressource/{ressourceType}/{ressourceId}") + List getLogsByRessource( + @PathParam("ressourceType") String ressourceType, + @PathParam("ressourceId") String ressourceId, + @QueryParam("limit") @DefaultValue("100") int limit + ); + + @GET + @Path("/action/{typeAction}") + List getLogsByAction( + @PathParam("typeAction") TypeActionAudit typeAction, + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin, + @QueryParam("limit") @DefaultValue("100") int limit + ); + + @GET + @Path("/statistics/actions") + Map getActionStatistics( + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin + ); + + @GET + @Path("/statistics/users") + Map getUserActivityStatistics( + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin + ); + + @GET + @Path("/statistics/failures") + Long getFailureCount( + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin + ); + + @GET + @Path("/statistics/successes") + Long getSuccessCount( + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin + ); + + @GET + @Path("/export/csv") + @Produces("text/csv") + String exportLogsToCSV( + @QueryParam("dateDebut") String dateDebut, + @QueryParam("dateFin") String dateFin + ); +} + diff --git a/src/main/java/dev/lions/user/manager/client/service/RoleServiceClient.java b/src/main/java/dev/lions/user/manager/client/service/RoleServiceClient.java new file mode 100644 index 0000000..28ed731 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/service/RoleServiceClient.java @@ -0,0 +1,135 @@ +package dev.lions.user.manager.client.service; + +import dev.lions.user.manager.dto.role.RoleAssignmentDTO; +import dev.lions.user.manager.dto.role.RoleDTO; +import dev.lions.user.manager.enums.role.TypeRole; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import java.util.List; + +/** + * REST Client pour le service de gestion des rôles + */ +@Path("/api/roles") +@RegisterRestClient(configKey = "lions-user-manager-api") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public interface RoleServiceClient { + + // ==================== Realm Roles ==================== + + @POST + @Path("/realm") + RoleDTO createRealmRole( + RoleDTO role, + @QueryParam("realm") String realmName + ); + + @GET + @Path("/realm/{roleName}") + RoleDTO getRealmRoleByName( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName + ); + + @GET + @Path("/realm") + List getAllRealmRoles( + @QueryParam("realm") String realmName + ); + + @PUT + @Path("/realm/{roleName}") + RoleDTO updateRealmRole( + @PathParam("roleName") String roleName, + RoleDTO role, + @QueryParam("realm") String realmName + ); + + @DELETE + @Path("/realm/{roleName}") + void deleteRealmRole( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName + ); + + // ==================== Client Roles ==================== + + @POST + @Path("/client") + RoleDTO createClientRole( + RoleDTO role, + @QueryParam("realm") String realmName, + @QueryParam("clientName") String clientName + ); + + @GET + @Path("/client") + List getAllClientRoles( + @QueryParam("realm") String realmName, + @QueryParam("clientName") String clientName + ); + + @GET + @Path("/client/{roleName}") + RoleDTO getClientRoleByName( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName, + @QueryParam("clientName") String clientName + ); + + @DELETE + @Path("/client/{roleName}") + void deleteClientRole( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName, + @QueryParam("clientName") String clientName + ); + + // ==================== Role Assignment ==================== + + @POST + @Path("/assign") + void assignRoleToUser(RoleAssignmentDTO assignment); + + @POST + @Path("/revoke") + void revokeRoleFromUser(RoleAssignmentDTO assignment); + + @GET + @Path("/user/{userId}") + List getUserRoles( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + // ==================== Composite Roles ==================== + + @GET + @Path("/composite/{roleName}") + List getCompositeRoles( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName, + @QueryParam("typeRole") TypeRole typeRole, + @QueryParam("clientName") String clientName + ); + + @POST + @Path("/composite/{roleName}/add") + void addCompositeRole( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName, + @QueryParam("compositeRoleName") String compositeRoleName + ); + + @DELETE + @Path("/composite/{roleName}/remove") + void removeCompositeRole( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName, + @QueryParam("compositeRoleName") String compositeRoleName + ); +} + diff --git a/src/main/java/dev/lions/user/manager/client/service/SyncServiceClient.java b/src/main/java/dev/lions/user/manager/client/service/SyncServiceClient.java new file mode 100644 index 0000000..c7919f5 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/service/SyncServiceClient.java @@ -0,0 +1,53 @@ +package dev.lions.user.manager.client.service; + +import dev.lions.user.manager.dto.sync.HealthStatusDTO; +import dev.lions.user.manager.dto.sync.SyncResultDTO; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +/** + * REST Client pour le service de synchronisation + */ +@Path("/api/sync") +@RegisterRestClient(configKey = "lions-user-manager-api") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public interface SyncServiceClient { + + @GET + @Path("/health") + HealthStatusDTO checkHealth(@QueryParam("realm") String realmName); + + @GET + @Path("/health/keycloak") + HealthStatusDTO checkKeycloakHealth(); + + @POST + @Path("/users") + SyncResultDTO syncUsers(@QueryParam("realm") String realmName); + + @POST + @Path("/roles") + SyncResultDTO syncRoles( + @QueryParam("realm") String realmName, + @QueryParam("clientName") String clientName + ); + + @GET + @Path("/exists/user/{username}") + Boolean userExists( + @PathParam("username") String username, + @QueryParam("realm") String realmName + ); + + @GET + @Path("/exists/role/{roleName}") + Boolean roleExists( + @PathParam("roleName") String roleName, + @QueryParam("realm") String realmName, + @QueryParam("typeRole") String typeRole, + @QueryParam("clientName") String clientName + ); +} + diff --git a/src/main/java/dev/lions/user/manager/client/service/UserServiceClient.java b/src/main/java/dev/lions/user/manager/client/service/UserServiceClient.java new file mode 100644 index 0000000..ffa4dae --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/service/UserServiceClient.java @@ -0,0 +1,140 @@ +package dev.lions.user.manager.client.service; + +import dev.lions.user.manager.dto.user.UserDTO; +import dev.lions.user.manager.dto.user.UserSearchCriteriaDTO; +import dev.lions.user.manager.dto.user.UserSearchResultDTO; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import java.util.List; + +/** + * REST Client pour le service de gestion des utilisateurs + * Interface pour communiquer avec l'API backend + */ +@Path("/api/users") +@RegisterRestClient(configKey = "lions-user-manager-api") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public interface UserServiceClient { + + /** + * Rechercher des utilisateurs selon des critères + */ + @POST + @Path("/search") + UserSearchResultDTO searchUsers(UserSearchCriteriaDTO criteria); + + /** + * Récupérer un utilisateur par ID + */ + @GET + @Path("/{userId}") + UserDTO getUserById( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + /** + * Lister tous les utilisateurs (paginé) + */ + @GET + UserSearchResultDTO getAllUsers( + @QueryParam("realm") String realmName, + @QueryParam("page") @DefaultValue("0") int page, + @QueryParam("pageSize") @DefaultValue("20") int pageSize + ); + + /** + * Créer un nouvel utilisateur + */ + @POST + UserDTO createUser( + UserDTO user, + @QueryParam("realm") String realmName + ); + + /** + * Mettre à jour un utilisateur + */ + @PUT + @Path("/{userId}") + UserDTO updateUser( + @PathParam("userId") String userId, + UserDTO user, + @QueryParam("realm") String realmName + ); + + /** + * Supprimer un utilisateur + */ + @DELETE + @Path("/{userId}") + void deleteUser( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + /** + * Activer un utilisateur + */ + @POST + @Path("/{userId}/activate") + void activateUser( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + /** + * Désactiver un utilisateur + */ + @POST + @Path("/{userId}/deactivate") + void deactivateUser( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + /** + * Réinitialiser le mot de passe + */ + @POST + @Path("/{userId}/reset-password") + void resetPassword( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName, + @QueryParam("newPassword") String newPassword + ); + + /** + * Envoyer un email de vérification + */ + @POST + @Path("/{userId}/send-verification-email") + void sendVerificationEmail( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + /** + * Déconnecter toutes les sessions d'un utilisateur + */ + @POST + @Path("/{userId}/logout-sessions") + void logoutAllSessions( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); + + /** + * Récupérer les sessions actives d'un utilisateur + */ + @GET + @Path("/{userId}/sessions") + List getActiveSessions( + @PathParam("userId") String userId, + @QueryParam("realm") String realmName + ); +} + diff --git a/src/main/java/dev/lions/user/manager/client/view/AuditConsultationBean.java b/src/main/java/dev/lions/user/manager/client/view/AuditConsultationBean.java new file mode 100644 index 0000000..e28b927 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/view/AuditConsultationBean.java @@ -0,0 +1,206 @@ +package dev.lions.user.manager.client.view; + +import dev.lions.user.manager.client.service.AuditServiceClient; +import dev.lions.user.manager.dto.audit.AuditLogDTO; +import dev.lions.user.manager.enums.audit.TypeActionAudit; +import jakarta.annotation.PostConstruct; +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import lombok.Data; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +/** + * Bean JSF pour la consultation des logs d'audit + * + * @author Lions User Manager + * @version 1.0.0 + */ +@Named("auditConsultationBean") +@ViewScoped +@Data +public class AuditConsultationBean implements Serializable { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(AuditConsultationBean.class.getName()); + + @Inject + @RestClient + private AuditServiceClient auditServiceClient; + + // Liste des logs + private List auditLogs = new ArrayList<>(); + private AuditLogDTO selectedLog; + + // Filtres de recherche + private String acteurUsername; + private LocalDateTime dateDebut; + private LocalDateTime dateFin; + private TypeActionAudit selectedTypeAction; + private String ressourceType; + private Boolean succes; + + // Pagination + private int currentPage = 0; + private int pageSize = 50; + private long totalRecords = 0; + + // Statistiques + private Map actionStatistics; + private Map userActivityStatistics; + private Long failureCount = 0L; + private Long successCount = 0L; + + // Options + private List typeActionOptions = List.of(TypeActionAudit.values()); + private List availableRealms = new ArrayList<>(); + + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + + @PostConstruct + public void init() { + loadRealms(); + loadStatistics(); + } + + /** + * Rechercher des logs d'audit + */ + public void searchLogs() { + try { + String dateDebutStr = dateDebut != null ? dateDebut.format(DATE_FORMATTER) : null; + String dateFinStr = dateFin != null ? dateFin.format(DATE_FORMATTER) : null; + + auditLogs = auditServiceClient.searchLogs( + acteurUsername, + dateDebutStr, + dateFinStr, + selectedTypeAction, + ressourceType, + succes, + currentPage, + pageSize + ); + + totalRecords = auditLogs.size(); + addSuccessMessage("Recherche effectuée: " + totalRecords + " résultat(s) trouvé(s)"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la recherche: " + e.getMessage()); + addErrorMessage("Erreur lors de la recherche: " + e.getMessage()); + } + } + + /** + * Charger les logs par acteur + */ + public void loadLogsByActeur(String username) { + try { + auditLogs = auditServiceClient.getLogsByActeur(username, 100); + totalRecords = auditLogs.size(); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement: " + e.getMessage()); + addErrorMessage("Erreur lors du chargement: " + e.getMessage()); + } + } + + /** + * Charger les logs par realm + */ + public void loadLogsByRealm(String realmName) { + try { + String dateDebutStr = dateDebut != null ? dateDebut.format(DATE_FORMATTER) : null; + String dateFinStr = dateFin != null ? dateFin.format(DATE_FORMATTER) : null; + + auditLogs = auditServiceClient.getLogsByRealm( + realmName, + dateDebutStr, + dateFinStr, + currentPage, + pageSize + ); + + totalRecords = auditLogs.size(); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement: " + e.getMessage()); + addErrorMessage("Erreur lors du chargement: " + e.getMessage()); + } + } + + /** + * Charger les statistiques + */ + public void loadStatistics() { + try { + String dateDebutStr = dateDebut != null ? dateDebut.format(DATE_FORMATTER) : null; + String dateFinStr = dateFin != null ? dateFin.format(DATE_FORMATTER) : null; + + actionStatistics = auditServiceClient.getActionStatistics(dateDebutStr, dateFinStr); + userActivityStatistics = auditServiceClient.getUserActivityStatistics(dateDebutStr, dateFinStr); + failureCount = auditServiceClient.getFailureCount(dateDebutStr, dateFinStr); + successCount = auditServiceClient.getSuccessCount(dateDebutStr, dateFinStr); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement des statistiques: " + e.getMessage()); + } + } + + /** + * Exporter les logs en CSV + */ + public void exportToCSV() { + try { + String dateDebutStr = dateDebut != null ? dateDebut.format(DATE_FORMATTER) : null; + String dateFinStr = dateFin != null ? dateFin.format(DATE_FORMATTER) : null; + + String csv = auditServiceClient.exportLogsToCSV(dateDebutStr, dateFinStr); + // TODO: Implémenter le téléchargement du fichier CSV + addSuccessMessage("Export CSV généré avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de l'export: " + e.getMessage()); + addErrorMessage("Erreur lors de l'export: " + e.getMessage()); + } + } + + /** + * Réinitialiser les filtres + */ + public void resetFilters() { + acteurUsername = null; + dateDebut = null; + dateFin = null; + selectedTypeAction = null; + ressourceType = null; + succes = null; + currentPage = 0; + auditLogs.clear(); + } + + /** + * Charger les realms disponibles + */ + private void loadRealms() { + // TODO: Implémenter la récupération des realms depuis Keycloak + availableRealms = List.of("master", "btpxpress", "unionflow"); + } + + // Méthodes utilitaires + private void addSuccessMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message)); + } + + private void addErrorMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message)); + } +} + diff --git a/src/main/java/dev/lions/user/manager/client/view/RoleGestionBean.java b/src/main/java/dev/lions/user/manager/client/view/RoleGestionBean.java new file mode 100644 index 0000000..b96a8bb --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/view/RoleGestionBean.java @@ -0,0 +1,242 @@ +package dev.lions.user.manager.client.view; + +import dev.lions.user.manager.client.service.RoleServiceClient; +import dev.lions.user.manager.dto.role.RoleAssignmentDTO; +import dev.lions.user.manager.dto.role.RoleDTO; +import dev.lions.user.manager.enums.role.TypeRole; +import jakarta.annotation.PostConstruct; +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import lombok.Data; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * Bean JSF pour la gestion des rôles + * + * @author Lions User Manager + * @version 1.0.0 + */ +@Named("roleGestionBean") +@ViewScoped +@Data +public class RoleGestionBean implements Serializable { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(RoleGestionBean.class.getName()); + + @Inject + @RestClient + private RoleServiceClient roleServiceClient; + + // Liste des rôles + private List realmRoles = new ArrayList<>(); + private List clientRoles = new ArrayList<>(); + private List allRoles = new ArrayList<>(); + private RoleDTO selectedRole; + + // Pour la création/édition + private RoleDTO newRole = RoleDTO.builder().build(); + private boolean editMode = false; + + // Filtres + private String realmName = "master"; + private String clientName; + private TypeRole selectedTypeRole; + private String roleSearchText; + + // Options + private List typeRoleOptions = List.of(TypeRole.values()); + private List availableRealms = new ArrayList<>(); + private List availableClients = new ArrayList<>(); + + @PostConstruct + public void init() { + loadRealms(); + loadRealmRoles(); + } + + /** + * Charger les rôles Realm + */ + public void loadRealmRoles() { + try { + realmRoles = roleServiceClient.getAllRealmRoles(realmName); + updateAllRoles(); + LOGGER.info("Chargement de " + realmRoles.size() + " rôles Realm"); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement des rôles Realm: " + e.getMessage()); + addErrorMessage("Erreur lors du chargement des rôles Realm"); + } + } + + /** + * Charger les rôles Client + */ + public void loadClientRoles() { + if (clientName == null || clientName.isEmpty()) { + return; + } + + try { + clientRoles = roleServiceClient.getAllClientRoles(realmName, clientName); + updateAllRoles(); + LOGGER.info("Chargement de " + clientRoles.size() + " rôles Client"); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement des rôles Client: " + e.getMessage()); + addErrorMessage("Erreur lors du chargement des rôles Client"); + } + } + + /** + * Mettre à jour la liste complète des rôles + */ + private void updateAllRoles() { + allRoles = new ArrayList<>(); + allRoles.addAll(realmRoles); + allRoles.addAll(clientRoles); + } + + /** + * Créer un nouveau rôle Realm + */ + public void createRealmRole() { + try { + RoleDTO created = roleServiceClient.createRealmRole(newRole, realmName); + addSuccessMessage("Rôle Realm créé avec succès: " + created.getName()); + resetForm(); + loadRealmRoles(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la création: " + e.getMessage()); + addErrorMessage("Erreur lors de la création: " + e.getMessage()); + } + } + + /** + * Créer un nouveau rôle Client + */ + public void createClientRole() { + if (clientName == null || clientName.isEmpty()) { + addErrorMessage("Le nom du client est obligatoire"); + return; + } + + try { + RoleDTO created = roleServiceClient.createClientRole(newRole, realmName, clientName); + addSuccessMessage("Rôle Client créé avec succès: " + created.getName()); + resetForm(); + loadClientRoles(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la création: " + e.getMessage()); + addErrorMessage("Erreur lors de la création: " + e.getMessage()); + } + } + + /** + * Supprimer un rôle Realm + */ + public void deleteRealmRole(String roleName) { + try { + roleServiceClient.deleteRealmRole(roleName, realmName); + addSuccessMessage("Rôle Realm supprimé avec succès"); + loadRealmRoles(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la suppression: " + e.getMessage()); + addErrorMessage("Erreur lors de la suppression: " + e.getMessage()); + } + } + + /** + * Supprimer un rôle Client + */ + public void deleteClientRole(String roleName) { + if (clientName == null || clientName.isEmpty()) { + addErrorMessage("Le nom du client est obligatoire"); + return; + } + + try { + roleServiceClient.deleteClientRole(roleName, realmName, clientName); + addSuccessMessage("Rôle Client supprimé avec succès"); + loadClientRoles(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la suppression: " + e.getMessage()); + addErrorMessage("Erreur lors de la suppression: " + e.getMessage()); + } + } + + /** + * Attribuer un rôle à un utilisateur + */ + public void assignRoleToUser(String userId, String roleName) { + try { + RoleAssignmentDTO assignment = RoleAssignmentDTO.builder() + .userId(userId) + .roleNames(List.of(roleName)) + .typeRole(TypeRole.REALM_ROLE) + .realmName(realmName) + .build(); + + roleServiceClient.assignRoleToUser(assignment); + addSuccessMessage("Rôle attribué avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de l'attribution: " + e.getMessage()); + addErrorMessage("Erreur lors de l'attribution: " + e.getMessage()); + } + } + + /** + * Révoquer un rôle d'un utilisateur + */ + public void revokeRoleFromUser(String userId, String roleName) { + try { + RoleAssignmentDTO assignment = RoleAssignmentDTO.builder() + .userId(userId) + .roleNames(List.of(roleName)) + .typeRole(TypeRole.REALM_ROLE) + .realmName(realmName) + .build(); + + roleServiceClient.revokeRoleFromUser(assignment); + addSuccessMessage("Rôle révoqué avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la révocation: " + e.getMessage()); + addErrorMessage("Erreur lors de la révocation: " + e.getMessage()); + } + } + + /** + * Réinitialiser le formulaire + */ + public void resetForm() { + newRole = RoleDTO.builder().build(); + editMode = false; + } + + /** + * Charger les realms disponibles + */ + private void loadRealms() { + // TODO: Implémenter la récupération des realms depuis Keycloak + availableRealms = List.of("master", "btpxpress", "unionflow"); + } + + // Méthodes utilitaires + private void addSuccessMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message)); + } + + private void addErrorMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message)); + } +} + diff --git a/src/main/java/dev/lions/user/manager/client/view/UserCreationBean.java b/src/main/java/dev/lions/user/manager/client/view/UserCreationBean.java new file mode 100644 index 0000000..f87a235 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/view/UserCreationBean.java @@ -0,0 +1,132 @@ +package dev.lions.user.manager.client.view; + +import dev.lions.user.manager.client.service.UserServiceClient; +import dev.lions.user.manager.dto.user.UserDTO; +import dev.lions.user.manager.enums.user.StatutUser; +import jakarta.annotation.PostConstruct; +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import lombok.Data; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * Bean JSF pour la création d'un nouvel utilisateur + * + * @author Lions User Manager + * @version 1.0.0 + */ +@Named("userCreationBean") +@ViewScoped +@Data +public class UserCreationBean implements Serializable { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(UserCreationBean.class.getName()); + + @Inject + @RestClient + private UserServiceClient userServiceClient; + + private UserDTO newUser = UserDTO.builder().build(); + private String realmName = "master"; + private String password; + private String passwordConfirm; + + // Options pour les selects + private List statutOptions = List.of(StatutUser.values()); + private List availableRealms = new ArrayList<>(); + + @PostConstruct + public void init() { + loadRealms(); + // Initialiser les valeurs par défaut + newUser.setEnabled(true); + newUser.setEmailVerified(false); + newUser.setStatut(StatutUser.ACTIF); + } + + /** + * Créer un nouvel utilisateur + */ + public String createUser() { + // Validation + if (password == null || password.isEmpty()) { + addErrorMessage("Le mot de passe est obligatoire"); + return null; + } + + if (!password.equals(passwordConfirm)) { + addErrorMessage("Les mots de passe ne correspondent pas"); + return null; + } + + if (password.length() < 8) { + addErrorMessage("Le mot de passe doit contenir au moins 8 caractères"); + return null; + } + + try { + // Créer l'utilisateur + UserDTO createdUser = userServiceClient.createUser(newUser, realmName); + + // Définir le mot de passe + userServiceClient.resetPassword(createdUser.getId(), realmName, password); + + addSuccessMessage("Utilisateur créé avec succès: " + createdUser.getUsername()); + resetForm(); + return "userListPage"; // Rediriger vers la liste + } catch (Exception e) { + LOGGER.severe("Erreur lors de la création: " + e.getMessage()); + addErrorMessage("Erreur lors de la création: " + e.getMessage()); + return null; + } + } + + /** + * Réinitialiser le formulaire + */ + public void resetForm() { + newUser = UserDTO.builder().build(); + password = null; + passwordConfirm = null; + newUser.setEnabled(true); + newUser.setEmailVerified(false); + newUser.setStatut(StatutUser.ACTIF); + } + + /** + * Annuler la création + */ + public String cancel() { + resetForm(); + return "userListPage"; + } + + /** + * Charger les realms disponibles + */ + private void loadRealms() { + // TODO: Implémenter la récupération des realms depuis Keycloak + availableRealms = List.of("master", "btpxpress", "unionflow"); + } + + // Méthodes utilitaires + private void addSuccessMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message)); + } + + private void addErrorMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message)); + } +} + diff --git a/src/main/java/dev/lions/user/manager/client/view/UserListBean.java b/src/main/java/dev/lions/user/manager/client/view/UserListBean.java new file mode 100644 index 0000000..4b4c77b --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/view/UserListBean.java @@ -0,0 +1,228 @@ +package dev.lions.user.manager.client.view; + +import dev.lions.user.manager.client.service.UserServiceClient; +import dev.lions.user.manager.dto.user.UserDTO; +import dev.lions.user.manager.dto.user.UserSearchCriteriaDTO; +import dev.lions.user.manager.dto.user.UserSearchResultDTO; +import dev.lions.user.manager.enums.user.StatutUser; +import jakarta.annotation.PostConstruct; +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import lombok.Data; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * Bean JSF pour la liste et la recherche d'utilisateurs + * + * @author Lions User Manager + * @version 1.0.0 + */ +@Named("userListBean") +@ViewScoped +@Data +public class UserListBean implements Serializable { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(UserListBean.class.getName()); + + // Constantes de navigation outcomes (WOU/DRY - réutilisables) + private static final String OUTCOME_USER_PROFILE = "userProfilePage"; + private static final String OUTCOME_USER_EDIT = "userEditPage"; + private static final String OUTCOME_USER_CREATE = "userCreatePage"; + + @Inject + @RestClient + private UserServiceClient userServiceClient; + + // Propriétés pour la liste + private List users = new ArrayList<>(); + private UserDTO selectedUser; + private List selectedUsers = new ArrayList<>(); + + // Propriétés pour la recherche + private UserSearchCriteriaDTO searchCriteria = UserSearchCriteriaDTO.builder().build(); + private String searchText; + private String realmName = "master"; + private StatutUser selectedStatut; + + // Propriétés pour la pagination + private int currentPage = 0; + private int pageSize = 20; + private long totalRecords = 0; + private int totalPages = 0; + + // Options pour les selects + private List statutOptions = List.of(StatutUser.values()); + private List availableRealms = new ArrayList<>(); + + @PostConstruct + public void init() { + LOGGER.info("Initialisation de UserListBean"); + loadUsers(); + loadRealms(); + } + + /** + * Charger la liste des utilisateurs + */ + public void loadUsers() { + try { + UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder() + .realmName(realmName) + .page(currentPage) + .pageSize(pageSize) + .build(); + + UserSearchResultDTO result = userServiceClient.searchUsers(criteria); + this.users = result.getUsers() != null ? result.getUsers() : new ArrayList<>(); + this.totalRecords = result.getTotalCount() != null ? result.getTotalCount() : 0; + this.totalPages = (int) Math.ceil((double) totalRecords / pageSize); + + LOGGER.info("Chargement de " + users.size() + " utilisateurs"); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement des utilisateurs: " + e.getMessage()); + addErrorMessage("Erreur lors du chargement des utilisateurs: " + e.getMessage()); + } + } + + /** + * Rechercher des utilisateurs + */ + public void search() { + try { + currentPage = 0; // Réinitialiser à la première page + + UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder() + .realmName(realmName) + .searchTerm(searchText) + .statut(selectedStatut) + .page(currentPage) + .pageSize(pageSize) + .build(); + + UserSearchResultDTO result = userServiceClient.searchUsers(criteria); + this.users = result.getUsers() != null ? result.getUsers() : new ArrayList<>(); + this.totalRecords = result.getTotalCount() != null ? result.getTotalCount() : 0; + this.totalPages = (int) Math.ceil((double) totalRecords / pageSize); + + addSuccessMessage("Recherche effectuée: " + totalRecords + " résultat(s) trouvé(s)"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la recherche: " + e.getMessage()); + addErrorMessage("Erreur lors de la recherche: " + e.getMessage()); + } + } + + /** + * Réinitialiser la recherche + */ + public void resetSearch() { + searchText = null; + selectedStatut = null; + currentPage = 0; + loadUsers(); + } + + /** + * Activer un utilisateur + */ + public void activateUser(String userId) { + try { + userServiceClient.activateUser(userId, realmName); + addSuccessMessage("Utilisateur activé avec succès"); + loadUsers(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de l'activation: " + e.getMessage()); + addErrorMessage("Erreur lors de l'activation: " + e.getMessage()); + } + } + + /** + * Désactiver un utilisateur + */ + public void deactivateUser(String userId) { + try { + userServiceClient.deactivateUser(userId, realmName); + addSuccessMessage("Utilisateur désactivé avec succès"); + loadUsers(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la désactivation: " + e.getMessage()); + addErrorMessage("Erreur lors de la désactivation: " + e.getMessage()); + } + } + + /** + * Supprimer un utilisateur + */ + public void deleteUser(String userId) { + try { + userServiceClient.deleteUser(userId, realmName); + addSuccessMessage("Utilisateur supprimé avec succès"); + loadUsers(); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la suppression: " + e.getMessage()); + addErrorMessage("Erreur lors de la suppression: " + e.getMessage()); + } + } + + /** + * Déconnecter toutes les sessions d'un utilisateur + */ + public void logoutAllSessions(String userId) { + try { + userServiceClient.logoutAllSessions(userId, realmName); + addSuccessMessage("Toutes les sessions ont été déconnectées"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la déconnexion: " + e.getMessage()); + addErrorMessage("Erreur lors de la déconnexion: " + e.getMessage()); + } + } + + /** + * Charger les realms disponibles + */ + private void loadRealms() { + // TODO: Implémenter la récupération des realms depuis Keycloak + availableRealms = List.of("master", "btpxpress", "unionflow"); + } + + /** + * Navigation vers le profil utilisateur + */ + public String goToUserProfile() { + return OUTCOME_USER_PROFILE; + } + + /** + * Navigation vers l'édition utilisateur + */ + public String goToUserEdit() { + return OUTCOME_USER_EDIT; + } + + /** + * Navigation vers la création utilisateur + */ + public String goToUserCreate() { + return OUTCOME_USER_CREATE; + } + + // Méthodes utilitaires pour les messages + private void addSuccessMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message)); + } + + private void addErrorMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message)); + } +} + diff --git a/src/main/java/dev/lions/user/manager/client/view/UserProfilBean.java b/src/main/java/dev/lions/user/manager/client/view/UserProfilBean.java new file mode 100644 index 0000000..91033c0 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/client/view/UserProfilBean.java @@ -0,0 +1,189 @@ +package dev.lions.user.manager.client.view; + +import dev.lions.user.manager.client.service.UserServiceClient; +import dev.lions.user.manager.dto.user.UserDTO; +import jakarta.annotation.PostConstruct; +import jakarta.faces.application.FacesMessage; +import jakarta.faces.context.FacesContext; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import lombok.Data; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import java.io.Serializable; +import java.util.logging.Logger; + +/** + * Bean JSF pour le profil et l'édition d'un utilisateur + * + * @author Lions User Manager + * @version 1.0.0 + */ +@Named("userProfilBean") +@ViewScoped +@Data +public class UserProfilBean implements Serializable { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(UserProfilBean.class.getName()); + + @Inject + @RestClient + private UserServiceClient userServiceClient; + + private UserDTO user; + private String userId; + private String realmName = "master"; + private boolean editMode = false; + + // Pour la réinitialisation de mot de passe + private String newPassword; + private String newPasswordConfirm; + + @PostConstruct + public void init() { + // Récupérer l'ID depuis les paramètres de requête + userId = FacesContext.getCurrentInstance().getExternalContext() + .getRequestParameterMap().get("userId"); + + if (userId != null && !userId.isEmpty()) { + loadUser(); + } else { + LOGGER.warning("Aucun userId fourni dans les paramètres"); + } + } + + /** + * Charger l'utilisateur + */ + public void loadUser() { + try { + user = userServiceClient.getUserById(userId, realmName); + LOGGER.info("Utilisateur chargé: " + user.getUsername()); + } catch (Exception e) { + LOGGER.severe("Erreur lors du chargement de l'utilisateur: " + e.getMessage()); + addErrorMessage("Erreur lors du chargement de l'utilisateur: " + e.getMessage()); + } + } + + /** + * Activer le mode édition + */ + public void enableEditMode() { + editMode = true; + } + + /** + * Désactiver le mode édition + */ + public void cancelEdit() { + editMode = false; + loadUser(); // Recharger les données originales + } + + /** + * Mettre à jour l'utilisateur + */ + public void updateUser() { + try { + user = userServiceClient.updateUser(userId, user, realmName); + editMode = false; + addSuccessMessage("Utilisateur mis à jour avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la mise à jour: " + e.getMessage()); + addErrorMessage("Erreur lors de la mise à jour: " + e.getMessage()); + } + } + + /** + * Réinitialiser le mot de passe + */ + public void resetPassword() { + if (newPassword == null || newPassword.isEmpty()) { + addErrorMessage("Le mot de passe ne peut pas être vide"); + return; + } + + if (!newPassword.equals(newPasswordConfirm)) { + addErrorMessage("Les mots de passe ne correspondent pas"); + return; + } + + try { + userServiceClient.resetPassword(userId, realmName, newPassword); + newPassword = null; + newPasswordConfirm = null; + addSuccessMessage("Mot de passe réinitialisé avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la réinitialisation: " + e.getMessage()); + addErrorMessage("Erreur lors de la réinitialisation: " + e.getMessage()); + } + } + + /** + * Activer l'utilisateur + */ + public void activateUser() { + try { + userServiceClient.activateUser(userId, realmName); + loadUser(); // Recharger pour mettre à jour le statut + addSuccessMessage("Utilisateur activé avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de l'activation: " + e.getMessage()); + addErrorMessage("Erreur lors de l'activation: " + e.getMessage()); + } + } + + /** + * Désactiver l'utilisateur + */ + public void deactivateUser() { + try { + userServiceClient.deactivateUser(userId, realmName); + loadUser(); // Recharger pour mettre à jour le statut + addSuccessMessage("Utilisateur désactivé avec succès"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la désactivation: " + e.getMessage()); + addErrorMessage("Erreur lors de la désactivation: " + e.getMessage()); + } + } + + /** + * Envoyer un email de vérification + */ + public void sendVerificationEmail() { + try { + userServiceClient.sendVerificationEmail(userId, realmName); + addSuccessMessage("Email de vérification envoyé"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de l'envoi: " + e.getMessage()); + addErrorMessage("Erreur lors de l'envoi: " + e.getMessage()); + } + } + + /** + * Déconnecter toutes les sessions + */ + public void logoutAllSessions() { + try { + userServiceClient.logoutAllSessions(userId, realmName); + addSuccessMessage("Toutes les sessions ont été déconnectées"); + } catch (Exception e) { + LOGGER.severe("Erreur lors de la déconnexion: " + e.getMessage()); + addErrorMessage("Erreur lors de la déconnexion: " + e.getMessage()); + } + } + + // Méthodes utilitaires + private void addSuccessMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message)); + } + + private void addErrorMessage(String message) { + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message)); + } +} + diff --git a/src/main/resources/META-INF/faces-config.xml b/src/main/resources/META-INF/faces-config.xml new file mode 100644 index 0000000..2516c8e --- /dev/null +++ b/src/main/resources/META-INF/faces-config.xml @@ -0,0 +1,91 @@ + + + + Lions User Manager + + + + fr + fr + en + + + + + * + + + + Page d'accueil / Dashboard + userManagerDashboardPage + /pages/user-manager/dashboard.xhtml + + + + + + Page de liste des utilisateurs + userListPage + /pages/user-manager/users/list.xhtml + + + + + Page de création d'utilisateur + userCreatePage + /pages/user-manager/users/create.xhtml + + + + + Page de profil utilisateur + userProfilePage + /pages/user-manager/users/profile.xhtml + + + + + Page d'édition utilisateur + userEditPage + /pages/user-manager/users/edit.xhtml + + + + + + Page de liste des rôles + roleListPage + /pages/user-manager/roles/list.xhtml + + + + + Page d'attribution de rôles + roleAssignPage + /pages/user-manager/roles/assign.xhtml + + + + + + Page de journal d'audit + auditLogsPage + /pages/user-manager/audit/logs.xhtml + + + + + + Page de dashboard synchronisation + syncDashboardPage + /pages/user-manager/sync/dashboard.xhtml + + + + + + diff --git a/src/main/resources/META-INF/quarkus-config.properties b/src/main/resources/META-INF/quarkus-config.properties new file mode 100644 index 0000000..9953ae1 --- /dev/null +++ b/src/main/resources/META-INF/quarkus-config.properties @@ -0,0 +1,3 @@ +# Configuration Quarkus pour PrimeFaces +# Exclusion de classes obsolètes de PrimeFaces 14.0.5 + diff --git a/src/main/resources/META-INF/resources/index.xhtml b/src/main/resources/META-INF/resources/index.xhtml new file mode 100644 index 0000000..03bdbf0 --- /dev/null +++ b/src/main/resources/META-INF/resources/index.xhtml @@ -0,0 +1,58 @@ + + + + + + + Lions User Manager - Gestion des Utilisateurs Keycloak + + + + + + + + +
+
+
+ +

Lions User Manager

+

Gestion centralisée des utilisateurs Keycloak

+ +
+ + + + + + + + + + + +
+ +
+

Version 1.0.0

+

Module réutilisable pour l'écosystème LionsDev

+
+
+
+
+
+ + + diff --git a/src/main/resources/META-INF/resources/pages/user-manager/audit/logs.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/audit/logs.xhtml new file mode 100644 index 0000000..3ab6763 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/audit/logs.xhtml @@ -0,0 +1,179 @@ + + + + + Journal d'Audit - Lions User Manager + + + + + + + + + +
+ + + + + + +
+
+
+
+ + +
+
+ + + + + + +
+
+ + + + + + +
+
+ + + + + + +
+
+ + + + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+
+ + +
+ +
Logs d'Audit
+
+ + + + + + + + +

Aucun log d'audit trouvé

+
+
+ + +
+ + Affichage de #{auditConsultationBean.currentPage * auditConsultationBean.pageSize + 1} + à #{auditConsultationBean.currentPage * auditConsultationBean.pageSize + auditConsultationBean.auditLogs.size()} + sur #{auditConsultationBean.totalRecords} + +
+ + +
+
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/roles/assign.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/roles/assign.xhtml new file mode 100644 index 0000000..5c1b1c8 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/roles/assign.xhtml @@ -0,0 +1,34 @@ + + + + + Attribution de Rôles - Lions User Manager + + + + + + + + + + +
+ + + + + + + + +
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/roles/list.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/roles/list.xhtml new file mode 100644 index 0000000..62abaa5 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/roles/list.xhtml @@ -0,0 +1,158 @@ + + + + + Gestion des Rôles - Lions User Manager + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ +
+ + + + +
+
+ +
+

Aucun rôle Realm trouvé

+
+
+
+
+
+
+ + +
+ + +
+ +
+ + + + +
+
+ +
+

Aucun rôle Client trouvé

+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/sync/dashboard.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/sync/dashboard.xhtml new file mode 100644 index 0000000..8f2b662 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/sync/dashboard.xhtml @@ -0,0 +1,49 @@ + + + + Synchronisation Keycloak - Lions User Manager + + + + + + + + + + +
+
+
+
État de Keycloak
+ + +
+
+
+
+
Actions de Synchronisation
+
+ + + + + + + + + + +
+
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/users/create.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/users/create.xhtml new file mode 100644 index 0000000..47f4c76 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/users/create.xhtml @@ -0,0 +1,34 @@ + + + + + Nouvel Utilisateur - Lions User Manager + + + + + + + + + + +
+ + + + + + + + +
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/users/edit.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/users/edit.xhtml new file mode 100644 index 0000000..2f75390 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/users/edit.xhtml @@ -0,0 +1,33 @@ + + + + + Modifier Utilisateur - Lions User Manager + + + + + + + + + + +
+ + + + + + + +
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/users/list.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/users/list.xhtml new file mode 100644 index 0000000..2153859 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/users/list.xhtml @@ -0,0 +1,98 @@ + + + + + Liste des Utilisateurs - Lions User Manager + + + + + + + + + +
+ + + + + + +
+
+
+
+ + +
+
+ + + + + + +
+
+ + + + + + +
+
+ + + + + + +
+
+ + + + + + +
+
+ + +
+ + + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/pages/user-manager/users/profile.xhtml b/src/main/resources/META-INF/resources/pages/user-manager/users/profile.xhtml new file mode 100644 index 0000000..ae6c082 --- /dev/null +++ b/src/main/resources/META-INF/resources/pages/user-manager/users/profile.xhtml @@ -0,0 +1,107 @@ + + + + + Profil Utilisateur - Lions User Manager + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+ + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+
Actions Rapides
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/README.md b/src/main/resources/META-INF/resources/templates/components/README.md new file mode 100644 index 0000000..fdc5170 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/README.md @@ -0,0 +1,399 @@ +# 📦 Composants Réutilisables - Lions User Manager + +**Version**: 1.0.0 +**Date**: 2025-01-29 +**Pattern**: WOU/DRY (Write Once Use / Don't Repeat Yourself) + +--- + +## 🎯 Vue d'Ensemble + +Ce répertoire contient tous les composants UI réutilisables pour le module **lions-user-manager**. Ces composants suivent les patterns établis dans **unionflow** et peuvent être utilisés dans n'importe quel projet de l'écosystème **lionsdev**. + +--- + +## 📂 Structure des Composants + +``` +templates/components/ +├── user-management/ # Composants spécifiques utilisateurs +│ ├── user-card.xhtml +│ ├── user-form.xhtml +│ ├── user-search-bar.xhtml +│ ├── user-actions.xhtml +│ └── user-role-badge.xhtml +├── role-management/ # Composants spécifiques rôles +│ ├── role-card.xhtml +│ ├── role-form.xhtml +│ └── role-assignment.xhtml +├── audit/ # Composants audit +│ ├── audit-log-row.xhtml +│ └── audit-stats-card.xhtml +└── shared/ # Composants génériques réutilisables + ├── buttons/ + │ └── button-user-action.xhtml + ├── cards/ + │ └── user-stat-card.xhtml + ├── forms/ + │ └── user-form-field.xhtml + └── tables/ + └── user-data-table.xhtml +``` + +--- + +## 📋 Liste Complète des Composants + +### 👤 User Management (5 composants) + +#### 1. `user-card.xhtml` +**Description**: Carte utilisateur avec informations principales et actions + +**Paramètres principaux**: +- `user`: UserDTO (requis) +- `showActions`: Boolean (défaut: true) +- `showRoles`: Boolean (défaut: true) +- `clickable`: Boolean (défaut: true) +- `outcome`: String (optionnel) + +**Exemple**: +```xhtml + + + + +``` + +#### 2. `user-form.xhtml` +**Description**: Formulaire complet pour créer/modifier un utilisateur + +**Paramètres principaux**: +- `user`: UserDTO (requis) +- `mode`: String (défaut: "create") - "create" ou "edit" +- `showRealmSelector`: Boolean (défaut: false) +- `showPasswordFields`: Boolean (défaut: true) +- `readonly`: Boolean (défaut: false) + +**Exemple**: +```xhtml + + + + + +``` + +#### 3. `user-search-bar.xhtml` +**Description**: Barre de recherche avancée pour utilisateurs + +**Paramètres principaux**: +- `searchCriteria`: UserSearchCriteriaDTO (requis) +- `searchAction`: String (requis) +- `showAdvanced`: Boolean (défaut: false) +- `showRealmFilter`: Boolean (défaut: true) +- `showStatusFilter`: Boolean (défaut: true) + +**Exemple**: +```xhtml + + + + + +``` + +#### 4. `user-actions.xhtml` +**Description**: Boutons d'action pour un utilisateur + +**Paramètres principaux**: +- `user`: UserDTO (requis) +- `showView`: Boolean (défaut: true) +- `showEdit`: Boolean (défaut: true) +- `showDelete`: Boolean (défaut: true) +- `showActivate`: Boolean (défaut: true) +- `layout`: String (défaut: "horizontal") - "horizontal", "vertical" ou "dropdown" + +**Exemple**: +```xhtml + + + + + +``` + +#### 5. `user-role-badge.xhtml` +**Description**: Badge pour un rôle utilisateur + +**Paramètres principaux**: +- `roleName`: String (requis) +- `roleType`: String (optionnel) - "REALM_ROLE", "CLIENT_ROLE", "COMPOSITE_ROLE" +- `severity`: String (optionnel) - "success", "info", "warning", "danger" +- `clickable`: Boolean (défaut: false) + +**Exemple**: +```xhtml + + + + +``` + +--- + +### 🛡️ Role Management (3 composants) + +#### 6. `role-card.xhtml` +**Description**: Carte rôle avec informations principales + +**Paramètres principaux**: +- `role`: RoleDTO (requis) +- `showActions`: Boolean (défaut: true) +- `showDescription`: Boolean (défaut: true) +- `showCompositeInfo`: Boolean (défaut: true) + +**Exemple**: +```xhtml + + + +``` + +#### 7. `role-form.xhtml` +**Description**: Formulaire pour créer/modifier un rôle + +**Paramètres principaux**: +- `role`: RoleDTO (requis) +- `mode`: String (défaut: "create") +- `showRealmSelector`: Boolean (défaut: true) +- `showClientSelector`: Boolean (défaut: false) +- `showCompositeOptions`: Boolean (défaut: true) + +**Exemple**: +```xhtml + + + + + +``` + +#### 8. `role-assignment.xhtml` +**Description**: Interface pour attribuer/révoquer des rôles + +**Paramètres principaux**: +- `user`: UserDTO (requis) +- `availableRoles`: List (requis) +- `userRoles`: List (requis) +- `assignAction`: String (requis) +- `revokeAction`: String (requis) + +**Exemple**: +```xhtml + + + + + + + +``` + +--- + +### 📊 Audit (2 composants) + +#### 9. `audit-log-row.xhtml` +**Description**: Ligne de log d'audit avec informations détaillées + +**Paramètres principaux**: +- `auditLog`: AuditLogDTO (requis) +- `showDetails`: Boolean (défaut: false) +- `showActions`: Boolean (défaut: false) +- `compact`: Boolean (défaut: false) + +**Exemple**: +```xhtml + + + + +``` + +#### 10. `audit-stats-card.xhtml` +**Description**: Carte statistiques d'audit + +**Paramètres principaux**: +- `title`: String (requis) +- `value`: Number (requis) +- `icon`: String (requis) +- `iconColor`: String (requis) +- `trend`: Number (optionnel) +- `trendLabel`: String (optionnel) + +**Exemple**: +```xhtml + + + + + + +``` + +--- + +### 🔧 Shared Components (4 composants) + +#### 11. `button-user-action.xhtml` +**Description**: Bouton générique pour actions utilisateur + +**Paramètres principaux**: +- `value`: String (requis) +- `icon`: String (optionnel) +- `action`: String (optionnel) +- `outcome`: String (optionnel) +- `severity`: String (défaut: "primary") +- `size`: String (défaut: "normal") + +**Exemple**: +```xhtml + + + + + +``` + +#### 12. `user-stat-card.xhtml` +**Description**: Carte statistique utilisateur + +**Paramètres principaux**: +- `title`: String (requis) +- `value`: String/Number (requis) +- `icon`: String (requis) +- `iconColor`: String (requis) +- `trend`: Number (optionnel) +- `clickable`: Boolean (défaut: false) + +**Exemple**: +```xhtml + + + + + + +``` + +#### 13. `user-form-field.xhtml` +**Description**: Champ de formulaire générique + +**Paramètres principaux**: +- `id`: String (requis) +- `label`: String (requis) +- `value`: Object (requis) +- `type`: String (défaut: "text") - "text", "email", "password", "number", "textarea", "select", "checkbox", "calendar" +- `required`: Boolean (défaut: false) +- `readonly`: Boolean (défaut: false) + +**Exemple**: +```xhtml + + + + + + +``` + +#### 14. `user-data-table.xhtml` +**Description**: Tableau de données pour utilisateurs + +**Paramètres principaux**: +- `users`: List (requis) +- `var`: String (défaut: "user") +- `paginator`: Boolean (défaut: true) +- `rows`: Number (défaut: 20) +- `showActions`: Boolean (défaut: true) +- `showRoles`: Boolean (défaut: true) +- `showSelection`: Boolean (défaut: false) + +**Exemple**: +```xhtml + + + + +``` + +--- + +## 🎨 Patterns et Conventions + +### Documentation Inline +Chaque composant contient une documentation complète en commentaire avec: +- Description du composant +- Liste des paramètres avec types et valeurs par défaut +- Exemples d'utilisation + +### Paramètres Optionnels +Tous les paramètres optionnels ont des valeurs par défaut définies avec ``. + +### Pattern WOU/DRY +- **Write Once Use**: Chaque composant est écrit une fois et réutilisé partout +- **Don't Repeat Yourself**: Pas de duplication de code + +### Naming Convention +- Noms de fichiers en `kebab-case` +- Paramètres en `camelCase` +- IDs de composants avec préfixe cohérent + +--- + +## 🚀 Utilisation dans d'Autres Projets + +Ces composants peuvent être utilisés dans n'importe quel projet de l'écosystème **lionsdev** en ajoutant la dépendance Maven: + +```xml + + dev.lions.user.manager + lions-user-manager-client-quarkus-primefaces-freya + 1.0.0 + +``` + +Puis inclure les composants dans vos pages XHTML: + +```xhtml + + + +``` + +--- + +## 📝 Notes Importantes + +1. **Dépendances**: Tous les composants nécessitent PrimeFaces 14.0.5+ et Quarkus PrimeFaces 3.13.3+ +2. **Thème**: Les composants utilisent le thème Freya (compatible avec unionflow) +3. **Validation**: Les composants de formulaire incluent la validation JSF +4. **Accessibilité**: Les composants suivent les bonnes pratiques d'accessibilité + +--- + +## 🔄 Maintenance + +Pour ajouter un nouveau composant: + +1. Créer le fichier dans le répertoire approprié +2. Suivre le pattern de documentation inline +3. Ajouter des exemples d'utilisation +4. Mettre à jour ce README + +--- + +**Dernière mise à jour**: 2025-01-29 +**Version**: 1.0.0 +**Auteur**: Lions User Manager Team + diff --git a/src/main/resources/META-INF/resources/templates/components/audit/audit-log-row.xhtml b/src/main/resources/META-INF/resources/templates/components/audit/audit-log-row.xhtml new file mode 100644 index 0000000..403ae5b --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/audit/audit-log-row.xhtml @@ -0,0 +1,110 @@ + + + + + + + + + + + + +
+ + +
+ +
+ + +
+
+ #{auditLog.typeAction} + +
+ +
+ + #{auditLog.acteurUsername} + + + #{auditLog.ressourceType} + + + #{auditLog.dateAction} + +
+ + + +
+ +
Ressource ID: #{auditLog.ressourceId}
+
+ +
Détails: #{auditLog.details}
+
+ +
IP: #{auditLog.adresseIp}
+
+ +
User Agent: #{auditLog.userAgent}
+
+ +
Erreur: #{auditLog.messageErreur}
+
+
+
+
+ + + +
+ +
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/audit/audit-stats-card.xhtml b/src/main/resources/META-INF/resources/templates/components/audit/audit-stats-card.xhtml new file mode 100644 index 0000000..999fadd --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/audit/audit-stats-card.xhtml @@ -0,0 +1,120 @@ + + + + + + + +
+ + + +
+
+ #{title} +
+ +
+
+ +
#{value}
+ + +
#{subtitle}
+
+ + +
+ + + #{trend >= 0 ? '+' : ''}#{trend}% + + + #{trendLabel} + +
+
+
+
+
+ + +
+
+ #{title} +
+ +
+
+ +
#{value}
+ + +
#{subtitle}
+
+ + +
+ + + #{trend >= 0 ? '+' : ''}#{trend}% + + + #{trendLabel} + +
+
+
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/layout/footer.xhtml b/src/main/resources/META-INF/resources/templates/components/layout/footer.xhtml new file mode 100644 index 0000000..440d19a --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/layout/footer.xhtml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/src/main/resources/META-INF/resources/templates/components/layout/menu.xhtml b/src/main/resources/META-INF/resources/templates/components/layout/menu.xhtml new file mode 100644 index 0000000..0c43e22 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/layout/menu.xhtml @@ -0,0 +1,63 @@ + + + + + + + + diff --git a/src/main/resources/META-INF/resources/templates/components/layout/page-header.xhtml b/src/main/resources/META-INF/resources/templates/components/layout/page-header.xhtml new file mode 100644 index 0000000..be6f0c7 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/layout/page-header.xhtml @@ -0,0 +1,52 @@ + + + + + + --> + +
+
+
+
+
+

+ + + + #{title} +

+

#{description}

+
+
+ +
+
+
+
+
+
+ diff --git a/src/main/resources/META-INF/resources/templates/components/layout/topbar.xhtml b/src/main/resources/META-INF/resources/templates/components/layout/topbar.xhtml new file mode 100644 index 0000000..c25f545 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/layout/topbar.xhtml @@ -0,0 +1,68 @@ + + + + +
+ +
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/role-management/role-assignment.xhtml b/src/main/resources/META-INF/resources/templates/components/role-management/role-assignment.xhtml new file mode 100644 index 0000000..d3993e4 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/role-management/role-assignment.xhtml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + +

Rôles actuels

+
+ + + + + + + + + + Aucun rôle attribué + +
+ + + + +

Rôles disponibles

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + +

Rechercher un rôle

+
+ + + +
+ +
+ +
+ +
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/role-management/role-card.xhtml b/src/main/resources/META-INF/resources/templates/components/role-management/role-card.xhtml new file mode 100644 index 0000000..cd9884e --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/role-management/role-card.xhtml @@ -0,0 +1,151 @@ + + + + + + + + + + + +
+
+ +
+

#{role.name}

+ + + Rôle Realm + Rôle Client + Rôle Composite + Rôle + + +
+
+ +
+
+ +
+ + +

#{role.description}

+
+ + +
+ +
+ + Realm: #{role.realmName} +
+
+ + +
+ + Client: #{role.clientId} +
+
+ + + +
+ + Rôle composite +
+
+
+
+ + + +
+ + + + + + + + + +
+
+
+
+ + + + + + + +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/role-management/role-form.xhtml b/src/main/resources/META-INF/resources/templates/components/role-management/role-form.xhtml new file mode 100644 index 0000000..9047391 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/role-management/role-form.xhtml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/shared/buttons/button-user-action.xhtml b/src/main/resources/META-INF/resources/templates/components/shared/buttons/button-user-action.xhtml new file mode 100644 index 0000000..78aa00e --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/shared/buttons/button-user-action.xhtml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/resources/templates/components/shared/cards/user-stat-card.xhtml b/src/main/resources/META-INF/resources/templates/components/shared/cards/user-stat-card.xhtml new file mode 100644 index 0000000..255aef3 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/shared/cards/user-stat-card.xhtml @@ -0,0 +1,120 @@ + + + + + + + +
+ + + +
+
+ #{title} +
+ +
+
+ +
#{value}
+ + +
#{subtitle}
+
+ + +
+ + + #{trend >= 0 ? '+' : ''}#{trend}% + + + #{trendLabel} + +
+
+
+
+
+ + +
+
+ #{title} +
+ +
+
+ +
#{value}
+ + +
#{subtitle}
+
+ + +
+ + + #{trend >= 0 ? '+' : ''}#{trend}% + + + #{trendLabel} + +
+
+
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/shared/forms/user-form-field.xhtml b/src/main/resources/META-INF/resources/templates/components/shared/forms/user-form-field.xhtml new file mode 100644 index 0000000..3a73ffc --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/shared/forms/user-form-field.xhtml @@ -0,0 +1,163 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #{helpText} + +
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/shared/tables/user-data-table.xhtml b/src/main/resources/META-INF/resources/templates/components/shared/tables/user-data-table.xhtml new file mode 100644 index 0000000..f6d7e99 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/shared/tables/user-data-table.xhtml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + #{var.username} +
+
+ + + +
+ #{var.firstName} #{var.lastName} + + #{var.fonction} + +
+
+ + + + +
+ + #{var.email} + + + +
+
+
+ + + + +
+ + + + + + + +
+
+
+ + + + +
+ + + + + + + + +
+
+
+ + + + + + + + + + + +
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/user-management/user-actions.xhtml b/src/main/resources/META-INF/resources/templates/components/user-management/user-actions.xhtml new file mode 100644 index 0000000..cb07a46 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/user-management/user-actions.xhtml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/user-management/user-card.xhtml b/src/main/resources/META-INF/resources/templates/components/user-management/user-card.xhtml new file mode 100644 index 0000000..4ea7a8a --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/user-management/user-card.xhtml @@ -0,0 +1,130 @@ + + + + + + + + + + +
+ +
+

#{user.firstName} #{user.lastName}

+ @#{user.username} +
+
+
+ +
+ +
+
+ + #{user.email} +
+ + +
+ + #{user.telephone} +
+
+ + +
+ + +
+
+ + + +
+
Rôles
+
+ + + +
+
+
+
+ + + +
+ + + + + + + + + +
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/user-management/user-form.xhtml b/src/main/resources/META-INF/resources/templates/components/user-management/user-form.xhtml new file mode 100644 index 0000000..42281a7 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/user-management/user-form.xhtml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Mot de passe

+ + + + + + + + + +
+ + + +
+ + + + +
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/components/user-management/user-role-badge.xhtml b/src/main/resources/META-INF/resources/templates/components/user-management/user-role-badge.xhtml new file mode 100644 index 0000000..3076b48 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/user-management/user-role-badge.xhtml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/resources/templates/components/user-management/user-search-bar.xhtml b/src/main/resources/META-INF/resources/templates/components/user-management/user-search-bar.xhtml new file mode 100644 index 0000000..6a327d5 --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/components/user-management/user-search-bar.xhtml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + +
+ Recherche d'utilisateurs + +
+
+ + +
+
+ + + + +
+ + + +
+ + + + + +
+
+ + + +
+ + + + + +
+
+ + +
+ +
+ + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+
+ +
+ diff --git a/src/main/resources/META-INF/resources/templates/main-template.xhtml b/src/main/resources/META-INF/resources/templates/main-template.xhtml new file mode 100644 index 0000000..64b6aef --- /dev/null +++ b/src/main/resources/META-INF/resources/templates/main-template.xhtml @@ -0,0 +1,52 @@ + + + + + + + <ui:insert name="title">Lions User Manager</ui:insert> + + + + + + + + +
+ + + + + + + + +
+
+ + + + + + + + + +
+ + + +
+ +
+ +
+ + + diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..e3b2ffd --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,24 @@ +# Configuration Développement - Lions User Manager Client + +# Logging plus détaillé en dev +quarkus.log.console.level=DEBUG +quarkus.log.category."dev.lions.user.manager".level=TRACE + +# MyFaces en mode développement +quarkus.myfaces.project-stage=Development +quarkus.myfaces.check-id-production-mode=false + +# Backend local +lions.user.manager.backend.url=http://localhost:8080 + +# Keycloak local (si disponible) +quarkus.oidc.auth-server-url=http://localhost:8180/realms/master +quarkus.oidc.client-id=lions-user-manager-client +quarkus.oidc.tls.verification=none + +# CORS permissif en dev +quarkus.http.cors.origins=* + +# Désactiver la vérification du token en dev (si nécessaire) +# quarkus.oidc.verify-access-token=false + diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties new file mode 100644 index 0000000..36131c3 --- /dev/null +++ b/src/main/resources/application-prod.properties @@ -0,0 +1,32 @@ +# Configuration Production - Lions User Manager Client + +# Logging production +quarkus.log.console.level=INFO +quarkus.log.category."dev.lions.user.manager".level=INFO + +# MyFaces en mode production +quarkus.myfaces.project-stage=Production +quarkus.myfaces.check-id-production-mode=true + +# Backend production +lions.user.manager.backend.url=${LIONS_USER_MANAGER_BACKEND_URL} + +# Keycloak production +quarkus.oidc.auth-server-url=https://security.lions.dev/realms/master +quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID} +quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET} +quarkus.oidc.tls.verification=required + +# CORS restrictif en prod +quarkus.http.cors.origins=${CORS_ORIGINS:https://unionflow.lions.dev,https://btpxpress.lions.dev} + +# Sécurité renforcée +quarkus.http.session-cookie-secure=true +quarkus.oidc.authentication.cookie-same-site=strict + +# Health checks obligatoires +quarkus.smallrye-health.root-path=/health + +# Métriques Prometheus +quarkus.micrometer.export.prometheus.enabled=true + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..2a30c95 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,97 @@ +# Configuration Lions User Manager Client +quarkus.application.name=lions-user-manager-client +quarkus.application.version=1.0.0 + +# Configuration HTTP +quarkus.http.port=8081 +quarkus.http.host=0.0.0.0 +quarkus.http.root-path=/ +quarkus.http.so-reuse-port=true + +# Configuration Session HTTP +quarkus.http.session-timeout=60m +quarkus.http.session-cookie-same-site=lax +quarkus.http.session-cookie-http-only=true +quarkus.http.session-cookie-secure=false + +# Configuration logging +quarkus.log.console.enable=true +quarkus.log.console.level=INFO +quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{2.}] (%t) %s%e%n +quarkus.log.category."dev.lions.user.manager".level=DEBUG + +# MyFaces Configuration +quarkus.myfaces.project-stage=Development +quarkus.myfaces.state-saving-method=server +quarkus.myfaces.number-of-views-in-session=50 +quarkus.myfaces.number-of-sequential-views-in-session=10 +quarkus.myfaces.serialize-state-in-session=false +quarkus.myfaces.client-view-state-timeout=3600000 +quarkus.myfaces.view-expired-exception-handler-redirect-page=/ +quarkus.myfaces.check-id-production-mode=false +quarkus.myfaces.strict-xhtml-links=false +quarkus.myfaces.refresh-transient-build-on-pss=true +quarkus.myfaces.resource-max-time-expires=604800000 +quarkus.myfaces.resource-buffer-size=2048 + +# PrimeFaces Configuration +primefaces.THEME=freya +primefaces.FONT_AWESOME=true +primefaces.CLIENT_SIDE_VALIDATION=true +primefaces.MOVE_SCRIPTS_TO_BOTTOM=true +primefaces.CSP=false +primefaces.UPLOADER=commons +primefaces.AUTO_UPDATE=false +primefaces.CACHE_PROVIDER=org.primefaces.cache.DefaultCacheProvider + +# Configuration Backend Lions User Manager +lions.user.manager.backend.url=${LIONS_USER_MANAGER_BACKEND_URL:http://localhost:8080} + +# Configuration REST Client +quarkus.rest-client."lions-user-manager-api".url=${lions.user.manager.backend.url} +quarkus.rest-client."lions-user-manager-api".scope=jakarta.inject.Singleton +quarkus.rest-client."lions-user-manager-api".connect-timeout=5000 +quarkus.rest-client."lions-user-manager-api".read-timeout=30000 + +# Configuration Keycloak OIDC +quarkus.oidc.enabled=true +quarkus.oidc.auth-server-url=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/master} +quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID:lions-user-manager-client} +quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET} +quarkus.oidc.application-type=web-app +quarkus.oidc.authentication.redirect-path=/auth/callback +quarkus.oidc.authentication.restore-path-after-redirect=true +quarkus.oidc.authentication.scopes=openid,profile,email,roles +quarkus.oidc.token.issuer=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/master} +quarkus.oidc.tls.verification=none +quarkus.oidc.authentication.cookie-same-site=lax +quarkus.oidc.authentication.java-script-auto-redirect=false +quarkus.oidc.discovery-enabled=true +quarkus.oidc.verify-access-token=true + +# Activation de la sécurité +quarkus.security.auth.enabled=true + +# Chemins publics (non protégés par OIDC) +quarkus.http.auth.permission.public.paths=/,/index.xhtml,/pages/public/*,/auth/*,/q/*,/q/oidc/*,/favicon.ico,/resources/*,/META-INF/resources/*,/images/*,/jakarta.faces.resource/*,/javax.faces.resource/* +quarkus.http.auth.permission.public.policy=permit + +# Chemins protégés (requièrent authentification) +quarkus.http.auth.permission.authenticated.paths=/pages/user-manager/* +quarkus.http.auth.permission.authenticated.policy=authenticated + +# CORS (si nécessaire pour développement) +quarkus.http.cors=true +quarkus.http.cors.origins=${CORS_ORIGINS:http://localhost:8081,http://localhost:8086} +quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS +quarkus.http.cors.headers=Accept,Authorization,Content-Type,X-Requested-With + +# Health Checks +quarkus.smallrye-health.root-path=/health +quarkus.smallrye-health.liveness-path=/health/live +quarkus.smallrye-health.readiness-path=/health/ready + +# Metrics (optionnel) +quarkus.micrometer.export.prometheus.enabled=true +quarkus.micrometer.export.prometheus.path=/metrics +