# 🔄 HANDOFF COMPLET - LIONS-USER-MANAGER
## Document de Transfert pour Agent IA Successeur

**Date**: 2025-11-09
**Projet**: lions-user-manager - Système de gestion utilisateurs Keycloak
**Localisation**: C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager
**Progression globale**: 45% complété

---

## 📋 TABLE DES MATIÈRES

1. [Vue d'ensemble du projet](#1-vue-densemble-du-projet)
2. [État actuel détaillé](#2-état-actuel-détaillé)
3. [Erreurs de compilation à corriger](#3-erreurs-de-compilation-à-corriger)
4. [Architecture et décisions techniques](#4-architecture-et-décisions-techniques)
5. [Configuration Git](#5-configuration-git)
6. [Tâches prioritaires](#6-tâches-prioritaires)
7. [Tâches complètes restantes](#7-tâches-complètes-restantes)
8. [Spécifications techniques détaillées](#8-spécifications-techniques-détaillées)
9. [Commandes utiles](#9-commandes-utiles)

---

## 1. VUE D'ENSEMBLE DU PROJET

### 1.1 Objectif Principal
Créer un système complet de gestion des utilisateurs Keycloak avec:
- **Backend Quarkus**: API REST pour gérer users, rôles, audit
- **Frontend PrimeFaces**: Interface JSF avec thème Freya
- **Kubernetes ready**: Déploiement via Helm charts
- **CONTRAINTE CRITIQUE**: ZÉRO accès direct à la DB Keycloak, uniquement via Admin REST API

### 1.2 Structure des Modules (Maven Multi-module)

```
lions-user-manager/
├── pom.xml (parent)
├── lions-user-manager-server-api/          ✅ 100% COMPLÉTÉ
│   ├── src/main/java/dev/lions/user/manager/
│   │   ├── dto/
│   │   │   ├── base/BaseDTO.java
│   │   │   ├── user/UserDTO.java, UserSearchCriteriaDTO.java, UserSearchResultDTO.java
│   │   │   ├── role/RoleDTO.java, RoleAssignmentDTO.java
│   │   │   └── audit/AuditLogDTO.java
│   │   ├── enums/
│   │   │   ├── user/StatutUser.java
│   │   │   ├── role/TypeRole.java
│   │   │   └── audit/TypeActionAudit.java
│   │   ├── service/
│   │   │   ├── UserService.java (interface - 25+ méthodes)
│   │   │   ├── RoleService.java (interface - 20+ méthodes)
│   │   │   ├── AuditService.java (interface - 12+ méthodes)
│   │   │   └── SyncService.java (interface - 8+ méthodes)
│   │   └── validation/ValidationConstants.java
│   └── pom.xml
│
├── lions-user-manager-server-impl-quarkus/ ⚠️ 70% COMPLÉTÉ - ERREURS COMPILATION
│   ├── src/main/java/dev/lions/user/manager/
│   │   ├── client/
│   │   │   ├── KeycloakAdminClient.java           ✅ OK
│   │   │   └── KeycloakAdminClientImpl.java       ✅ OK (Circuit Breaker, Retry, Timeout)
│   │   ├── mapper/
│   │   │   ├── UserMapper.java                    ✅ OK
│   │   │   └── RoleMapper.java                    ❌ ERREURS (name vs nom)
│   │   ├── service/impl/
│   │   │   ├── UserServiceImpl.java               ✅ OK
│   │   │   ├── RoleServiceImpl.java               ❌ ERREURS (signatures méthodes)
│   │   │   ├── AuditServiceImpl.java              ❌ ERREURS (signatures méthodes)
│   │   │   └── SyncServiceImpl.java               ❌ ERREURS (signatures méthodes)
│   │   ├── resource/
│   │   │   ├── UserResource.java                  ✅ OK
│   │   │   ├── RoleResource.java                  ❌ ERREURS
│   │   │   ├── AuditResource.java                 ❌ ERREURS
│   │   │   ├── SyncResource.java                  ❌ ERREURS
│   │   │   ├── KeycloakHealthCheck.java           ✅ OK
│   │   │   └── HealthResourceEndpoint.java        ✅ OK
│   │   └── src/main/resources/
│   │       ├── application.properties             ✅ OK
│   │       ├── application-dev.properties         ✅ OK
│   │       └── application-prod.properties        ✅ OK
│   └── pom.xml
│
├── lions-user-manager-client-quarkus-primefaces-freya/  ⏳ 0% - PAS COMMENCÉ
│   └── pom.xml (structure de base seulement)
│
├── README.md                                      ✅ CRÉÉ
├── PROGRESS_REPORT.md                             ✅ CRÉÉ
├── HANDOFF_COMPLET.md                             ✅ CE FICHIER
└── .gitignore                                     ✅ CRÉÉ
```

### 1.3 Technologies Stack

**Backend**:
- Quarkus 3.15.1
- Keycloak Admin Client 23.0.3 (avec exclusions RESTEasy)
- SmallRye Fault Tolerance (Circuit Breaker, Retry, Timeout)
- MicroProfile OpenAPI
- Jakarta EE (Validation, Inject, REST)
- Lombok 1.18.30
- MapStruct 1.5.5.Final (pas encore utilisé)

**Frontend** (à implémenter):
- PrimeFaces 14.0.5
- Freya Theme 5.0.0-jakarta (depuis git.lions.dev/lionsdev/btpxpress-maven-repo)
- JSF (Jakarta Faces)
- MicroProfile Rest Client

**Tests** (à implémenter):
- JUnit 5
- Testcontainers 1.19.3 (Keycloak, PostgreSQL)
- RestAssured
- Jacoco (objectif 80% coverage)

---

## 2. ÉTAT ACTUEL DÉTAILLÉ

### 2.1 Fichiers Créés et Fonctionnels ✅

#### Module server-api (15 fichiers - 100% OK)
```
lions-user-manager-server-api/src/main/java/dev/lions/user/manager/
├── dto/
│   ├── base/BaseDTO.java                          [FONCTIONNEL]
│   ├── user/
│   │   ├── UserDTO.java                           [FONCTIONNEL - 60+ champs]
│   │   ├── UserSearchCriteriaDTO.java             [FONCTIONNEL]
│   │   └── UserSearchResultDTO.java               [FONCTIONNEL]
│   ├── role/
│   │   ├── RoleDTO.java                           [FONCTIONNEL - champ: name]
│   │   └── RoleAssignmentDTO.java                 [FONCTIONNEL]
│   └── audit/
│       └── AuditLogDTO.java                       [FONCTIONNEL]
├── enums/
│   ├── user/StatutUser.java                       [FONCTIONNEL - 7 états]
│   ├── role/TypeRole.java                         [FONCTIONNEL]
│   └── audit/TypeActionAudit.java                 [FONCTIONNEL - 15+ actions]
├── service/
│   ├── UserService.java                           [INTERFACE OK]
│   ├── RoleService.java                           [INTERFACE OK]
│   ├── AuditService.java                          [INTERFACE OK]
│   └── SyncService.java                           [INTERFACE OK]
└── validation/ValidationConstants.java            [FONCTIONNEL]
```

#### Module server-impl (7 fichiers fonctionnels, 6 avec erreurs)

**Fichiers 100% fonctionnels**:
1. `KeycloakAdminClient.java` - Interface
2. `KeycloakAdminClientImpl.java` - Implémentation avec résilience
3. `UserMapper.java` - Conversions UserDTO <-> Keycloak
4. `UserServiceImpl.java` - Service utilisateurs (25+ méthodes)
5. `UserResource.java` - REST API users (12 endpoints)
6. `KeycloakHealthCheck.java` - Health check Keycloak
7. `HealthResourceEndpoint.java` - Endpoint health

**Fichiers avec erreurs de compilation**:
1. `RoleMapper.java` - Utilise getNom() au lieu de getName()
2. `RoleServiceImpl.java` - Signatures méthodes incompatibles avec interface
3. `AuditServiceImpl.java` - Méthodes pas dans l'interface
4. `SyncServiceImpl.java` - Méthodes incompatibles
5. `RoleResource.java` - Appelle méthodes inexistantes
6. `AuditResource.java` - Appelle méthodes inexistantes
7. `SyncResource.java` - Probablement OK mais dépend de SyncServiceImpl

### 2.2 Configuration Git ✅

**4 repositories configurés**:
1. **master**: https://git.lions.dev/lionsdev/lions-user-manager.git
2. **server-api**: https://git.lions.dev/lionsdev/lions-user-manager-server-api.git
3. **server-impl**: https://git.lions.dev/lionsdev/lions-user-manager-server-impl-quarkus.git
4. **client**: https://git.lions.dev/lionsdev/lions-user-manager-client-quarkus-primefaces-freya.git

**Credentials Git**:
- Username: `lionsdev`
- Password: `lions@2025`
- Format URL: `https://lionsdev:lions%402025@git.lions.dev/...`

**Structure Git actuelle**:
- ✅ Chaque sous-module a son propre repository Git avec UNIQUEMENT son code
- ✅ Le repository master contient tout le projet
- ✅ Tous les repos sont sur la branche `main`
- ✅ Derniers commits effectués et pushés

---

## 3. ERREURS DE COMPILATION À CORRIGER

### 3.1 Erreurs Critiques (bloquent la compilation)

#### Erreur #1: RoleDTO - Incohérence nom du champ
**Fichier**: `RoleDTO.java` ligne 38
**Problème**: Le champ s'appelle `name` et non `nom`
```java
// RoleDTO.java:38
private String name;  // ✅ CORRECT dans le DTO
```

**Fichiers utilisant incorrectement `getNom()`**:
- `RoleMapper.java` lignes 25, 43
- `RoleServiceImpl.java` lignes 170, 179
- `RoleResource.java` lignes 56

**Solution**: Remplacer tous les `getNom()` par `getName()` et `setNom()` par `setName()`

#### Erreur #2: RoleMapper - Mauvaise propriété dans builder
**Fichier**: `RoleMapper.java` ligne 25
**Code actuel**:
```java
.nom(roleRep.getName())  // ❌ ERREUR
```
**Correction**:
```java
.name(roleRep.getName())  // ✅ CORRECT
```

#### Erreur #3: RoleMapper - Test null incorrect
**Fichier**: `RoleMapper.java` ligne 29
**Code actuel**:
```java
.composite(roleRep.isComposite() != null ? roleRep.isComposite() : false)  // ❌ ERREUR
```
**Problème**: `isComposite()` retourne `boolean`, pas `Boolean`
**Correction**:
```java
.composite(roleRep.isComposite())  // ✅ CORRECT
```

#### Erreur #4: RoleServiceImpl - Signatures incompatibles avec interface

**Interface RoleService attend**:
```java
// RoleService.java
List<RoleDTO> getAllClientRoles(@NotBlank String realmName, @NotBlank String clientName);
Optional<RoleDTO> getRoleByName(@NotBlank String roleName, @NotBlank String realmName,
                                @NotNull TypeRole typeRole, String clientName);
RoleDTO createClientRole(@Valid @NotNull RoleDTO role, @NotBlank String realmName,
                        @NotBlank String clientName);
RoleDTO updateRole(@NotBlank String roleId, @Valid @NotNull RoleDTO role,
                  @NotBlank String realmName, @NotNull TypeRole typeRole, String clientName);
void deleteRole(@NotBlank String roleId, @NotBlank String realmName,
               @NotNull TypeRole typeRole, String clientName);
// ... et beaucoup d'autres méthodes différentes
```

**RoleServiceImpl implémente**:
```java
// RoleServiceImpl.java - SIGNATURES DIFFÉRENTES
Optional<RoleDTO> getRealmRoleByName(String roleName, String realmName);  // ❌ Pas dans interface
RoleDTO updateRealmRole(String roleName, RoleDTO roleDTO, String realmName);  // ❌ Pas dans interface
void deleteRealmRole(String roleName, String realmName);  // ❌ Pas dans interface
List<RoleDTO> getAllClientRoles(String clientId, String realmName);  // ❌ Ordre paramètres inversé
// ... etc
```

**SOLUTION RECOMMANDÉE**:
Deux options:
1. **Option A** (RECOMMANDÉE): Modifier `RoleServiceImpl` pour implémenter EXACTEMENT les méthodes de l'interface
2. **Option B**: Modifier l'interface `RoleService.java` pour correspondre à l'implémentation (moins propre)

#### Erreur #5: AuditService - Méthodes manquantes dans l'interface

**AuditServiceImpl utilise**:
```java
List<AuditLogDTO> searchLogs(String acteur, LocalDateTime debut, LocalDateTime fin,
                             TypeActionAudit type, String ressourceType, Boolean succes,
                             int page, int pageSize);
List<AuditLogDTO> getLogsByActeur(String acteur, int limit);
List<AuditLogDTO> getLogsByRessource(String type, String id, int limit);
Map<TypeActionAudit, Long> getActionStatistics(LocalDateTime debut, LocalDateTime fin);
long getFailureCount(LocalDateTime debut, LocalDateTime fin);
// ... etc
```

**Mais l'interface AuditService.java ne déclare pas toutes ces méthodes!**

**SOLUTION**: Ajouter toutes les méthodes manquantes dans `AuditService.java`

#### Erreur #6: SyncServiceImpl - Classes internes pas exportées

**Fichier**: `SyncServiceImpl.java`
**Problème**: Classes internes `SyncResult` et `HealthStatus` utilisées dans l'interface mais non visibles

**SOLUTION**:
1. Soit déplacer ces classes dans le module `server-api` comme DTOs séparés
2. Soit les garder dans l'implémentation et créer des interfaces dans `server-api`

#### Erreur #7: RoleResource - Appels à méthodes inexistantes

**Fichier**: `RoleResource.java`
**Lignes avec erreurs**: 91, 146, 172, 237, 292, 321, 348, 377, 457, 482

**Exemple ligne 91**:
```java
return roleService.getRealmRoleByName(roleName, realmName)  // ❌ Méthode n'existe pas
```

**SOLUTION**: Utiliser les méthodes correctes de l'interface après correction de RoleService

#### Erreur #8: Conversion Set vers List

**Fichier**: `RoleServiceImpl.java` ligne 539
**Code actuel**:
```java
List<RoleRepresentation> composites = roleResource.getRoleComposites();  // ❌ Retourne Set
```
**Correction**:
```java
List<RoleRepresentation> composites = new ArrayList<>(roleResource.getRoleComposites());
```

### 3.2 Plan de Correction Étape par Étape

**ÉTAPE 1: Corriger RoleMapper** (5 minutes)
```bash
# Fichier: lions-user-manager-server-impl-quarkus/src/main/java/dev/lions/user/manager/mapper/RoleMapper.java
# Ligne 25: .nom(roleRep.getName()) → .name(roleRep.getName())
# Ligne 29: roleRep.isComposite() != null ? ... → roleRep.isComposite()
# Ligne 43: roleDTO.getNom() → roleDTO.getName()
```

**ÉTAPE 2: Aligner RoleService et RoleServiceImpl** (30 minutes)
Deux sous-options:

**Option 2A** (RECOMMANDÉE): Simplifier l'interface RoleService
```java
// Nouvelle interface simplifiée dans server-api/service/RoleService.java
public interface RoleService {
    // CRUD Realm Roles
    RoleDTO createRealmRole(RoleDTO roleDTO, String realmName);
    Optional<RoleDTO> getRealmRoleById(String roleId, String realmName);
    Optional<RoleDTO> getRealmRoleByName(String roleName, String realmName);
    RoleDTO updateRealmRole(String roleName, RoleDTO roleDTO, String realmName);
    void deleteRealmRole(String roleName, String realmName);
    List<RoleDTO> getAllRealmRoles(String realmName);

    // CRUD Client Roles
    RoleDTO createClientRole(RoleDTO roleDTO, String clientId, String realmName);
    Optional<RoleDTO> getClientRoleByName(String roleName, String clientId, String realmName);
    void deleteClientRole(String roleName, String clientId, String realmName);
    List<RoleDTO> getAllClientRoles(String clientId, String realmName);

    // Attribution de rôles
    void assignRealmRolesToUser(String userId, List<String> roleNames, String realmName);
    void revokeRealmRolesFromUser(String userId, List<String> roleNames, String realmName);
    void assignClientRolesToUser(String userId, String clientId, List<String> roleNames, String realmName);
    void revokeClientRolesFromUser(String userId, String clientId, List<String> roleNames, String realmName);
    List<RoleDTO> getUserRealmRoles(String userId, String realmName);
    List<RoleDTO> getUserClientRoles(String userId, String clientId, String realmName);

    // Rôles composites
    void addCompositesToRealmRole(String roleName, List<String> compositeRoleNames, String realmName);
    void removeCompositesFromRealmRole(String roleName, List<String> compositeRoleNames, String realmName);
    List<RoleDTO> getCompositeRoles(String roleName, String realmName);

    // Vérification permissions
    boolean userHasRealmRole(String userId, String roleName, String realmName);
    boolean userHasClientRole(String userId, String clientId, String roleName, String realmName);
    List<RoleDTO> getUserEffectiveRealmRoles(String userId, String realmName);
}
```

**Option 2B**: Modifier RoleServiceImpl pour correspondre à l'interface actuelle (plus de travail)

**ÉTAPE 3: Compléter AuditService.java** (10 minutes)
```java
// Ajouter dans server-api/service/AuditService.java
List<AuditLogDTO> searchLogs(String acteurUsername, LocalDateTime dateDebut, LocalDateTime dateFin,
                             TypeActionAudit typeAction, String ressourceType, Boolean succes,
                             int page, int pageSize);
List<AuditLogDTO> getLogsByActeur(String acteurUsername, int limit);
List<AuditLogDTO> getLogsByRessource(String ressourceType, String ressourceId, int limit);
List<AuditLogDTO> getLogsByAction(TypeActionAudit typeAction, LocalDateTime dateDebut,
                                   LocalDateTime dateFin, int limit);
Map<TypeActionAudit, Long> getActionStatistics(LocalDateTime dateDebut, LocalDateTime dateFin);
Map<String, Long> getUserActivityStatistics(LocalDateTime dateDebut, LocalDateTime dateFin);
long getFailureCount(LocalDateTime dateDebut, LocalDateTime dateFin);
long getSuccessCount(LocalDateTime dateDebut, LocalDateTime dateFin);
List<String> exportLogsToCSV(LocalDateTime dateDebut, LocalDateTime dateFin);
void purgeOldLogs(int joursDAnciennete);
```

**ÉTAPE 4: Créer DTOs pour Sync** (15 minutes)
```bash
# Créer: server-api/dto/sync/SyncResultDTO.java
# Créer: server-api/dto/sync/HealthStatusDTO.java
# Modifier SyncService.java pour utiliser ces DTOs
# Modifier SyncServiceImpl.java pour retourner ces DTOs
```

**ÉTAPE 5: Corriger RoleServiceImpl ligne 539** (2 minutes)
```java
List<RoleRepresentation> composites = new ArrayList<>(
    keycloakAdminClient.getInstance()
        .realm(realmName)
        .roles()
        .get(roleName)
        .getRoleComposites()
);
```

**ÉTAPE 6: Compiler et vérifier** (5 minutes)
```bash
cd C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager
mvn clean compile -DskipTests
```

---

## 4. ARCHITECTURE ET DÉCISIONS TECHNIQUES

### 4.1 Décisions Architecturales Clés

#### A. Pattern Circuit Breaker + Retry
**Localisation**: `KeycloakAdminClientImpl.java`
**Raison**: Keycloak peut être temporairement indisponible
**Configuration**:
```java
@Retry(maxRetries = 3, delay = 2, delayUnit = ChronoUnit.SECONDS)
@Timeout(value = 30, unit = ChronoUnit.SECONDS)
@CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 5000)
```

#### B. Exclusion RESTEasy Classic
**Localisation**: `server-impl/pom.xml`
**Problème résolu**: "Mixing Quarkus REST and RESTEasy Classic"
**Solution**:
```xml
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-admin-client</artifactId>
    <version>23.0.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-multipart-provider</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
        </exclusion>
    </exclusions>
</dependency>
```

#### C. Audit Logging - En Mémoire pour Dev
**Localisation**: `AuditServiceImpl.java`
**État actuel**: Stockage en mémoire via ConcurrentHashMap
**TODO Production**: Implémenter persistence PostgreSQL avec Panache
**Logging parallèle**: SLF4J pour capture par systèmes centralisés (Graylog, ELK)

#### D. Mappers - Statiques vs MapStruct
**État actuel**: Méthodes statiques dans UserMapper et RoleMapper
**Décision**: Pas encore utilisé MapStruct (dépendance présente mais pas configurée)
**TODO**: Migrer vers MapStruct pour génération automatique

#### E. Séparation Dev/Prod
**Fichiers**:
- `application.properties` - Configuration de base
- `application-dev.properties` - Localhost, DEBUG logs
- `application-prod.properties` - HTTPS, DB obligatoire, SSL/TLS

**Activation**:
```bash
# Dev
mvn quarkus:dev -Dquarkus.profile=dev

# Prod
java -jar app.jar -Dquarkus.profile=prod
```

### 4.2 Contraintes Critiques du Projet

1. **ZÉRO accès direct DB Keycloak** - Utiliser UNIQUEMENT Keycloak Admin REST API
2. **Multi-realm support** - Toutes les opérations prennent `realmName` en paramètre
3. **Audit trail complet** - Qui, quoi, quand, IP, succès/échec pour TOUTES les opérations
4. **Test coverage 80%** - Objectif Jacoco avec Testcontainers
5. **Helm charts requis** - Déploiement Kubernetes
6. **Freya theme custom** - Depuis repository Git lions.dev

### 4.3 Patterns de Code Utilisés

#### Pattern DTO
```java
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserDTO extends BaseDTO {
    // ...
}
```

#### Pattern Service/Resource
```
Service Interface (server-api)
    ↓ implémente
Service Implementation (server-impl)
    ↓ injecté dans
REST Resource (server-impl)
    ↓ expose
API REST OpenAPI
```

#### Pattern Mapper
```java
public class UserMapper {
    public static UserDTO toDTO(UserRepresentation keycloakUser, String realmName) { }
    public static UserRepresentation toRepresentation(UserDTO userDTO) { }
    public static List<UserDTO> toDTOList(...) { }
}
```

---

## 5. CONFIGURATION GIT

### 5.1 Structure Multi-Repository

```
Repository Master (contient TOUT)
https://git.lions.dev/lionsdev/lions-user-manager.git
└── C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\

Repository Server-API (contient UNIQUEMENT server-api)
https://git.lions.dev/lionsdev/lions-user-manager-server-api.git
└── C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\

Repository Server-Impl (contient UNIQUEMENT server-impl)
https://git.lions.dev/lionsdev/lions-user-manager-server-impl-quarkus.git
└── C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\

Repository Client (contient UNIQUEMENT client)
https://git.lions.dev/lionsdev/lions-user-manager-client-quarkus-primefaces-freya.git
└── C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-client-quarkus-primefaces-freya\
```

### 5.2 Commandes Git Configurées

**Credentials**:
- Username: `lionsdev`
- Password: `lions@2025` (encoder en URL: `lions%402025`)

**Push vers tous les repos depuis la racine**:
```bash
cd C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager

# Commit dans le repo racine
git add .
git commit -m "feat: Description du commit"
git push origin main
```

**Push d'un sous-module spécifique**:
```bash
# Server-API
cd lions-user-manager-server-api
git add .
git commit -m "feat: Description"
git push origin main

# Server-Impl
cd ../lions-user-manager-server-impl-quarkus
git add .
git commit -m "feat: Description"
git push origin main

# Client
cd ../lions-user-manager-client-quarkus-primefaces-freya
git add .
git commit -m "feat: Description"
git push origin main
```

**Règle de synchronisation**:
Le repository master doit TOUJOURS être mis à jour APRÈS au moins un des repos enfants.

### 5.3 Branches

Toutes les branches sont sur `main` (pas `master`).

---

## 6. TÂCHES PRIORITAIRES

### Priorité 1: Corriger les erreurs de compilation (2-3 heures)

**Objectif**: Faire compiler le projet sans erreurs

**Étapes**:
1. ✅ Corriger RoleMapper (name vs nom)
2. ✅ Aligner RoleService interface et implémentation
3. ✅ Compléter AuditService interface
4. ✅ Créer DTOs pour SyncService
5. ✅ Corriger RoleServiceImpl Set→List
6. ✅ Vérifier compilation: `mvn clean compile -DskipTests`
7. ✅ Commit et push

**Commande de vérification**:
```bash
cd C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager
mvn clean compile -DskipTests 2>&1 | grep -i "BUILD SUCCESS"
```

### Priorité 2: Créer les tests unitaires de base (3-4 heures)

**Objectif**: Atteindre 30% de coverage minimum

**Fichiers à créer**:
```
server-impl/src/test/java/dev/lions/user/manager/
├── service/impl/
│   ├── UserServiceImplTest.java
│   ├── RoleServiceImplTest.java
│   ├── AuditServiceImplTest.java
│   └── SyncServiceImplTest.java
├── mapper/
│   ├── UserMapperTest.java
│   └── RoleMapperTest.java
└── resource/
    ├── UserResourceTest.java
    ├── RoleResourceTest.java
    ├── AuditResourceTest.java
    └── SyncResourceTest.java
```

**Exemple de test**:
```java
@QuarkusTest
public class UserServiceImplTest {

    @InjectMock
    KeycloakAdminClient keycloakAdminClient;

    @Inject
    UserService userService;

    @Test
    void testCreateUser() {
        // Given
        UserDTO userDTO = UserDTO.builder()
            .username("testuser")
            .email("test@example.com")
            .build();

        // Mock Keycloak
        when(keycloakAdminClient.getInstance()...)
            .thenReturn(...);

        // When
        UserDTO created = userService.createUser(userDTO, "test-realm");

        // Then
        assertNotNull(created.getId());
        assertEquals("testuser", created.getUsername());
    }
}
```

### Priorité 3: Créer les tests d'intégration avec Testcontainers (4-6 heures)

**Objectif**: Tests réels contre Keycloak en Docker

**Fichiers à créer**:
```
server-impl/src/test/java/dev/lions/user/manager/integration/
├── KeycloakTestResource.java (configure Testcontainer)
├── UserServiceIntegrationTest.java
├── RoleServiceIntegrationTest.java
└── AuditServiceIntegrationTest.java
```

**Configuration Testcontainer**:
```java
@QuarkusTestResource(KeycloakTestResource.class)
@QuarkusTest
public class UserServiceIntegrationTest {

    public static class KeycloakTestResource implements QuarkusTestResourceLifecycleManager {

        private static final KeycloakContainer keycloak = new KeycloakContainer()
            .withRealmImportFile("test-realm.json");

        @Override
        public Map<String, String> start() {
            keycloak.start();
            return Map.of(
                "lions.keycloak.server-url", keycloak.getAuthServerUrl(),
                "lions.keycloak.admin-username", "admin",
                "lions.keycloak.admin-password", "admin"
            );
        }

        @Override
        public void stop() {
            keycloak.stop();
        }
    }

    @Inject
    UserService userService;

    @Test
    void testCreateUserInRealKeycloak() {
        // Test contre un vrai Keycloak dans Docker
        UserDTO user = userService.createUser(...);
        assertNotNull(user.getId());
    }
}
```

### Priorité 4: Commencer le module client (8-12 heures)

**Objectif**: Interface utilisateur fonctionnelle de base

Voir section 7.4 pour les détails complets.

---

## 7. TÂCHES COMPLÈTES RESTANTES

### 7.1 Backend - Services Restants

#### A. Implémenter Persistence Audit (PostgreSQL)

**Actuellement**: Stockage en mémoire dans `AuditServiceImpl`
**À faire**: Persistence PostgreSQL avec Panache

**Étapes**:

1. **Créer l'entité Panache**:
```java
// server-impl/src/main/java/dev/lions/user/manager/entity/AuditLogEntity.java
@Entity
@Table(name = "audit_log", indexes = {
    @Index(name = "idx_audit_acteur", columnList = "acteur_username"),
    @Index(name = "idx_audit_date", columnList = "date_action"),
    @Index(name = "idx_audit_ressource", columnList = "ressource_type, ressource_id")
})
public class AuditLogEntity extends PanacheEntityBase {

    @Id
    @Column(length = 36)
    public String id;

    @Column(name = "date_action", nullable = false)
    public LocalDateTime dateAction;

    @Column(name = "acteur_username", length = 100, nullable = false)
    public String acteurUsername;

    @Enumerated(EnumType.STRING)
    @Column(name = "type_action", length = 50, nullable = false)
    public TypeActionAudit typeAction;

    @Column(name = "ressource_type", length = 50, nullable = false)
    public String ressourceType;

    @Column(name = "ressource_id", length = 100, nullable = false)
    public String ressourceId;

    @Column(name = "succes", nullable = false)
    public boolean succes;

    @Column(name = "adresse_ip", length = 45)
    public String adresseIp;

    @Column(name = "details", length = 1000)
    public String details;

    @Column(name = "message_erreur", length = 500)
    public String messageErreur;

    @Column(name = "donnees_avant", columnDefinition = "TEXT")
    public String donneesAvant;

    @Column(name = "donnees_apres", columnDefinition = "TEXT")
    public String donneesApres;
}
```

2. **Créer le mapper entité**:
```java
// server-impl/.../mapper/AuditLogMapper.java
public class AuditLogMapper {
    public static AuditLogEntity toEntity(AuditLogDTO dto) { }
    public static AuditLogDTO toDTO(AuditLogEntity entity) { }
}
```

3. **Créer le repository Panache**:
```java
// server-impl/.../repository/AuditLogRepository.java
@ApplicationScoped
public class AuditLogRepository implements PanacheRepository<AuditLogEntity> {

    public List<AuditLogEntity> findByActeur(String username, int limit) {
        return find("acteurUsername = ?1 ORDER BY dateAction DESC", username)
            .page(0, limit).list();
    }

    public List<AuditLogEntity> searchLogs(String acteur, LocalDateTime debut,
                                          LocalDateTime fin, ...) {
        // Query complexe avec critères dynamiques
    }

    public long countFailures(LocalDateTime debut, LocalDateTime fin) {
        return count("succes = false AND dateAction BETWEEN ?1 AND ?2", debut, fin);
    }

    @Transactional
    public void purgeOldLogs(LocalDateTime before) {
        delete("dateAction < ?1", before);
    }
}
```

4. **Modifier AuditServiceImpl**:
```java
@ApplicationScoped
@Slf4j
public class AuditServiceImpl implements AuditService {

    @Inject
    AuditLogRepository repository;

    @ConfigProperty(name = "lions.audit.log-to-database", defaultValue = "false")
    boolean logToDatabase;

    @Override
    @Transactional
    public AuditLogDTO logAction(@Valid @NotNull AuditLogDTO auditLog) {
        // Log SLF4J
        log.info("AUDIT | ...");

        // Persister en DB si activé
        if (logToDatabase) {
            AuditLogEntity entity = AuditLogMapper.toEntity(auditLog);
            repository.persist(entity);
        }

        return auditLog;
    }

    // Modifier toutes les méthodes pour utiliser repository au lieu de Map
}
```

5. **Créer migration Flyway**:
```sql
-- server-impl/src/main/resources/db/migration/V1.0.0__create_audit_log.sql
CREATE TABLE audit_log (
    id VARCHAR(36) PRIMARY KEY,
    date_action TIMESTAMP NOT NULL,
    acteur_username VARCHAR(100) NOT NULL,
    type_action VARCHAR(50) NOT NULL,
    ressource_type VARCHAR(50) NOT NULL,
    ressource_id VARCHAR(100) NOT NULL,
    succes BOOLEAN NOT NULL,
    adresse_ip VARCHAR(45),
    details VARCHAR(1000),
    message_erreur VARCHAR(500),
    donnees_avant TEXT,
    donnees_apres TEXT
);

CREATE INDEX idx_audit_acteur ON audit_log(acteur_username);
CREATE INDEX idx_audit_date ON audit_log(date_action);
CREATE INDEX idx_audit_ressource ON audit_log(ressource_type, ressource_id);
CREATE INDEX idx_audit_type_action ON audit_log(type_action);
```

6. **Configurer datasource**:
```properties
# application-prod.properties
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${DB_USERNAME:audit_user}
quarkus.datasource.password=${DB_PASSWORD:audit_password}
quarkus.datasource.jdbc.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:lions_audit}

quarkus.hibernate-orm.database.generation=none
quarkus.flyway.migrate-at-start=true

lions.audit.log-to-database=true
```

#### B. Ajouter MapStruct pour Mappers

**Actuellement**: Mappers manuels avec méthodes statiques
**À faire**: Utiliser MapStruct pour génération automatique

**Étapes**:

1. **Configurer annotation processor dans POM**:
```xml
<!-- Déjà présent dans parent POM, à vérifier -->
<path>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${mapstruct.version}</version>
</path>
```

2. **Créer mapper UserMapper avec MapStruct**:
```java
@Mapper(componentModel = "cdi")
public interface UserMapper {

    @Mapping(source = "id", target = "id")
    @Mapping(source = "username", target = "username")
    @Mapping(source = "email", target = "email")
    @Mapping(source = "enabled", target = "statut", qualifiedByName = "enabledToStatut")
    UserDTO toDTO(UserRepresentation userRep, @Context String realmName);

    @Mapping(source = "username", target = "username")
    @Mapping(source = "email", target = "email")
    @Mapping(source = "statut", target = "enabled", qualifiedByName = "statutToEnabled")
    UserRepresentation toRepresentation(UserDTO userDTO);

    @Named("enabledToStatut")
    default StatutUser enabledToStatut(Boolean enabled) {
        return StatutUser.fromEnabled(enabled != null ? enabled : false);
    }

    @Named("statutToEnabled")
    default Boolean statutToEnabled(StatutUser statut) {
        return statut != null && statut.peutSeConnecter();
    }
}
```

#### C. Implémenter Cache avec Quarkus Cache

**Objectif**: Cache des rôles et users pour réduire appels Keycloak

**Étapes**:

1. **Ajouter dépendance**:
```xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-cache</artifactId>
</dependency>
```

2. **Annoter méthodes**:
```java
@ApplicationScoped
public class RoleServiceImpl implements RoleService {

    @CacheResult(cacheName = "realm-roles")
    public List<RoleDTO> getAllRealmRoles(@NotBlank String realmName) {
        // Appel Keycloak
    }

    @CacheInvalidate(cacheName = "realm-roles")
    public RoleDTO createRealmRole(RoleDTO roleDTO, String realmName) {
        // Création role
    }
}
```

3. **Configurer cache**:
```properties
quarkus.cache.caffeine.realm-roles.expire-after-write=5M
quarkus.cache.caffeine.client-roles.expire-after-write=5M
quarkus.cache.caffeine.users.expire-after-write=2M
```

### 7.2 Backend - Sécurité et Validation

#### A. Implémenter OIDC Authentication

**Objectif**: Sécuriser les endpoints REST avec Keycloak OIDC

**Configuration déjà présente** dans `application.properties`:
```properties
quarkus.oidc.auth-server-url=${KEYCLOAK_URL:http://localhost:8180}/realms/master
quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID:lions-user-manager}
quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET:secret}
```

**À faire**:

1. **Tester avec vrai Keycloak**
2. **Ajouter JWT claims mapping**:
```java
@ApplicationScoped
public class UserIdentityProvider {

    @Inject
    JsonWebToken jwt;

    public String getCurrentUsername() {
        return jwt.getName();
    }

    public String getCurrentUserEmail() {
        return jwt.getClaim("email");
    }

    public Set<String> getCurrentUserRoles() {
        return jwt.getGroups();
    }
}
```

3. **Utiliser dans audit logging**:
```java
@Inject
UserIdentityProvider userIdentity;

@Override
public UserDTO createUser(UserDTO user, String realmName) {
    String acteur = userIdentity.getCurrentUsername();

    try {
        UserDTO created = // ... création

        auditService.logSuccess(acteur, TypeActionAudit.USER_CREATE,
                                "user", created.getId(), ...);
        return created;
    } catch (Exception e) {
        auditService.logFailure(acteur, TypeActionAudit.USER_CREATE,
                               "user", user.getUsername(), ...);
        throw e;
    }
}
```

#### B. Ajouter Validation Personnalisée

**Objectif**: Validateurs custom pour règles métier complexes

**Exemple**: Validation format téléphone français
```java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FrenchPhoneValidator.class)
public @interface FrenchPhone {
    String message() default "Numéro de téléphone français invalide";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class FrenchPhoneValidator implements ConstraintValidator<FrenchPhone, String> {

    private static final Pattern FRENCH_PHONE = Pattern.compile("^(\\+33|0)[1-9](\\d{2}){4}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isEmpty()) {
            return true;
        }
        return FRENCH_PHONE.matcher(value).matches();
    }
}
```

**Utilisation dans UserDTO**:
```java
@FrenchPhone
private String telephone;
```

### 7.3 Backend - Observabilité

#### A. Métriques Prometheus

**Objectif**: Exposer métriques custom

**Dépendance déjà présente**: `quarkus-micrometer-registry-prometheus`

**À implémenter**:
```java
@ApplicationScoped
public class UserServiceMetrics {

    private final Counter userCreationCounter;
    private final Counter userCreationFailureCounter;
    private final Timer userCreationTimer;

    public UserServiceMetrics(MeterRegistry registry) {
        this.userCreationCounter = registry.counter("lions.user.creation.total");
        this.userCreationFailureCounter = registry.counter("lions.user.creation.failures");
        this.userCreationTimer = registry.timer("lions.user.creation.duration");
    }

    public void recordUserCreation(boolean success, long durationMs) {
        if (success) {
            userCreationCounter.increment();
        } else {
            userCreationFailureCounter.increment();
        }
        userCreationTimer.record(durationMs, TimeUnit.MILLISECONDS);
    }
}
```

**Utiliser dans UserServiceImpl**:
```java
@Inject
UserServiceMetrics metrics;

@Override
public UserDTO createUser(UserDTO user, String realmName) {
    long start = System.currentTimeMillis();
    try {
        UserDTO created = // ... création
        metrics.recordUserCreation(true, System.currentTimeMillis() - start);
        return created;
    } catch (Exception e) {
        metrics.recordUserCreation(false, System.currentTimeMillis() - start);
        throw e;
    }
}
```

**Vérifier métriques**:
```bash
curl http://localhost:8081/q/metrics
```

#### B. Tracing OpenTelemetry

**Dépendance à ajouter**:
```xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-opentelemetry</artifactId>
</dependency>
```

**Configuration**:
```properties
quarkus.opentelemetry.enabled=true
quarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://jaeger:4317
```

### 7.4 Module Client - Interface Utilisateur

**ÉTAT**: 0% - Non commencé
**PRIORITÉ**: Haute (après correction des erreurs backend)

#### A. Structure du Module Client

```
lions-user-manager-client-quarkus-primefaces-freya/
├── src/main/java/dev/lions/user/manager/client/
│   ├── api/                          (REST Clients vers backend)
│   │   ├── UserApiClient.java
│   │   ├── RoleApiClient.java
│   │   ├── AuditApiClient.java
│   │   └── SyncApiClient.java
│   ├── bean/                         (JSF Managed Beans)
│   │   ├── user/
│   │   │   ├── UserListBean.java     (Liste utilisateurs)
│   │   │   ├── UserEditBean.java     (Création/Édition)
│   │   │   ├── UserDetailBean.java   (Détails utilisateur)
│   │   │   └── UserSearchBean.java   (Recherche avancée)
│   │   ├── role/
│   │   │   ├── RoleListBean.java
│   │   │   ├── RoleEditBean.java
│   │   │   └── RoleAssignBean.java   (Attribution rôles)
│   │   ├── audit/
│   │   │   ├── AuditLogBean.java
│   │   │   └── AuditStatsBean.java
│   │   └── navigation/
│   │       ├── MenuBean.java
│   │       └── BreadcrumbBean.java
│   ├── security/
│   │   ├── SecurityFilter.java       (Vérif auth OIDC)
│   │   ├── RoleChecker.java
│   │   └── UserSessionBean.java      (Session utilisateur)
│   ├── converter/                    (JSF Converters)
│   │   ├── StatutUserConverter.java
│   │   └── TypeRoleConverter.java
│   └── validator/                    (JSF Validators)
│       ├── UsernameValidator.java
│       └── EmailValidator.java
│
├── src/main/resources/
│   ├── META-INF/
│   │   └── resources/
│   │       ├── WEB-INF/
│   │       │   ├── web.xml
│   │       │   ├── faces-config.xml
│   │       │   ├── template.xhtml    (Layout principal)
│   │       │   └── menu.xhtml        (Menu navigation)
│   │       ├── users/
│   │       │   ├── list.xhtml        (Liste utilisateurs)
│   │       │   ├── edit.xhtml        (Formulaire utilisateur)
│   │       │   ├── detail.xhtml      (Détails utilisateur)
│   │       │   └── search.xhtml      (Recherche avancée)
│   │       ├── roles/
│   │       │   ├── list.xhtml        (Liste rôles)
│   │       │   ├── edit.xhtml        (Formulaire rôle)
│   │       │   └── assign.xhtml      (Attribution rôles)
│   │       ├── audit/
│   │       │   ├── logs.xhtml        (Logs d'audit)
│   │       │   └── stats.xhtml       (Statistiques)
│   │       ├── resources/
│   │       │   ├── css/
│   │       │   │   └── custom.css
│   │       │   └── js/
│   │       │       └── app.js
│   │       └── index.xhtml           (Page d'accueil)
│   └── application.properties
└── pom.xml
```

#### B. Configuration POM Client

**Dépendances critiques à ajouter**:
```xml
<dependencies>
    <!-- Dépendance sur server-api -->
    <dependency>
        <groupId>dev.lions.user.manager</groupId>
        <artifactId>lions-user-manager-server-api</artifactId>
        <version>${project.version}</version>
    </dependency>

    <!-- Quarkus JSF (MyFaces) -->
    <dependency>
        <groupId>io.quarkiverse.myfaces</groupId>
        <artifactId>quarkus-myfaces</artifactId>
        <version>3.0.0</version>
    </dependency>

    <!-- PrimeFaces -->
    <dependency>
        <groupId>org.primefaces</groupId>
        <artifactId>primefaces</artifactId>
        <version>14.0.5</version>
        <classifier>jakarta</classifier>
    </dependency>

    <!-- Freya Theme depuis repo custom -->
    <dependency>
        <groupId>org.primefaces</groupId>
        <artifactId>freya-theme</artifactId>
        <version>5.0.0-jakarta</version>
    </dependency>

    <!-- MicroProfile Rest Client -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-rest-client-reactive-jackson</artifactId>
    </dependency>

    <!-- OIDC Client -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-oidc</artifactId>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>btpxpress-maven-repo</id>
        <url>https://git.lions.dev/lionsdev/btpxpress-maven-repo/raw/branch/main/</url>
    </repository>
</repositories>
```

#### C. REST Clients vers Backend

**Exemple**: UserApiClient.java
```java
package dev.lions.user.manager.client.api;

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;

@Path("/api/users")
@RegisterRestClient(configKey = "user-api")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface UserApiClient {

    @POST
    @Path("/search")
    UserSearchResultDTO searchUsers(UserSearchCriteriaDTO criteria);

    @GET
    @Path("/{userId}")
    UserDTO getUser(@PathParam("userId") String userId,
                    @QueryParam("realm") String realmName);

    @POST
    UserDTO createUser(UserDTO user, @QueryParam("realm") String realmName);

    @PUT
    @Path("/{userId}")
    UserDTO updateUser(@PathParam("userId") String userId,
                      UserDTO user,
                      @QueryParam("realm") String realmName);

    @DELETE
    @Path("/{userId}")
    void deleteUser(@PathParam("userId") String userId,
                   @QueryParam("realm") String realmName);
}
```

**Configuration**:
```properties
# application.properties
quarkus.rest-client.user-api.url=http://localhost:8081
quarkus.rest-client.role-api.url=http://localhost:8081
quarkus.rest-client.audit-api.url=http://localhost:8081
```

#### D. JSF Managed Bean Exemple

**UserListBean.java**:
```java
package dev.lions.user.manager.client.bean.user;

import dev.lions.user.manager.client.api.UserApiClient;
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.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import lombok.Getter;
import lombok.Setter;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import java.io.Serializable;
import java.util.List;

@Named("userListBean")
@ViewScoped
public class UserListBean implements Serializable {

    @Inject
    @RestClient
    UserApiClient userApiClient;

    @Getter
    @Setter
    private List<UserDTO> users;

    @Getter
    @Setter
    private UserDTO selectedUser;

    @Getter
    @Setter
    private String searchKeyword;

    @Getter
    @Setter
    private String currentRealm = "master";

    @PostConstruct
    public void init() {
        loadUsers();
    }

    public void loadUsers() {
        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(currentRealm)
            .keyword(searchKeyword)
            .page(0)
            .pageSize(50)
            .build();

        UserSearchResultDTO result = userApiClient.searchUsers(criteria);
        this.users = result.getUsers();
    }

    public void deleteUser(UserDTO user) {
        userApiClient.deleteUser(user.getId(), currentRealm);
        loadUsers();
    }

    public String editUser(UserDTO user) {
        return "edit?faces-redirect=true&userId=" + user.getId();
    }
}
```

#### E. Page XHTML Exemple

**users/list.xhtml**:
```xhtml
<!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"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

<ui:composition template="/WEB-INF/template.xhtml">

    <ui:define name="content">
        <h:form id="userListForm">

            <p:panel header="Gestion des Utilisateurs">

                <!-- Barre de recherche -->
                <div class="p-grid p-mb-3">
                    <div class="p-col-12 p-md-8">
                        <p:inputText id="searchKeyword"
                                    value="#{userListBean.searchKeyword}"
                                    placeholder="Rechercher un utilisateur..."
                                    styleClass="p-mr-2" />
                        <p:commandButton value="Rechercher"
                                        action="#{userListBean.loadUsers}"
                                        update="usersTable"
                                        icon="pi pi-search" />
                    </div>
                    <div class="p-col-12 p-md-4 p-text-right">
                        <p:commandButton value="Nouvel Utilisateur"
                                        action="/users/edit?faces-redirect=true"
                                        icon="pi pi-plus"
                                        styleClass="p-button-success" />
                    </div>
                </div>

                <!-- Tableau des utilisateurs -->
                <p:dataTable id="usersTable"
                            value="#{userListBean.users}"
                            var="user"
                            paginator="true"
                            rows="20"
                            paginatorPosition="bottom"
                            selectionMode="single"
                            selection="#{userListBean.selectedUser}"
                            rowKey="#{user.id}"
                            emptyMessage="Aucun utilisateur trouvé">

                    <p:column headerText="Username" sortBy="#{user.username}">
                        <h:outputText value="#{user.username}" />
                    </p:column>

                    <p:column headerText="Email" sortBy="#{user.email}">
                        <h:outputText value="#{user.email}" />
                    </p:column>

                    <p:column headerText="Nom Complet">
                        <h:outputText value="#{user.prenom} #{user.nom}" />
                    </p:column>

                    <p:column headerText="Statut">
                        <p:tag value="#{user.statut.libelle}"
                              severity="#{user.statut.name() == 'ACTIF' ? 'success' : 'danger'}" />
                    </p:column>

                    <p:column headerText="Dernière Connexion">
                        <h:outputText value="#{user.derniereConnexion}">
                            <f:convertDateTime pattern="dd/MM/yyyy HH:mm" />
                        </h:outputText>
                    </p:column>

                    <p:column headerText="Actions" style="width:150px">
                        <p:commandButton icon="pi pi-pencil"
                                        action="#{userListBean.editUser(user)}"
                                        styleClass="p-button-rounded p-button-warning p-mr-2"
                                        title="Éditer" />

                        <p:commandButton icon="pi pi-trash"
                                        action="#{userListBean.deleteUser(user)}"
                                        update="usersTable"
                                        styleClass="p-button-rounded p-button-danger"
                                        title="Supprimer">
                            <p:confirm header="Confirmation"
                                      message="Êtes-vous sûr de vouloir supprimer #{user.username} ?"
                                      icon="pi pi-exclamation-triangle" />
                        </p:commandButton>
                    </p:column>

                </p:dataTable>

            </p:panel>

            <p:confirmDialog global="true" showEffect="fade" hideEffect="fade">
                <p:commandButton value="Oui" type="button"
                                styleClass="ui-confirmdialog-yes p-button-success" />
                <p:commandButton value="Non" type="button"
                                styleClass="ui-confirmdialog-no p-button-danger" />
            </p:confirmDialog>

        </h:form>
    </ui:define>

</ui:composition>
</html>
```

#### F. Template Principal

**WEB-INF/template.xhtml**:
```xhtml
<!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"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

<h:head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Lions User Manager</title>
    <h:outputStylesheet name="css/custom.css" />
</h:head>

<h:body>

    <p:layout fullPage="true">

        <!-- Top Bar -->
        <p:layoutUnit position="north" size="60">
            <div class="topbar">
                <h1>Lions User Manager</h1>
                <div class="user-info">
                    <span>#{userSessionBean.currentUserName}</span>
                    <p:commandButton value="Déconnexion"
                                    action="#{userSessionBean.logout}"
                                    icon="pi pi-sign-out" />
                </div>
            </div>
        </p:layoutUnit>

        <!-- Left Menu -->
        <p:layoutUnit position="west" size="250">
            <ui:include src="menu.xhtml" />
        </p:layoutUnit>

        <!-- Content -->
        <p:layoutUnit position="center">
            <div class="content-wrapper">
                <p:messages id="messages" showDetail="true" closable="true" />
                <ui:insert name="content" />
            </div>
        </p:layoutUnit>

    </p:layout>

</h:body>
</html>
```

#### G. Configuration JSF

**WEB-INF/web.xml**:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                             https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <display-name>Lions User Manager</display-name>

    <!-- JSF Servlet -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

    <!-- PrimeFaces Configuration -->
    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>freya</param-value>
    </context-param>

    <context-param>
        <param-name>primefaces.FONT_AWESOME</param-name>
        <param-value>true</param-value>
    </context-param>

    <!-- JSF Configuration -->
    <context-param>
        <param-name>jakarta.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>

    <context-param>
        <param-name>jakarta.faces.FACELETS_REFRESH_PERIOD</param-name>
        <param-value>0</param-value>
    </context-param>

    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>

</web-app>
```

**WEB-INF/faces-config.xml**:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="https://jakarta.ee/xml/ns/jakartaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                                  https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd"
              version="4.0">

    <application>
        <locale-config>
            <default-locale>fr</default-locale>
            <supported-locale>fr</supported-locale>
            <supported-locale>en</supported-locale>
        </locale-config>

        <resource-bundle>
            <base-name>i18n.messages</base-name>
            <var>msg</var>
        </resource-bundle>
    </application>

</faces-config>
```

### 7.5 Infrastructure - Kubernetes et Helm

#### A. Dockerfile pour Server-Impl

```dockerfile
# server-impl/src/main/docker/Dockerfile.jvm
FROM registry.access.redhat.com/ubi8/openjdk-17:1.18

ENV LANGUAGE='en_US:en'

# Configure JVM
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"

# Copy application
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=185 target/quarkus-app/*.jar /deployments/
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/

EXPOSE 8080
USER 185

ENTRYPOINT [ "java", "-jar", "/deployments/quarkus-run.jar" ]
```

#### B. Helm Chart Structure

```
helm/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-prod.yaml
└── templates/
    ├── _helpers.tpl
    ├── deployment-server.yaml
    ├── deployment-client.yaml
    ├── service-server.yaml
    ├── service-client.yaml
    ├── ingress.yaml
    ├── configmap.yaml
    ├── secret.yaml
    ├── serviceaccount.yaml
    └── hpa.yaml
```

**Chart.yaml**:
```yaml
apiVersion: v2
name: lions-user-manager
description: Système de gestion utilisateurs Keycloak
type: application
version: 1.0.0
appVersion: "1.0.0"
keywords:
  - keycloak
  - user-management
  - quarkus
  - primefaces
maintainers:
  - name: Lions Dev Team
    email: dev@lions.dev
```

**values.yaml**:
```yaml
# Configuration globale
global:
  environment: production
  domain: lions.dev

# Server Backend
server:
  enabled: true
  replicaCount: 2
  image:
    repository: registry.lions.dev/lions-user-manager-server
    tag: 1.0.0
    pullPolicy: IfNotPresent

  resources:
    requests:
      cpu: 500m
      memory: 512Mi
    limits:
      cpu: 1000m
      memory: 1Gi

  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 10
    targetCPUUtilizationPercentage: 70

  service:
    type: ClusterIP
    port: 8080

  env:
    - name: QUARKUS_PROFILE
      value: "prod"
    - name: KEYCLOAK_URL
      value: "https://security.lions.dev"
    - name: KEYCLOAK_CLIENT_SECRET
      valueFrom:
        secretKeyRef:
          name: lions-user-manager-secrets
          key: keycloak-client-secret
    - name: DB_HOST
      value: "postgresql.lions.svc.cluster.local"
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: lions-user-manager-secrets
          key: db-username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: lions-user-manager-secrets
          key: db-password

# Client Frontend
client:
  enabled: true
  replicaCount: 2
  image:
    repository: registry.lions.dev/lions-user-manager-client
    tag: 1.0.0
    pullPolicy: IfNotPresent

  resources:
    requests:
      cpu: 250m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 512Mi

  service:
    type: ClusterIP
    port: 8080

# Ingress
ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  hosts:
    - host: user-manager.lions.dev
      paths:
        - path: /
          pathType: Prefix
          backend: client
        - path: /api
          pathType: Prefix
          backend: server
  tls:
    - secretName: user-manager-tls
      hosts:
        - user-manager.lions.dev

# PostgreSQL (optionnel si base externe)
postgresql:
  enabled: true
  auth:
    username: audit_user
    password: changeme
    database: lions_audit
  primary:
    persistence:
      enabled: true
      size: 10Gi
```

**templates/deployment-server.yaml**:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "lions-user-manager.fullname" . }}-server
  labels:
    {{- include "lions-user-manager.labels" . | nindent 4 }}
    app.kubernetes.io/component: server
spec:
  {{- if not .Values.server.autoscaling.enabled }}
  replicas: {{ .Values.server.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "lions-user-manager.selectorLabels" . | nindent 6 }}
      app.kubernetes.io/component: server
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/q/metrics"
      labels:
        {{- include "lions-user-manager.selectorLabels" . | nindent 8 }}
        app.kubernetes.io/component: server
    spec:
      serviceAccountName: {{ include "lions-user-manager.serviceAccountName" . }}
      containers:
        - name: server
          image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}"
          imagePullPolicy: {{ .Values.server.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          env:
            {{- toYaml .Values.server.env | nindent 12 }}
          livenessProbe:
            httpGet:
              path: /q/health/live
              port: http
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /q/health/ready
              port: http
            initialDelaySeconds: 10
            periodSeconds: 5
          resources:
            {{- toYaml .Values.server.resources | nindent 12 }}
```

**Installation Helm**:
```bash
# Dev
helm install lions-user-manager ./helm -f ./helm/values-dev.yaml --namespace lions-dev

# Prod
helm install lions-user-manager ./helm -f ./helm/values-prod.yaml --namespace lions-prod
```

### 7.6 Scripts Keycloak - Provisioning

#### A. Script d'initialisation Keycloak

**scripts/keycloak-init.sh**:
```bash
#!/bin/bash
set -e

# Configuration
KEYCLOAK_URL=${KEYCLOAK_URL:-http://localhost:8180}
ADMIN_USER=${ADMIN_USER:-admin}
ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}

echo "🚀 Initialisation de Keycloak pour Lions User Manager"

# 1. Obtenir token admin
echo "📝 Obtention du token admin..."
TOKEN=$(curl -s -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=${ADMIN_USER}" \
  -d "password=${ADMIN_PASSWORD}" \
  -d "grant_type=password" \
  -d "client_id=admin-cli" \
  | jq -r '.access_token')

if [ -z "$TOKEN" ] || [ "$TOKEN" == "null" ]; then
  echo "❌ Erreur: Impossible d'obtenir le token admin"
  exit 1
fi

echo "✅ Token obtenu"

# 2. Créer le realm lions (si n'existe pas)
echo "📝 Création du realm 'lions'..."
curl -s -X POST "${KEYCLOAK_URL}/admin/realms" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "realm": "lions",
    "enabled": true,
    "displayName": "Lions Realm",
    "registrationAllowed": false,
    "resetPasswordAllowed": true,
    "rememberMe": true,
    "verifyEmail": true,
    "loginWithEmailAllowed": true,
    "duplicateEmailsAllowed": false,
    "sslRequired": "external",
    "passwordPolicy": "length(8) and digits(1) and lowerCase(1) and upperCase(1) and specialChars(1)",
    "accessTokenLifespan": 3600,
    "ssoSessionIdleTimeout": 1800,
    "ssoSessionMaxLifespan": 36000
  }' || echo "⚠️  Realm 'lions' existe probablement déjà"

# 3. Créer le client lions-user-manager
echo "📝 Création du client 'lions-user-manager'..."
CLIENT_ID=$(curl -s -X POST "${KEYCLOAK_URL}/admin/realms/lions/clients" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "lions-user-manager",
    "name": "Lions User Manager",
    "enabled": true,
    "protocol": "openid-connect",
    "publicClient": false,
    "standardFlowEnabled": true,
    "directAccessGrantsEnabled": true,
    "serviceAccountsEnabled": true,
    "authorizationServicesEnabled": true,
    "redirectUris": [
      "http://localhost:8082/*",
      "https://user-manager.lions.dev/*"
    ],
    "webOrigins": ["+"],
    "attributes": {
      "access.token.lifespan": "3600",
      "client.secret.creation.time": "0"
    }
  }' \
  -w "%{http_code}" -o /tmp/client-response.json)

if [ "$CLIENT_ID" == "201" ]; then
  CLIENT_UUID=$(jq -r '.id' /tmp/client-response.json)
  echo "✅ Client créé avec UUID: $CLIENT_UUID"

  # Récupérer le secret du client
  CLIENT_SECRET=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/lions/clients/${CLIENT_UUID}/client-secret" \
    -H "Authorization: Bearer ${TOKEN}" \
    | jq -r '.value')

  echo "🔑 Client Secret: ${CLIENT_SECRET}"
  echo "   ⚠️  Sauvegardez ce secret dans vos variables d'environnement!"
else
  echo "⚠️  Client existe probablement déjà"
fi

# 4. Créer les rôles realm
echo "📝 Création des rôles realm..."
for role in admin user_manager role_manager auditor sync_manager role_viewer; do
  curl -s -X POST "${KEYCLOAK_URL}/admin/realms/lions/roles" \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Content-Type: application/json" \
    -d "{\"name\": \"${role}\", \"description\": \"Role ${role}\"}" \
    && echo "  ✅ Rôle '${role}' créé" \
    || echo "  ⚠️  Rôle '${role}' existe déjà"
done

# 5. Créer un utilisateur admin de test
echo "📝 Création de l'utilisateur admin de test..."
curl -s -X POST "${KEYCLOAK_URL}/admin/realms/lions/users" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin@lions.dev",
    "email": "admin@lions.dev",
    "firstName": "Admin",
    "lastName": "Lions",
    "enabled": true,
    "emailVerified": true,
    "credentials": [{
      "type": "password",
      "value": "Admin@2025",
      "temporary": false
    }]
  }' && echo "✅ Utilisateur admin créé" || echo "⚠️  Utilisateur admin existe déjà"

# 6. Assigner le rôle admin à l'utilisateur
echo "📝 Attribution du rôle admin..."
USER_ID=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/lions/users?username=admin@lions.dev" \
  -H "Authorization: Bearer ${TOKEN}" \
  | jq -r '.[0].id')

ADMIN_ROLE=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/lions/roles/admin" \
  -H "Authorization: Bearer ${TOKEN}")

curl -s -X POST "${KEYCLOAK_URL}/admin/realms/lions/users/${USER_ID}/role-mappings/realm" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d "[${ADMIN_ROLE}]" \
  && echo "✅ Rôle admin attribué" || echo "⚠️  Erreur attribution rôle"

echo ""
echo "🎉 Initialisation Keycloak terminée!"
echo ""
echo "📋 Informations de connexion:"
echo "   URL: ${KEYCLOAK_URL}"
echo "   Realm: lions"
echo "   Client ID: lions-user-manager"
echo "   Client Secret: ${CLIENT_SECRET:-<voir ci-dessus>}"
echo "   Utilisateur test: admin@lions.dev / Admin@2025"
```

**Exécution**:
```bash
chmod +x scripts/keycloak-init.sh
./scripts/keycloak-init.sh
```

#### B. Realm Export JSON

**config/lions-realm-export.json**:
```json
{
  "realm": "lions",
  "enabled": true,
  "displayName": "Lions Realm",
  "roles": {
    "realm": [
      {"name": "admin", "description": "Administrateur complet"},
      {"name": "user_manager", "description": "Gestionnaire d'utilisateurs"},
      {"name": "role_manager", "description": "Gestionnaire de rôles"},
      {"name": "auditor", "description": "Auditeur (lecture seule)"},
      {"name": "sync_manager", "description": "Gestionnaire de synchronisation"},
      {"name": "role_viewer", "description": "Visualiseur de rôles"}
    ]
  },
  "clients": [
    {
      "clientId": "lions-user-manager",
      "name": "Lions User Manager",
      "enabled": true,
      "protocol": "openid-connect",
      "publicClient": false,
      "standardFlowEnabled": true,
      "directAccessGrantsEnabled": true,
      "serviceAccountsEnabled": true,
      "authorizationServicesEnabled": true,
      "redirectUris": [
        "http://localhost:8082/*",
        "https://user-manager.lions.dev/*"
      ],
      "webOrigins": ["+"]
    }
  ]
}
```

**Import**:
```bash
# Avec docker
docker exec -it keycloak /opt/keycloak/bin/kc.sh import \
  --file /opt/keycloak/data/import/lions-realm-export.json

# Avec commande locale
./kc.sh import --file config/lions-realm-export.json
```

---

## 8. SPÉCIFICATIONS TECHNIQUES DÉTAILLÉES

### 8.1 Contraintes et Règles Métier

#### A. Gestion Utilisateurs

**Règles de validation**:
- Username: 3-50 caractères, alphanumérique + _ -
- Email: format RFC 5322
- Mot de passe: min 8 caractères, 1 maj, 1 min, 1 chiffre, 1 spécial
- Téléphone (France): format +33XXXXXXXXX ou 0XXXXXXXXX

**États utilisateur** (enum StatutUser):
```
ACTIF       → Peut se connecter
INACTIF     → Désactivé temporairement
SUSPENDU    → Suspendu par admin
EN_ATTENTE  → Attend validation
VERROUILLE  → Verrouillé après X tentatives
EXPIRE      → Compte expiré
SUPPRIME    → Suppression logique
```

**Règles métier**:
- Un utilisateur VERROUILLE peut être déverrouillé par un admin
- Un utilisateur SUSPENDU ne peut être réactivé que par un admin
- La suppression est TOUJOURS logique (soft delete), jamais physique
- Lors de la désactivation, toutes les sessions doivent être révoquées

#### B. Gestion Rôles

**Types de rôles** (enum TypeRole):
```
REALM_ROLE      → Rôle au niveau du realm
CLIENT_ROLE     → Rôle spécifique à un client
COMPOSITE_ROLE  → Rôle composite (contient d'autres rôles)
```

**Règles métier**:
- Un rôle composite ne peut pas être supprimé s'il est utilisé
- L'attribution de rôles doit être auditée
- Les rôles système (admin, user) ne peuvent être supprimés
- Un utilisateur peut avoir plusieurs rôles realm ET plusieurs rôles client

#### C. Audit Trail

**Actions auditées** (enum TypeActionAudit):
```
USER_CREATE, USER_UPDATE, USER_DELETE
USER_ACTIVATE, USER_DEACTIVATE, USER_LOCK, USER_UNLOCK
PASSWORD_RESET, PASSWORD_CHANGE
ROLE_CREATE, ROLE_UPDATE, ROLE_DELETE
ROLE_ASSIGN, ROLE_REVOKE
SESSION_LOGOUT, SESSION_REVOKE_ALL
SYNC_USERS, SYNC_ROLES
```

**Informations obligatoires**:
- Qui: username de l'acteur
- Quoi: type d'action
- Quand: timestamp précis
- Sur quoi: type et ID de la ressource
- Résultat: succès ou échec
- Où: adresse IP de l'acteur

**Rétention**:
- Dev: 30 jours
- Prod: 2 ans minimum (contrainte légale)

### 8.2 Sécurité

#### A. Authentication & Authorization

**Flow OIDC**:
```
1. Utilisateur → Frontend → Redirect vers Keycloak
2. Keycloak → Authentification → Génère ID Token + Access Token
3. Frontend → Stocke tokens (HttpOnly cookies)
4. Frontend → API calls avec Access Token dans header Authorization
5. Backend → Valide token avec Keycloak
6. Backend → Vérifie rôles (@RolesAllowed)
7. Backend → Execute action + Audit log
```

**Matrice de permissions**:
```
Rôle             | Users | Roles | Audit | Sync
-----------------+-------+-------+-------+------
admin            |  RWD  |  RWD  |  RWD  | RWD
user_manager     |  RWD  |   -   |   R   |  -
role_manager     |   R   |  RWD  |   R   |  -
auditor          |   R   |   R   |   R   |  -
sync_manager     |   R   |   R   |   R   | RWD
role_viewer      |   -   |   R   |   -   |  -

R = Read, W = Write, D = Delete
```

#### B. Validation des Entrées

**Côté Backend (obligatoire)**:
- Jakarta Bean Validation sur tous les DTOs
- Custom validators pour règles complexes
- Sanitization des inputs pour prévenir XSS/SQL injection

**Côté Frontend (UX)**:
- Validation JavaScript pour feedback immédiat
- Messages d'erreur contextuels
- Boutons submit désactivés si formulaire invalide

#### C. Protection CSRF

**Backend**:
- Validation token CSRF sur endpoints state-changing (POST, PUT, DELETE)
- Token inclus dans JWT

**Frontend**:
- Token CSRF dans meta tag
- Ajouté automatiquement dans headers Ajax

### 8.3 Performance

#### A. Objectifs de Performance

**Backend**:
- Temps réponse API: < 200ms (p95)
- Throughput: > 1000 req/s
- Taux erreur: < 0.1%

**Frontend**:
- Time to Interactive: < 3s
- First Contentful Paint: < 1.5s
- Largest Contentful Paint: < 2.5s

#### B. Optimisations

**Backend**:
- Cache des rôles (TTL 5min)
- Cache des users (TTL 2min)
- Connection pooling Keycloak
- Database connection pooling (HikariCP)
- Pagination systématique (max 100 items)

**Frontend**:
- Lazy loading des composants PrimeFaces
- Compression Gzip/Brotli
- Cache statique (CSS, JS, images)
- CDN pour Freya theme

### 8.4 Monitoring et Alerting

#### A. Métriques Clés

**Métriques applicatives**:
```
lions_user_creation_total          Counter
lions_user_creation_failures       Counter
lions_user_creation_duration_ms    Histogram
lions_keycloak_api_calls_total     Counter
lions_keycloak_api_errors_total    Counter
lions_audit_logs_total             Counter
```

**Métriques système**:
```
jvm_memory_used_bytes
jvm_threads_current
http_server_requests_seconds
```

#### B. Alertes Critiques

```
Alert: KeycloakDown
Condition: up{job="keycloak"} == 0
Severity: Critical
Action: Page on-call

Alert: HighErrorRate
Condition: rate(lions_user_creation_failures[5m]) > 0.1
Severity: Warning
Action: Notify team

Alert: SlowResponse
Condition: histogram_quantile(0.95, lions_user_creation_duration_ms) > 500
Severity: Warning
Action: Investigate performance
```

#### C. Logs Centralisés

**Format JSON structuré**:
```json
{
  "timestamp": "2025-11-09T14:30:00Z",
  "level": "INFO",
  "logger": "UserServiceImpl",
  "message": "User created",
  "userId": "123-456",
  "username": "jdoe",
  "acteur": "admin@lions.dev",
  "realmName": "lions",
  "duration_ms": 45
}
```

**Stack recommandé**:
- Filebeat → collect logs
- Logstash → parse et enrich
- Elasticsearch → store
- Kibana → visualize

---

## 9. COMMANDES UTILES

### 9.1 Build et Compilation

```bash
# Compilation complète
cd C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager
mvn clean compile -DskipTests

# Compilation avec tests
mvn clean test

# Package (JAR)
mvn clean package

# Package natif (GraalVM - long!)
mvn clean package -Pnative

# Install dans repo Maven local
mvn clean install
```

### 9.2 Exécution

```bash
# Dev mode avec hot reload
cd lions-user-manager-server-impl-quarkus
mvn quarkus:dev

# Dev mode avec profil dev
mvn quarkus:dev -Dquarkus.profile=dev

# Dev mode avec debug port 5005
mvn quarkus:dev -Ddebug=5005

# Exécution du JAR packagé
java -jar target/quarkus-app/quarkus-run.jar

# Exécution avec profil prod
java -jar target/quarkus-app/quarkus-run.jar -Dquarkus.profile=prod
```

### 9.3 Tests

```bash
# Tous les tests
mvn test

# Tests d'un module spécifique
cd lions-user-manager-server-impl-quarkus
mvn test

# Test d'une classe spécifique
mvn test -Dtest=UserServiceImplTest

# Test d'une méthode spécifique
mvn test -Dtest=UserServiceImplTest#testCreateUser

# Tests d'intégration uniquement
mvn verify -Pintegration-tests

# Coverage Jacoco
mvn clean test jacoco:report
# Rapport: target/site/jacoco/index.html
```

### 9.4 Git

```bash
# Status de tous les modules
cd C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager
git status

# Commit et push repository master
git add .
git commit -m "feat: Description"
git push origin main

# Commit et push sous-module server-api
cd lions-user-manager-server-api
git add .
git commit -m "feat: Description"
git push origin main
cd ..

# Commit et push sous-module server-impl
cd lions-user-manager-server-impl-quarkus
git add .
git commit -m "feat: Description"
git push origin main
cd ..

# Commit et push sous-module client
cd lions-user-manager-client-quarkus-primefaces-freya
git add .
git commit -m "feat: Description"
git push origin main
cd ..

# Puis mettre à jour le master
git add .
git commit -m "chore: Sync submodules"
git push origin main
```

### 9.5 Docker

```bash
# Build image Docker
docker build -f src/main/docker/Dockerfile.jvm -t lions-user-manager-server:1.0.0 .

# Run container
docker run -p 8081:8080 \
  -e KEYCLOAK_URL=http://keycloak:8080 \
  -e KEYCLOAK_CLIENT_SECRET=secret \
  lions-user-manager-server:1.0.0

# Docker Compose (si créé)
docker-compose up -d

# Voir logs
docker logs -f lions-user-manager-server
```

### 9.6 Kubernetes

```bash
# Apply manifests
kubectl apply -f k8s/

# Helm install dev
helm install lions-user-manager ./helm -f ./helm/values-dev.yaml -n lions-dev

# Helm upgrade
helm upgrade lions-user-manager ./helm -f ./helm/values-prod.yaml -n lions-prod

# Check pods
kubectl get pods -n lions-prod

# Logs d'un pod
kubectl logs -f deployment/lions-user-manager-server -n lions-prod

# Port-forward pour debug
kubectl port-forward svc/lions-user-manager-server 8081:8080 -n lions-prod
```

### 9.7 Keycloak Admin

```bash
# Obtenir token admin
curl -X POST http://localhost:8180/realms/master/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=admin" \
  -d "password=admin" \
  -d "grant_type=password" \
  -d "client_id=admin-cli"

# Lister les users d'un realm
curl -X GET http://localhost:8180/admin/realms/lions/users \
  -H "Authorization: Bearer ${TOKEN}"

# Créer un user
curl -X POST http://localhost:8180/admin/realms/lions/users \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"username": "test", "email": "test@example.com", "enabled": true}'
```

### 9.8 OpenAPI / Swagger

```bash
# Accéder à Swagger UI (en dev mode)
http://localhost:8081/q/swagger-ui

# Télécharger spec OpenAPI
curl http://localhost:8081/q/openapi -o openapi.json
```

---

## 10. CHECKLIST FINALE POUR LE PROCHAIN AGENT

### ✅ Actions Immédiates (1-2 heures)

- [ ] Lire entièrement ce document HANDOFF_COMPLET.md
- [ ] Vérifier que tous les fichiers sont présents dans le workspace
- [ ] Compiler le projet: `mvn clean compile -DskipTests`
- [ ] **Corriger les erreurs de compilation** (voir section 3)
  - [ ] RoleMapper: name vs nom
  - [ ] RoleServiceImpl: signatures méthodes
  - [ ] AuditService: compléter interface
  - [ ] SyncService: créer DTOs
- [ ] Re-compiler pour vérifier: `mvn clean compile -DskipTests`
- [ ] Commit et push les corrections

### 📊 Phase 1: Stabilisation Backend (3-5 heures)

- [ ] Créer tests unitaires de base (30% coverage minimum)
- [ ] Créer tests d'intégration avec Testcontainers
- [ ] Implémenter persistence audit PostgreSQL + Panache
- [ ] Migrer vers MapStruct pour les mappers
- [ ] Ajouter cache Quarkus pour réduire appels Keycloak
- [ ] Vérifier coverage Jacoco: `mvn test jacoco:report`
- [ ] Commit et push

### 🎨 Phase 2: Module Client (10-15 heures)

- [ ] Configurer POM avec PrimeFaces + Freya
- [ ] Créer REST Clients (@RegisterRestClient)
- [ ] Créer template principal + menu
- [ ] Créer pages Users (list, edit, detail, search)
- [ ] Créer pages Roles (list, edit, assign)
- [ ] Créer pages Audit (logs, stats)
- [ ] Tester interface en local
- [ ] Commit et push

### 🚀 Phase 3: Infrastructure (8-12 heures)

- [ ] Créer Dockerfiles optimisés
- [ ] Créer Helm charts complets
- [ ] Créer scripts Keycloak provisioning
- [ ] Tester déploiement Kubernetes local (minikube/kind)
- [ ] Créer pipeline CI/CD (GitHub Actions ou GitLab CI)
- [ ] Documenter procédure de déploiement
- [ ] Commit et push

### 📈 Phase 4: Monitoring & Docs (5-8 heures)

- [ ] Implémenter métriques Prometheus custom
- [ ] Configurer tracing OpenTelemetry
- [ ] Créer dashboards Grafana
- [ ] Configurer alertes critiques
- [ ] Écrire documentation utilisateur
- [ ] Créer guide d'administration
- [ ] Créer guide de contribution développeurs
- [ ] Commit et push

### 🎯 Phase 5: Production Readiness (3-5 heures)

- [ ] Load testing avec Gatling ou k6
- [ ] Security scanning (OWASP ZAP, Snyk)
- [ ] Performance tuning
- [ ] Backup/restore procedures
- [ ] Disaster recovery plan
- [ ] Runbook pour on-call
- [ ] Final QA testing
- [ ] Release 1.0.0 🎉

---

## 11. CONTACTS ET RESSOURCES

### Documentation Externe

**Quarkus**:
- Guide: https://quarkus.io/guides/
- REST Client: https://quarkus.io/guides/rest-client
- OIDC: https://quarkus.io/guides/security-oidc-code-flow-authentication

**Keycloak**:
- Admin REST API: https://www.keycloak.org/docs-api/23.0.3/rest-api/index.html
- Admin Client Java: https://www.keycloak.org/docs/latest/server_development/#admin-rest-api

**PrimeFaces**:
- Showcase: https://www.primefaces.org/showcase/
- Freya Theme: (repository custom lions.dev)

**Testcontainers**:
- Keycloak Module: https://java.testcontainers.org/modules/databases/keycloak/

### Repositories Git

- Master: https://git.lions.dev/lionsdev/lions-user-manager
- Server-API: https://git.lions.dev/lionsdev/lions-user-manager-server-api
- Server-Impl: https://git.lions.dev/lionsdev/lions-user-manager-server-impl-quarkus
- Client: https://git.lions.dev/lionsdev/lions-user-manager-client-quarkus-primefaces-freya
- Maven Repo: https://git.lions.dev/lionsdev/btpxpress-maven-repo

---

## 🎯 OBJECTIF FINAL

**Livrer un système de gestion utilisateurs Keycloak production-ready** comprenant:

1. ✅ Backend REST API complet et testé (80% coverage)
2. ✅ Frontend PrimeFaces ergonomique et responsive
3. ✅ Audit trail complet de toutes les opérations
4. ✅ Déploiement Kubernetes via Helm
5. ✅ Monitoring et alerting opérationnels
6. ✅ Documentation complète (technique + utilisateur)
7. ✅ CI/CD pipeline automatisé

**Durée estimée restante**: 40-60 heures de développement

**Priorité absolue**: Corriger les erreurs de compilation AVANT toute autre tâche!

---

## 📝 NOTES FINALES

Ce document contient TOUTES les informations nécessaires pour continuer le développement de manière autonome.

**Points d'attention critiques**:
1. Ne JAMAIS accéder directement à la DB Keycloak
2. Toujours auditer les actions sensibles
3. Respecter la structure Git multi-repository
4. Tester avec Testcontainers avant déploiement
5. Maintenir 80% de test coverage minimum

**En cas de blocage**, se référer aux sections:
- Section 3 pour les erreurs de compilation
- Section 7 pour les implémentations détaillées
- Section 9 pour les commandes utiles

Bon développement! 🚀

---

**Document généré le**: 2025-11-09
**Par**: Agent IA Claude (Anthropic)
**Pour**: Continuation projet lions-user-manager
**Version**: 1.0.0
