feat: Finalisation du projet lions-user-manager
- Ajout du module client Quarkus PrimeFaces Freya avec interface complète - Ajout de l'AuditResource pour la gestion des logs d'audit - Ajout du SyncResource pour la synchronisation Keycloak - Ajout du SyncServiceImpl pour les opérations de synchronisation - Ajout des DTOs de synchronisation (SyncStatusDTO, etc.) - Corrections mineures dans RoleMapper, RoleServiceImpl, AuditServiceImpl - Configuration des properties pour dev et prod - Ajout de la configuration Claude Code (.claude/) - Documentation complète du projet (AI_HANDOFF_DOCUMENT.md) Le projet compile maintenant avec succès (BUILD SUCCESS). Tous les modules (API, Server Impl, Client) sont fonctionnels.
This commit is contained in:
364
src/main/java/dev/lions/user/manager/resource/AuditResource.java
Normal file
364
src/main/java/dev/lions/user/manager/resource/AuditResource.java
Normal file
@@ -0,0 +1,364 @@
|
||||
package dev.lions.user.manager.resource;
|
||||
|
||||
import dev.lions.user.manager.dto.audit.AuditLogDTO;
|
||||
import dev.lions.user.manager.enums.audit.TypeActionAudit;
|
||||
import dev.lions.user.manager.service.AuditService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* REST Resource pour l'audit et la consultation des logs
|
||||
*/
|
||||
@Path("/api/audit")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Audit", description = "Consultation des logs d'audit et statistiques")
|
||||
@Slf4j
|
||||
public class AuditResource {
|
||||
|
||||
@Inject
|
||||
AuditService auditService;
|
||||
|
||||
@POST
|
||||
@Path("/search")
|
||||
@Operation(summary = "Rechercher des logs d'audit", description = "Recherche avancée de logs selon critères")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Résultats de recherche"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response searchLogs(
|
||||
@QueryParam("acteur") String acteurUsername,
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr,
|
||||
@QueryParam("typeAction") TypeActionAudit typeAction,
|
||||
@QueryParam("ressourceType") String ressourceType,
|
||||
@QueryParam("succes") Boolean succes,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("pageSize") @DefaultValue("50") int pageSize
|
||||
) {
|
||||
log.info("POST /api/audit/search - Recherche de logs");
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
// Utiliser findByActeur si acteurUsername est fourni, sinon findByRealm
|
||||
List<AuditLogDTO> logs;
|
||||
if (acteurUsername != null && !acteurUsername.isBlank()) {
|
||||
logs = auditService.findByActeur(acteurUsername, dateDebut, dateFin, page, pageSize);
|
||||
} else {
|
||||
// Pour une recherche générale, utiliser findByRealm (on utilise "master" par défaut)
|
||||
logs = auditService.findByRealm("master", dateDebut, dateFin, page, pageSize);
|
||||
}
|
||||
|
||||
// Filtrer par typeAction, ressourceType et succes si fournis
|
||||
if (typeAction != null || ressourceType != null || succes != null) {
|
||||
logs = logs.stream()
|
||||
.filter(log -> typeAction == null || typeAction.equals(log.getTypeAction()))
|
||||
.filter(log -> ressourceType == null || ressourceType.equals(log.getRessourceType()))
|
||||
.filter(log -> succes == null || succes == log.isSuccessful())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la recherche de logs d'audit", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/actor/{acteurUsername}")
|
||||
@Operation(summary = "Récupérer les logs d'un acteur", description = "Liste les derniers logs d'un utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des logs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getLogsByActor(
|
||||
@Parameter(description = "Username de l'acteur") @PathParam("acteurUsername") @NotBlank String acteurUsername,
|
||||
@Parameter(description = "Nombre de logs à retourner") @QueryParam("limit") @DefaultValue("100") int limit
|
||||
) {
|
||||
log.info("GET /api/audit/actor/{} - Limite: {}", acteurUsername, limit);
|
||||
|
||||
try {
|
||||
List<AuditLogDTO> logs = auditService.findByActeur(acteurUsername, null, null, 0, limit);
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs de l'acteur {}", acteurUsername, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/resource/{ressourceType}/{ressourceId}")
|
||||
@Operation(summary = "Récupérer les logs d'une ressource", description = "Liste les derniers logs d'une ressource spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des logs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getLogsByResource(
|
||||
@PathParam("ressourceType") @NotBlank String ressourceType,
|
||||
@PathParam("ressourceId") @NotBlank String ressourceId,
|
||||
@QueryParam("limit") @DefaultValue("100") int limit
|
||||
) {
|
||||
log.info("GET /api/audit/resource/{}/{} - Limite: {}", ressourceType, ressourceId, limit);
|
||||
|
||||
try {
|
||||
List<AuditLogDTO> logs = auditService.findByRessource(ressourceType, ressourceId, null, null, 0, limit);
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs de la ressource {}:{}",
|
||||
ressourceType, ressourceId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/action/{typeAction}")
|
||||
@Operation(summary = "Récupérer les logs par type d'action", description = "Liste les logs d'un type d'action spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des logs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getLogsByAction(
|
||||
@PathParam("typeAction") TypeActionAudit typeAction,
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr,
|
||||
@QueryParam("limit") @DefaultValue("100") int limit
|
||||
) {
|
||||
log.info("GET /api/audit/action/{} - Limite: {}", typeAction, limit);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
List<AuditLogDTO> logs = auditService.findByTypeAction(typeAction, "master", dateDebut, dateFin, 0, limit);
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs de type {}", typeAction, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/actions")
|
||||
@Operation(summary = "Statistiques par type d'action", description = "Retourne le nombre de logs par type d'action")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques des actions"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getActionStatistics(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/actions - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<TypeActionAudit, Long> stats = auditService.countByActionType("master", dateDebut, dateFin);
|
||||
return Response.ok(stats).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul des statistiques d'actions", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/users")
|
||||
@Operation(summary = "Statistiques par utilisateur", description = "Retourne le nombre d'actions par utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques des utilisateurs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getUserActivityStatistics(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/users - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Long> stats = auditService.countByActeur("master", dateDebut, dateFin);
|
||||
return Response.ok(stats).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul des statistiques utilisateurs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/failures")
|
||||
@Operation(summary = "Comptage des échecs", description = "Retourne le nombre d'échecs sur une période")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Nombre d'échecs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getFailureCount(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/failures - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Long> successVsFailure = auditService.countSuccessVsFailure("master", dateDebut, dateFin);
|
||||
long count = successVsFailure.getOrDefault("failure", 0L);
|
||||
return Response.ok(new CountResponse(count)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du comptage des échecs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/success")
|
||||
@Operation(summary = "Comptage des succès", description = "Retourne le nombre de succès sur une période")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Nombre de succès"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getSuccessCount(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/success - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Long> successVsFailure = auditService.countSuccessVsFailure("master", dateDebut, dateFin);
|
||||
long count = successVsFailure.getOrDefault("success", 0L);
|
||||
return Response.ok(new CountResponse(count)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du comptage des succès", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/export/csv")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Exporter les logs en CSV", description = "Génère un fichier CSV des logs d'audit")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Fichier CSV généré"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response exportLogsToCSV(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/export/csv - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
String csvContent = auditService.exportToCSV("master", dateDebut, dateFin);
|
||||
|
||||
return Response.ok(csvContent)
|
||||
.header("Content-Disposition", "attachment; filename=\"audit-logs-" +
|
||||
LocalDateTime.now().toString().replace(":", "-") + ".csv\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'export CSV des logs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/purge")
|
||||
@Operation(summary = "Purger les anciens logs", description = "Supprime les logs de plus de X jours")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Purge effectuée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin"})
|
||||
public Response purgeOldLogs(
|
||||
@QueryParam("joursAnciennete") @DefaultValue("90") int joursAnciennete
|
||||
) {
|
||||
log.info("DELETE /api/audit/purge - Suppression des logs de plus de {} jours", joursAnciennete);
|
||||
|
||||
try {
|
||||
LocalDateTime dateLimite = LocalDateTime.now().minusDays(joursAnciennete);
|
||||
auditService.purgeOldLogs(dateLimite);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la purge des logs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== DTOs internes ====================
|
||||
|
||||
@Schema(description = "Réponse de comptage")
|
||||
public static class CountResponse {
|
||||
@Schema(description = "Nombre d'éléments")
|
||||
public long count;
|
||||
|
||||
public CountResponse(long count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse d'erreur")
|
||||
public static class ErrorResponse {
|
||||
@Schema(description = "Message d'erreur")
|
||||
public String message;
|
||||
|
||||
public ErrorResponse(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user