findByRealm(String realmName) {
- return list("realmName = ?1 order by timestamp desc", realmName);
- }
-
- /**
- * Supprime tous les logs d'audit plus anciens qu'une date donnée.
- * Utile pour la maintenance et le respect des politiques de rétention.
- *
- * @param beforeDate Date limite (les logs avant cette date seront supprimés)
- * @return Nombre de logs supprimés
- */
- public static long deleteOlderThan(LocalDateTime beforeDate) {
- return delete("timestamp < ?1", beforeDate);
- }
-
- /**
- * Compte le nombre d'actions effectuées par un auteur donné.
- *
- * @param auteurAction Identifiant de l'auteur
- * @return Nombre d'actions
- */
- public static long countByAuteur(String auteurAction) {
- return count("auteurAction = ?1", auteurAction);
- }
-
- /**
- * Compte le nombre d'échecs pour un utilisateur donné.
- * Utile pour détecter des problèmes récurrents.
- *
- * @param userId ID de l'utilisateur
- * @return Nombre d'échecs
- */
- public static long countFailuresByUserId(String userId) {
- return count("userId = ?1 and success = false", userId);
- }
-}
+package dev.lions.user.manager.server.impl.entity;
+
+import dev.lions.user.manager.enums.audit.TypeActionAudit;
+import io.quarkus.hibernate.orm.panache.PanacheEntity;
+import jakarta.persistence.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * Entité JPA pour la persistance des logs d'audit en base de données PostgreSQL.
+ *
+ * Cette entité représente un enregistrement d'audit qui track toutes les actions
+ * effectuées sur les utilisateurs du système (création, modification, suppression, etc.).
+ *
+ * Utilisation:
+ *
+ * AuditLogEntity auditLog = new AuditLogEntity();
+ * auditLog.setUserId("user-123");
+ * auditLog.setAction(TypeActionAudit.CREATION_UTILISATEUR);
+ * auditLog.setDetails("Utilisateur créé avec succès");
+ * auditLog.setAuteurAction("admin");
+ * auditLog.setTimestamp(LocalDateTime.now());
+ * auditLog.persist();
+ *
+ *
+ * @see dev.lions.user.manager.server.api.dto.AuditLogDTO
+ * @see TypeActionAudit
+ * @author Lions Development Team
+ * @version 1.0.0
+ * @since 2026-01-02
+ */
+@Entity
+@Table(
+ name = "audit_logs",
+ indexes = {
+ @Index(name = "idx_audit_user_id", columnList = "user_id"),
+ @Index(name = "idx_audit_action", columnList = "action"),
+ @Index(name = "idx_audit_timestamp", columnList = "timestamp"),
+ @Index(name = "idx_audit_auteur", columnList = "auteur_action")
+ }
+)
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class AuditLogEntity extends PanacheEntity {
+
+ /**
+ * ID de l'utilisateur concerné par l'action.
+ * Peut être null pour les actions système qui ne concernent pas un utilisateur spécifique.
+ */
+ @Column(name = "user_id", length = 255)
+ private String userId;
+
+ /**
+ * Type d'action effectuée (CREATION_UTILISATEUR, MODIFICATION_UTILISATEUR, etc.).
+ * Stocké en tant que STRING pour faciliter la lecture en base de données.
+ */
+ @Column(name = "action", nullable = false, length = 100)
+ @Enumerated(EnumType.STRING)
+ private TypeActionAudit action;
+
+ /**
+ * Détails complémentaires sur l'action effectuée.
+ * Peut contenir des informations contextuelles comme les champs modifiés,
+ * les raisons d'une action, ou des messages d'erreur.
+ */
+ @Column(name = "details", columnDefinition = "TEXT")
+ private String details;
+
+ /**
+ * Identifiant de l'utilisateur qui a effectué l'action.
+ * Généralement l'username ou l'ID de l'administrateur/utilisateur connecté.
+ */
+ @Column(name = "auteur_action", nullable = false, length = 255)
+ private String auteurAction;
+
+ /**
+ * Timestamp précis de l'action.
+ * Utilisé pour l'ordre chronologique des logs et le filtrage temporel.
+ */
+ @Column(name = "timestamp", nullable = false)
+ private LocalDateTime timestamp;
+
+ /**
+ * Adresse IP de l'auteur de l'action.
+ * Utile pour la traçabilité et la détection d'anomalies.
+ */
+ @Column(name = "ip_address", length = 45)
+ private String ipAddress;
+
+ /**
+ * User-Agent du client (navigateur, application, etc.).
+ * Permet d'identifier le type de client utilisé pour l'action.
+ */
+ @Column(name = "user_agent", length = 500)
+ private String userAgent;
+
+ /**
+ * Nom du realm Keycloak concerné.
+ * Important dans un environnement multi-tenant pour isoler les logs par realm.
+ */
+ @Column(name = "realm_name", length = 255)
+ private String realmName;
+
+ /**
+ * Indique si l'action a réussi ou échoué.
+ * Permet de filtrer facilement les actions en erreur pour analyse.
+ */
+ @Column(name = "success", nullable = false)
+ private Boolean success = true;
+
+ /**
+ * Message d'erreur en cas d'échec de l'action.
+ * Null si success = true.
+ */
+ @Column(name = "error_message", columnDefinition = "TEXT")
+ private String errorMessage;
+
+ /**
+ * Constructeur par défaut requis par JPA.
+ */
+ public AuditLogEntity() {
+ this.timestamp = LocalDateTime.now();
+ }
+
+ /**
+ * Recherche tous les logs d'audit pour un utilisateur donné.
+ *
+ * @param userId ID de l'utilisateur
+ * @return Liste des logs triés par timestamp décroissant
+ */
+ public static java.util.List findByUserId(String userId) {
+ return list("userId = ?1 order by timestamp desc", userId);
+ }
+
+ /**
+ * Recherche tous les logs d'audit d'un type d'action donné.
+ *
+ * @param action Type d'action
+ * @return Liste des logs triés par timestamp décroissant
+ */
+ public static java.util.List findByAction(TypeActionAudit action) {
+ return list("action = ?1 order by timestamp desc", action);
+ }
+
+ /**
+ * Recherche tous les logs d'audit pour un auteur donné.
+ *
+ * @param auteurAction Identifiant de l'auteur
+ * @return Liste des logs triés par timestamp décroissant
+ */
+ public static java.util.List findByAuteur(String auteurAction) {
+ return list("auteurAction = ?1 order by timestamp desc", auteurAction);
+ }
+
+ /**
+ * Recherche tous les logs d'audit dans une période donnée.
+ *
+ * @param startDate Date de début (inclusive)
+ * @param endDate Date de fin (inclusive)
+ * @return Liste des logs dans la période, triés par timestamp décroissant
+ */
+ public static java.util.List findByPeriod(LocalDateTime startDate, LocalDateTime endDate) {
+ return list("timestamp >= ?1 and timestamp <= ?2 order by timestamp desc", startDate, endDate);
+ }
+
+ /**
+ * Recherche tous les logs d'audit pour un realm donné.
+ *
+ * @param realmName Nom du realm
+ * @return Liste des logs triés par timestamp décroissant
+ */
+ public static java.util.List findByRealm(String realmName) {
+ return list("realmName = ?1 order by timestamp desc", realmName);
+ }
+
+ /**
+ * Supprime tous les logs d'audit plus anciens qu'une date donnée.
+ * Utile pour la maintenance et le respect des politiques de rétention.
+ *
+ * @param beforeDate Date limite (les logs avant cette date seront supprimés)
+ * @return Nombre de logs supprimés
+ */
+ public static long deleteOlderThan(LocalDateTime beforeDate) {
+ return delete("timestamp < ?1", beforeDate);
+ }
+
+ /**
+ * Compte le nombre d'actions effectuées par un auteur donné.
+ *
+ * @param auteurAction Identifiant de l'auteur
+ * @return Nombre d'actions
+ */
+ public static long countByAuteur(String auteurAction) {
+ return count("auteurAction = ?1", auteurAction);
+ }
+
+ /**
+ * Compte le nombre d'échecs pour un utilisateur donné.
+ * Utile pour détecter des problèmes récurrents.
+ *
+ * @param userId ID de l'utilisateur
+ * @return Nombre d'échecs
+ */
+ public static long countFailuresByUserId(String userId) {
+ return count("userId = ?1 and success = false", userId);
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/entity/SyncHistoryEntity.java b/src/main/java/dev/lions/user/manager/server/impl/entity/SyncHistoryEntity.java
index c11764f..50ab452 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/entity/SyncHistoryEntity.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/entity/SyncHistoryEntity.java
@@ -1,50 +1,50 @@
-package dev.lions.user.manager.server.impl.entity;
-
-import io.quarkus.hibernate.orm.panache.PanacheEntity;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Table;
-import jakarta.persistence.Index;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-import java.time.LocalDateTime;
-
-/**
- * Entité représentant l'historique des synchronisations avec Keycloak.
- */
-@Entity
-@Table(name = "sync_history", indexes = {
- @Index(name = "idx_sync_realm", columnList = "realm_name"),
- @Index(name = "idx_sync_date", columnList = "sync_date")
-})
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class SyncHistoryEntity extends PanacheEntity {
-
- @Column(name = "realm_name", nullable = false)
- private String realmName;
-
- @Column(name = "sync_date", nullable = false)
- private LocalDateTime syncDate;
-
- // USER ou ROLE
- @Column(name = "sync_type", nullable = false)
- private String syncType;
-
- @Column(name = "status", nullable = false) // SUCCESS, FAILURE
- private String status;
-
- @Column(name = "items_processed")
- private Integer itemsProcessed;
-
- @Column(name = "duration_ms")
- private Long durationMs;
-
- @Column(name = "error_message", columnDefinition = "TEXT")
- private String errorMessage;
-
- public SyncHistoryEntity() {
- this.syncDate = LocalDateTime.now();
- }
-}
+package dev.lions.user.manager.server.impl.entity;
+
+import io.quarkus.hibernate.orm.panache.PanacheEntity;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import jakarta.persistence.Index;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * Entité représentant l'historique des synchronisations avec Keycloak.
+ */
+@Entity
+@Table(name = "sync_history", indexes = {
+ @Index(name = "idx_sync_realm", columnList = "realm_name"),
+ @Index(name = "idx_sync_date", columnList = "sync_date")
+})
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SyncHistoryEntity extends PanacheEntity {
+
+ @Column(name = "realm_name", nullable = false)
+ private String realmName;
+
+ @Column(name = "sync_date", nullable = false)
+ private LocalDateTime syncDate;
+
+ // USER ou ROLE
+ @Column(name = "sync_type", nullable = false)
+ private String syncType;
+
+ @Column(name = "status", nullable = false) // SUCCESS, FAILURE
+ private String status;
+
+ @Column(name = "items_processed")
+ private Integer itemsProcessed;
+
+ @Column(name = "duration_ms")
+ private Long durationMs;
+
+ @Column(name = "error_message", columnDefinition = "TEXT")
+ private String errorMessage;
+
+ public SyncHistoryEntity() {
+ this.syncDate = LocalDateTime.now();
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedRoleEntity.java b/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedRoleEntity.java
index 311631d..6595df4 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedRoleEntity.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedRoleEntity.java
@@ -1,32 +1,32 @@
-package dev.lions.user.manager.server.impl.entity;
-
-import io.quarkus.hibernate.orm.panache.PanacheEntity;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-/**
- * Snapshot local d'un rôle Keycloak synchronisé.
- */
-@Entity
-@Table(name = "synced_role", indexes = {
- @Index(name = "idx_synced_role_realm", columnList = "realm_name"),
- @Index(name = "idx_synced_role_realm_name", columnList = "realm_name,role_name", unique = true)
-})
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class SyncedRoleEntity extends PanacheEntity {
-
- @Column(name = "realm_name", nullable = false)
- private String realmName;
-
- @Column(name = "role_name", nullable = false)
- private String roleName;
-
- @Column(name = "description")
- private String description;
-}
-
+package dev.lions.user.manager.server.impl.entity;
+
+import io.quarkus.hibernate.orm.panache.PanacheEntity;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Snapshot local d'un rôle Keycloak synchronisé.
+ */
+@Entity
+@Table(name = "synced_role", indexes = {
+ @Index(name = "idx_synced_role_realm", columnList = "realm_name"),
+ @Index(name = "idx_synced_role_realm_name", columnList = "realm_name,role_name", unique = true)
+})
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SyncedRoleEntity extends PanacheEntity {
+
+ @Column(name = "realm_name", nullable = false)
+ private String realmName;
+
+ @Column(name = "role_name", nullable = false)
+ private String roleName;
+
+ @Column(name = "description")
+ private String description;
+}
+
diff --git a/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedUserEntity.java b/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedUserEntity.java
index 843a914..4270726 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedUserEntity.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/entity/SyncedUserEntity.java
@@ -1,47 +1,47 @@
-package dev.lions.user.manager.server.impl.entity;
-
-import io.quarkus.hibernate.orm.panache.PanacheEntity;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-import java.time.LocalDateTime;
-
-/**
- * Snapshot local d'un utilisateur Keycloak synchronisé.
- * Permet de conserver un état minimal pour des rapports ou vérifications de cohérence.
- */
-@Entity
-@Table(name = "synced_user", indexes = {
- @Index(name = "idx_synced_user_realm", columnList = "realm_name"),
- @Index(name = "idx_synced_user_realm_kc_id", columnList = "realm_name,keycloak_id", unique = true)
-})
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class SyncedUserEntity extends PanacheEntity {
-
- @Column(name = "realm_name", nullable = false)
- private String realmName;
-
- @Column(name = "keycloak_id", nullable = false)
- private String keycloakId;
-
- @Column(name = "username", nullable = false)
- private String username;
-
- @Column(name = "email")
- private String email;
-
- @Column(name = "enabled")
- private Boolean enabled;
-
- @Column(name = "email_verified")
- private Boolean emailVerified;
-
- @Column(name = "created_at")
- private LocalDateTime createdAt;
-}
-
+package dev.lions.user.manager.server.impl.entity;
+
+import io.quarkus.hibernate.orm.panache.PanacheEntity;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * Snapshot local d'un utilisateur Keycloak synchronisé.
+ * Permet de conserver un état minimal pour des rapports ou vérifications de cohérence.
+ */
+@Entity
+@Table(name = "synced_user", indexes = {
+ @Index(name = "idx_synced_user_realm", columnList = "realm_name"),
+ @Index(name = "idx_synced_user_realm_kc_id", columnList = "realm_name,keycloak_id", unique = true)
+})
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SyncedUserEntity extends PanacheEntity {
+
+ @Column(name = "realm_name", nullable = false)
+ private String realmName;
+
+ @Column(name = "keycloak_id", nullable = false)
+ private String keycloakId;
+
+ @Column(name = "username", nullable = false)
+ private String username;
+
+ @Column(name = "email")
+ private String email;
+
+ @Column(name = "enabled")
+ private Boolean enabled;
+
+ @Column(name = "email_verified")
+ private Boolean emailVerified;
+
+ @Column(name = "created_at")
+ private LocalDateTime createdAt;
+}
+
diff --git a/src/main/java/dev/lions/user/manager/server/impl/interceptor/AuditInterceptor.java b/src/main/java/dev/lions/user/manager/server/impl/interceptor/AuditInterceptor.java
index e968735..dadc32c 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/interceptor/AuditInterceptor.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/interceptor/AuditInterceptor.java
@@ -1,93 +1,93 @@
-package dev.lions.user.manager.server.impl.interceptor;
-
-import dev.lions.user.manager.dto.audit.AuditLogDTO;
-import dev.lions.user.manager.enums.audit.TypeActionAudit;
-import dev.lions.user.manager.service.AuditService;
-import io.quarkus.security.identity.SecurityIdentity;
-import jakarta.annotation.Priority;
-import jakarta.inject.Inject;
-import jakarta.interceptor.AroundInvoke;
-import jakarta.interceptor.Interceptor;
-import jakarta.interceptor.InvocationContext;
-import lombok.extern.slf4j.Slf4j;
-
-import java.time.LocalDateTime;
-
-@Logged
-@Interceptor
-@Priority(Interceptor.Priority.APPLICATION)
-@Slf4j
-public class AuditInterceptor {
-
- @Inject
- AuditService auditService;
-
- @Inject
- SecurityIdentity securityIdentity;
-
- @AroundInvoke
- public Object auditMethod(InvocationContext context) throws Exception {
- Logged annotation = context.getMethod().getAnnotation(Logged.class);
- if (annotation == null) {
- annotation = context.getTarget().getClass().getAnnotation(Logged.class);
- }
-
- String actionStr = annotation != null ? annotation.action() : "UNKNOWN";
- String resourceType = annotation != null ? annotation.resource() : "UNKNOWN";
- String username = securityIdentity.isAnonymous() ? "anonymous" : securityIdentity.getPrincipal().getName();
-
- // Extraction du realm depuis l'issuer JWT (ex: http://keycloak/realms/lions-user-manager)
- String realmName = "unknown";
- if (!securityIdentity.isAnonymous()
- && securityIdentity.getPrincipal() instanceof org.eclipse.microprofile.jwt.JsonWebToken jwt) {
- String issuer = jwt.getIssuer();
- if (issuer != null && issuer.contains("/realms/")) {
- realmName = issuer.substring(issuer.lastIndexOf("/realms/") + 8);
- }
- }
-
- // Tentative d'extraction de l'ID de la ressource (1er argument String)
- String resourceId = "";
- if (context.getParameters().length > 0 && context.getParameters()[0] instanceof String) {
- resourceId = (String) context.getParameters()[0];
- }
-
- try {
- Object result = context.proceed();
-
- // Log Success
- try {
- TypeActionAudit action = TypeActionAudit.valueOf(actionStr);
- auditService.logSuccess(
- action,
- resourceType,
- resourceId,
- null,
- realmName,
- username,
- "Action réussie via AOP");
- } catch (IllegalArgumentException e) {
- log.warn("Type d'action audit inconnu: {}", actionStr);
- }
-
- return result;
- } catch (Exception e) {
- // Log Failure
- try {
- TypeActionAudit action = TypeActionAudit.valueOf(actionStr);
- auditService.logFailure(
- action,
- resourceType,
- resourceId,
- null,
- realmName,
- username,
- "ERROR",
- e.getMessage());
- } catch (IllegalArgumentException ex) {
- log.warn("Type d'action audit inconnu: {}", actionStr);
- }
- throw e;
- }
- }
-}
+package dev.lions.user.manager.server.impl.interceptor;
+
+import dev.lions.user.manager.dto.audit.AuditLogDTO;
+import dev.lions.user.manager.enums.audit.TypeActionAudit;
+import dev.lions.user.manager.service.AuditService;
+import io.quarkus.security.identity.SecurityIdentity;
+import jakarta.annotation.Priority;
+import jakarta.inject.Inject;
+import jakarta.interceptor.AroundInvoke;
+import jakarta.interceptor.Interceptor;
+import jakarta.interceptor.InvocationContext;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.LocalDateTime;
+
+@Logged
+@Interceptor
+@Priority(Interceptor.Priority.APPLICATION)
+@Slf4j
+public class AuditInterceptor {
+
+ @Inject
+ AuditService auditService;
+
+ @Inject
+ SecurityIdentity securityIdentity;
+
+ @AroundInvoke
+ public Object auditMethod(InvocationContext context) throws Exception {
+ Logged annotation = context.getMethod().getAnnotation(Logged.class);
+ if (annotation == null) {
+ annotation = context.getTarget().getClass().getAnnotation(Logged.class);
+ }
+
+ String actionStr = annotation != null ? annotation.action() : "UNKNOWN";
+ String resourceType = annotation != null ? annotation.resource() : "UNKNOWN";
+ String username = securityIdentity.isAnonymous() ? "anonymous" : securityIdentity.getPrincipal().getName();
+
+ // Extraction du realm depuis l'issuer JWT (ex: http://keycloak/realms/lions-user-manager)
+ String realmName = "unknown";
+ if (!securityIdentity.isAnonymous()
+ && securityIdentity.getPrincipal() instanceof org.eclipse.microprofile.jwt.JsonWebToken jwt) {
+ String issuer = jwt.getIssuer();
+ if (issuer != null && issuer.contains("/realms/")) {
+ realmName = issuer.substring(issuer.lastIndexOf("/realms/") + 8);
+ }
+ }
+
+ // Tentative d'extraction de l'ID de la ressource (1er argument String)
+ String resourceId = "";
+ if (context.getParameters().length > 0 && context.getParameters()[0] instanceof String) {
+ resourceId = (String) context.getParameters()[0];
+ }
+
+ try {
+ Object result = context.proceed();
+
+ // Log Success
+ try {
+ TypeActionAudit action = TypeActionAudit.valueOf(actionStr);
+ auditService.logSuccess(
+ action,
+ resourceType,
+ resourceId,
+ null,
+ realmName,
+ username,
+ "Action réussie via AOP");
+ } catch (IllegalArgumentException e) {
+ log.warn("Type d'action audit inconnu: {}", actionStr);
+ }
+
+ return result;
+ } catch (Exception e) {
+ // Log Failure
+ try {
+ TypeActionAudit action = TypeActionAudit.valueOf(actionStr);
+ auditService.logFailure(
+ action,
+ resourceType,
+ resourceId,
+ null,
+ realmName,
+ username,
+ "ERROR",
+ e.getMessage());
+ } catch (IllegalArgumentException ex) {
+ log.warn("Type d'action audit inconnu: {}", actionStr);
+ }
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/interceptor/Logged.java b/src/main/java/dev/lions/user/manager/server/impl/interceptor/Logged.java
index 728ed43..d4833f7 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/interceptor/Logged.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/interceptor/Logged.java
@@ -1,26 +1,26 @@
-package dev.lions.user.manager.server.impl.interceptor;
-
-import jakarta.interceptor.InterceptorBinding;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation pour auditer automatiquement l'exécution d'une méthode.
- */
-@InterceptorBinding
-@Target({ ElementType.METHOD, ElementType.TYPE })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Logged {
-
- /**
- * Type d'action d'audit (ex: UPDATE_USER).
- */
- String action() default "";
-
- /**
- * Type de ressource concernée (ex: USER).
- */
- String resource() default "";
-}
+package dev.lions.user.manager.server.impl.interceptor;
+
+import jakarta.interceptor.InterceptorBinding;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation pour auditer automatiquement l'exécution d'une méthode.
+ */
+@InterceptorBinding
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Logged {
+
+ /**
+ * Type d'action d'audit (ex: UPDATE_USER).
+ */
+ String action() default "";
+
+ /**
+ * Type de ressource concernée (ex: USER).
+ */
+ String resource() default "";
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java b/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java
index 2685c21..d504bfa 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java
@@ -1,179 +1,179 @@
-package dev.lions.user.manager.server.impl.mapper;
-
-import dev.lions.user.manager.dto.audit.AuditLogDTO;
-import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
-import org.mapstruct.*;
-
-import java.util.List;
-
-/**
- * Mapper MapStruct pour la conversion entre AuditLogEntity (JPA) et AuditLogDTO (API).
- *
- * Ce mapper gère la transformation bidirectionnelle entre l'entité de persistance
- * et le DTO exposé via l'API REST, avec mapping automatique des champs compatibles.
- *
- * Fonctionnalités:
- *
- * - Conversion Entity → DTO pour lecture/API
- * - Conversion DTO → Entity pour persistance
- * - Mapping de listes pour opérations bulk
- * - Gestion automatique des types LocalDateTime
- * - Mapping des enums (TypeActionAudit)
- *
- *
- * Utilisation:
- *
- * {@literal @}Inject
- * AuditLogMapper mapper;
- *
- * // Entity → DTO
- * AuditLogDTO dto = mapper.toDTO(entity);
- *
- * // DTO → Entity
- * AuditLogEntity entity = mapper.toEntity(dto);
- *
- * // Liste Entity → Liste DTO
- * List<AuditLogDTO> dtos = mapper.toDTOList(entities);
- *
- *
- * @see AuditLogEntity
- * @see AuditLogDTO
- * @author Lions Development Team
- * @version 1.0.0
- * @since 2026-01-02
- */
-@Mapper(
- componentModel = MappingConstants.ComponentModel.JAKARTA_CDI,
- injectionStrategy = InjectionStrategy.CONSTRUCTOR,
- unmappedTargetPolicy = ReportingPolicy.IGNORE
-)
-public interface AuditLogMapper {
-
- /**
- * Convertit une entité AuditLogEntity en DTO AuditLogDTO.
- *
- * Mapping des champs Entity → DTO:
- *
- * - id (Long) → id (String)
- * - userId → ressourceId
- * - action → typeAction
- * - details → description
- * - auteurAction → acteurUsername
- * - timestamp → dateAction
- * - ipAddress → ipAddress
- * - userAgent → userAgent
- * - realmName → realmName
- * - success → success
- * - errorMessage → errorMessage
- *
- *
- * @param entity L'entité JPA à convertir (peut être null)
- * @return Le DTO correspondant, ou null si l'entité est null
- */
- @Mapping(target = "id", source = "id", qualifiedByName = "longToString")
- @Mapping(target = "ressourceId", source = "userId")
- @Mapping(target = "typeAction", source = "action")
- @Mapping(target = "description", source = "details")
- @Mapping(target = "acteurUsername", source = "auteurAction")
- @Mapping(target = "dateAction", source = "timestamp")
- AuditLogDTO toDTO(AuditLogEntity entity);
-
- /**
- * Convertit un DTO AuditLogDTO en entité AuditLogEntity.
- *
- * Utilisé pour créer une nouvelle entité à persister depuis les données API.
- *
- * Note: L'ID de l'entité sera null (auto-généré par la DB),
- * même si l'ID du DTO est renseigné.
- *
- * @param dto Le DTO à convertir (peut être null)
- * @return L'entité JPA correspondante, ou null si le DTO est null
- */
- @Mapping(target = "id", ignore = true) // L'ID sera généré par la DB
- @Mapping(target = "userId", source = "ressourceId")
- @Mapping(target = "action", source = "typeAction")
- @Mapping(target = "details", source = "description")
- @Mapping(target = "auteurAction", source = "acteurUsername")
- @Mapping(target = "timestamp", source = "dateAction")
- AuditLogEntity toEntity(AuditLogDTO dto);
-
- /**
- * Convertit une liste d'entités en liste de DTOs.
- *
- * Utile pour les recherches qui retournent plusieurs résultats.
- *
- * @param entities Liste des entités à convertir (peut être null ou vide)
- * @return Liste des DTOs correspondants, ou liste vide si entities est null/vide
- */
- List toDTOList(List entities);
-
- /**
- * Convertit une liste de DTOs en liste d'entités.
- *
- * Utile pour les opérations d'import ou de création en masse.
- *
- * @param dtos Liste des DTOs à convertir (peut être null ou vide)
- * @return Liste des entités correspondantes, ou liste vide si dtos est null/vide
- */
- List toEntityList(List dtos);
-
- /**
- * Met à jour une entité existante avec les données d'un DTO.
- *
- * Préserve l'ID de l'entité et ne met à jour que les champs
- * présents dans le DTO.
- *
- * Utilisation:
- *
- * AuditLogEntity existingEntity = AuditLogEntity.findById(id);
- * mapper.updateEntityFromDTO(dto, existingEntity);
- * existingEntity.persist();
- *
- *
- * @param dto Le DTO source contenant les nouvelles valeurs
- * @param entity L'entité cible à mettre à jour
- */
- @Mapping(target = "id", ignore = true) // Préserve l'ID existant
- @Mapping(target = "userId", source = "ressourceId")
- @Mapping(target = "action", source = "typeAction")
- @Mapping(target = "details", source = "description")
- @Mapping(target = "auteurAction", source = "acteurUsername")
- @Mapping(target = "timestamp", source = "dateAction")
- @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
- void updateEntityFromDTO(AuditLogDTO dto, @MappingTarget AuditLogEntity entity);
-
- /**
- * Convertit un Long (ID de l'entité) en String (ID du DTO).
- *
- * MapStruct appelle automatiquement cette méthode pour le mapping de l'ID.
- *
- * @param id L'ID de type Long (peut être null)
- * @return L'ID converti en String, ou null si l'input est null
- */
- @Named("longToString")
- default String longToString(Long id) {
- return id != null ? id.toString() : null;
- }
-
- /**
- * Convertit un String (ID du DTO) en Long (ID de l'entité).
- *
- * Utilisé lors de la conversion DTO → Entity si nécessaire.
- *
- * @param id L'ID de type String (peut être null)
- * @return L'ID converti en Long, ou null si l'input est null ou invalide
- */
- @Named("stringToLong")
- default Long stringToLong(String id) {
- if (id == null || id.isBlank()) {
- return null;
- }
- try {
- return Long.parseLong(id);
- } catch (NumberFormatException e) {
- // Log warning et retourne null en cas de format invalide
- System.err.println("WARN: Invalid ID format for conversion to Long: " + id);
- return null;
- }
- }
-}
+package dev.lions.user.manager.server.impl.mapper;
+
+import dev.lions.user.manager.dto.audit.AuditLogDTO;
+import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
+import org.mapstruct.*;
+
+import java.util.List;
+
+/**
+ * Mapper MapStruct pour la conversion entre AuditLogEntity (JPA) et AuditLogDTO (API).
+ *
+ * Ce mapper gère la transformation bidirectionnelle entre l'entité de persistance
+ * et le DTO exposé via l'API REST, avec mapping automatique des champs compatibles.
+ *
+ * Fonctionnalités:
+ *
+ * - Conversion Entity → DTO pour lecture/API
+ * - Conversion DTO → Entity pour persistance
+ * - Mapping de listes pour opérations bulk
+ * - Gestion automatique des types LocalDateTime
+ * - Mapping des enums (TypeActionAudit)
+ *
+ *
+ * Utilisation:
+ *
+ * {@literal @}Inject
+ * AuditLogMapper mapper;
+ *
+ * // Entity → DTO
+ * AuditLogDTO dto = mapper.toDTO(entity);
+ *
+ * // DTO → Entity
+ * AuditLogEntity entity = mapper.toEntity(dto);
+ *
+ * // Liste Entity → Liste DTO
+ * List<AuditLogDTO> dtos = mapper.toDTOList(entities);
+ *
+ *
+ * @see AuditLogEntity
+ * @see AuditLogDTO
+ * @author Lions Development Team
+ * @version 1.0.0
+ * @since 2026-01-02
+ */
+@Mapper(
+ componentModel = MappingConstants.ComponentModel.JAKARTA_CDI,
+ injectionStrategy = InjectionStrategy.CONSTRUCTOR,
+ unmappedTargetPolicy = ReportingPolicy.IGNORE
+)
+public interface AuditLogMapper {
+
+ /**
+ * Convertit une entité AuditLogEntity en DTO AuditLogDTO.
+ *
+ * Mapping des champs Entity → DTO:
+ *
+ * - id (Long) → id (String)
+ * - userId → ressourceId
+ * - action → typeAction
+ * - details → description
+ * - auteurAction → acteurUsername
+ * - timestamp → dateAction
+ * - ipAddress → ipAddress
+ * - userAgent → userAgent
+ * - realmName → realmName
+ * - success → success
+ * - errorMessage → errorMessage
+ *
+ *
+ * @param entity L'entité JPA à convertir (peut être null)
+ * @return Le DTO correspondant, ou null si l'entité est null
+ */
+ @Mapping(target = "id", source = "id", qualifiedByName = "longToString")
+ @Mapping(target = "ressourceId", source = "userId")
+ @Mapping(target = "typeAction", source = "action")
+ @Mapping(target = "description", source = "details")
+ @Mapping(target = "acteurUsername", source = "auteurAction")
+ @Mapping(target = "dateAction", source = "timestamp")
+ AuditLogDTO toDTO(AuditLogEntity entity);
+
+ /**
+ * Convertit un DTO AuditLogDTO en entité AuditLogEntity.
+ *
+ * Utilisé pour créer une nouvelle entité à persister depuis les données API.
+ *
+ * Note: L'ID de l'entité sera null (auto-généré par la DB),
+ * même si l'ID du DTO est renseigné.
+ *
+ * @param dto Le DTO à convertir (peut être null)
+ * @return L'entité JPA correspondante, ou null si le DTO est null
+ */
+ @Mapping(target = "id", ignore = true) // L'ID sera généré par la DB
+ @Mapping(target = "userId", source = "ressourceId")
+ @Mapping(target = "action", source = "typeAction")
+ @Mapping(target = "details", source = "description")
+ @Mapping(target = "auteurAction", source = "acteurUsername")
+ @Mapping(target = "timestamp", source = "dateAction")
+ AuditLogEntity toEntity(AuditLogDTO dto);
+
+ /**
+ * Convertit une liste d'entités en liste de DTOs.
+ *
+ * Utile pour les recherches qui retournent plusieurs résultats.
+ *
+ * @param entities Liste des entités à convertir (peut être null ou vide)
+ * @return Liste des DTOs correspondants, ou liste vide si entities est null/vide
+ */
+ List toDTOList(List entities);
+
+ /**
+ * Convertit une liste de DTOs en liste d'entités.
+ *
+ * Utile pour les opérations d'import ou de création en masse.
+ *
+ * @param dtos Liste des DTOs à convertir (peut être null ou vide)
+ * @return Liste des entités correspondantes, ou liste vide si dtos est null/vide
+ */
+ List toEntityList(List dtos);
+
+ /**
+ * Met à jour une entité existante avec les données d'un DTO.
+ *
+ * Préserve l'ID de l'entité et ne met à jour que les champs
+ * présents dans le DTO.
+ *
+ * Utilisation:
+ *
+ * AuditLogEntity existingEntity = AuditLogEntity.findById(id);
+ * mapper.updateEntityFromDTO(dto, existingEntity);
+ * existingEntity.persist();
+ *
+ *
+ * @param dto Le DTO source contenant les nouvelles valeurs
+ * @param entity L'entité cible à mettre à jour
+ */
+ @Mapping(target = "id", ignore = true) // Préserve l'ID existant
+ @Mapping(target = "userId", source = "ressourceId")
+ @Mapping(target = "action", source = "typeAction")
+ @Mapping(target = "details", source = "description")
+ @Mapping(target = "auteurAction", source = "acteurUsername")
+ @Mapping(target = "timestamp", source = "dateAction")
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void updateEntityFromDTO(AuditLogDTO dto, @MappingTarget AuditLogEntity entity);
+
+ /**
+ * Convertit un Long (ID de l'entité) en String (ID du DTO).
+ *
+ * MapStruct appelle automatiquement cette méthode pour le mapping de l'ID.
+ *
+ * @param id L'ID de type Long (peut être null)
+ * @return L'ID converti en String, ou null si l'input est null
+ */
+ @Named("longToString")
+ default String longToString(Long id) {
+ return id != null ? id.toString() : null;
+ }
+
+ /**
+ * Convertit un String (ID du DTO) en Long (ID de l'entité).
+ *
+ * Utilisé lors de la conversion DTO → Entity si nécessaire.
+ *
+ * @param id L'ID de type String (peut être null)
+ * @return L'ID converti en Long, ou null si l'input est null ou invalide
+ */
+ @Named("stringToLong")
+ default Long stringToLong(String id) {
+ if (id == null || id.isBlank()) {
+ return null;
+ }
+ try {
+ return Long.parseLong(id);
+ } catch (NumberFormatException e) {
+ // Log warning et retourne null en cas de format invalide
+ System.err.println("WARN: Invalid ID format for conversion to Long: " + id);
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/mapper/SyncHistoryMapper.java b/src/main/java/dev/lions/user/manager/server/impl/mapper/SyncHistoryMapper.java
index 48beff6..495eeb6 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/mapper/SyncHistoryMapper.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/mapper/SyncHistoryMapper.java
@@ -1,21 +1,21 @@
-package dev.lions.user.manager.server.impl.mapper;
-
-import dev.lions.user.manager.dto.sync.SyncHistoryDTO;
-import dev.lions.user.manager.server.impl.entity.SyncHistoryEntity;
-import org.mapstruct.*;
-
-import java.util.List;
-
-@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA_CDI, injectionStrategy = InjectionStrategy.CONSTRUCTOR, unmappedTargetPolicy = ReportingPolicy.IGNORE)
-public interface SyncHistoryMapper {
-
- @Mapping(target = "id", source = "id", qualifiedByName = "longToString")
- SyncHistoryDTO toDTO(SyncHistoryEntity entity);
-
- List toDTOList(List entities);
-
- @Named("longToString")
- default String longToString(Long id) {
- return id != null ? id.toString() : null;
- }
-}
+package dev.lions.user.manager.server.impl.mapper;
+
+import dev.lions.user.manager.dto.sync.SyncHistoryDTO;
+import dev.lions.user.manager.server.impl.entity.SyncHistoryEntity;
+import org.mapstruct.*;
+
+import java.util.List;
+
+@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA_CDI, injectionStrategy = InjectionStrategy.CONSTRUCTOR, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface SyncHistoryMapper {
+
+ @Mapping(target = "id", source = "id", qualifiedByName = "longToString")
+ SyncHistoryDTO toDTO(SyncHistoryEntity entity);
+
+ List toDTOList(List entities);
+
+ @Named("longToString")
+ default String longToString(Long id) {
+ return id != null ? id.toString() : null;
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/repository/AuditLogRepository.java b/src/main/java/dev/lions/user/manager/server/impl/repository/AuditLogRepository.java
index 95d56ea..7a715a2 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/repository/AuditLogRepository.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/repository/AuditLogRepository.java
@@ -1,62 +1,62 @@
-package dev.lions.user.manager.server.impl.repository;
-
-import dev.lions.user.manager.enums.audit.TypeActionAudit;
-import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
-import io.quarkus.hibernate.orm.panache.PanacheRepository;
-import jakarta.enterprise.context.ApplicationScoped;
-
-import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@ApplicationScoped
-public class AuditLogRepository implements PanacheRepository {
-
- public List search(String realmName,
- String auteurAction,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- String typeAction,
- Boolean success,
- int page,
- int pageSize) {
-
- StringBuilder query = new StringBuilder("1=1");
- Map params = new HashMap<>();
-
- // Construction dynamique de la requête
- if (realmName != null && !realmName.isEmpty()) {
- query.append(" AND realmName = :realmName");
- params.put("realmName", realmName);
- }
- if (auteurAction != null && !auteurAction.isEmpty()) {
- query.append(" AND auteurAction = :auteurAction");
- params.put("auteurAction", auteurAction);
- }
- if (dateDebut != null) {
- query.append(" AND timestamp >= :dateDebut");
- params.put("dateDebut", dateDebut);
- }
- if (dateFin != null) {
- query.append(" AND timestamp <= :dateFin");
- params.put("dateFin", dateFin);
- }
- if (typeAction != null && !typeAction.isEmpty()) {
- try {
- TypeActionAudit actionEnum = TypeActionAudit.valueOf(typeAction);
- query.append(" AND action = :actionEnum");
- params.put("actionEnum", actionEnum);
- } catch (IllegalArgumentException e) {
- // Ignore invalid enum value filter
- }
- }
- if (success != null) {
- query.append(" AND success = :success");
- params.put("success", success);
- }
-
- query.append(" ORDER BY timestamp DESC");
- return find(query.toString(), params).page(page, pageSize).list();
- }
-}
+package dev.lions.user.manager.server.impl.repository;
+
+import dev.lions.user.manager.enums.audit.TypeActionAudit;
+import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@ApplicationScoped
+public class AuditLogRepository implements PanacheRepository {
+
+ public List search(String realmName,
+ String auteurAction,
+ LocalDateTime dateDebut,
+ LocalDateTime dateFin,
+ String typeAction,
+ Boolean success,
+ int page,
+ int pageSize) {
+
+ StringBuilder query = new StringBuilder("1=1");
+ Map params = new HashMap<>();
+
+ // Construction dynamique de la requête
+ if (realmName != null && !realmName.isEmpty()) {
+ query.append(" AND realmName = :realmName");
+ params.put("realmName", realmName);
+ }
+ if (auteurAction != null && !auteurAction.isEmpty()) {
+ query.append(" AND auteurAction = :auteurAction");
+ params.put("auteurAction", auteurAction);
+ }
+ if (dateDebut != null) {
+ query.append(" AND timestamp >= :dateDebut");
+ params.put("dateDebut", dateDebut);
+ }
+ if (dateFin != null) {
+ query.append(" AND timestamp <= :dateFin");
+ params.put("dateFin", dateFin);
+ }
+ if (typeAction != null && !typeAction.isEmpty()) {
+ try {
+ TypeActionAudit actionEnum = TypeActionAudit.valueOf(typeAction);
+ query.append(" AND action = :actionEnum");
+ params.put("actionEnum", actionEnum);
+ } catch (IllegalArgumentException e) {
+ // Ignore invalid enum value filter
+ }
+ }
+ if (success != null) {
+ query.append(" AND success = :success");
+ params.put("success", success);
+ }
+
+ query.append(" ORDER BY timestamp DESC");
+ return find(query.toString(), params).page(page, pageSize).list();
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/repository/SyncHistoryRepository.java b/src/main/java/dev/lions/user/manager/server/impl/repository/SyncHistoryRepository.java
index 7aa00bc..dbe8348 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/repository/SyncHistoryRepository.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/repository/SyncHistoryRepository.java
@@ -1,17 +1,17 @@
-package dev.lions.user.manager.server.impl.repository;
-
-import dev.lions.user.manager.server.impl.entity.SyncHistoryEntity;
-import io.quarkus.hibernate.orm.panache.PanacheRepository;
-import jakarta.enterprise.context.ApplicationScoped;
-
-import java.util.List;
-
-@ApplicationScoped
-public class SyncHistoryRepository implements PanacheRepository {
-
- public List findLatestByRealm(String realmName, int limit) {
- return find("realmName = ?1 ORDER BY syncDate DESC", realmName)
- .page(0, limit)
- .list();
- }
-}
+package dev.lions.user.manager.server.impl.repository;
+
+import dev.lions.user.manager.server.impl.entity.SyncHistoryEntity;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+
+import java.util.List;
+
+@ApplicationScoped
+public class SyncHistoryRepository implements PanacheRepository {
+
+ public List findLatestByRealm(String realmName, int limit) {
+ return find("realmName = ?1 ORDER BY syncDate DESC", realmName)
+ .page(0, limit)
+ .list();
+ }
+}
diff --git a/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedRoleRepository.java b/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedRoleRepository.java
index 228ab4e..25599d4 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedRoleRepository.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedRoleRepository.java
@@ -1,20 +1,20 @@
-package dev.lions.user.manager.server.impl.repository;
-
-import dev.lions.user.manager.server.impl.entity.SyncedRoleEntity;
-import io.quarkus.hibernate.orm.panache.PanacheRepository;
-import jakarta.enterprise.context.ApplicationScoped;
-
-import java.util.List;
-
-@ApplicationScoped
-public class SyncedRoleRepository implements PanacheRepository {
-
- /**
- * Remplace l'ensemble des snapshots de rôles pour un realm donné.
- */
- public void replaceForRealm(String realmName, List roles) {
- delete("realmName", realmName);
- persist(roles);
- }
-}
-
+package dev.lions.user.manager.server.impl.repository;
+
+import dev.lions.user.manager.server.impl.entity.SyncedRoleEntity;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+
+import java.util.List;
+
+@ApplicationScoped
+public class SyncedRoleRepository implements PanacheRepository {
+
+ /**
+ * Remplace l'ensemble des snapshots de rôles pour un realm donné.
+ */
+ public void replaceForRealm(String realmName, List roles) {
+ delete("realmName", realmName);
+ persist(roles);
+ }
+}
+
diff --git a/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedUserRepository.java b/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedUserRepository.java
index 806bb2f..a74f24e 100644
--- a/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedUserRepository.java
+++ b/src/main/java/dev/lions/user/manager/server/impl/repository/SyncedUserRepository.java
@@ -1,20 +1,20 @@
-package dev.lions.user.manager.server.impl.repository;
-
-import dev.lions.user.manager.server.impl.entity.SyncedUserEntity;
-import io.quarkus.hibernate.orm.panache.PanacheRepository;
-import jakarta.enterprise.context.ApplicationScoped;
-
-import java.util.List;
-
-@ApplicationScoped
-public class SyncedUserRepository implements PanacheRepository {
-
- /**
- * Remplace l'ensemble des snapshots d'utilisateurs pour un realm donné.
- */
- public void replaceForRealm(String realmName, List users) {
- delete("realmName", realmName);
- persist(users);
- }
-}
-
+package dev.lions.user.manager.server.impl.repository;
+
+import dev.lions.user.manager.server.impl.entity.SyncedUserEntity;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+
+import java.util.List;
+
+@ApplicationScoped
+public class SyncedUserRepository implements PanacheRepository {
+
+ /**
+ * Remplace l'ensemble des snapshots d'utilisateurs pour un realm donné.
+ */
+ public void replaceForRealm(String realmName, List users) {
+ delete("realmName", realmName);
+ persist(users);
+ }
+}
+
diff --git a/src/main/java/dev/lions/user/manager/service/exception/KeycloakServiceException.java b/src/main/java/dev/lions/user/manager/service/exception/KeycloakServiceException.java
index b95786f..c161767 100644
--- a/src/main/java/dev/lions/user/manager/service/exception/KeycloakServiceException.java
+++ b/src/main/java/dev/lions/user/manager/service/exception/KeycloakServiceException.java
@@ -1,72 +1,72 @@
-package dev.lions.user.manager.service.exception;
-
-/**
- * Exception levée lorsqu'une erreur survient lors de l'appel au service Keycloak.
- *
- * @author Lions User Manager Team
- * @version 1.0
- */
-public class KeycloakServiceException extends RuntimeException {
-
- private final int httpStatus;
- private final String serviceName;
-
- public KeycloakServiceException(String message) {
- super(message);
- this.httpStatus = 0;
- this.serviceName = "Keycloak";
- }
-
- public KeycloakServiceException(String message, Throwable cause) {
- super(message, cause);
- this.httpStatus = 0;
- this.serviceName = "Keycloak";
- }
-
- public KeycloakServiceException(String message, int httpStatus) {
- super(message);
- this.httpStatus = httpStatus;
- this.serviceName = "Keycloak";
- }
-
- public KeycloakServiceException(String message, int httpStatus, Throwable cause) {
- super(message, cause);
- this.httpStatus = httpStatus;
- this.serviceName = "Keycloak";
- }
-
- public int getHttpStatus() {
- return httpStatus;
- }
-
- public String getServiceName() {
- return serviceName;
- }
-
- /**
- * Exception spécifique pour les erreurs de connexion (service indisponible)
- */
- public static class ServiceUnavailableException extends KeycloakServiceException {
- public ServiceUnavailableException(String message) {
- super("Service Keycloak indisponible: " + message);
- }
-
- public ServiceUnavailableException(String message, Throwable cause) {
- super("Service Keycloak indisponible: " + message, cause);
- }
- }
-
- /**
- * Exception spécifique pour les erreurs de timeout
- */
- public static class TimeoutException extends KeycloakServiceException {
- public TimeoutException(String message) {
- super("Timeout lors de l'appel au service Keycloak: " + message);
- }
-
- public TimeoutException(String message, Throwable cause) {
- super("Timeout lors de l'appel au service Keycloak: " + message, cause);
- }
- }
-}
-
+package dev.lions.user.manager.service.exception;
+
+/**
+ * Exception levée lorsqu'une erreur survient lors de l'appel au service Keycloak.
+ *
+ * @author Lions User Manager Team
+ * @version 1.0
+ */
+public class KeycloakServiceException extends RuntimeException {
+
+ private final int httpStatus;
+ private final String serviceName;
+
+ public KeycloakServiceException(String message) {
+ super(message);
+ this.httpStatus = 0;
+ this.serviceName = "Keycloak";
+ }
+
+ public KeycloakServiceException(String message, Throwable cause) {
+ super(message, cause);
+ this.httpStatus = 0;
+ this.serviceName = "Keycloak";
+ }
+
+ public KeycloakServiceException(String message, int httpStatus) {
+ super(message);
+ this.httpStatus = httpStatus;
+ this.serviceName = "Keycloak";
+ }
+
+ public KeycloakServiceException(String message, int httpStatus, Throwable cause) {
+ super(message, cause);
+ this.httpStatus = httpStatus;
+ this.serviceName = "Keycloak";
+ }
+
+ public int getHttpStatus() {
+ return httpStatus;
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ /**
+ * Exception spécifique pour les erreurs de connexion (service indisponible)
+ */
+ public static class ServiceUnavailableException extends KeycloakServiceException {
+ public ServiceUnavailableException(String message) {
+ super("Service Keycloak indisponible: " + message);
+ }
+
+ public ServiceUnavailableException(String message, Throwable cause) {
+ super("Service Keycloak indisponible: " + message, cause);
+ }
+ }
+
+ /**
+ * Exception spécifique pour les erreurs de timeout
+ */
+ public static class TimeoutException extends KeycloakServiceException {
+ public TimeoutException(String message) {
+ super("Timeout lors de l'appel au service Keycloak: " + message);
+ }
+
+ public TimeoutException(String message, Throwable cause) {
+ super("Timeout lors de l'appel au service Keycloak: " + message, cause);
+ }
+ }
+}
+
diff --git a/src/main/java/dev/lions/user/manager/service/impl/AuditServiceImpl.java b/src/main/java/dev/lions/user/manager/service/impl/AuditServiceImpl.java
index a856519..e92b3d4 100644
--- a/src/main/java/dev/lions/user/manager/service/impl/AuditServiceImpl.java
+++ b/src/main/java/dev/lions/user/manager/service/impl/AuditServiceImpl.java
@@ -1,362 +1,362 @@
-package dev.lions.user.manager.service.impl;
-
-import dev.lions.user.manager.dto.audit.AuditLogDTO;
-import dev.lions.user.manager.enums.audit.TypeActionAudit;
-// import dev.lions.user.manager.mapper.AuditLogMapper; // DELETE - Wrong package
-import dev.lions.user.manager.server.impl.mapper.AuditLogMapper; // ADD - Correct package
-import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
-import dev.lions.user.manager.server.impl.repository.AuditLogRepository;
-import dev.lions.user.manager.service.AuditService;
-import io.quarkus.hibernate.orm.panache.PanacheQuery;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
-import jakarta.persistence.EntityManager;
-import jakarta.transaction.Transactional;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-import java.time.LocalDateTime;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-@ApplicationScoped
-@Slf4j
-public class AuditServiceImpl implements AuditService {
-
- @Inject
- AuditLogRepository auditLogRepository;
-
- @Inject
- AuditLogMapper auditLogMapper;
-
- @Inject
- EntityManager entityManager;
-
- @ConfigProperty(name = "lions.audit.enabled", defaultValue = "true")
- boolean auditEnabled;
-
- @ConfigProperty(name = "lions.audit.log-to-database", defaultValue = "true")
- boolean logToDatabase;
-
- @Override
- @Transactional(Transactional.TxType.REQUIRES_NEW)
- public AuditLogDTO logAction(@Valid @NotNull AuditLogDTO auditLog) {
- if (!auditEnabled) {
- log.debug("Audit désactivé, action ignorée: {}", auditLog.getTypeAction());
- return auditLog;
- }
-
- log.info("AUDIT: [{}] {} - user:{} - ressource:{}/{} - status:{}",
- auditLog.getRealmName(),
- auditLog.getTypeAction(),
- auditLog.getActeurUsername(), // ou getActeurUserId()
- auditLog.getRessourceType(),
- auditLog.getRessourceId(),
- auditLog.getSuccess() != null && auditLog.getSuccess() ? "SUCCESS" : "FAILURE");
-
- if (logToDatabase) {
- try {
- // Ensure dateAction is set
- if (auditLog.getDateAction() == null) {
- auditLog.setDateAction(LocalDateTime.now());
- }
-
- AuditLogEntity entity = auditLogMapper.toEntity(auditLog);
- auditLogRepository.persist(entity);
-
- // Mettre à jour l'ID du DTO avec l'ID généré par la base
- if (entity.id != null) {
- auditLog.setId(entity.id.toString());
- }
- } catch (Exception e) {
- log.error("Erreur lors de la persistance du log d'audit", e);
- // On ne bloque pas l'action métier si l'audit échoue (sauf exigence contraire)
- }
- }
-
- return auditLog;
- }
-
- @Override
- @Transactional(Transactional.TxType.REQUIRES_NEW)
- public void logSuccess(@NotNull TypeActionAudit typeAction,
- @NotBlank String ressourceType,
- String ressourceId,
- String ressourceName,
- @NotBlank String realmName,
- @NotBlank String acteurUserId,
- String description) {
-
- AuditLogDTO log = AuditLogDTO.builder()
- .typeAction(typeAction)
- .ressourceType(ressourceType)
- .ressourceId(ressourceId)
- .ressourceName(ressourceName)
- .realmName(realmName)
- .acteurUserId(acteurUserId)
- .acteurUsername(acteurUserId) // On map aussi le username pour la persistence Entity
- .description(description)
- .dateAction(LocalDateTime.now())
- .success(true)
- .build();
-
- logAction(log);
- }
-
- @Override
- @Transactional(Transactional.TxType.REQUIRES_NEW)
- public void logFailure(@NotNull TypeActionAudit typeAction,
- @NotBlank String ressourceType,
- String ressourceId,
- String ressourceName,
- @NotBlank String realmName,
- @NotBlank String acteurUserId,
- String errorCode,
- String errorMessage) {
-
- AuditLogDTO log = AuditLogDTO.builder()
- .typeAction(typeAction)
- .ressourceType(ressourceType)
- .ressourceId(ressourceId)
- .ressourceName(ressourceName)
- .realmName(realmName)
- .acteurUserId(acteurUserId)
- .acteurUsername(acteurUserId)
- .description("Echec: " + errorCode)
- .errorMessage(errorMessage)
- .dateAction(LocalDateTime.now())
- .success(false)
- .build();
-
- logAction(log);
- }
-
- @Override
- public List findByActeur(@NotBlank String acteurUserId,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- int page,
- int pageSize) {
- // Le repository cherche par auteurAction, qui est mappé sur acteurUsername dans
- // le DTO
- List entities = auditLogRepository.search(null, acteurUserId, dateDebut, dateFin, null, null,
- page,
- pageSize);
- return auditLogMapper.toDTOList(entities);
- }
-
- @Override
- public List findByRessource(@NotBlank String ressourceType,
- @NotBlank String ressourceId,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- int page,
- int pageSize) {
-
- // Utilisation de Panache query directe car le repo search générique est limité
- // On cherche dans 'details' (description) ou 'userId' (ressourceId)
- String filter = "%" + ressourceId + "%";
- // Correction: userId est le nom du champ dans l'entité qui mappe ressourceId
- PanacheQuery q = auditLogRepository.find("userId = ?1 or details like ?2", ressourceId, filter);
-
- return auditLogMapper.toDTOList(q.page(page, pageSize).list());
- }
-
- @Override
- public List findByTypeAction(@NotNull TypeActionAudit typeAction,
- @NotBlank String realmName,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- int page,
- int pageSize) {
- List entities = auditLogRepository.search(realmName, null, dateDebut, dateFin,
- typeAction.name(), null, page,
- pageSize);
- return auditLogMapper.toDTOList(entities);
- }
-
- @Override
- public List findByRealm(@NotBlank String realmName,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- int page,
- int pageSize) {
- List entities = auditLogRepository.search(realmName, null, dateDebut, dateFin, null, null, page,
- pageSize);
- return auditLogMapper.toDTOList(entities);
- }
-
- @Override
- public List findFailures(@NotBlank String realmName,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- int page,
- int pageSize) {
- List entities = auditLogRepository.search(realmName, null, dateDebut, dateFin, null, false,
- page,
- pageSize);
- return auditLogMapper.toDTOList(entities);
- }
-
- @Override
- public List findCriticalActions(@NotBlank String realmName,
- LocalDateTime dateDebut,
- LocalDateTime dateFin,
- int page,
- int pageSize) {
- List entities = auditLogRepository.search(realmName, null, dateDebut, dateFin, null, false,
- page, pageSize);
- return auditLogMapper.toDTOList(entities);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Map countByActionType(@NotBlank String realmName,
- LocalDateTime dateDebut,
- LocalDateTime dateFin) {
- StringBuilder sql = new StringBuilder("SELECT action, COUNT(*) AS cnt FROM audit_logs WHERE realm_name = :realmName");
- if (dateDebut != null) sql.append(" AND timestamp >= :dateDebut");
- if (dateFin != null) sql.append(" AND timestamp <= :dateFin");
- sql.append(" GROUP BY action");
- var query = entityManager.createNativeQuery(sql.toString())
- .setParameter("realmName", realmName);
- if (dateDebut != null) query.setParameter("dateDebut", dateDebut);
- if (dateFin != null) query.setParameter("dateFin", dateFin);
- List