feat: Initial lions-user-manager project structure

Phase 1 & 2 Implementation (40% completion)

Module server-api ( COMPLETED - 15 files):
- DTOs complets (User, Role, Audit, Search)
- Enums (StatutUser, TypeRole, TypeActionAudit)
- Service interfaces (User, Role, Audit, Sync)
- ValidationConstants
- 100% compilé et testé

Module server-impl-quarkus (🔄 EN COURS - 7 files):
- KeycloakAdminClient avec Circuit Breaker, Retry, Timeout
- UserServiceImpl avec 25+ méthodes
- UserResource REST API (12 endpoints)
- Health checks Keycloak
- Configurations dev/prod séparées
- Mappers UserDTO <-> Keycloak UserRepresentation

Module client ( À FAIRE - 0 files):
- Configuration PrimeFaces Freya à venir
- Interface utilisateur JSF à venir

Infrastructure:
- Maven multi-modules (parent + 3 enfants)
- Quarkus 3.15.1
- Keycloak Admin Client 23.0.3
- PrimeFaces 14.0.5
- Documentation complète (README, PROGRESS_REPORT)

Contraintes respectées:
- ZÉRO accès direct DB Keycloak (Admin API uniquement)
- Multi-realm avec délégation
- Résilience (Circuit Breaker, Retry)
- Sécurité (@RolesAllowed, OIDC)
- Observabilité (Health, Metrics)

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
dahoud
2025-11-09 13:12:59 +00:00
commit 8cdb31cac4
32 changed files with 5226 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
package dev.lions.user.manager.enums.audit;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
/**
* Type d'action effectuée sur une ressource
* Utilisé pour l'audit trail
*/
@Getter
@RequiredArgsConstructor
@Schema(description = "Type d'action pour l'audit")
public enum TypeActionAudit {
// Actions Utilisateur
USER_CREATE("Création utilisateur", "USER", "CREATE"),
USER_UPDATE("Modification utilisateur", "USER", "UPDATE"),
USER_DELETE("Suppression utilisateur", "USER", "DELETE"),
USER_ACTIVATE("Activation utilisateur", "USER", "ACTIVATE"),
USER_DEACTIVATE("Désactivation utilisateur", "USER", "DEACTIVATE"),
USER_SUSPEND("Suspension utilisateur", "USER", "SUSPEND"),
USER_UNLOCK("Déverrouillage utilisateur", "USER", "UNLOCK"),
USER_PASSWORD_RESET("Réinitialisation mot de passe", "USER", "PASSWORD_RESET"),
USER_EMAIL_VERIFY("Vérification email", "USER", "EMAIL_VERIFY"),
USER_FORCE_LOGOUT("Déconnexion forcée", "USER", "FORCE_LOGOUT"),
// Actions Rôle
ROLE_CREATE("Création rôle", "ROLE", "CREATE"),
ROLE_UPDATE("Modification rôle", "ROLE", "UPDATE"),
ROLE_DELETE("Suppression rôle", "ROLE", "DELETE"),
ROLE_ASSIGN("Attribution rôle", "ROLE", "ASSIGN"),
ROLE_REVOKE("Révocation rôle", "ROLE", "REVOKE"),
ROLE_ADD_COMPOSITE("Ajout rôle composite", "ROLE", "ADD_COMPOSITE"),
ROLE_REMOVE_COMPOSITE("Retrait rôle composite", "ROLE", "REMOVE_COMPOSITE"),
// Actions Groupe
GROUP_CREATE("Création groupe", "GROUP", "CREATE"),
GROUP_UPDATE("Modification groupe", "GROUP", "UPDATE"),
GROUP_DELETE("Suppression groupe", "GROUP", "DELETE"),
GROUP_ADD_MEMBER("Ajout membre groupe", "GROUP", "ADD_MEMBER"),
GROUP_REMOVE_MEMBER("Retrait membre groupe", "GROUP", "REMOVE_MEMBER"),
// Actions Realm
REALM_SYNC("Synchronisation realm", "REALM", "SYNC"),
REALM_EXPORT("Export realm", "REALM", "EXPORT"),
REALM_IMPORT("Import realm", "REALM", "IMPORT"),
// Actions Session
SESSION_CREATE("Création session", "SESSION", "CREATE"),
SESSION_DELETE("Suppression session", "SESSION", "DELETE"),
SESSION_REVOKE_ALL("Révocation toutes sessions", "SESSION", "REVOKE_ALL"),
// Actions Système
SYSTEM_BACKUP("Sauvegarde système", "SYSTEM", "BACKUP"),
SYSTEM_RESTORE("Restauration système", "SYSTEM", "RESTORE"),
SYSTEM_CONFIG_CHANGE("Modification configuration", "SYSTEM", "CONFIG_CHANGE");
private final String libelle;
private final String ressourceType;
private final String actionType;
/**
* Détermine si l'action concerne un utilisateur
*/
public boolean isUserAction() {
return ressourceType.equals("USER");
}
/**
* Détermine si l'action concerne un rôle
*/
public boolean isRoleAction() {
return ressourceType.equals("ROLE");
}
/**
* Détermine si l'action est une création
*/
public boolean isCreateAction() {
return actionType.equals("CREATE");
}
/**
* Détermine si l'action est une modification
*/
public boolean isUpdateAction() {
return actionType.equals("UPDATE");
}
/**
* Détermine si l'action est une suppression
*/
public boolean isDeleteAction() {
return actionType.equals("DELETE");
}
/**
* Détermine si l'action est critique (nécessite alerte)
*/
public boolean isCritical() {
return this == USER_DELETE
|| this == ROLE_DELETE
|| this == USER_SUSPEND
|| this == SESSION_REVOKE_ALL
|| this == SYSTEM_RESTORE;
}
}

View File

@@ -0,0 +1,60 @@
package dev.lions.user.manager.enums.role;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
/**
* Type de rôle dans Keycloak
* Distingue les rôles au niveau Realm vs Client
*/
@Getter
@RequiredArgsConstructor
@Schema(description = "Type de rôle Keycloak")
public enum TypeRole {
/**
* Rôle global au niveau du Realm
* Applicable à tous les clients du realm
*/
REALM_ROLE("Realm Role", "Rôle global applicable à tous les clients du realm", "realm-role"),
/**
* Rôle spécifique à un client
* Limité au scope d'un client particulier
*/
CLIENT_ROLE("Client Role", "Rôle spécifique à un client particulier", "client-role"),
/**
* Rôle composite (contient d'autres rôles)
*/
COMPOSITE_ROLE("Composite Role", "Rôle composite contenant d'autres rôles", "composite-role");
private final String libelle;
private final String description;
private final String codeKeycloak;
/**
* Détermine si le rôle est au niveau realm
* @return true si c'est un realm role
*/
public boolean isRealmRole() {
return this == REALM_ROLE;
}
/**
* Détermine si le rôle est au niveau client
* @return true si c'est un client role
*/
public boolean isClientRole() {
return this == CLIENT_ROLE;
}
/**
* Détermine si le rôle est composite
* @return true si c'est un composite role
*/
public boolean isComposite() {
return this == COMPOSITE_ROLE;
}
}

View File

@@ -0,0 +1,79 @@
package dev.lions.user.manager.enums.user;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
/**
* Statut d'un utilisateur dans Keycloak
* Mappé depuis le champ "enabled" et attributs personnalisés
*/
@Getter
@RequiredArgsConstructor
@Schema(description = "Statut d'un utilisateur")
public enum StatutUser {
/**
* Utilisateur actif et opérationnel
*/
ACTIF("Actif", "Utilisateur actif avec accès complet", true),
/**
* Utilisateur désactivé temporairement (peut être réactivé)
*/
INACTIF("Inactif", "Utilisateur désactivé temporairement", false),
/**
* Utilisateur suspendu suite à une action administrative
*/
SUSPENDU("Suspendu", "Compte suspendu par un administrateur", false),
/**
* Utilisateur en attente de validation
*/
EN_ATTENTE("En attente", "Compte en attente de validation", false),
/**
* Utilisateur verrouillé suite à des tentatives échouées
*/
VERROUILLE("Verrouillé", "Compte verrouillé suite à plusieurs échecs d'authentification", false),
/**
* Utilisateur dont le compte a expiré
*/
EXPIRE("Expiré", "Compte expiré et nécessite une réactivation", false),
/**
* Utilisateur supprimé (soft delete)
*/
SUPPRIME("Supprimé", "Compte supprimé logiquement", false);
private final String libelle;
private final String description;
private final boolean enabled;
/**
* Convertit un statut Keycloak "enabled" en StatutUser
* @param enabled état enabled de Keycloak
* @return ACTIF si enabled=true, INACTIF sinon
*/
public static StatutUser fromEnabled(boolean enabled) {
return enabled ? ACTIF : INACTIF;
}
/**
* Détermine si l'utilisateur peut se connecter
* @return true si le statut permet la connexion
*/
public boolean peutSeConnecter() {
return this == ACTIF;
}
/**
* Détermine si l'utilisateur peut être réactivé
* @return true si le statut permet la réactivation
*/
public boolean peutEtreReactive() {
return this == INACTIF || this == SUSPENDU || this == EXPIRE;
}
}