diff --git a/src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java b/src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java new file mode 100644 index 0000000..b984608 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java @@ -0,0 +1,209 @@ +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.ListUtile 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/mapper/AuditLogMapper.java b/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java new file mode 100644 index 0000000..2685c21 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java @@ -0,0 +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:
+ *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:
+ *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 + */ + ListUtile 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 + */ + ListPré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; + } + } +}