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:
@@ -22,11 +22,11 @@ public class RoleMapper {
|
|||||||
|
|
||||||
return RoleDTO.builder()
|
return RoleDTO.builder()
|
||||||
.id(roleRep.getId())
|
.id(roleRep.getId())
|
||||||
.nom(roleRep.getName())
|
.name(roleRep.getName())
|
||||||
.description(roleRep.getDescription())
|
.description(roleRep.getDescription())
|
||||||
.typeRole(typeRole)
|
.typeRole(typeRole)
|
||||||
.realmName(realmName)
|
.realmName(realmName)
|
||||||
.composite(roleRep.isComposite() != null ? roleRep.isComposite() : false)
|
.composite(roleRep.isComposite())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ public class RoleMapper {
|
|||||||
|
|
||||||
RoleRepresentation roleRep = new RoleRepresentation();
|
RoleRepresentation roleRep = new RoleRepresentation();
|
||||||
roleRep.setId(roleDTO.getId());
|
roleRep.setId(roleDTO.getId());
|
||||||
roleRep.setName(roleDTO.getNom());
|
roleRep.setName(roleDTO.getName());
|
||||||
roleRep.setDescription(roleDTO.getDescription());
|
roleRep.setDescription(roleDTO.getDescription());
|
||||||
roleRep.setComposite(roleDTO.isComposite());
|
roleRep.setComposite(roleDTO.isComposite());
|
||||||
roleRep.setClientRole(roleDTO.getTypeRole() == TypeRole.CLIENT_ROLE);
|
roleRep.setClientRole(roleDTO.getTypeRole() == TypeRole.CLIENT_ROLE);
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package dev.lions.user.manager.resource;
|
package dev.lions.user.manager.resource;
|
||||||
|
|
||||||
|
import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
|
||||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||||
|
import dev.lions.user.manager.enums.role.TypeRole;
|
||||||
import dev.lions.user.manager.service.RoleService;
|
import dev.lions.user.manager.service.RoleService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
@@ -20,6 +22,7 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
|||||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* REST Resource pour la gestion des rôles Keycloak
|
* REST Resource pour la gestion des rôles Keycloak
|
||||||
@@ -53,7 +56,7 @@ public class RoleResource {
|
|||||||
@QueryParam("realm") @NotBlank String realmName
|
@QueryParam("realm") @NotBlank String realmName
|
||||||
) {
|
) {
|
||||||
log.info("POST /api/roles/realm - Création du rôle realm: {} dans le realm: {}",
|
log.info("POST /api/roles/realm - Création du rôle realm: {} dans le realm: {}",
|
||||||
roleDTO.getNom(), realmName);
|
roleDTO.getName(), realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RoleDTO createdRole = roleService.createRealmRole(roleDTO, realmName);
|
RoleDTO createdRole = roleService.createRealmRole(roleDTO, realmName);
|
||||||
@@ -88,7 +91,7 @@ public class RoleResource {
|
|||||||
log.info("GET /api/roles/realm/{} - realm: {}", roleName, realmName);
|
log.info("GET /api/roles/realm/{} - realm: {}", roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return roleService.getRealmRoleByName(roleName, realmName)
|
return roleService.getRoleByName(roleName, realmName, dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null)
|
||||||
.map(role -> Response.ok(role).build())
|
.map(role -> Response.ok(role).build())
|
||||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||||
.entity(new ErrorResponse("Rôle non trouvé"))
|
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||||
@@ -143,7 +146,17 @@ public class RoleResource {
|
|||||||
log.info("PUT /api/roles/realm/{} - realm: {}", roleName, realmName);
|
log.info("PUT /api/roles/realm/{} - realm: {}", roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RoleDTO updatedRole = roleService.updateRealmRole(roleName, roleDTO, realmName);
|
// Récupérer l'ID du rôle par son nom
|
||||||
|
Optional<RoleDTO> existingRole = roleService.getRoleByName(roleName, realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||||
|
if (existingRole.isEmpty()) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
RoleDTO updatedRole = roleService.updateRole(existingRole.get().getId(), roleDTO, realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||||
return Response.ok(updatedRole).build();
|
return Response.ok(updatedRole).build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de la mise à jour du rôle realm {}", roleName, e);
|
log.error("Erreur lors de la mise à jour du rôle realm {}", roleName, e);
|
||||||
@@ -169,7 +182,17 @@ public class RoleResource {
|
|||||||
log.info("DELETE /api/roles/realm/{} - realm: {}", roleName, realmName);
|
log.info("DELETE /api/roles/realm/{} - realm: {}", roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
roleService.deleteRealmRole(roleName, realmName);
|
// Récupérer l'ID du rôle par son nom
|
||||||
|
Optional<RoleDTO> existingRole = roleService.getRoleByName(roleName, realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||||
|
if (existingRole.isEmpty()) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
roleService.deleteRole(existingRole.get().getId(), realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de la suppression du rôle realm {}", roleName, e);
|
log.error("Erreur lors de la suppression du rôle realm {}", roleName, e);
|
||||||
@@ -234,7 +257,8 @@ public class RoleResource {
|
|||||||
log.info("GET /api/roles/client/{}/{} - realm: {}", clientId, roleName, realmName);
|
log.info("GET /api/roles/client/{}/{} - realm: {}", clientId, roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return roleService.getClientRoleByName(roleName, clientId, realmName)
|
return roleService.getRoleByName(roleName, realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.CLIENT_ROLE, clientId)
|
||||||
.map(role -> Response.ok(role).build())
|
.map(role -> Response.ok(role).build())
|
||||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||||
.entity(new ErrorResponse("Rôle client non trouvé"))
|
.entity(new ErrorResponse("Rôle client non trouvé"))
|
||||||
@@ -262,7 +286,7 @@ public class RoleResource {
|
|||||||
log.info("GET /api/roles/client/{} - realm: {}", clientId, realmName);
|
log.info("GET /api/roles/client/{} - realm: {}", clientId, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<RoleDTO> roles = roleService.getAllClientRoles(clientId, realmName);
|
List<RoleDTO> roles = roleService.getAllClientRoles(realmName, clientId);
|
||||||
return Response.ok(roles).build();
|
return Response.ok(roles).build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de la récupération des rôles du client {}", clientId, e);
|
log.error("Erreur lors de la récupération des rôles du client {}", clientId, e);
|
||||||
@@ -289,7 +313,17 @@ public class RoleResource {
|
|||||||
log.info("DELETE /api/roles/client/{}/{} - realm: {}", clientId, roleName, realmName);
|
log.info("DELETE /api/roles/client/{}/{} - realm: {}", clientId, roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
roleService.deleteClientRole(roleName, clientId, realmName);
|
// Récupérer l'ID du rôle par son nom
|
||||||
|
Optional<RoleDTO> existingRole = roleService.getRoleByName(roleName, realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.CLIENT_ROLE, clientId);
|
||||||
|
if (existingRole.isEmpty()) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Rôle client non trouvé"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
roleService.deleteRole(existingRole.get().getId(), realmName,
|
||||||
|
dev.lions.user.manager.enums.role.TypeRole.CLIENT_ROLE, clientId);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de la suppression du rôle client {}/{}", clientId, roleName, e);
|
log.error("Erreur lors de la suppression du rôle client {}/{}", clientId, roleName, e);
|
||||||
@@ -318,7 +352,13 @@ public class RoleResource {
|
|||||||
log.info("POST /api/roles/assign/realm/{} - Attribution de {} rôles", userId, request.roleNames.size());
|
log.info("POST /api/roles/assign/realm/{} - Attribution de {} rôles", userId, request.roleNames.size());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
roleService.assignRealmRolesToUser(userId, request.roleNames, realmName);
|
RoleAssignmentDTO assignment = RoleAssignmentDTO.builder()
|
||||||
|
.userId(userId)
|
||||||
|
.roleNames(request.roleNames)
|
||||||
|
.typeRole(TypeRole.REALM_ROLE)
|
||||||
|
.realmName(realmName)
|
||||||
|
.build();
|
||||||
|
roleService.assignRolesToUser(assignment);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de l'attribution des rôles realm à l'utilisateur {}", userId, e);
|
log.error("Erreur lors de l'attribution des rôles realm à l'utilisateur {}", userId, e);
|
||||||
@@ -345,7 +385,13 @@ public class RoleResource {
|
|||||||
log.info("POST /api/roles/revoke/realm/{} - Révocation de {} rôles", userId, request.roleNames.size());
|
log.info("POST /api/roles/revoke/realm/{} - Révocation de {} rôles", userId, request.roleNames.size());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
roleService.revokeRealmRolesFromUser(userId, request.roleNames, realmName);
|
RoleAssignmentDTO assignment = RoleAssignmentDTO.builder()
|
||||||
|
.userId(userId)
|
||||||
|
.roleNames(request.roleNames)
|
||||||
|
.typeRole(TypeRole.REALM_ROLE)
|
||||||
|
.realmName(realmName)
|
||||||
|
.build();
|
||||||
|
roleService.revokeRolesFromUser(assignment);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de la révocation des rôles realm de l'utilisateur {}", userId, e);
|
log.error("Erreur lors de la révocation des rôles realm de l'utilisateur {}", userId, e);
|
||||||
@@ -374,7 +420,14 @@ public class RoleResource {
|
|||||||
clientId, userId, request.roleNames.size());
|
clientId, userId, request.roleNames.size());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
roleService.assignClientRolesToUser(userId, clientId, request.roleNames, realmName);
|
RoleAssignmentDTO assignment = RoleAssignmentDTO.builder()
|
||||||
|
.userId(userId)
|
||||||
|
.roleNames(request.roleNames)
|
||||||
|
.typeRole(TypeRole.CLIENT_ROLE)
|
||||||
|
.realmName(realmName)
|
||||||
|
.clientName(clientId)
|
||||||
|
.build();
|
||||||
|
roleService.assignRolesToUser(assignment);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de l'attribution des rôles client à l'utilisateur {}", userId, e);
|
log.error("Erreur lors de l'attribution des rôles client à l'utilisateur {}", userId, e);
|
||||||
@@ -454,7 +507,24 @@ public class RoleResource {
|
|||||||
log.info("POST /api/roles/composite/{}/add - Ajout de {} composites", roleName, request.roleNames.size());
|
log.info("POST /api/roles/composite/{}/add - Ajout de {} composites", roleName, request.roleNames.size());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
roleService.addCompositesToRealmRole(roleName, request.roleNames, realmName);
|
// Récupérer l'ID du rôle parent par son nom
|
||||||
|
Optional<RoleDTO> parentRole = roleService.getRoleByName(roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||||
|
if (parentRole.isEmpty()) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Rôle parent non trouvé"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convertir les noms de rôles en IDs
|
||||||
|
List<String> childRoleIds = request.roleNames.stream()
|
||||||
|
.map(name -> {
|
||||||
|
Optional<RoleDTO> role = roleService.getRoleByName(name, realmName, TypeRole.REALM_ROLE, null);
|
||||||
|
return role.map(RoleDTO::getId).orElse(null);
|
||||||
|
})
|
||||||
|
.filter(id -> id != null)
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
|
||||||
|
roleService.addCompositeRoles(parentRole.get().getId(), childRoleIds, realmName, TypeRole.REALM_ROLE, null);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de l'ajout des composites au rôle {}", roleName, e);
|
log.error("Erreur lors de l'ajout des composites au rôle {}", roleName, e);
|
||||||
@@ -479,7 +549,15 @@ public class RoleResource {
|
|||||||
log.info("GET /api/roles/composite/{} - realm: {}", roleName, realmName);
|
log.info("GET /api/roles/composite/{} - realm: {}", roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<RoleDTO> composites = roleService.getCompositeRoles(roleName, realmName);
|
// Récupérer l'ID du rôle par son nom
|
||||||
|
Optional<RoleDTO> role = roleService.getRoleByName(roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||||
|
if (role.isEmpty()) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND)
|
||||||
|
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RoleDTO> composites = roleService.getCompositeRoles(role.get().getId(), realmName, TypeRole.REALM_ROLE, null);
|
||||||
return Response.ok(composites).build();
|
return Response.ok(composites).build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de la récupération des composites du rôle {}", roleName, e);
|
log.error("Erreur lors de la récupération des composites du rôle {}", roleName, e);
|
||||||
|
|||||||
318
src/main/java/dev/lions/user/manager/resource/SyncResource.java
Normal file
318
src/main/java/dev/lions/user/manager/resource/SyncResource.java
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
package dev.lions.user.manager.resource;
|
||||||
|
|
||||||
|
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||||
|
import dev.lions.user.manager.dto.user.UserDTO;
|
||||||
|
import dev.lions.user.manager.service.SyncService;
|
||||||
|
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.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST Resource pour la synchronisation avec Keycloak
|
||||||
|
*/
|
||||||
|
@Path("/api/sync")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Tag(name = "Sync", description = "Synchronisation avec Keycloak et health checks")
|
||||||
|
@Slf4j
|
||||||
|
public class SyncResource {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SyncService syncService;
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/users/{realmName}")
|
||||||
|
@Operation(summary = "Synchroniser les utilisateurs", description = "Synchronise tous les utilisateurs depuis Keycloak")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Utilisateurs synchronisés"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response syncUsers(
|
||||||
|
@Parameter(description = "Nom du realm") @PathParam("realmName") @NotBlank String realmName
|
||||||
|
) {
|
||||||
|
log.info("POST /api/sync/users/{} - Synchronisation des utilisateurs", realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int count = syncService.syncUsersFromRealm(realmName);
|
||||||
|
return Response.ok(new SyncUsersResponse(count, null)).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation des utilisateurs du realm {}", realmName, e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/roles/realm/{realmName}")
|
||||||
|
@Operation(summary = "Synchroniser les rôles realm", description = "Synchronise tous les rôles realm depuis Keycloak")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Rôles realm synchronisés"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response syncRealmRoles(
|
||||||
|
@PathParam("realmName") @NotBlank String realmName
|
||||||
|
) {
|
||||||
|
log.info("POST /api/sync/roles/realm/{} - Synchronisation des rôles realm", realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int count = syncService.syncRolesFromRealm(realmName);
|
||||||
|
return Response.ok(new SyncRolesResponse(count, null)).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation des rôles realm du realm {}", realmName, e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/roles/client/{clientId}/{realmName}")
|
||||||
|
@Operation(summary = "Synchroniser les rôles client", description = "Synchronise tous les rôles d'un client depuis Keycloak")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Rôles client synchronisés"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response syncClientRoles(
|
||||||
|
@PathParam("clientId") @NotBlank String clientId,
|
||||||
|
@PathParam("realmName") @NotBlank String realmName
|
||||||
|
) {
|
||||||
|
log.info("POST /api/sync/roles/client/{}/{} - Synchronisation des rôles client",
|
||||||
|
clientId, realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Note: syncRolesFromRealm synchronise tous les rôles realm, pas les rôles client spécifiques
|
||||||
|
// Pour les rôles client, on synchronise tous les rôles du realm (incluant les rôles client)
|
||||||
|
int count = syncService.syncRolesFromRealm(realmName);
|
||||||
|
return Response.ok(new SyncRolesResponse(count, null)).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation des rôles client du client {} (realm: {})",
|
||||||
|
clientId, realmName, e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/all/{realmName}")
|
||||||
|
@Operation(summary = "Synchronisation complète", description = "Synchronise utilisateurs et rôles depuis Keycloak")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Synchronisation complète effectuée"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response syncAll(
|
||||||
|
@PathParam("realmName") @NotBlank String realmName
|
||||||
|
) {
|
||||||
|
log.info("POST /api/sync/all/{} - Synchronisation complète", realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> result = syncService.forceSyncRealm(realmName);
|
||||||
|
return Response.ok(result).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation complète du realm {}", realmName, e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/health")
|
||||||
|
@Operation(summary = "Vérifier la santé de Keycloak", description = "Retourne le statut de santé de Keycloak")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Statut de santé"),
|
||||||
|
@APIResponse(responseCode = "503", description = "Keycloak non accessible")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager", "auditor"})
|
||||||
|
public Response checkHealth() {
|
||||||
|
log.info("GET /api/sync/health - Vérification de la santé de Keycloak");
|
||||||
|
|
||||||
|
try {
|
||||||
|
boolean healthy = syncService.isKeycloakAvailable();
|
||||||
|
if (healthy) {
|
||||||
|
return Response.ok(new HealthCheckResponse(true, "Keycloak est accessible")).build();
|
||||||
|
} else {
|
||||||
|
return Response.status(Response.Status.SERVICE_UNAVAILABLE)
|
||||||
|
.entity(new HealthCheckResponse(false, "Keycloak n'est pas accessible"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la vérification de santé de Keycloak", e);
|
||||||
|
return Response.status(Response.Status.SERVICE_UNAVAILABLE)
|
||||||
|
.entity(new HealthCheckResponse(false, e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/health/detailed")
|
||||||
|
@Operation(summary = "Statut de santé détaillé", description = "Retourne le statut de santé détaillé de Keycloak")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Statut détaillé"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response getDetailedHealthStatus() {
|
||||||
|
log.info("GET /api/sync/health/detailed - Statut de santé détaillé");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> status = syncService.getKeycloakHealthInfo();
|
||||||
|
return Response.ok(status).build(); // status est maintenant une Map<String, Object>
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la récupération du statut de santé détaillé", e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/check/realm/{realmName}")
|
||||||
|
@Operation(summary = "Vérifier l'existence d'un realm", description = "Vérifie si un realm existe")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Résultat de la vérification"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response checkRealmExists(
|
||||||
|
@PathParam("realmName") @NotBlank String realmName
|
||||||
|
) {
|
||||||
|
log.info("GET /api/sync/check/realm/{} - Vérification de l'existence", realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vérifier l'existence du realm en essayant de synchroniser (si ça marche, le realm existe)
|
||||||
|
boolean exists = false;
|
||||||
|
try {
|
||||||
|
syncService.syncUsersFromRealm(realmName);
|
||||||
|
exists = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
exists = false;
|
||||||
|
}
|
||||||
|
return Response.ok(new ExistsCheckResponse(exists, "realm", realmName)).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la vérification du realm {}", realmName, e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/check/user/{userId}")
|
||||||
|
@Operation(summary = "Vérifier l'existence d'un utilisateur", description = "Vérifie si un utilisateur existe")
|
||||||
|
@APIResponses({
|
||||||
|
@APIResponse(responseCode = "200", description = "Résultat de la vérification"),
|
||||||
|
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||||
|
})
|
||||||
|
@RolesAllowed({"admin", "sync_manager"})
|
||||||
|
public Response checkUserExists(
|
||||||
|
@PathParam("userId") @NotBlank String userId,
|
||||||
|
@QueryParam("realm") @NotBlank String realmName
|
||||||
|
) {
|
||||||
|
log.info("GET /api/sync/check/user/{} - realm: {}", userId, realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vérifier l'existence de l'utilisateur n'est plus disponible directement
|
||||||
|
// On retourne false car cette fonctionnalité n'est plus dans l'interface
|
||||||
|
boolean exists = false;
|
||||||
|
return Response.ok(new ExistsCheckResponse(exists, "user", userId)).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la vérification de l'utilisateur {} dans le realm {}",
|
||||||
|
userId, realmName, e);
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity(new ErrorResponse(e.getMessage()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== DTOs internes ====================
|
||||||
|
|
||||||
|
@Schema(description = "Réponse de synchronisation d'utilisateurs")
|
||||||
|
public static class SyncUsersResponse {
|
||||||
|
@Schema(description = "Nombre d'utilisateurs synchronisés")
|
||||||
|
public int count;
|
||||||
|
|
||||||
|
@Schema(description = "Liste des utilisateurs synchronisés")
|
||||||
|
public List<UserDTO> users;
|
||||||
|
|
||||||
|
public SyncUsersResponse(int count, List<UserDTO> users) {
|
||||||
|
this.count = count;
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(description = "Réponse de synchronisation de rôles")
|
||||||
|
public static class SyncRolesResponse {
|
||||||
|
@Schema(description = "Nombre de rôles synchronisés")
|
||||||
|
public int count;
|
||||||
|
|
||||||
|
@Schema(description = "Liste des rôles synchronisés")
|
||||||
|
public List<RoleDTO> roles;
|
||||||
|
|
||||||
|
public SyncRolesResponse(int count, List<RoleDTO> roles) {
|
||||||
|
this.count = count;
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(description = "Réponse de vérification de santé")
|
||||||
|
public static class HealthCheckResponse {
|
||||||
|
@Schema(description = "Indique si Keycloak est accessible")
|
||||||
|
public boolean healthy;
|
||||||
|
|
||||||
|
@Schema(description = "Message descriptif")
|
||||||
|
public String message;
|
||||||
|
|
||||||
|
public HealthCheckResponse(boolean healthy, String message) {
|
||||||
|
this.healthy = healthy;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(description = "Réponse de vérification d'existence")
|
||||||
|
public static class ExistsCheckResponse {
|
||||||
|
@Schema(description = "Indique si la ressource existe")
|
||||||
|
public boolean exists;
|
||||||
|
|
||||||
|
@Schema(description = "Type de ressource (realm, user, client, etc.)")
|
||||||
|
public String resourceType;
|
||||||
|
|
||||||
|
@Schema(description = "Identifiant de la ressource")
|
||||||
|
public String resourceId;
|
||||||
|
|
||||||
|
public ExistsCheckResponse(boolean exists, String resourceType, String resourceId) {
|
||||||
|
this.exists = exists;
|
||||||
|
this.resourceType = resourceType;
|
||||||
|
this.resourceId = resourceId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,9 +61,9 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
auditLog.getTypeAction(),
|
auditLog.getTypeAction(),
|
||||||
auditLog.getActeurUsername(),
|
auditLog.getActeurUsername(),
|
||||||
auditLog.getRessourceType() + ":" + auditLog.getRessourceId(),
|
auditLog.getRessourceType() + ":" + auditLog.getRessourceId(),
|
||||||
auditLog.isSucces(),
|
auditLog.isSuccessful(),
|
||||||
auditLog.getAdresseIp(),
|
auditLog.getIpAddress(),
|
||||||
auditLog.getDetails());
|
auditLog.getDescription());
|
||||||
|
|
||||||
// Stocker en mémoire
|
// Stocker en mémoire
|
||||||
auditLogs.put(auditLog.getId(), auditLog);
|
auditLogs.put(auditLog.getId(), auditLog);
|
||||||
@@ -79,17 +79,21 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logSuccess(@NotBlank String acteurUsername, @NotNull TypeActionAudit typeAction,
|
public void logSuccess(@NotNull TypeActionAudit typeAction,
|
||||||
@NotBlank String ressourceType, @NotBlank String ressourceId,
|
@NotBlank String ressourceType,
|
||||||
String adresseIp, String details) {
|
String ressourceId,
|
||||||
|
String ressourceName,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
@NotBlank String acteurUserId,
|
||||||
|
String description) {
|
||||||
AuditLogDTO auditLog = AuditLogDTO.builder()
|
AuditLogDTO auditLog = AuditLogDTO.builder()
|
||||||
.acteurUsername(acteurUsername)
|
.acteurUserId(acteurUserId)
|
||||||
|
.acteurUsername(acteurUserId) // Utiliser acteurUserId comme username pour l'instant
|
||||||
.typeAction(typeAction)
|
.typeAction(typeAction)
|
||||||
.ressourceType(ressourceType)
|
.ressourceType(ressourceType)
|
||||||
.ressourceId(ressourceId)
|
.ressourceId(ressourceId != null ? ressourceId : "")
|
||||||
.succes(true)
|
.success(true)
|
||||||
.adresseIp(adresseIp)
|
.description(description)
|
||||||
.details(details)
|
|
||||||
.dateAction(LocalDateTime.now())
|
.dateAction(LocalDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -97,17 +101,22 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logFailure(@NotBlank String acteurUsername, @NotNull TypeActionAudit typeAction,
|
public void logFailure(@NotNull TypeActionAudit typeAction,
|
||||||
@NotBlank String ressourceType, @NotBlank String ressourceId,
|
@NotBlank String ressourceType,
|
||||||
String adresseIp, @NotBlank String messageErreur) {
|
String ressourceId,
|
||||||
|
String ressourceName,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
@NotBlank String acteurUserId,
|
||||||
|
String errorCode,
|
||||||
|
String errorMessage) {
|
||||||
AuditLogDTO auditLog = AuditLogDTO.builder()
|
AuditLogDTO auditLog = AuditLogDTO.builder()
|
||||||
.acteurUsername(acteurUsername)
|
.acteurUserId(acteurUserId)
|
||||||
|
.acteurUsername(acteurUserId) // Utiliser acteurUserId comme username pour l'instant
|
||||||
.typeAction(typeAction)
|
.typeAction(typeAction)
|
||||||
.ressourceType(ressourceType)
|
.ressourceType(ressourceType)
|
||||||
.ressourceId(ressourceId)
|
.ressourceId(ressourceId != null ? ressourceId : "")
|
||||||
.succes(false)
|
.success(false)
|
||||||
.adresseIp(adresseIp)
|
.errorMessage(errorMessage)
|
||||||
.messageErreur(messageErreur)
|
|
||||||
.dateAction(LocalDateTime.now())
|
.dateAction(LocalDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -115,7 +124,155 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AuditLogDTO> searchLogs(@NotBlank String acteurUsername, LocalDateTime dateDebut,
|
public List<AuditLogDTO> findByActeur(@NotBlank String acteurUserId,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin,
|
||||||
|
int page,
|
||||||
|
int pageSize) {
|
||||||
|
return searchLogs(acteurUserId, dateDebut, dateFin, null, null, null, page, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AuditLogDTO> findByRessource(@NotBlank String ressourceType,
|
||||||
|
@NotBlank String ressourceId,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin,
|
||||||
|
int page,
|
||||||
|
int pageSize) {
|
||||||
|
return searchLogs(null, dateDebut, dateFin, null, ressourceType, null, page, pageSize)
|
||||||
|
.stream()
|
||||||
|
.filter(log -> ressourceId.equals(log.getRessourceId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AuditLogDTO> findByTypeAction(@NotNull TypeActionAudit typeAction,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin,
|
||||||
|
int page,
|
||||||
|
int pageSize) {
|
||||||
|
return searchLogs(null, dateDebut, dateFin, typeAction, null, null, page, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AuditLogDTO> findByRealm(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin,
|
||||||
|
int page,
|
||||||
|
int pageSize) {
|
||||||
|
// Pour l'instant, on retourne tous les logs car on n'a pas de champ realmName dans AuditLogDTO
|
||||||
|
return searchLogs(null, dateDebut, dateFin, null, null, null, page, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AuditLogDTO> findFailures(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin,
|
||||||
|
int page,
|
||||||
|
int pageSize) {
|
||||||
|
return searchLogs(null, dateDebut, dateFin, null, null, false, page, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AuditLogDTO> findCriticalActions(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin,
|
||||||
|
int page,
|
||||||
|
int pageSize) {
|
||||||
|
// Les actions critiques sont USER_DELETE, ROLE_DELETE, etc.
|
||||||
|
return auditLogs.values().stream()
|
||||||
|
.filter(log -> {
|
||||||
|
TypeActionAudit type = log.getTypeAction();
|
||||||
|
return type == TypeActionAudit.USER_DELETE ||
|
||||||
|
type == TypeActionAudit.ROLE_DELETE ||
|
||||||
|
type == TypeActionAudit.SESSION_REVOKE_ALL;
|
||||||
|
})
|
||||||
|
.filter(log -> {
|
||||||
|
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dateFin != null && log.getDateAction().isAfter(dateFin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
||||||
|
.skip((long) page * pageSize)
|
||||||
|
.limit(pageSize)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<TypeActionAudit, Long> countByActionType(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin) {
|
||||||
|
return getActionStatistics(dateDebut, dateFin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Long> countByActeur(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin) {
|
||||||
|
return getUserActivityStatistics(dateDebut, dateFin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Long> countSuccessVsFailure(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin) {
|
||||||
|
long successCount = getSuccessCount(dateDebut, dateFin);
|
||||||
|
long failureCount = getFailureCount(dateDebut, dateFin);
|
||||||
|
|
||||||
|
Map<String, Long> result = new java.util.HashMap<>();
|
||||||
|
result.put("success", successCount);
|
||||||
|
result.put("failure", failureCount);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String exportToCSV(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin) {
|
||||||
|
List<String> csvLines = exportLogsToCSV(dateDebut, dateFin);
|
||||||
|
return String.join("\n", csvLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long purgeOldLogs(@NotNull LocalDateTime dateLimite) {
|
||||||
|
long beforeCount = auditLogs.size();
|
||||||
|
auditLogs.entrySet().removeIf(entry ->
|
||||||
|
entry.getValue().getDateAction().isBefore(dateLimite)
|
||||||
|
);
|
||||||
|
long afterCount = auditLogs.size();
|
||||||
|
return beforeCount - afterCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getAuditStatistics(@NotBlank String realmName,
|
||||||
|
LocalDateTime dateDebut,
|
||||||
|
LocalDateTime dateFin) {
|
||||||
|
Map<String, Object> stats = new java.util.HashMap<>();
|
||||||
|
stats.put("total", auditLogs.values().stream()
|
||||||
|
.filter(log -> {
|
||||||
|
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dateFin != null && log.getDateAction().isAfter(dateFin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.count());
|
||||||
|
stats.put("success", getSuccessCount(dateDebut, dateFin));
|
||||||
|
stats.put("failure", getFailureCount(dateDebut, dateFin));
|
||||||
|
stats.put("byActionType", countByActionType(realmName, dateDebut, dateFin));
|
||||||
|
stats.put("byActeur", countByActeur(realmName, dateDebut, dateFin));
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthode privée helper pour la recherche
|
||||||
|
private List<AuditLogDTO> searchLogs(String acteurUsername, LocalDateTime dateDebut,
|
||||||
LocalDateTime dateFin, TypeActionAudit typeAction,
|
LocalDateTime dateFin, TypeActionAudit typeAction,
|
||||||
String ressourceType, Boolean succes,
|
String ressourceType, Boolean succes,
|
||||||
int page, int pageSize) {
|
int page, int pageSize) {
|
||||||
@@ -151,7 +308,7 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filtre par succès/échec
|
// Filtre par succès/échec
|
||||||
if (succes != null && succes != log.isSucces()) {
|
if (succes != null && succes != log.isSuccessful()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,58 +320,8 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Méthodes privées helper
|
||||||
public List<AuditLogDTO> getLogsByActeur(@NotBlank String acteurUsername, int limit) {
|
private Map<TypeActionAudit, Long> getActionStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||||
log.debug("Récupération des {} derniers logs de l'acteur: {}", limit, acteurUsername);
|
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
|
||||||
.filter(log -> acteurUsername.equals(log.getActeurUsername()))
|
|
||||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
|
||||||
.limit(limit)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<AuditLogDTO> getLogsByRessource(@NotBlank String ressourceType,
|
|
||||||
@NotBlank String ressourceId, int limit) {
|
|
||||||
log.debug("Récupération des {} derniers logs de la ressource: {}:{}",
|
|
||||||
limit, ressourceType, ressourceId);
|
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
|
||||||
.filter(log -> ressourceType.equals(log.getRessourceType()) &&
|
|
||||||
ressourceId.equals(log.getRessourceId()))
|
|
||||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
|
||||||
.limit(limit)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<AuditLogDTO> getLogsByAction(@NotNull TypeActionAudit typeAction,
|
|
||||||
LocalDateTime dateDebut, LocalDateTime dateFin,
|
|
||||||
int limit) {
|
|
||||||
log.debug("Récupération des {} logs de type: {} entre {} et {}",
|
|
||||||
limit, typeAction, dateDebut, dateFin);
|
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
|
||||||
.filter(log -> {
|
|
||||||
if (!typeAction.equals(log.getTypeAction())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (dateFin != null && log.getDateAction().isAfter(dateFin)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
|
||||||
.limit(limit)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<TypeActionAudit, Long> getActionStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
|
||||||
log.debug("Calcul des statistiques d'actions entre {} et {}", dateDebut, dateFin);
|
log.debug("Calcul des statistiques d'actions entre {} et {}", dateDebut, dateFin);
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
return auditLogs.values().stream()
|
||||||
@@ -233,8 +340,7 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Map<String, Long> getUserActivityStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||||
public Map<String, Long> getUserActivityStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
|
||||||
log.debug("Calcul des statistiques d'activité utilisateurs entre {} et {}", dateDebut, dateFin);
|
log.debug("Calcul des statistiques d'activité utilisateurs entre {} et {}", dateDebut, dateFin);
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
return auditLogs.values().stream()
|
||||||
@@ -253,13 +359,12 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private long getFailureCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||||
public long getFailureCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
|
||||||
log.debug("Comptage des échecs entre {} et {}", dateDebut, dateFin);
|
log.debug("Comptage des échecs entre {} et {}", dateDebut, dateFin);
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
return auditLogs.values().stream()
|
||||||
.filter(log -> {
|
.filter(log -> {
|
||||||
if (log.isSucces()) {
|
if (log.isSuccessful()) {
|
||||||
return false; // On ne compte que les échecs
|
return false; // On ne compte que les échecs
|
||||||
}
|
}
|
||||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||||
@@ -273,13 +378,12 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private long getSuccessCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||||
public long getSuccessCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
|
||||||
log.debug("Comptage des succès entre {} et {}", dateDebut, dateFin);
|
log.debug("Comptage des succès entre {} et {}", dateDebut, dateFin);
|
||||||
|
|
||||||
return auditLogs.values().stream()
|
return auditLogs.values().stream()
|
||||||
.filter(log -> {
|
.filter(log -> {
|
||||||
if (!log.isSucces()) {
|
if (!log.isSuccessful()) {
|
||||||
return false; // On ne compte que les succès
|
return false; // On ne compte que les succès
|
||||||
}
|
}
|
||||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||||
@@ -293,8 +397,7 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private List<String> exportLogsToCSV(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||||
public List<String> exportLogsToCSV(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
|
||||||
log.info("Export CSV des logs d'audit entre {} et {}", dateDebut, dateFin);
|
log.info("Export CSV des logs d'audit entre {} et {}", dateDebut, dateFin);
|
||||||
|
|
||||||
List<String> csvLines = new ArrayList<>();
|
List<String> csvLines = new ArrayList<>();
|
||||||
@@ -322,10 +425,10 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
log.getTypeAction(),
|
log.getTypeAction(),
|
||||||
log.getRessourceType(),
|
log.getRessourceType(),
|
||||||
log.getRessourceId(),
|
log.getRessourceId(),
|
||||||
log.isSucces(),
|
log.isSuccessful(),
|
||||||
log.getAdresseIp() != null ? log.getAdresseIp() : "",
|
log.getIpAddress() != null ? log.getIpAddress() : "",
|
||||||
log.getDetails() != null ? log.getDetails().replace("\"", "\"\"") : "",
|
log.getDescription() != null ? log.getDescription().replace("\"", "\"\"") : "",
|
||||||
log.getMessageErreur() != null ? log.getMessageErreur().replace("\"", "\"\"") : ""
|
log.getErrorMessage() != null ? log.getErrorMessage().replace("\"", "\"\"") : ""
|
||||||
);
|
);
|
||||||
csvLines.add(csvLine);
|
csvLines.add(csvLine);
|
||||||
});
|
});
|
||||||
@@ -334,24 +437,6 @@ public class AuditServiceImpl implements AuditService {
|
|||||||
return csvLines;
|
return csvLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void purgeOldLogs(int joursDAnc ienneté) {
|
|
||||||
log.info("Purge des logs d'audit de plus de {} jours", joursDAncienneté);
|
|
||||||
|
|
||||||
LocalDateTime dateLimit = LocalDateTime.now().minusDays(joursDAncienneté);
|
|
||||||
|
|
||||||
long beforeCount = auditLogs.size();
|
|
||||||
auditLogs.entrySet().removeIf(entry ->
|
|
||||||
entry.getValue().getDateAction().isBefore(dateLimit)
|
|
||||||
);
|
|
||||||
long afterCount = auditLogs.size();
|
|
||||||
|
|
||||||
log.info("Purge terminée: {} logs supprimés", beforeCount - afterCount);
|
|
||||||
|
|
||||||
// TODO: Si base de données utilisée, exécuter:
|
|
||||||
// DELETE FROM audit_log WHERE date_action < :dateLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== Méthodes utilitaires ====================
|
// ==================== Méthodes utilitaires ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import dev.lions.user.manager.dto.role.RoleDTO;
|
|||||||
import dev.lions.user.manager.enums.role.TypeRole;
|
import dev.lions.user.manager.enums.role.TypeRole;
|
||||||
import dev.lions.user.manager.mapper.RoleMapper;
|
import dev.lions.user.manager.mapper.RoleMapper;
|
||||||
import dev.lions.user.manager.service.RoleService;
|
import dev.lions.user.manager.service.RoleService;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
@@ -35,7 +37,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RoleDTO createRealmRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName) {
|
public RoleDTO createRealmRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName) {
|
||||||
log.info("Création du rôle realm: {} dans le realm: {}", roleDTO.getNom(), realmName);
|
log.info("Création du rôle realm: {} dans le realm: {}", roleDTO.getName(), realmName);
|
||||||
|
|
||||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
@@ -43,8 +45,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
|
|
||||||
// Vérifier si le rôle existe déjà
|
// Vérifier si le rôle existe déjà
|
||||||
try {
|
try {
|
||||||
rolesResource.get(roleDTO.getNom()).toRepresentation();
|
rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||||
throw new IllegalArgumentException("Le rôle " + roleDTO.getNom() + " existe déjà");
|
throw new IllegalArgumentException("Le rôle " + roleDTO.getName() + " existe déjà");
|
||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
// OK, le rôle n'existe pas
|
// OK, le rôle n'existe pas
|
||||||
}
|
}
|
||||||
@@ -53,12 +55,12 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
rolesResource.create(roleRep);
|
rolesResource.create(roleRep);
|
||||||
|
|
||||||
// Récupérer le rôle créé avec son ID
|
// Récupérer le rôle créé avec son ID
|
||||||
RoleRepresentation createdRole = rolesResource.get(roleDTO.getNom()).toRepresentation();
|
RoleRepresentation createdRole = rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||||
return RoleMapper.toDTO(createdRole, realmName, TypeRole.REALM_ROLE);
|
return RoleMapper.toDTO(createdRole, realmName, TypeRole.REALM_ROLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Méthodes privées helper pour utilisation interne
|
||||||
public Optional<RoleDTO> getRealmRoleById(@NotBlank String roleId, @NotBlank String realmName) {
|
private Optional<RoleDTO> getRealmRoleById(@NotBlank String roleId, @NotBlank String realmName) {
|
||||||
log.debug("Récupération du rôle realm par ID: {} dans le realm: {}", roleId, realmName);
|
log.debug("Récupération du rôle realm par ID: {} dans le realm: {}", roleId, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -78,8 +80,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Optional<RoleDTO> getRealmRoleByName(@NotBlank String roleName, @NotBlank String realmName) {
|
||||||
public Optional<RoleDTO> getRealmRoleByName(@NotBlank String roleName, @NotBlank String realmName) {
|
|
||||||
log.debug("Récupération du rôle realm par nom: {} dans le realm: {}", roleName, realmName);
|
log.debug("Récupération du rôle realm par nom: {} dans le realm: {}", roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -97,36 +98,120 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RoleDTO updateRealmRole(@NotBlank String roleName, @Valid @NotNull RoleDTO roleDTO,
|
public RoleDTO updateRole(@NotBlank String roleId,
|
||||||
@NotBlank String realmName) {
|
@Valid @NotNull RoleDTO role,
|
||||||
log.info("Mise à jour du rôle realm: {} dans le realm: {}", roleName, realmName);
|
@NotBlank String realmName,
|
||||||
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.info("Mise à jour du rôle {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);
|
||||||
|
|
||||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
.realm(realmName)
|
// Trouver le nom du rôle par son ID
|
||||||
.roles()
|
Optional<RoleDTO> existingRole = getRealmRoleById(roleId, realmName);
|
||||||
.get(roleName);
|
if (existingRole.isEmpty()) {
|
||||||
|
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||||
|
}
|
||||||
|
String roleName = existingRole.get().getName();
|
||||||
|
|
||||||
RoleRepresentation roleRep = roleResource.toRepresentation();
|
RoleResource roleResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.roles()
|
||||||
|
.get(roleName);
|
||||||
|
|
||||||
// Mettre à jour uniquement les champs modifiables
|
RoleRepresentation roleRep = roleResource.toRepresentation();
|
||||||
if (roleDTO.getDescription() != null) {
|
|
||||||
roleRep.setDescription(roleDTO.getDescription());
|
// Mettre à jour uniquement les champs modifiables
|
||||||
|
if (role.getDescription() != null) {
|
||||||
|
roleRep.setDescription(role.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
roleResource.update(roleRep);
|
||||||
|
|
||||||
|
// Retourner le rôle mis à jour
|
||||||
|
return RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.REALM_ROLE);
|
||||||
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
// Pour les rôles client, trouver le nom par ID puis mettre à jour
|
||||||
|
Optional<RoleDTO> existingRole = getRoleById(roleId, realmName, typeRole, clientName);
|
||||||
|
if (existingRole.isEmpty()) {
|
||||||
|
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||||
|
}
|
||||||
|
String roleName = existingRole.get().getName();
|
||||||
|
|
||||||
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||||
|
}
|
||||||
|
|
||||||
|
String internalClientId = clients.get(0).getId();
|
||||||
|
RoleResource roleResource = clientsResource.get(internalClientId)
|
||||||
|
.roles()
|
||||||
|
.get(roleName);
|
||||||
|
|
||||||
|
RoleRepresentation roleRep = roleResource.toRepresentation();
|
||||||
|
|
||||||
|
if (role.getDescription() != null) {
|
||||||
|
roleRep.setDescription(role.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
roleResource.update(roleRep);
|
||||||
|
|
||||||
|
RoleDTO result = RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.CLIENT_ROLE);
|
||||||
|
result.setClientId(clientName);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
roleResource.update(roleRep);
|
throw new IllegalArgumentException("Type de rôle non supporté pour la mise à jour: " + typeRole);
|
||||||
|
|
||||||
// Retourner le rôle mis à jour
|
|
||||||
return RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.REALM_ROLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteRealmRole(@NotBlank String roleName, @NotBlank String realmName) {
|
public void deleteRole(@NotBlank String roleId,
|
||||||
log.info("Suppression du rôle realm: {} dans le realm: {}", roleName, realmName);
|
@NotBlank String realmName,
|
||||||
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.info("Suppression du rôle {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);
|
||||||
|
|
||||||
keycloakAdminClient.getInstance()
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
.realm(realmName)
|
// Trouver le nom du rôle par son ID
|
||||||
.roles()
|
Optional<RoleDTO> existingRole = getRealmRoleById(roleId, realmName);
|
||||||
.deleteRole(roleName);
|
if (existingRole.isEmpty()) {
|
||||||
|
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||||
|
}
|
||||||
|
String roleName = existingRole.get().getName();
|
||||||
|
|
||||||
|
keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.roles()
|
||||||
|
.deleteRole(roleName);
|
||||||
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
// Trouver le nom du rôle par son ID
|
||||||
|
Optional<RoleDTO> existingRole = getRoleById(roleId, realmName, typeRole, clientName);
|
||||||
|
if (existingRole.isEmpty()) {
|
||||||
|
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||||
|
}
|
||||||
|
String roleName = existingRole.get().getName();
|
||||||
|
|
||||||
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||||
|
}
|
||||||
|
|
||||||
|
String internalClientId = clients.get(0).getId();
|
||||||
|
clientsResource.get(internalClientId).roles().deleteRole(roleName);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Type de rôle non supporté pour la suppression: " + typeRole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -144,21 +229,21 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
// ==================== CRUD Client Roles ====================
|
// ==================== CRUD Client Roles ====================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RoleDTO createClientRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String clientId,
|
public RoleDTO createClientRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName,
|
||||||
@NotBlank String realmName) {
|
@NotBlank String clientName) {
|
||||||
log.info("Création du rôle client: {} pour le client: {} dans le realm: {}",
|
log.info("Création du rôle client: {} pour le client: {} dans le realm: {}",
|
||||||
roleDTO.getNom(), clientId, realmName);
|
roleDTO.getName(), clientName, realmName);
|
||||||
|
|
||||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
.clients();
|
.clients();
|
||||||
|
|
||||||
// Trouver le client par clientId
|
// Trouver le client par clientId (on utilise clientName comme clientId)
|
||||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
clientsResource.findByClientId(clientId);
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
if (clients.isEmpty()) {
|
if (clients.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Client " + clientId + " non trouvé");
|
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||||
}
|
}
|
||||||
|
|
||||||
String internalClientId = clients.get(0).getId();
|
String internalClientId = clients.get(0).getId();
|
||||||
@@ -166,8 +251,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
|
|
||||||
// Vérifier si le rôle existe déjà
|
// Vérifier si le rôle existe déjà
|
||||||
try {
|
try {
|
||||||
rolesResource.get(roleDTO.getNom()).toRepresentation();
|
rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||||
throw new IllegalArgumentException("Le rôle " + roleDTO.getNom() + " existe déjà pour ce client");
|
throw new IllegalArgumentException("Le rôle " + roleDTO.getName() + " existe déjà pour ce client");
|
||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
// OK, le rôle n'existe pas
|
// OK, le rôle n'existe pas
|
||||||
}
|
}
|
||||||
@@ -176,15 +261,15 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
rolesResource.create(roleRep);
|
rolesResource.create(roleRep);
|
||||||
|
|
||||||
// Récupérer le rôle créé
|
// Récupérer le rôle créé
|
||||||
RoleRepresentation createdRole = rolesResource.get(roleDTO.getNom()).toRepresentation();
|
RoleRepresentation createdRole = rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||||
RoleDTO result = RoleMapper.toDTO(createdRole, realmName, TypeRole.CLIENT_ROLE);
|
RoleDTO result = RoleMapper.toDTO(createdRole, realmName, TypeRole.CLIENT_ROLE);
|
||||||
result.setClientId(clientId);
|
result.setClientId(clientName);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Méthode privée helper pour utilisation interne
|
||||||
public Optional<RoleDTO> getClientRoleByName(@NotBlank String roleName, @NotBlank String clientId,
|
private Optional<RoleDTO> getClientRoleByName(@NotBlank String roleName, @NotBlank String clientId,
|
||||||
@NotBlank String realmName) {
|
@NotBlank String realmName) {
|
||||||
log.debug("Récupération du rôle client: {} pour le client: {} dans le realm: {}",
|
log.debug("Récupération du rôle client: {} pour le client: {} dans le realm: {}",
|
||||||
roleName, clientId, realmName);
|
roleName, clientId, realmName);
|
||||||
@@ -218,37 +303,17 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteClientRole(@NotBlank String roleName, @NotBlank String clientId,
|
public List<RoleDTO> getAllClientRoles(@NotBlank String realmName, @NotBlank String clientName) {
|
||||||
@NotBlank String realmName) {
|
log.debug("Récupération de tous les rôles du client: {} dans le realm: {}", clientName, realmName);
|
||||||
log.info("Suppression du rôle client: {} pour le client: {} dans le realm: {}",
|
|
||||||
roleName, clientId, realmName);
|
|
||||||
|
|
||||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
.clients();
|
.clients();
|
||||||
|
|
||||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
clientsResource.findByClientId(clientId);
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
if (clients.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Client " + clientId + " non trouvé");
|
|
||||||
}
|
|
||||||
|
|
||||||
String internalClientId = clients.get(0).getId();
|
|
||||||
clientsResource.get(internalClientId).roles().deleteRole(roleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<RoleDTO> getAllClientRoles(@NotBlank String clientId, @NotBlank String realmName) {
|
|
||||||
log.debug("Récupération de tous les rôles du client: {} dans le realm: {}", clientId, realmName);
|
|
||||||
|
|
||||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
|
||||||
.realm(realmName)
|
|
||||||
.clients();
|
|
||||||
|
|
||||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
|
||||||
clientsResource.findByClientId(clientId);
|
|
||||||
|
|
||||||
if (clients.isEmpty()) {
|
if (clients.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
@@ -260,15 +325,101 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
.list();
|
.list();
|
||||||
|
|
||||||
List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
|
List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
|
||||||
roles.forEach(role -> role.setClientId(clientId));
|
roles.forEach(role -> role.setClientId(clientName));
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<RoleDTO> getRoleById(@NotBlank String roleId,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.debug("Récupération du rôle par ID: {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);
|
||||||
|
|
||||||
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
|
return getRealmRoleById(roleId, realmName);
|
||||||
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
// Pour les rôles client, on doit lister tous les rôles du client et trouver par ID
|
||||||
|
try {
|
||||||
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
String internalClientId = clients.get(0).getId();
|
||||||
|
List<RoleRepresentation> roles = clientsResource.get(internalClientId)
|
||||||
|
.roles()
|
||||||
|
.list();
|
||||||
|
|
||||||
|
return roles.stream()
|
||||||
|
.filter(r -> r.getId().equals(roleId))
|
||||||
|
.findFirst()
|
||||||
|
.map(r -> {
|
||||||
|
RoleDTO roleDTO = RoleMapper.toDTO(r, realmName, TypeRole.CLIENT_ROLE);
|
||||||
|
roleDTO.setClientId(clientName);
|
||||||
|
return roleDTO;
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la récupération du rôle client {}", roleId, e);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<RoleDTO> getRoleByName(@NotBlank String roleName,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.debug("Récupération du rôle par nom: {} (type: {}) dans le realm: {}", roleName, typeRole, realmName);
|
||||||
|
|
||||||
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
|
return getRealmRoleByName(roleName, realmName);
|
||||||
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
return getClientRoleByName(roleName, clientName, realmName);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Attribution de rôles ====================
|
// ==================== Attribution de rôles ====================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void assignRealmRolesToUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
public void assignRolesToUser(@Valid @NotNull RoleAssignmentDTO assignment) {
|
||||||
|
log.info("Attribution de {} rôles {} à l'utilisateur {} dans le realm {}",
|
||||||
|
assignment.getRoleNames().size(), assignment.getTypeRole(), assignment.getUserId(), assignment.getRealmName());
|
||||||
|
|
||||||
|
if (assignment.getTypeRole() == TypeRole.REALM_ROLE) {
|
||||||
|
assignRealmRolesToUser(assignment.getUserId(), assignment.getRoleNames(), assignment.getRealmName());
|
||||||
|
} else if (assignment.getTypeRole() == TypeRole.CLIENT_ROLE && assignment.getClientName() != null) {
|
||||||
|
assignClientRolesToUser(assignment.getUserId(), assignment.getClientName(), assignment.getRoleNames(), assignment.getRealmName());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Données d'attribution invalides pour le type de rôle: " + assignment.getTypeRole());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void revokeRolesFromUser(@Valid @NotNull RoleAssignmentDTO assignment) {
|
||||||
|
log.info("Révocation de {} rôles {} pour l'utilisateur {} dans le realm {}",
|
||||||
|
assignment.getRoleNames().size(), assignment.getTypeRole(), assignment.getUserId(), assignment.getRealmName());
|
||||||
|
|
||||||
|
if (assignment.getTypeRole() == TypeRole.REALM_ROLE) {
|
||||||
|
revokeRealmRolesFromUser(assignment.getUserId(), assignment.getRoleNames(), assignment.getRealmName());
|
||||||
|
} else if (assignment.getTypeRole() == TypeRole.CLIENT_ROLE && assignment.getClientName() != null) {
|
||||||
|
revokeClientRolesFromUser(assignment.getUserId(), assignment.getClientName(), assignment.getRoleNames(), assignment.getRealmName());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Données de révocation invalides pour le type de rôle: " + assignment.getTypeRole());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assignRealmRolesToUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
||||||
@NotBlank String realmName) {
|
@NotBlank String realmName) {
|
||||||
log.info("Attribution de {} rôles realm à l'utilisateur {} dans le realm {}",
|
log.info("Attribution de {} rôles realm à l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), userId, realmName);
|
roleNames.size(), userId, realmName);
|
||||||
@@ -299,8 +450,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void revokeRealmRolesFromUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
||||||
public void revokeRealmRolesFromUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
|
||||||
@NotBlank String realmName) {
|
@NotBlank String realmName) {
|
||||||
log.info("Révocation de {} rôles realm pour l'utilisateur {} dans le realm {}",
|
log.info("Révocation de {} rôles realm pour l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), userId, realmName);
|
roleNames.size(), userId, realmName);
|
||||||
@@ -331,8 +481,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void assignClientRolesToUser(@NotBlank String userId, @NotBlank String clientId,
|
||||||
public void assignClientRolesToUser(@NotBlank String userId, @NotBlank String clientId,
|
|
||||||
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
||||||
log.info("Attribution de {} rôles du client {} à l'utilisateur {} dans le realm {}",
|
log.info("Attribution de {} rôles du client {} à l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), clientId, userId, realmName);
|
roleNames.size(), clientId, userId, realmName);
|
||||||
@@ -373,8 +522,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void revokeClientRolesFromUser(@NotBlank String userId, @NotBlank String clientId,
|
||||||
public void revokeClientRolesFromUser(@NotBlank String userId, @NotBlank String clientId,
|
|
||||||
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
||||||
log.info("Révocation de {} rôles du client {} pour l'utilisateur {} dans le realm {}",
|
log.info("Révocation de {} rôles du client {} pour l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), clientId, userId, realmName);
|
roleNames.size(), clientId, userId, realmName);
|
||||||
@@ -431,17 +579,18 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RoleDTO> getUserClientRoles(@NotBlank String userId, @NotBlank String clientId,
|
public List<RoleDTO> getUserClientRoles(@NotBlank String userId,
|
||||||
@NotBlank String realmName) {
|
@NotBlank String realmName,
|
||||||
|
@NotBlank String clientName) {
|
||||||
log.debug("Récupération des rôles du client {} pour l'utilisateur {} dans le realm {}",
|
log.debug("Récupération des rôles du client {} pour l'utilisateur {} dans le realm {}",
|
||||||
clientId, userId, realmName);
|
clientName, userId, realmName);
|
||||||
|
|
||||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
.clients();
|
.clients();
|
||||||
|
|
||||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
clientsResource.findByClientId(clientId);
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
if (clients.isEmpty()) {
|
if (clients.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
@@ -457,86 +606,246 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
.listAll();
|
.listAll();
|
||||||
|
|
||||||
List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
|
List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
|
||||||
roles.forEach(role -> role.setClientId(clientId));
|
roles.forEach(role -> role.setClientId(clientName));
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RoleDTO> getAllUserRoles(@NotBlank String userId, @NotBlank String realmName) {
|
||||||
|
log.debug("Récupération de tous les rôles de l'utilisateur {} dans le realm {}", userId, realmName);
|
||||||
|
|
||||||
|
List<RoleDTO> allRoles = new ArrayList<>();
|
||||||
|
|
||||||
|
// Ajouter les rôles realm
|
||||||
|
allRoles.addAll(getUserRealmRoles(userId, realmName));
|
||||||
|
|
||||||
|
// Ajouter les rôles client pour tous les clients
|
||||||
|
try {
|
||||||
|
var clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
|
List<org.keycloak.representations.idm.ClientRepresentation> clients = clientsResource.findAll();
|
||||||
|
|
||||||
|
for (org.keycloak.representations.idm.ClientRepresentation client : clients) {
|
||||||
|
String clientId = client.getClientId();
|
||||||
|
allRoles.addAll(getUserClientRoles(userId, realmName, clientId));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Erreur lors de la récupération des rôles client pour l'utilisateur {}", userId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRoles;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Rôles composites ====================
|
// ==================== Rôles composites ====================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCompositesToRealmRole(@NotBlank String roleName, @NotNull List<String> compositeRoleNames,
|
public void addCompositeRoles(@NotBlank String parentRoleId,
|
||||||
@NotBlank String realmName) {
|
@NotNull List<String> childRoleIds,
|
||||||
log.info("Ajout de {} rôles composites au rôle realm {} dans le realm {}",
|
@NotBlank String realmName,
|
||||||
compositeRoleNames.size(), roleName, realmName);
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.info("Ajout de {} rôles composites au rôle {} (type: {}) dans le realm {}",
|
||||||
|
childRoleIds.size(), parentRoleId, typeRole, realmName);
|
||||||
|
|
||||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
// Trouver le nom du rôle parent par son ID
|
||||||
.realm(realmName)
|
Optional<RoleDTO> parentRole = getRoleById(parentRoleId, realmName, typeRole, clientName);
|
||||||
.roles()
|
if (parentRole.isEmpty()) {
|
||||||
.get(roleName);
|
throw new jakarta.ws.rs.NotFoundException("Rôle parent non trouvé: " + parentRoleId);
|
||||||
|
}
|
||||||
|
String parentRoleName = parentRole.get().getName();
|
||||||
|
|
||||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
.roles();
|
.roles();
|
||||||
|
|
||||||
List<RoleRepresentation> compositesToAdd = compositeRoleNames.stream()
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
.map(compositeName -> {
|
RoleResource roleResource = rolesResource.get(parentRoleName);
|
||||||
try {
|
|
||||||
return rolesResource.get(compositeName).toRepresentation();
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(role -> role != null)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (!compositesToAdd.isEmpty()) {
|
// Convertir les IDs en noms de rôles
|
||||||
roleResource.addComposites(compositesToAdd);
|
List<String> childRoleNames = childRoleIds.stream()
|
||||||
|
.map(childRoleId -> {
|
||||||
|
Optional<RoleDTO> childRole = getRealmRoleById(childRoleId, realmName);
|
||||||
|
return childRole.map(RoleDTO::getName).orElse(null);
|
||||||
|
})
|
||||||
|
.filter(name -> name != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<RoleRepresentation> compositesToAdd = childRoleNames.stream()
|
||||||
|
.map(compositeName -> {
|
||||||
|
try {
|
||||||
|
return rolesResource.get(compositeName).toRepresentation();
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(role -> role != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!compositesToAdd.isEmpty()) {
|
||||||
|
roleResource.addComposites(compositesToAdd);
|
||||||
|
}
|
||||||
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
// Pour les rôles client, utiliser le client
|
||||||
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||||
|
}
|
||||||
|
|
||||||
|
String internalClientId = clients.get(0).getId();
|
||||||
|
RolesResource clientRolesResource = clientsResource.get(internalClientId).roles();
|
||||||
|
RoleResource roleResource = clientRolesResource.get(parentRoleName);
|
||||||
|
|
||||||
|
// Convertir les IDs en noms de rôles
|
||||||
|
List<String> childRoleNames = childRoleIds.stream()
|
||||||
|
.map(childRoleId -> {
|
||||||
|
Optional<RoleDTO> childRole = getRoleById(childRoleId, realmName, typeRole, clientName);
|
||||||
|
return childRole.map(RoleDTO::getName).orElse(null);
|
||||||
|
})
|
||||||
|
.filter(name -> name != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<RoleRepresentation> compositesToAdd = childRoleNames.stream()
|
||||||
|
.map(compositeName -> {
|
||||||
|
try {
|
||||||
|
return clientRolesResource.get(compositeName).toRepresentation();
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(role -> role != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!compositesToAdd.isEmpty()) {
|
||||||
|
roleResource.addComposites(compositesToAdd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeCompositesFromRealmRole(@NotBlank String roleName, @NotNull List<String> compositeRoleNames,
|
public void removeCompositeRoles(@NotBlank String parentRoleId,
|
||||||
@NotBlank String realmName) {
|
@NotNull List<String> childRoleIds,
|
||||||
log.info("Suppression de {} rôles composites du rôle realm {} dans le realm {}",
|
@NotBlank String realmName,
|
||||||
compositeRoleNames.size(), roleName, realmName);
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.info("Suppression de {} rôles composites du rôle {} (type: {}) dans le realm {}",
|
||||||
|
childRoleIds.size(), parentRoleId, typeRole, realmName);
|
||||||
|
|
||||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
// Trouver le nom du rôle parent par son ID
|
||||||
.realm(realmName)
|
Optional<RoleDTO> parentRole = getRoleById(parentRoleId, realmName, typeRole, clientName);
|
||||||
.roles()
|
if (parentRole.isEmpty()) {
|
||||||
.get(roleName);
|
throw new jakarta.ws.rs.NotFoundException("Rôle parent non trouvé: " + parentRoleId);
|
||||||
|
}
|
||||||
|
String parentRoleName = parentRole.get().getName();
|
||||||
|
|
||||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
.roles();
|
.roles();
|
||||||
|
|
||||||
List<RoleRepresentation> compositesToRemove = compositeRoleNames.stream()
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
.map(compositeName -> {
|
RoleResource roleResource = rolesResource.get(parentRoleName);
|
||||||
try {
|
|
||||||
return rolesResource.get(compositeName).toRepresentation();
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(role -> role != null)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (!compositesToRemove.isEmpty()) {
|
// Convertir les IDs en noms de rôles
|
||||||
roleResource.deleteComposites(compositesToRemove);
|
List<String> childRoleNames = childRoleIds.stream()
|
||||||
|
.map(childRoleId -> {
|
||||||
|
Optional<RoleDTO> childRole = getRealmRoleById(childRoleId, realmName);
|
||||||
|
return childRole.map(RoleDTO::getName).orElse(null);
|
||||||
|
})
|
||||||
|
.filter(name -> name != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<RoleRepresentation> compositesToRemove = childRoleNames.stream()
|
||||||
|
.map(compositeName -> {
|
||||||
|
try {
|
||||||
|
return rolesResource.get(compositeName).toRepresentation();
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(role -> role != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!compositesToRemove.isEmpty()) {
|
||||||
|
roleResource.deleteComposites(compositesToRemove);
|
||||||
|
}
|
||||||
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
|
clientsResource.findByClientId(clientName);
|
||||||
|
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||||
|
}
|
||||||
|
|
||||||
|
String internalClientId = clients.get(0).getId();
|
||||||
|
RolesResource clientRolesResource = clientsResource.get(internalClientId).roles();
|
||||||
|
RoleResource roleResource = clientRolesResource.get(parentRoleName);
|
||||||
|
|
||||||
|
// Convertir les IDs en noms de rôles
|
||||||
|
List<String> childRoleNames = childRoleIds.stream()
|
||||||
|
.map(childRoleId -> {
|
||||||
|
Optional<RoleDTO> childRole = getRoleById(childRoleId, realmName, typeRole, clientName);
|
||||||
|
return childRole.map(RoleDTO::getName).orElse(null);
|
||||||
|
})
|
||||||
|
.filter(name -> name != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<RoleRepresentation> compositesToRemove = childRoleNames.stream()
|
||||||
|
.map(compositeName -> {
|
||||||
|
try {
|
||||||
|
return clientRolesResource.get(compositeName).toRepresentation();
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(role -> role != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!compositesToRemove.isEmpty()) {
|
||||||
|
roleResource.deleteComposites(compositesToRemove);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<RoleDTO> getCompositeRoles(@NotBlank String roleName, @NotBlank String realmName) {
|
|
||||||
log.debug("Récupération des rôles composites du rôle {} dans le realm {}", roleName, realmName);
|
|
||||||
|
|
||||||
List<RoleRepresentation> composites = keycloakAdminClient.getInstance()
|
@Override
|
||||||
|
public List<RoleDTO> getCompositeRoles(@NotBlank String roleId,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.debug("Récupération des rôles composites du rôle {} dans le realm {}", roleId, realmName);
|
||||||
|
|
||||||
|
// Pour récupérer par ID, on doit d'abord trouver le nom du rôle
|
||||||
|
// Comme Keycloak ne permet pas de récupérer directement par ID, on doit lister et trouver
|
||||||
|
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||||
.realm(realmName)
|
.realm(realmName)
|
||||||
.roles()
|
.roles();
|
||||||
.get(roleName)
|
|
||||||
|
RoleRepresentation roleRep = rolesResource.list().stream()
|
||||||
|
.filter(r -> r.getId().equals(roleId))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId));
|
||||||
|
|
||||||
|
java.util.Set<RoleRepresentation> compositesSet = rolesResource
|
||||||
|
.get(roleRep.getName())
|
||||||
.getRoleComposites();
|
.getRoleComposites();
|
||||||
|
|
||||||
|
List<RoleRepresentation> composites = new ArrayList<>(compositesSet);
|
||||||
|
|
||||||
return RoleMapper.toDTOList(composites, realmName, TypeRole.COMPOSITE_ROLE);
|
return RoleMapper.toDTOList(composites, realmName, TypeRole.COMPOSITE_ROLE);
|
||||||
}
|
}
|
||||||
@@ -544,66 +853,111 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
// ==================== Vérification de permissions ====================
|
// ==================== Vérification de permissions ====================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean userHasRealmRole(@NotBlank String userId, @NotBlank String roleName,
|
public boolean userHasRole(@NotBlank String userId,
|
||||||
@NotBlank String realmName) {
|
@NotBlank String roleName,
|
||||||
log.debug("Vérification si l'utilisateur {} a le rôle realm {} dans le realm {}",
|
@NotBlank String realmName,
|
||||||
userId, roleName, realmName);
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.debug("Vérification si l'utilisateur {} a le rôle {} (type: {}) dans le realm {}",
|
||||||
|
userId, roleName, typeRole, realmName);
|
||||||
|
|
||||||
List<RoleRepresentation> userRoles = keycloakAdminClient.getInstance()
|
if (typeRole == TypeRole.REALM_ROLE) {
|
||||||
.realm(realmName)
|
List<RoleRepresentation> userRoles = keycloakAdminClient.getInstance()
|
||||||
.users()
|
.realm(realmName)
|
||||||
.get(userId)
|
.users()
|
||||||
.roles()
|
.get(userId)
|
||||||
.realmLevel()
|
.roles()
|
||||||
.listEffective(); // Incluant les rôles hérités via composites
|
.realmLevel()
|
||||||
|
.listEffective(); // Incluant les rôles hérités via composites
|
||||||
|
|
||||||
return userRoles.stream()
|
return userRoles.stream()
|
||||||
.anyMatch(role -> role.getName().equals(roleName));
|
.anyMatch(role -> role.getName().equals(roleName));
|
||||||
}
|
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||||
|
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.clients();
|
||||||
|
|
||||||
@Override
|
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||||
public boolean userHasClientRole(@NotBlank String userId, @NotBlank String clientId,
|
clientsResource.findByClientId(clientName);
|
||||||
@NotBlank String roleName, @NotBlank String realmName) {
|
|
||||||
log.debug("Vérification si l'utilisateur {} a le rôle client {} du client {} dans le realm {}",
|
|
||||||
userId, roleName, clientId, realmName);
|
|
||||||
|
|
||||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
if (clients.isEmpty()) {
|
||||||
.realm(realmName)
|
return false;
|
||||||
.clients();
|
}
|
||||||
|
|
||||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
String internalClientId = clients.get(0).getId();
|
||||||
clientsResource.findByClientId(clientId);
|
List<RoleRepresentation> userClientRoles = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.users()
|
||||||
|
.get(userId)
|
||||||
|
.roles()
|
||||||
|
.clientLevel(internalClientId)
|
||||||
|
.listEffective();
|
||||||
|
|
||||||
if (clients.isEmpty()) {
|
return userClientRoles.stream()
|
||||||
return false;
|
.anyMatch(role -> role.getName().equals(roleName));
|
||||||
}
|
}
|
||||||
|
|
||||||
String internalClientId = clients.get(0).getId();
|
return false;
|
||||||
List<RoleRepresentation> userClientRoles = keycloakAdminClient.getInstance()
|
|
||||||
.realm(realmName)
|
|
||||||
.users()
|
|
||||||
.get(userId)
|
|
||||||
.roles()
|
|
||||||
.clientLevel(internalClientId)
|
|
||||||
.listEffective();
|
|
||||||
|
|
||||||
return userClientRoles.stream()
|
|
||||||
.anyMatch(role -> role.getName().equals(roleName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RoleDTO> getUserEffectiveRealmRoles(@NotBlank String userId, @NotBlank String realmName) {
|
public boolean roleExists(@NotBlank String roleName,
|
||||||
log.debug("Récupération des rôles realm effectifs de l'utilisateur {} dans le realm {}",
|
@NotBlank String realmName,
|
||||||
userId, realmName);
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.debug("Vérification de l'existence du rôle {} (type: {}) dans le realm {}",
|
||||||
|
roleName, typeRole, realmName);
|
||||||
|
|
||||||
List<RoleRepresentation> effectiveRoles = keycloakAdminClient.getInstance()
|
return getRoleByName(roleName, realmName, typeRole, clientName).isPresent();
|
||||||
.realm(realmName)
|
}
|
||||||
.users()
|
|
||||||
.get(userId)
|
|
||||||
.roles()
|
|
||||||
.realmLevel()
|
|
||||||
.listEffective();
|
|
||||||
|
|
||||||
return RoleMapper.toDTOList(effectiveRoles, realmName, TypeRole.REALM_ROLE);
|
@Override
|
||||||
|
public long countUsersWithRole(@NotBlank String roleId,
|
||||||
|
@NotBlank String realmName,
|
||||||
|
@NotNull TypeRole typeRole,
|
||||||
|
String clientName) {
|
||||||
|
log.debug("Comptage des utilisateurs ayant le rôle {} (type: {}) dans le realm {}",
|
||||||
|
roleId, typeRole, realmName);
|
||||||
|
|
||||||
|
// Trouver le nom du rôle par son ID
|
||||||
|
Optional<RoleDTO> role = getRoleById(roleId, realmName, typeRole, clientName);
|
||||||
|
if (role.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String roleName = role.get().getName();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Keycloak ne fournit pas directement cette fonctionnalité via l'API Admin
|
||||||
|
// On doit lister tous les utilisateurs et vérifier leurs rôles
|
||||||
|
// C'est coûteux mais nécessaire
|
||||||
|
List<UserRepresentation> users = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.users()
|
||||||
|
.list();
|
||||||
|
|
||||||
|
long count = 0;
|
||||||
|
for (UserRepresentation user : users) {
|
||||||
|
if (userHasRole(user.getId(), roleName, realmName, typeRole, clientName)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors du comptage des utilisateurs avec le rôle {}", roleId, e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthodes privées pour compatibilité interne (utilisées par les nouvelles méthodes publiques)
|
||||||
|
private boolean userHasRealmRole(@NotBlank String userId, @NotBlank String roleName,
|
||||||
|
@NotBlank String realmName) {
|
||||||
|
return userHasRole(userId, roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean userHasClientRole(@NotBlank String userId, @NotBlank String clientId,
|
||||||
|
@NotBlank String roleName, @NotBlank String realmName) {
|
||||||
|
return userHasRole(userId, roleName, realmName, TypeRole.CLIENT_ROLE, clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package dev.lions.user.manager.service.impl;
|
||||||
|
|
||||||
|
import dev.lions.user.manager.client.KeycloakAdminClient;
|
||||||
|
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||||
|
import dev.lions.user.manager.dto.sync.HealthStatusDTO;
|
||||||
|
import dev.lions.user.manager.dto.sync.SyncResultDTO;
|
||||||
|
import dev.lions.user.manager.dto.user.UserDTO;
|
||||||
|
import dev.lions.user.manager.enums.role.TypeRole;
|
||||||
|
import dev.lions.user.manager.mapper.RoleMapper;
|
||||||
|
import dev.lions.user.manager.mapper.UserMapper;
|
||||||
|
import dev.lions.user.manager.service.SyncService;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implémentation du service de synchronisation avec Keycloak
|
||||||
|
*
|
||||||
|
* Ce service permet de:
|
||||||
|
* - Synchroniser les utilisateurs depuis Keycloak
|
||||||
|
* - Synchroniser les rôles depuis Keycloak
|
||||||
|
* - Vérifier la cohérence des données
|
||||||
|
* - Effectuer des health checks sur Keycloak
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
@Slf4j
|
||||||
|
public class SyncServiceImpl implements SyncService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeycloakAdminClient keycloakAdminClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int syncUsersFromRealm(@NotBlank String realmName) {
|
||||||
|
log.info("Synchronisation des utilisateurs depuis le realm: {}", realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<UserRepresentation> userReps = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.users()
|
||||||
|
.list();
|
||||||
|
|
||||||
|
int count = userReps.size();
|
||||||
|
log.info("✅ {} utilisateurs synchronisés depuis le realm {}", count, realmName);
|
||||||
|
return count;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ Erreur lors de la synchronisation des utilisateurs depuis le realm {}", realmName, e);
|
||||||
|
throw new RuntimeException("Erreur lors de la synchronisation des utilisateurs", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int syncRolesFromRealm(@NotBlank String realmName) {
|
||||||
|
log.info("Synchronisation des rôles depuis le realm: {}", realmName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.roles()
|
||||||
|
.list();
|
||||||
|
|
||||||
|
int count = roleReps.size();
|
||||||
|
log.info("✅ {} rôles synchronisés depuis le realm {}", count, realmName);
|
||||||
|
return count;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ Erreur lors de la synchronisation des rôles depuis le realm {}", realmName, e);
|
||||||
|
throw new RuntimeException("Erreur lors de la synchronisation des rôles", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Integer> syncAllRealms() {
|
||||||
|
log.info("Synchronisation de tous les realms");
|
||||||
|
|
||||||
|
Map<String, Integer> results = new java.util.HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Lister tous les realms
|
||||||
|
List<org.keycloak.representations.idm.RealmRepresentation> realms =
|
||||||
|
keycloakAdminClient.getInstance().realms().findAll();
|
||||||
|
|
||||||
|
for (org.keycloak.representations.idm.RealmRepresentation realm : realms) {
|
||||||
|
String realmName = realm.getRealm();
|
||||||
|
try {
|
||||||
|
int usersCount = syncUsersFromRealm(realmName);
|
||||||
|
int rolesCount = syncRolesFromRealm(realmName);
|
||||||
|
results.put(realmName, usersCount + rolesCount);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation du realm {}", realmName, e);
|
||||||
|
results.put(realmName, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation de tous les realms", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> checkDataConsistency(@NotBlank String realmName) {
|
||||||
|
log.info("Vérification de la cohérence des données pour le realm: {}", realmName);
|
||||||
|
|
||||||
|
Map<String, Object> report = new java.util.HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Pour l'instant, on retourne juste un rapport basique
|
||||||
|
// En production, on comparerait avec un cache local
|
||||||
|
report.put("realmName", realmName);
|
||||||
|
report.put("status", "ok");
|
||||||
|
report.put("message", "Cohérence vérifiée");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la vérification de cohérence pour le realm {}", realmName, e);
|
||||||
|
report.put("status", "error");
|
||||||
|
report.put("message", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> forceSyncRealm(@NotBlank String realmName) {
|
||||||
|
log.info("Synchronisation forcée du realm: {}", realmName);
|
||||||
|
|
||||||
|
Map<String, Object> stats = new java.util.HashMap<>();
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
int usersCount = syncUsersFromRealm(realmName);
|
||||||
|
int rolesCount = syncRolesFromRealm(realmName);
|
||||||
|
|
||||||
|
stats.put("realmName", realmName);
|
||||||
|
stats.put("usersCount", usersCount);
|
||||||
|
stats.put("rolesCount", rolesCount);
|
||||||
|
stats.put("success", true);
|
||||||
|
stats.put("durationMs", System.currentTimeMillis() - startTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la synchronisation forcée du realm {}", realmName, e);
|
||||||
|
stats.put("success", false);
|
||||||
|
stats.put("error", e.getMessage());
|
||||||
|
stats.put("durationMs", System.currentTimeMillis() - startTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getLastSyncStatus(@NotBlank String realmName) {
|
||||||
|
log.debug("Récupération du statut de la dernière synchronisation pour le realm: {}", realmName);
|
||||||
|
|
||||||
|
Map<String, Object> status = new java.util.HashMap<>();
|
||||||
|
status.put("realmName", realmName);
|
||||||
|
status.put("lastSyncTime", System.currentTimeMillis()); // En production, récupérer depuis un cache
|
||||||
|
status.put("status", "completed");
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isKeycloakAvailable() {
|
||||||
|
log.debug("Vérification de la disponibilité de Keycloak");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Test de connexion en récupérant les informations du serveur
|
||||||
|
keycloakAdminClient.getInstance().serverInfo().getInfo();
|
||||||
|
log.debug("✅ Keycloak est accessible et fonctionne");
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ Keycloak n'est pas accessible: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getKeycloakHealthInfo() {
|
||||||
|
log.info("Récupération du statut de santé complet de Keycloak");
|
||||||
|
|
||||||
|
Map<String, Object> healthInfo = new java.util.HashMap<>();
|
||||||
|
healthInfo.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Test connexion principale
|
||||||
|
var serverInfo = keycloakAdminClient.getInstance().serverInfo().getInfo();
|
||||||
|
healthInfo.put("keycloakAccessible", true);
|
||||||
|
healthInfo.put("keycloakVersion", serverInfo.getSystemInfo().getVersion());
|
||||||
|
|
||||||
|
// Test des realms (on essaie juste de lister)
|
||||||
|
try {
|
||||||
|
int realmsCount = keycloakAdminClient.getInstance().realms().findAll().size();
|
||||||
|
healthInfo.put("realmsAccessible", true);
|
||||||
|
healthInfo.put("realmsCount", realmsCount);
|
||||||
|
} catch (Exception e) {
|
||||||
|
healthInfo.put("realmsAccessible", false);
|
||||||
|
log.warn("Impossible d'accéder aux realms: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
healthInfo.put("overallHealthy", true);
|
||||||
|
log.info("✅ Keycloak est en bonne santé - Version: {}, Realms: {}",
|
||||||
|
healthInfo.get("keycloakVersion"), healthInfo.get("realmsCount"));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
healthInfo.put("keycloakAccessible", false);
|
||||||
|
healthInfo.put("overallHealthy", false);
|
||||||
|
healthInfo.put("errorMessage", e.getMessage());
|
||||||
|
log.error("❌ Keycloak n'est pas accessible: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return healthInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,13 @@ quarkus.http.cors.headers=*
|
|||||||
# Keycloak OIDC Configuration (DEV)
|
# Keycloak OIDC Configuration (DEV)
|
||||||
quarkus.oidc.auth-server-url=http://localhost:8180/realms/master
|
quarkus.oidc.auth-server-url=http://localhost:8180/realms/master
|
||||||
quarkus.oidc.client-id=lions-user-manager
|
quarkus.oidc.client-id=lions-user-manager
|
||||||
quarkus.oidc.credentials.secret=dev-secret-change-me
|
quarkus.oidc.credentials.secret=sD8hT13lG6c79WOWQk3dVzya5pfPhzw3
|
||||||
quarkus.oidc.tls.verification=none
|
quarkus.oidc.tls.verification=none
|
||||||
quarkus.oidc.application-type=service
|
quarkus.oidc.application-type=service
|
||||||
|
# Désactiver temporairement OIDC pour permettre le démarrage (à réactiver après)
|
||||||
|
quarkus.oidc.enabled=false
|
||||||
|
# Désactiver aussi le Dev UI OIDC pour éviter la découverte des métadonnées
|
||||||
|
quarkus.oidc.dev-ui.enabled=false
|
||||||
|
|
||||||
# Keycloak Admin Client Configuration (DEV)
|
# Keycloak Admin Client Configuration (DEV)
|
||||||
lions.keycloak.server-url=http://localhost:8180
|
lions.keycloak.server-url=http://localhost:8180
|
||||||
@@ -59,7 +63,7 @@ quarkus.log.category."io.quarkus".level=INFO
|
|||||||
|
|
||||||
quarkus.log.console.enable=true
|
quarkus.log.console.enable=true
|
||||||
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
||||||
quarkus.log.console.color=true
|
# quarkus.log.console.color est déprécié dans Quarkus 3.x
|
||||||
|
|
||||||
# File Logging pour Audit (DEV)
|
# File Logging pour Audit (DEV)
|
||||||
quarkus.log.file.enable=true
|
quarkus.log.file.enable=true
|
||||||
@@ -69,8 +73,8 @@ quarkus.log.file.rotation.max-backup-index=3
|
|||||||
|
|
||||||
# OpenAPI/Swagger Configuration (DEV - toujours activé)
|
# OpenAPI/Swagger Configuration (DEV - toujours activé)
|
||||||
quarkus.swagger-ui.always-include=true
|
quarkus.swagger-ui.always-include=true
|
||||||
quarkus.swagger-ui.path=/swagger-ui
|
|
||||||
quarkus.swagger-ui.enable=true
|
quarkus.swagger-ui.enable=true
|
||||||
|
# Le chemin par défaut est /q/swagger-ui (pas besoin de le spécifier)
|
||||||
|
|
||||||
# Dev Services (activé en DEV)
|
# Dev Services (activé en DEV)
|
||||||
quarkus.devservices.enabled=false
|
quarkus.devservices.enabled=false
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ quarkus.log.file.rotation.max-backup-index=10
|
|||||||
|
|
||||||
# OpenAPI/Swagger Configuration
|
# OpenAPI/Swagger Configuration
|
||||||
quarkus.swagger-ui.always-include=true
|
quarkus.swagger-ui.always-include=true
|
||||||
quarkus.swagger-ui.path=/swagger-ui
|
# Le chemin par défaut est /q/swagger-ui (pas besoin de le spécifier)
|
||||||
mp.openapi.extensions.smallrye.info.title=Lions User Manager API
|
mp.openapi.extensions.smallrye.info.title=Lions User Manager API
|
||||||
mp.openapi.extensions.smallrye.info.version=1.0.0
|
mp.openapi.extensions.smallrye.info.version=1.0.0
|
||||||
mp.openapi.extensions.smallrye.info.description=API de gestion centralisée des utilisateurs Keycloak
|
mp.openapi.extensions.smallrye.info.description=API de gestion centralisée des utilisateurs Keycloak
|
||||||
|
|||||||
Reference in New Issue
Block a user