feat: Module server-api initial
Module d'API pour lions-user-manager Contenu: - DTOs (User, Role, Audit, Search) - Enums (StatutUser, TypeRole, TypeActionAudit) - Service interfaces (UserService, RoleService, AuditService, SyncService) - ValidationConstants Statut: ✅ 100% complété 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
65
pom.xml
Normal file
65
pom.xml
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>dev.lions.user.manager</groupId>
|
||||
<artifactId>lions-user-manager-parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>lions-user-manager-server-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Lions User Manager - Server API</name>
|
||||
<description>Contrats API: DTOs, interfaces de services, enums et validations</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jakarta EE APIs -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.ws.rs</groupId>
|
||||
<artifactId>jakarta.ws.rs-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson for JSON -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenAPI annotations -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.openapi</groupId>
|
||||
<artifactId>microprofile-openapi-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
178
src/main/java/dev/lions/user/manager/dto/audit/AuditLogDTO.java
Normal file
178
src/main/java/dev/lions/user/manager/dto/audit/AuditLogDTO.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package dev.lions.user.manager.dto.audit;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import dev.lions.user.manager.dto.base.BaseDTO;
|
||||
import dev.lions.user.manager.enums.audit.TypeActionAudit;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* DTO représentant une entrée d'audit
|
||||
* Enregistre toutes les actions effectuées via l'API de gestion
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "Entrée d'audit des actions utilisateur")
|
||||
public class AuditLogDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Qui a fait l'action
|
||||
@Schema(description = "ID de l'utilisateur qui a effectué l'action", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479")
|
||||
private String acteurUserId;
|
||||
|
||||
@Schema(description = "Username de l'utilisateur qui a effectué l'action", example = "admin@lions.dev")
|
||||
private String acteurUsername;
|
||||
|
||||
@Schema(description = "Nom complet de l'acteur", example = "Admin Principal")
|
||||
private String acteurNomComplet;
|
||||
|
||||
@Schema(description = "Rôles de l'acteur au moment de l'action")
|
||||
private String acteurRoles;
|
||||
|
||||
// Quand
|
||||
@Schema(description = "Date et heure de l'action", example = "2025-01-15T10:30:00")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime dateAction;
|
||||
|
||||
// Quoi
|
||||
@Schema(description = "Type d'action effectuée", example = "USER_CREATE")
|
||||
private TypeActionAudit typeAction;
|
||||
|
||||
@Schema(description = "Type de ressource affectée", example = "USER")
|
||||
private String ressourceType;
|
||||
|
||||
@Schema(description = "ID de la ressource affectée", example = "a1b2c3d4-e5f6-7890-1234-567890abcdef")
|
||||
private String ressourceId;
|
||||
|
||||
@Schema(description = "Nom/Identifiant de la ressource", example = "jdupont")
|
||||
private String ressourceName;
|
||||
|
||||
// Où
|
||||
@Schema(description = "Nom du Realm", example = "btpxpress")
|
||||
private String realmName;
|
||||
|
||||
@Schema(description = "Adresse IP de l'acteur", example = "192.168.1.100")
|
||||
private String ipAddress;
|
||||
|
||||
@Schema(description = "User-Agent du client", example = "Mozilla/5.0...")
|
||||
private String userAgent;
|
||||
|
||||
@Schema(description = "Localisation géographique", example = "Abidjan, Côte d'Ivoire")
|
||||
private String geolocation;
|
||||
|
||||
// Comment
|
||||
@Schema(description = "Endpoint API appelé", example = "/api/users/create")
|
||||
private String apiEndpoint;
|
||||
|
||||
@Schema(description = "Méthode HTTP", example = "POST")
|
||||
private String httpMethod;
|
||||
|
||||
// Détails
|
||||
@Schema(description = "Description de l'action", example = "Création d'un nouvel utilisateur")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "Détails de l'action au format JSON")
|
||||
private String detailsJson;
|
||||
|
||||
@Schema(description = "Ancienne valeur (avant modification)")
|
||||
private String oldValue;
|
||||
|
||||
@Schema(description = "Nouvelle valeur (après modification)")
|
||||
private String newValue;
|
||||
|
||||
@Schema(description = "Différences entre ancienne et nouvelle valeur")
|
||||
private String diff;
|
||||
|
||||
// Résultat
|
||||
@Schema(description = "Succès de l'opération", example = "true")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "Code d'erreur (si échec)", example = "USER_ALREADY_EXISTS")
|
||||
private String errorCode;
|
||||
|
||||
@Schema(description = "Message d'erreur (si échec)")
|
||||
private String errorMessage;
|
||||
|
||||
@Schema(description = "Trace d'erreur complète (si échec)")
|
||||
private String stackTrace;
|
||||
|
||||
// Métadonnées
|
||||
@Schema(description = "Durée d'exécution en millisecondes", example = "145")
|
||||
private Long executionTimeMs;
|
||||
|
||||
@Schema(description = "ID de session/transaction", example = "sess_abc123")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description = "ID de corrélation (pour tracer requêtes liées)", example = "corr_xyz789")
|
||||
private String correlationId;
|
||||
|
||||
@Schema(description = "Raison de l'action", example = "Demande du manager")
|
||||
private String raison;
|
||||
|
||||
@Schema(description = "Commentaires administratifs", example = "Promotion suite à évaluation annuelle")
|
||||
private String commentaires;
|
||||
|
||||
@Schema(description = "Métadonnées supplémentaires")
|
||||
private Map<String, String> metadata;
|
||||
|
||||
// Flags
|
||||
@Schema(description = "Indique si l'action est critique", example = "false")
|
||||
private Boolean critique;
|
||||
|
||||
@Schema(description = "Indique si l'action nécessite une alerte", example = "false")
|
||||
private Boolean requiresAlert;
|
||||
|
||||
@Schema(description = "Indique si l'action a été notifiée", example = "true")
|
||||
private Boolean notified;
|
||||
|
||||
/**
|
||||
* Détermine si l'action a réussi
|
||||
* @return true si success = true
|
||||
*/
|
||||
public boolean isSuccessful() {
|
||||
return Boolean.TRUE.equals(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action a échoué
|
||||
* @return true si success = false
|
||||
*/
|
||||
public boolean isFailed() {
|
||||
return Boolean.FALSE.equals(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action est critique
|
||||
* @return true si critique = true ou si typeAction est critique
|
||||
*/
|
||||
public boolean isCritique() {
|
||||
return Boolean.TRUE.equals(critique) || (typeAction != null && typeAction.isCritical());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un résumé court de l'action
|
||||
* @return résumé
|
||||
*/
|
||||
public String getSummary() {
|
||||
return String.format("%s: %s effectué par %s sur %s %s",
|
||||
dateAction,
|
||||
typeAction != null ? typeAction.getLibelle() : "Action inconnue",
|
||||
acteurUsername != null ? acteurUsername : "Inconnu",
|
||||
ressourceType != null ? ressourceType : "Ressource",
|
||||
ressourceName != null ? ressourceName : ressourceId
|
||||
);
|
||||
}
|
||||
}
|
||||
47
src/main/java/dev/lions/user/manager/dto/base/BaseDTO.java
Normal file
47
src/main/java/dev/lions/user/manager/dto/base/BaseDTO.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package dev.lions.user.manager.dto.base;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* DTO de base pour tous les objets métier
|
||||
* Contient les attributs communs (id, dates, audit)
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "DTO de base contenant les attributs communs à tous les objets")
|
||||
public abstract class BaseDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "Identifiant unique (UUID Keycloak)", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "Date de création", example = "2025-01-15T10:30:00")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime dateCreation;
|
||||
|
||||
@Schema(description = "Date de dernière modification", example = "2025-01-15T14:20:00")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime dateModification;
|
||||
|
||||
@Schema(description = "Utilisateur ayant créé l'entité", example = "admin@lions.dev")
|
||||
private String creeParUsername;
|
||||
|
||||
@Schema(description = "Utilisateur ayant modifié l'entité", example = "superadmin@lions.dev")
|
||||
private String modifieParUsername;
|
||||
|
||||
@Schema(description = "Numéro de version pour gestion optimiste", example = "1")
|
||||
private Long version;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package dev.lions.user.manager.dto.role;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import dev.lions.user.manager.enums.role.TypeRole;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DTO pour assigner ou révoquer des rôles à un utilisateur
|
||||
* Utilisé dans les opérations d'attribution de rôles
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "Attribution ou révocation de rôles")
|
||||
public class RoleAssignmentDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank(message = "L'ID utilisateur est obligatoire")
|
||||
@Schema(description = "ID de l'utilisateur cible", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479", required = true)
|
||||
private String userId;
|
||||
|
||||
@Schema(description = "Username de l'utilisateur cible (optionnel)", example = "jdupont")
|
||||
private String username;
|
||||
|
||||
@NotEmpty(message = "Au moins un rôle doit être spécifié")
|
||||
@Schema(description = "Liste des noms de rôles à attribuer ou révoquer", required = true)
|
||||
private List<String> roleNames;
|
||||
|
||||
@Schema(description = "Liste des IDs de rôles à attribuer ou révoquer")
|
||||
private List<String> roleIds;
|
||||
|
||||
@NotNull(message = "Le type de rôle est obligatoire")
|
||||
@Schema(description = "Type de rôle", example = "REALM_ROLE", required = true)
|
||||
private TypeRole typeRole;
|
||||
|
||||
@Schema(description = "Nom du Realm", example = "btpxpress")
|
||||
private String realmName;
|
||||
|
||||
@Schema(description = "Nom du Client (requis si typeRole = CLIENT_ROLE)", example = "btpxpress-app")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "ID du Client (optionnel)")
|
||||
private String clientId;
|
||||
|
||||
@Schema(description = "Raison de l'attribution/révocation", example = "Promotion au poste de gestionnaire")
|
||||
private String raison;
|
||||
|
||||
@Schema(description = "Commentaires administratifs", example = "Demandé par le manager")
|
||||
private String commentaires;
|
||||
|
||||
@Schema(description = "Indique si c'est une attribution temporaire", example = "false")
|
||||
private Boolean temporaire;
|
||||
|
||||
@Schema(description = "Date d'expiration de l'attribution temporaire", example = "2025-12-31T23:59:59")
|
||||
private String dateExpiration;
|
||||
|
||||
@Schema(description = "Indique si les rôles composites doivent être inclus", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean includeComposites = true;
|
||||
|
||||
@Schema(description = "Indique si l'opération doit notifier l'utilisateur", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean notifyUser = false;
|
||||
|
||||
/**
|
||||
* Valide que les données nécessaires sont présentes pour un rôle client
|
||||
* @return true si valide
|
||||
*/
|
||||
public boolean isValidForClientRole() {
|
||||
return typeRole == TypeRole.CLIENT_ROLE && clientName != null && !clientName.isBlank();
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide que les données nécessaires sont présentes pour un rôle realm
|
||||
* @return true si valide
|
||||
*/
|
||||
public boolean isValidForRealmRole() {
|
||||
return typeRole == TypeRole.REALM_ROLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nombre de rôles à assigner/révoquer
|
||||
* @return nombre de rôles
|
||||
*/
|
||||
public int getRoleCount() {
|
||||
return roleNames != null ? roleNames.size() : 0;
|
||||
}
|
||||
}
|
||||
144
src/main/java/dev/lions/user/manager/dto/role/RoleDTO.java
Normal file
144
src/main/java/dev/lions/user/manager/dto/role/RoleDTO.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package dev.lions.user.manager.dto.role;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import dev.lions.user.manager.dto.base.BaseDTO;
|
||||
import dev.lions.user.manager.enums.role.TypeRole;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* DTO représentant un rôle Keycloak
|
||||
* Mappé depuis RoleRepresentation de Keycloak Admin API
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "Rôle Keycloak (Realm ou Client)")
|
||||
public class RoleDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank(message = "Le nom du rôle est obligatoire")
|
||||
@Size(min = 2, max = 100, message = "Le nom du rôle doit contenir entre 2 et 100 caractères")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_-]+$", message = "Le nom du rôle ne peut contenir que des lettres, chiffres, underscores et tirets")
|
||||
@Schema(description = "Nom du rôle", example = "admin_btpxpress", required = true)
|
||||
private String name;
|
||||
|
||||
@Schema(description = "Description du rôle", example = "Administrateur avec tous les privilèges")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "Type de rôle", example = "REALM_ROLE")
|
||||
private TypeRole typeRole;
|
||||
|
||||
@Schema(description = "Indique si c'est un rôle composite", example = "false")
|
||||
private Boolean composite;
|
||||
|
||||
@Schema(description = "ID du conteneur (Realm ou Client)", example = "btpxpress")
|
||||
private String containerId;
|
||||
|
||||
@Schema(description = "Nom du Realm", example = "btpxpress")
|
||||
private String realmName;
|
||||
|
||||
@Schema(description = "Nom du Client (si rôle client)", example = "btpxpress-app")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "ID du Client (si rôle client)")
|
||||
private String clientId;
|
||||
|
||||
@Schema(description = "Rôles composites inclus dans ce rôle")
|
||||
private List<String> compositeRoles;
|
||||
|
||||
@Schema(description = "Rôles Realm composites")
|
||||
private List<RoleCompositeDTO> compositeRealmRoles;
|
||||
|
||||
@Schema(description = "Rôles Client composites par client")
|
||||
private Map<String, List<RoleCompositeDTO>> compositeClientRoles;
|
||||
|
||||
@Schema(description = "Attributs personnalisés du rôle")
|
||||
private Map<String, List<String>> attributes;
|
||||
|
||||
@Schema(description = "Nombre d'utilisateurs ayant ce rôle", example = "15")
|
||||
private Integer userCount;
|
||||
|
||||
@Schema(description = "Indique si le rôle est un rôle système", example = "false")
|
||||
private Boolean systemRole;
|
||||
|
||||
@Schema(description = "Indique si le rôle peut être supprimé", example = "true")
|
||||
private Boolean deletable;
|
||||
|
||||
/**
|
||||
* Détermine si c'est un rôle Realm
|
||||
* @return true si typeRole est REALM_ROLE
|
||||
*/
|
||||
public boolean isRealmRole() {
|
||||
return typeRole == TypeRole.REALM_ROLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si c'est un rôle Client
|
||||
* @return true si typeRole est CLIENT_ROLE
|
||||
*/
|
||||
public boolean isClientRole() {
|
||||
return typeRole == TypeRole.CLIENT_ROLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si le rôle est composite
|
||||
* @return true si composite = true et a des rôles composites
|
||||
*/
|
||||
public boolean isComposite() {
|
||||
return Boolean.TRUE.equals(composite)
|
||||
&& ((compositeRoles != null && !compositeRoles.isEmpty())
|
||||
|| (compositeRealmRoles != null && !compositeRealmRoles.isEmpty())
|
||||
|| (compositeClientRoles != null && !compositeClientRoles.isEmpty()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nom complet du rôle (avec préfixe client si applicable)
|
||||
* @return nom complet
|
||||
*/
|
||||
public String getFullName() {
|
||||
if (isClientRole() && clientName != null) {
|
||||
return clientName + ":" + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO pour rôle composite
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder
|
||||
@Schema(description = "Rôle composite")
|
||||
public static class RoleCompositeDTO {
|
||||
@Schema(description = "ID du rôle", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "Nom du rôle", example = "gestionnaire")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "Description du rôle")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "Type de rôle", example = "REALM_ROLE")
|
||||
private TypeRole typeRole;
|
||||
|
||||
@Schema(description = "Nom du client (si client role)")
|
||||
private String clientName;
|
||||
}
|
||||
}
|
||||
209
src/main/java/dev/lions/user/manager/dto/user/UserDTO.java
Normal file
209
src/main/java/dev/lions/user/manager/dto/user/UserDTO.java
Normal file
@@ -0,0 +1,209 @@
|
||||
package dev.lions.user.manager.dto.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import dev.lions.user.manager.dto.base.BaseDTO;
|
||||
import dev.lions.user.manager.enums.user.StatutUser;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* DTO représentant un utilisateur Keycloak
|
||||
* Mappé depuis la représentation UserRepresentation de Keycloak Admin API
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "Utilisateur Keycloak")
|
||||
public class UserDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Informations de base
|
||||
@NotBlank(message = "Le nom d'utilisateur est obligatoire")
|
||||
@Size(min = 3, max = 100, message = "Le nom d'utilisateur doit contenir entre 3 et 100 caractères")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9._-]+$", message = "Le nom d'utilisateur ne peut contenir que des lettres, chiffres, points, tirets et underscores")
|
||||
@Schema(description = "Nom d'utilisateur unique", example = "jdupont", required = true)
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "L'email est obligatoire")
|
||||
@Email(message = "Format d'email invalide")
|
||||
@Schema(description = "Adresse email", example = "jean.dupont@lions.dev", required = true)
|
||||
private String email;
|
||||
|
||||
@Schema(description = "Email vérifié", example = "true")
|
||||
private Boolean emailVerified;
|
||||
|
||||
@NotBlank(message = "Le prénom est obligatoire")
|
||||
@Size(min = 2, max = 100, message = "Le prénom doit contenir entre 2 et 100 caractères")
|
||||
@Schema(description = "Prénom", example = "Jean", required = true)
|
||||
private String prenom;
|
||||
|
||||
@NotBlank(message = "Le nom est obligatoire")
|
||||
@Size(min = 2, max = 100, message = "Le nom doit contenir entre 2 et 100 caractères")
|
||||
@Schema(description = "Nom de famille", example = "Dupont", required = true)
|
||||
private String nom;
|
||||
|
||||
// Statut
|
||||
@Schema(description = "Statut de l'utilisateur", example = "ACTIF")
|
||||
private StatutUser statut;
|
||||
|
||||
@Schema(description = "Compte activé", example = "true")
|
||||
private Boolean enabled;
|
||||
|
||||
// Informations supplémentaires
|
||||
@Schema(description = "Numéro de téléphone", example = "+225 07 12 34 56 78")
|
||||
private String telephone;
|
||||
|
||||
@Schema(description = "Organisation/Entreprise", example = "Lions Dev")
|
||||
private String organisation;
|
||||
|
||||
@Schema(description = "Département", example = "IT")
|
||||
private String departement;
|
||||
|
||||
@Schema(description = "Fonction/Poste", example = "Développeur Senior")
|
||||
private String fonction;
|
||||
|
||||
@Schema(description = "Pays", example = "Côte d'Ivoire")
|
||||
private String pays;
|
||||
|
||||
@Schema(description = "Ville", example = "Abidjan")
|
||||
private String ville;
|
||||
|
||||
@Schema(description = "Langue préférée", example = "fr")
|
||||
private String langue;
|
||||
|
||||
@Schema(description = "Fuseau horaire", example = "Africa/Abidjan")
|
||||
private String timezone;
|
||||
|
||||
// Realm et rôles
|
||||
@Schema(description = "Realm Keycloak", example = "btpxpress")
|
||||
private String realmName;
|
||||
|
||||
@Schema(description = "Liste des rôles Realm assignés")
|
||||
private List<String> realmRoles;
|
||||
|
||||
@Schema(description = "Liste des rôles Client assignés")
|
||||
private Map<String, List<String>> clientRoles;
|
||||
|
||||
@Schema(description = "Liste des groupes auxquels appartient l'utilisateur")
|
||||
private List<String> groups;
|
||||
|
||||
// Dates importantes
|
||||
@Schema(description = "Date de dernière connexion", example = "2025-01-15T10:30:00")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime derniereConnexion;
|
||||
|
||||
@Schema(description = "Date d'expiration du compte", example = "2026-01-15T23:59:59")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime dateExpiration;
|
||||
|
||||
@Schema(description = "Date de verrouillage du compte", example = "2025-01-15T16:00:00")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime dateVerrouillage;
|
||||
|
||||
// Attributs personnalisés Keycloak
|
||||
@Schema(description = "Attributs personnalisés Keycloak")
|
||||
private Map<String, List<String>> attributes;
|
||||
|
||||
// Actions requises
|
||||
@Schema(description = "Actions requises (ex: UPDATE_PASSWORD, VERIFY_EMAIL)")
|
||||
private List<String> requiredActions;
|
||||
|
||||
// Fédération
|
||||
@Schema(description = "Fournisseur d'identité fédéré", example = "google")
|
||||
private String federatedIdentityProvider;
|
||||
|
||||
@Schema(description = "Lien d'identité fédérée")
|
||||
private List<FederatedIdentityDTO> federatedIdentities;
|
||||
|
||||
// Crédentiels temporaires
|
||||
@Schema(description = "Mot de passe temporaire (création uniquement)")
|
||||
private String temporaryPassword;
|
||||
|
||||
@Schema(description = "Indique si le mot de passe est temporaire")
|
||||
private Boolean temporaryPasswordFlag;
|
||||
|
||||
// Informations de session
|
||||
@Schema(description = "Nombre de sessions actives", example = "2")
|
||||
private Integer activeSessions;
|
||||
|
||||
@Schema(description = "Nombre d'échecs de connexion", example = "0")
|
||||
private Integer failedLoginAttempts;
|
||||
|
||||
// Audit
|
||||
@Schema(description = "Raison de la dernière modification")
|
||||
private String raisonModification;
|
||||
|
||||
@Schema(description = "Commentaires administratifs")
|
||||
private String commentaires;
|
||||
|
||||
/**
|
||||
* Retourne le nom complet de l'utilisateur
|
||||
* @return prénom + nom
|
||||
*/
|
||||
public String getNomComplet() {
|
||||
if (prenom != null && nom != null) {
|
||||
return prenom + " " + nom;
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'utilisateur est actif
|
||||
* @return true si statut ACTIF et enabled
|
||||
*/
|
||||
public boolean isActif() {
|
||||
return statut == StatutUser.ACTIF && Boolean.TRUE.equals(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si le compte a expiré
|
||||
* @return true si dateExpiration est passée
|
||||
*/
|
||||
public boolean isExpire() {
|
||||
return dateExpiration != null && dateExpiration.isBefore(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'utilisateur a des actions requises
|
||||
* @return true si des actions sont requises
|
||||
*/
|
||||
public boolean hasRequiredActions() {
|
||||
return requiredActions != null && !requiredActions.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO pour identité fédérée
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder
|
||||
@Schema(description = "Identité fédérée")
|
||||
public static class FederatedIdentityDTO {
|
||||
@Schema(description = "Fournisseur d'identité", example = "google")
|
||||
private String identityProvider;
|
||||
|
||||
@Schema(description = "ID utilisateur chez le fournisseur")
|
||||
private String userId;
|
||||
|
||||
@Schema(description = "Nom d'utilisateur chez le fournisseur")
|
||||
private String userName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package dev.lions.user.manager.dto.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import dev.lions.user.manager.enums.user.StatutUser;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Critères de recherche pour les utilisateurs
|
||||
* Utilisé pour filtrer les utilisateurs via l'API Keycloak Admin
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "Critères de recherche d'utilisateurs")
|
||||
public class UserSearchCriteriaDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Recherche textuelle
|
||||
@Schema(description = "Terme de recherche générale (username, email, nom, prénom)", example = "dupont")
|
||||
private String searchTerm;
|
||||
|
||||
@Schema(description = "Nom d'utilisateur exact", example = "jdupont")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "Email exact", example = "jean.dupont@lions.dev")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "Prénom", example = "Jean")
|
||||
private String prenom;
|
||||
|
||||
@Schema(description = "Nom de famille", example = "Dupont")
|
||||
private String nom;
|
||||
|
||||
// Filtres de statut
|
||||
@Schema(description = "Statut de l'utilisateur", example = "ACTIF")
|
||||
private StatutUser statut;
|
||||
|
||||
@Schema(description = "Compte activé", example = "true")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "Email vérifié", example = "true")
|
||||
private Boolean emailVerified;
|
||||
|
||||
// Filtres de rôle et groupe
|
||||
@Schema(description = "Liste des rôles Realm à filtrer")
|
||||
private List<String> realmRoles;
|
||||
|
||||
@Schema(description = "Liste des rôles Client à filtrer")
|
||||
private List<String> clientRoles;
|
||||
|
||||
@Schema(description = "Liste des groupes à filtrer")
|
||||
private List<String> groups;
|
||||
|
||||
@Schema(description = "Nom du client pour filtrer par rôles client", example = "btpxpress-app")
|
||||
private String clientName;
|
||||
|
||||
// Filtres organisationnels
|
||||
@Schema(description = "Organisation/Entreprise", example = "Lions Dev")
|
||||
private String organisation;
|
||||
|
||||
@Schema(description = "Département", example = "IT")
|
||||
private String departement;
|
||||
|
||||
@Schema(description = "Fonction/Poste", example = "Développeur")
|
||||
private String fonction;
|
||||
|
||||
@Schema(description = "Pays", example = "Côte d'Ivoire")
|
||||
private String pays;
|
||||
|
||||
@Schema(description = "Ville", example = "Abidjan")
|
||||
private String ville;
|
||||
|
||||
// Filtres temporels
|
||||
@Schema(description = "Date de création minimum", example = "2025-01-01T00:00:00")
|
||||
private LocalDateTime dateCreationMin;
|
||||
|
||||
@Schema(description = "Date de création maximum", example = "2025-12-31T23:59:59")
|
||||
private LocalDateTime dateCreationMax;
|
||||
|
||||
@Schema(description = "Date de dernière connexion minimum", example = "2025-01-01T00:00:00")
|
||||
private LocalDateTime derniereConnexionMin;
|
||||
|
||||
@Schema(description = "Date de dernière connexion maximum", example = "2025-01-31T23:59:59")
|
||||
private LocalDateTime derniereConnexionMax;
|
||||
|
||||
// Filtres spéciaux
|
||||
@Schema(description = "Utilisateurs avec actions requises uniquement", example = "true")
|
||||
private Boolean hasRequiredActions;
|
||||
|
||||
@Schema(description = "Utilisateurs verrouillés uniquement", example = "false")
|
||||
private Boolean isLocked;
|
||||
|
||||
@Schema(description = "Utilisateurs expirés uniquement", example = "false")
|
||||
private Boolean isExpired;
|
||||
|
||||
@Schema(description = "Utilisateurs avec sessions actives uniquement", example = "true")
|
||||
private Boolean hasActiveSessions;
|
||||
|
||||
// Realm
|
||||
@Schema(description = "Nom du Realm à filtrer", example = "btpxpress")
|
||||
private String realmName;
|
||||
|
||||
// Pagination
|
||||
@Schema(description = "Numéro de page (commence à 0)", example = "0", defaultValue = "0")
|
||||
@Builder.Default
|
||||
private Integer page = 0;
|
||||
|
||||
@Schema(description = "Taille de la page", example = "20", defaultValue = "20")
|
||||
@Builder.Default
|
||||
private Integer pageSize = 20;
|
||||
|
||||
@Schema(description = "Nombre maximum de résultats", example = "100")
|
||||
private Integer maxResults;
|
||||
|
||||
// Tri
|
||||
@Schema(description = "Champ de tri (username, email, prenom, nom, dateCreation, derniereConnexion)", example = "username")
|
||||
@Builder.Default
|
||||
private String sortBy = "username";
|
||||
|
||||
@Schema(description = "Ordre de tri (ASC ou DESC)", example = "ASC")
|
||||
@Builder.Default
|
||||
private String sortOrder = "ASC";
|
||||
|
||||
// Options d'inclusion
|
||||
@Schema(description = "Inclure les rôles dans les résultats", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean includeRoles = false;
|
||||
|
||||
@Schema(description = "Inclure les groupes dans les résultats", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean includeGroups = false;
|
||||
|
||||
@Schema(description = "Inclure les attributs personnalisés", example = "false")
|
||||
@Builder.Default
|
||||
private Boolean includeAttributes = false;
|
||||
|
||||
@Schema(description = "Inclure les informations de session", example = "false")
|
||||
@Builder.Default
|
||||
private Boolean includeSessionInfo = false;
|
||||
|
||||
/**
|
||||
* Détermine si des filtres de recherche sont appliqués
|
||||
* @return true si au moins un filtre est défini
|
||||
*/
|
||||
public boolean hasFilters() {
|
||||
return searchTerm != null
|
||||
|| username != null
|
||||
|| email != null
|
||||
|| prenom != null
|
||||
|| nom != null
|
||||
|| statut != null
|
||||
|| enabled != null
|
||||
|| emailVerified != null
|
||||
|| (realmRoles != null && !realmRoles.isEmpty())
|
||||
|| (clientRoles != null && !clientRoles.isEmpty())
|
||||
|| (groups != null && !groups.isEmpty())
|
||||
|| organisation != null
|
||||
|| departement != null
|
||||
|| fonction != null
|
||||
|| pays != null
|
||||
|| ville != null
|
||||
|| dateCreationMin != null
|
||||
|| dateCreationMax != null
|
||||
|| derniereConnexionMin != null
|
||||
|| derniereConnexionMax != null
|
||||
|| hasRequiredActions != null
|
||||
|| isLocked != null
|
||||
|| isExpired != null
|
||||
|| hasActiveSessions != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'offset pour la pagination Keycloak
|
||||
* @return offset calculé à partir de page et pageSize
|
||||
*/
|
||||
public int getOffset() {
|
||||
return page * pageSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package dev.lions.user.manager.dto.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Résultat paginé de recherche d'utilisateurs
|
||||
* Contient la liste des utilisateurs et les métadonnées de pagination
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "Résultat paginé de recherche d'utilisateurs")
|
||||
public class UserSearchResultDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "Liste des utilisateurs trouvés")
|
||||
private List<UserDTO> users;
|
||||
|
||||
@Schema(description = "Nombre total d'utilisateurs correspondant aux critères", example = "156")
|
||||
private Long totalCount;
|
||||
|
||||
@Schema(description = "Numéro de la page actuelle (commence à 0)", example = "0")
|
||||
private Integer currentPage;
|
||||
|
||||
@Schema(description = "Taille de la page", example = "20")
|
||||
private Integer pageSize;
|
||||
|
||||
@Schema(description = "Nombre total de pages", example = "8")
|
||||
private Integer totalPages;
|
||||
|
||||
@Schema(description = "Indique s'il y a une page suivante", example = "true")
|
||||
private Boolean hasNextPage;
|
||||
|
||||
@Schema(description = "Indique s'il y a une page précédente", example = "false")
|
||||
private Boolean hasPreviousPage;
|
||||
|
||||
@Schema(description = "Index du premier élément de la page", example = "0")
|
||||
private Integer firstElement;
|
||||
|
||||
@Schema(description = "Index du dernier élément de la page", example = "19")
|
||||
private Integer lastElement;
|
||||
|
||||
@Schema(description = "Indique si la page est vide", example = "false")
|
||||
private Boolean isEmpty;
|
||||
|
||||
@Schema(description = "Indique si c'est la première page", example = "true")
|
||||
private Boolean isFirstPage;
|
||||
|
||||
@Schema(description = "Indique si c'est la dernière page", example = "false")
|
||||
private Boolean isLastPage;
|
||||
|
||||
@Schema(description = "Critères de recherche utilisés")
|
||||
private UserSearchCriteriaDTO criteria;
|
||||
|
||||
@Schema(description = "Temps d'exécution de la recherche en millisecondes", example = "145")
|
||||
private Long executionTimeMs;
|
||||
|
||||
/**
|
||||
* Construit un résultat de recherche à partir d'une liste d'utilisateurs
|
||||
* @param users liste des utilisateurs
|
||||
* @param criteria critères de recherche
|
||||
* @param totalCount nombre total de résultats
|
||||
* @return UserSearchResultDTO
|
||||
*/
|
||||
public static UserSearchResultDTO of(List<UserDTO> users, UserSearchCriteriaDTO criteria, Long totalCount) {
|
||||
int pageSize = criteria.getPageSize();
|
||||
int currentPage = criteria.getPage();
|
||||
long totalPages = (totalCount + pageSize - 1) / pageSize;
|
||||
|
||||
return UserSearchResultDTO.builder()
|
||||
.users(users)
|
||||
.totalCount(totalCount)
|
||||
.currentPage(currentPage)
|
||||
.pageSize(pageSize)
|
||||
.totalPages((int) totalPages)
|
||||
.hasNextPage(currentPage < totalPages - 1)
|
||||
.hasPreviousPage(currentPage > 0)
|
||||
.firstElement(currentPage * pageSize)
|
||||
.lastElement(Math.min((currentPage + 1) * pageSize - 1, totalCount.intValue()))
|
||||
.isEmpty(users == null || users.isEmpty())
|
||||
.isFirstPage(currentPage == 0)
|
||||
.isLastPage(currentPage >= totalPages - 1)
|
||||
.criteria(criteria)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nombre d'utilisateurs dans la page courante
|
||||
* @return nombre d'utilisateurs
|
||||
*/
|
||||
public int getCurrentPageSize() {
|
||||
return users != null ? users.size() : 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package dev.lions.user.manager.enums.audit;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* Type d'action effectuée sur une ressource
|
||||
* Utilisé pour l'audit trail
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@Schema(description = "Type d'action pour l'audit")
|
||||
public enum TypeActionAudit {
|
||||
|
||||
// Actions Utilisateur
|
||||
USER_CREATE("Création utilisateur", "USER", "CREATE"),
|
||||
USER_UPDATE("Modification utilisateur", "USER", "UPDATE"),
|
||||
USER_DELETE("Suppression utilisateur", "USER", "DELETE"),
|
||||
USER_ACTIVATE("Activation utilisateur", "USER", "ACTIVATE"),
|
||||
USER_DEACTIVATE("Désactivation utilisateur", "USER", "DEACTIVATE"),
|
||||
USER_SUSPEND("Suspension utilisateur", "USER", "SUSPEND"),
|
||||
USER_UNLOCK("Déverrouillage utilisateur", "USER", "UNLOCK"),
|
||||
USER_PASSWORD_RESET("Réinitialisation mot de passe", "USER", "PASSWORD_RESET"),
|
||||
USER_EMAIL_VERIFY("Vérification email", "USER", "EMAIL_VERIFY"),
|
||||
USER_FORCE_LOGOUT("Déconnexion forcée", "USER", "FORCE_LOGOUT"),
|
||||
|
||||
// Actions Rôle
|
||||
ROLE_CREATE("Création rôle", "ROLE", "CREATE"),
|
||||
ROLE_UPDATE("Modification rôle", "ROLE", "UPDATE"),
|
||||
ROLE_DELETE("Suppression rôle", "ROLE", "DELETE"),
|
||||
ROLE_ASSIGN("Attribution rôle", "ROLE", "ASSIGN"),
|
||||
ROLE_REVOKE("Révocation rôle", "ROLE", "REVOKE"),
|
||||
ROLE_ADD_COMPOSITE("Ajout rôle composite", "ROLE", "ADD_COMPOSITE"),
|
||||
ROLE_REMOVE_COMPOSITE("Retrait rôle composite", "ROLE", "REMOVE_COMPOSITE"),
|
||||
|
||||
// Actions Groupe
|
||||
GROUP_CREATE("Création groupe", "GROUP", "CREATE"),
|
||||
GROUP_UPDATE("Modification groupe", "GROUP", "UPDATE"),
|
||||
GROUP_DELETE("Suppression groupe", "GROUP", "DELETE"),
|
||||
GROUP_ADD_MEMBER("Ajout membre groupe", "GROUP", "ADD_MEMBER"),
|
||||
GROUP_REMOVE_MEMBER("Retrait membre groupe", "GROUP", "REMOVE_MEMBER"),
|
||||
|
||||
// Actions Realm
|
||||
REALM_SYNC("Synchronisation realm", "REALM", "SYNC"),
|
||||
REALM_EXPORT("Export realm", "REALM", "EXPORT"),
|
||||
REALM_IMPORT("Import realm", "REALM", "IMPORT"),
|
||||
|
||||
// Actions Session
|
||||
SESSION_CREATE("Création session", "SESSION", "CREATE"),
|
||||
SESSION_DELETE("Suppression session", "SESSION", "DELETE"),
|
||||
SESSION_REVOKE_ALL("Révocation toutes sessions", "SESSION", "REVOKE_ALL"),
|
||||
|
||||
// Actions Système
|
||||
SYSTEM_BACKUP("Sauvegarde système", "SYSTEM", "BACKUP"),
|
||||
SYSTEM_RESTORE("Restauration système", "SYSTEM", "RESTORE"),
|
||||
SYSTEM_CONFIG_CHANGE("Modification configuration", "SYSTEM", "CONFIG_CHANGE");
|
||||
|
||||
private final String libelle;
|
||||
private final String ressourceType;
|
||||
private final String actionType;
|
||||
|
||||
/**
|
||||
* Détermine si l'action concerne un utilisateur
|
||||
*/
|
||||
public boolean isUserAction() {
|
||||
return ressourceType.equals("USER");
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action concerne un rôle
|
||||
*/
|
||||
public boolean isRoleAction() {
|
||||
return ressourceType.equals("ROLE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action est une création
|
||||
*/
|
||||
public boolean isCreateAction() {
|
||||
return actionType.equals("CREATE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action est une modification
|
||||
*/
|
||||
public boolean isUpdateAction() {
|
||||
return actionType.equals("UPDATE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action est une suppression
|
||||
*/
|
||||
public boolean isDeleteAction() {
|
||||
return actionType.equals("DELETE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'action est critique (nécessite alerte)
|
||||
*/
|
||||
public boolean isCritical() {
|
||||
return this == USER_DELETE
|
||||
|| this == ROLE_DELETE
|
||||
|| this == USER_SUSPEND
|
||||
|| this == SESSION_REVOKE_ALL
|
||||
|| this == SYSTEM_RESTORE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package dev.lions.user.manager.enums.role;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* Type de rôle dans Keycloak
|
||||
* Distingue les rôles au niveau Realm vs Client
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@Schema(description = "Type de rôle Keycloak")
|
||||
public enum TypeRole {
|
||||
|
||||
/**
|
||||
* Rôle global au niveau du Realm
|
||||
* Applicable à tous les clients du realm
|
||||
*/
|
||||
REALM_ROLE("Realm Role", "Rôle global applicable à tous les clients du realm", "realm-role"),
|
||||
|
||||
/**
|
||||
* Rôle spécifique à un client
|
||||
* Limité au scope d'un client particulier
|
||||
*/
|
||||
CLIENT_ROLE("Client Role", "Rôle spécifique à un client particulier", "client-role"),
|
||||
|
||||
/**
|
||||
* Rôle composite (contient d'autres rôles)
|
||||
*/
|
||||
COMPOSITE_ROLE("Composite Role", "Rôle composite contenant d'autres rôles", "composite-role");
|
||||
|
||||
private final String libelle;
|
||||
private final String description;
|
||||
private final String codeKeycloak;
|
||||
|
||||
/**
|
||||
* Détermine si le rôle est au niveau realm
|
||||
* @return true si c'est un realm role
|
||||
*/
|
||||
public boolean isRealmRole() {
|
||||
return this == REALM_ROLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si le rôle est au niveau client
|
||||
* @return true si c'est un client role
|
||||
*/
|
||||
public boolean isClientRole() {
|
||||
return this == CLIENT_ROLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si le rôle est composite
|
||||
* @return true si c'est un composite role
|
||||
*/
|
||||
public boolean isComposite() {
|
||||
return this == COMPOSITE_ROLE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package dev.lions.user.manager.enums.user;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* Statut d'un utilisateur dans Keycloak
|
||||
* Mappé depuis le champ "enabled" et attributs personnalisés
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@Schema(description = "Statut d'un utilisateur")
|
||||
public enum StatutUser {
|
||||
|
||||
/**
|
||||
* Utilisateur actif et opérationnel
|
||||
*/
|
||||
ACTIF("Actif", "Utilisateur actif avec accès complet", true),
|
||||
|
||||
/**
|
||||
* Utilisateur désactivé temporairement (peut être réactivé)
|
||||
*/
|
||||
INACTIF("Inactif", "Utilisateur désactivé temporairement", false),
|
||||
|
||||
/**
|
||||
* Utilisateur suspendu suite à une action administrative
|
||||
*/
|
||||
SUSPENDU("Suspendu", "Compte suspendu par un administrateur", false),
|
||||
|
||||
/**
|
||||
* Utilisateur en attente de validation
|
||||
*/
|
||||
EN_ATTENTE("En attente", "Compte en attente de validation", false),
|
||||
|
||||
/**
|
||||
* Utilisateur verrouillé suite à des tentatives échouées
|
||||
*/
|
||||
VERROUILLE("Verrouillé", "Compte verrouillé suite à plusieurs échecs d'authentification", false),
|
||||
|
||||
/**
|
||||
* Utilisateur dont le compte a expiré
|
||||
*/
|
||||
EXPIRE("Expiré", "Compte expiré et nécessite une réactivation", false),
|
||||
|
||||
/**
|
||||
* Utilisateur supprimé (soft delete)
|
||||
*/
|
||||
SUPPRIME("Supprimé", "Compte supprimé logiquement", false);
|
||||
|
||||
private final String libelle;
|
||||
private final String description;
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Convertit un statut Keycloak "enabled" en StatutUser
|
||||
* @param enabled état enabled de Keycloak
|
||||
* @return ACTIF si enabled=true, INACTIF sinon
|
||||
*/
|
||||
public static StatutUser fromEnabled(boolean enabled) {
|
||||
return enabled ? ACTIF : INACTIF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'utilisateur peut se connecter
|
||||
* @return true si le statut permet la connexion
|
||||
*/
|
||||
public boolean peutSeConnecter() {
|
||||
return this == ACTIF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine si l'utilisateur peut être réactivé
|
||||
* @return true si le statut permet la réactivation
|
||||
*/
|
||||
public boolean peutEtreReactive() {
|
||||
return this == INACTIF || this == SUSPENDU || this == EXPIRE;
|
||||
}
|
||||
}
|
||||
219
src/main/java/dev/lions/user/manager/service/AuditService.java
Normal file
219
src/main/java/dev/lions/user/manager/service/AuditService.java
Normal file
@@ -0,0 +1,219 @@
|
||||
package dev.lions.user.manager.service;
|
||||
|
||||
import dev.lions.user.manager.dto.audit.AuditLogDTO;
|
||||
import dev.lions.user.manager.enums.audit.TypeActionAudit;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Service de gestion des logs d'audit
|
||||
* Enregistre toutes les actions effectuées via l'API
|
||||
*/
|
||||
public interface AuditService {
|
||||
|
||||
/**
|
||||
* Enregistre une entrée d'audit
|
||||
* @param auditLog entrée d'audit
|
||||
* @return entrée enregistrée avec son ID
|
||||
*/
|
||||
AuditLogDTO logAction(@Valid @NotNull AuditLogDTO auditLog);
|
||||
|
||||
/**
|
||||
* Enregistre une action réussie
|
||||
* @param typeAction type d'action
|
||||
* @param ressourceType type de ressource
|
||||
* @param ressourceId ID de la ressource
|
||||
* @param ressourceName nom de la ressource
|
||||
* @param realmName nom du realm
|
||||
* @param acteurUserId ID de l'acteur
|
||||
* @param description description
|
||||
*/
|
||||
void logSuccess(@NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String ressourceType,
|
||||
String ressourceId,
|
||||
String ressourceName,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String acteurUserId,
|
||||
String description);
|
||||
|
||||
/**
|
||||
* Enregistre une action échouée
|
||||
* @param typeAction type d'action
|
||||
* @param ressourceType type de ressource
|
||||
* @param ressourceId ID de la ressource
|
||||
* @param ressourceName nom de la ressource
|
||||
* @param realmName nom du realm
|
||||
* @param acteurUserId ID de l'acteur
|
||||
* @param errorCode code d'erreur
|
||||
* @param errorMessage message d'erreur
|
||||
*/
|
||||
void logFailure(@NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String ressourceType,
|
||||
String ressourceId,
|
||||
String ressourceName,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String acteurUserId,
|
||||
String errorCode,
|
||||
String errorMessage);
|
||||
|
||||
/**
|
||||
* Recherche les logs d'audit par utilisateur acteur
|
||||
* @param acteurUserId ID de l'utilisateur acteur
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste des logs
|
||||
*/
|
||||
List<AuditLogDTO> findByActeur(@NotBlank String acteurUserId,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize);
|
||||
|
||||
/**
|
||||
* Recherche les logs d'audit par ressource
|
||||
* @param ressourceType type de ressource
|
||||
* @param ressourceId ID de la ressource
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste des logs
|
||||
*/
|
||||
List<AuditLogDTO> findByRessource(@NotBlank String ressourceType,
|
||||
@NotBlank String ressourceId,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize);
|
||||
|
||||
/**
|
||||
* Recherche les logs d'audit par type d'action
|
||||
* @param typeAction type d'action
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste des logs
|
||||
*/
|
||||
List<AuditLogDTO> findByTypeAction(@NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize);
|
||||
|
||||
/**
|
||||
* Recherche les logs d'audit par realm
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste des logs
|
||||
*/
|
||||
List<AuditLogDTO> findByRealm(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize);
|
||||
|
||||
/**
|
||||
* Recherche les actions échouées
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste des logs d'échec
|
||||
*/
|
||||
List<AuditLogDTO> findFailures(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize);
|
||||
|
||||
/**
|
||||
* Recherche les actions critiques
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste des logs critiques
|
||||
*/
|
||||
List<AuditLogDTO> findCriticalActions(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize);
|
||||
|
||||
/**
|
||||
* Compte les actions par type
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @return map type d'action -> nombre
|
||||
*/
|
||||
Map<TypeActionAudit, Long> countByActionType(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin);
|
||||
|
||||
/**
|
||||
* Compte les actions par utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @return map username -> nombre d'actions
|
||||
*/
|
||||
Map<String, Long> countByActeur(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin);
|
||||
|
||||
/**
|
||||
* Compte les actions réussies vs échouées
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @return map "success" -> count, "failure" -> count
|
||||
*/
|
||||
Map<String, Long> countSuccessVsFailure(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin);
|
||||
|
||||
/**
|
||||
* Exporte les logs d'audit au format CSV
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @return contenu CSV
|
||||
*/
|
||||
String exportToCSV(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin);
|
||||
|
||||
/**
|
||||
* Supprime les logs d'audit plus anciens qu'une date
|
||||
* @param dateLimite date limite (logs antérieurs seront supprimés)
|
||||
* @return nombre de logs supprimés
|
||||
*/
|
||||
long purgeOldLogs(@NotNull LocalDateTime dateLimite);
|
||||
|
||||
/**
|
||||
* Récupère les statistiques d'audit pour un dashboard
|
||||
* @param realmName nom du realm
|
||||
* @param dateDebut date de début
|
||||
* @param dateFin date de fin
|
||||
* @return map de statistiques
|
||||
*/
|
||||
Map<String, Object> getAuditStatistics(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin);
|
||||
}
|
||||
226
src/main/java/dev/lions/user/manager/service/RoleService.java
Normal file
226
src/main/java/dev/lions/user/manager/service/RoleService.java
Normal file
@@ -0,0 +1,226 @@
|
||||
package dev.lions.user.manager.service;
|
||||
|
||||
import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
|
||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.enums.role.TypeRole;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Service de gestion des rôles Keycloak
|
||||
* Utilise uniquement l'API Admin Keycloak (AUCUN accès direct à la DB)
|
||||
*/
|
||||
public interface RoleService {
|
||||
|
||||
/**
|
||||
* Récupère tous les rôles d'un realm
|
||||
* @param realmName nom du realm
|
||||
* @return liste des rôles
|
||||
*/
|
||||
List<RoleDTO> getAllRealmRoles(@NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Récupère tous les rôles d'un client
|
||||
* @param realmName nom du realm
|
||||
* @param clientName nom du client
|
||||
* @return liste des rôles
|
||||
*/
|
||||
List<RoleDTO> getAllClientRoles(@NotBlank String realmName, @NotBlank String clientName);
|
||||
|
||||
/**
|
||||
* Récupère un rôle par son ID
|
||||
* @param roleId ID du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle (REALM ou CLIENT)
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return rôle ou Optional vide
|
||||
*/
|
||||
Optional<RoleDTO> getRoleById(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Récupère un rôle par son nom
|
||||
* @param roleName nom du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle (REALM ou CLIENT)
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return rôle ou Optional vide
|
||||
*/
|
||||
Optional<RoleDTO> getRoleByName(@NotBlank String roleName,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Crée un nouveau rôle realm
|
||||
* @param role données du rôle
|
||||
* @param realmName nom du realm
|
||||
* @return rôle créé
|
||||
*/
|
||||
RoleDTO createRealmRole(@Valid @NotNull RoleDTO role, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Crée un nouveau rôle client
|
||||
* @param role données du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param clientName nom du client
|
||||
* @return rôle créé
|
||||
*/
|
||||
RoleDTO createClientRole(@Valid @NotNull RoleDTO role,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String clientName);
|
||||
|
||||
/**
|
||||
* Met à jour un rôle
|
||||
* @param roleId ID du rôle
|
||||
* @param role données modifiées
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return rôle mis à jour
|
||||
*/
|
||||
RoleDTO updateRole(@NotBlank String roleId,
|
||||
@Valid @NotNull RoleDTO role,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Supprime un rôle
|
||||
* @param roleId ID du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
*/
|
||||
void deleteRole(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Assigne des rôles à un utilisateur
|
||||
* @param assignment données d'attribution
|
||||
*/
|
||||
void assignRolesToUser(@Valid @NotNull RoleAssignmentDTO assignment);
|
||||
|
||||
/**
|
||||
* Révoque des rôles d'un utilisateur
|
||||
* @param assignment données de révocation
|
||||
*/
|
||||
void revokeRolesFromUser(@Valid @NotNull RoleAssignmentDTO assignment);
|
||||
|
||||
/**
|
||||
* Récupère les rôles realm d'un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @return liste des rôles
|
||||
*/
|
||||
List<RoleDTO> getUserRealmRoles(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Récupère les rôles client d'un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @param clientName nom du client
|
||||
* @return liste des rôles
|
||||
*/
|
||||
List<RoleDTO> getUserClientRoles(@NotBlank String userId,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String clientName);
|
||||
|
||||
/**
|
||||
* Récupère tous les rôles d'un utilisateur (realm + clients)
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @return liste des rôles
|
||||
*/
|
||||
List<RoleDTO> getAllUserRoles(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Ajoute un rôle composite
|
||||
* @param parentRoleId ID du rôle parent
|
||||
* @param childRoleIds IDs des rôles enfants à ajouter
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type du rôle parent
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
*/
|
||||
void addCompositeRoles(@NotBlank String parentRoleId,
|
||||
@NotNull List<String> childRoleIds,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Retire un rôle composite
|
||||
* @param parentRoleId ID du rôle parent
|
||||
* @param childRoleIds IDs des rôles enfants à retirer
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type du rôle parent
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
*/
|
||||
void removeCompositeRoles(@NotBlank String parentRoleId,
|
||||
@NotNull List<String> childRoleIds,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Récupère les rôles composites d'un rôle
|
||||
* @param roleId ID du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return liste des rôles composites
|
||||
*/
|
||||
List<RoleDTO> getCompositeRoles(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Compte le nombre d'utilisateurs ayant un rôle
|
||||
* @param roleId ID du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return nombre d'utilisateurs
|
||||
*/
|
||||
long countUsersWithRole(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Vérifie si un rôle existe
|
||||
* @param roleName nom du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return true si existe
|
||||
*/
|
||||
boolean roleExists(@NotBlank String roleName,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
|
||||
/**
|
||||
* Vérifie si un utilisateur a un rôle spécifique
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param roleName nom du rôle
|
||||
* @param realmName nom du realm
|
||||
* @param typeRole type de rôle
|
||||
* @param clientName nom du client (si CLIENT_ROLE)
|
||||
* @return true si l'utilisateur a le rôle
|
||||
*/
|
||||
boolean userHasRole(@NotBlank String userId,
|
||||
@NotBlank String roleName,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package dev.lions.user.manager.service;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Service de synchronisation avec Keycloak
|
||||
* Permet la synchronisation des données entre différents realms
|
||||
*/
|
||||
public interface SyncService {
|
||||
|
||||
/**
|
||||
* Synchronise les utilisateurs d'un realm
|
||||
* @param realmName nom du realm à synchroniser
|
||||
* @return nombre d'utilisateurs synchronisés
|
||||
*/
|
||||
int syncUsersFromRealm(@NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Synchronise les rôles d'un realm
|
||||
* @param realmName nom du realm à synchroniser
|
||||
* @return nombre de rôles synchronisés
|
||||
*/
|
||||
int syncRolesFromRealm(@NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Synchronise tous les realms configurés
|
||||
* @return map realm -> nombre d'éléments synchronisés
|
||||
*/
|
||||
Map<String, Integer> syncAllRealms();
|
||||
|
||||
/**
|
||||
* Vérifie la cohérence des données entre cache local et Keycloak
|
||||
* @param realmName nom du realm
|
||||
* @return rapport de cohérence
|
||||
*/
|
||||
Map<String, Object> checkDataConsistency(@NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Force la resynchronisation complète d'un realm
|
||||
* @param realmName nom du realm
|
||||
* @return statistiques de synchronisation
|
||||
*/
|
||||
Map<String, Object> forceSyncRealm(@NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Récupère le statut de la dernière synchronisation
|
||||
* @param realmName nom du realm
|
||||
* @return statut de synchronisation
|
||||
*/
|
||||
Map<String, Object> getLastSyncStatus(@NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Vérifie la disponibilité de Keycloak
|
||||
* @return true si Keycloak est disponible
|
||||
*/
|
||||
boolean isKeycloakAvailable();
|
||||
|
||||
/**
|
||||
* Récupère les informations de santé de Keycloak
|
||||
* @return informations de santé
|
||||
*/
|
||||
Map<String, Object> getKeycloakHealthInfo();
|
||||
}
|
||||
185
src/main/java/dev/lions/user/manager/service/UserService.java
Normal file
185
src/main/java/dev/lions/user/manager/service/UserService.java
Normal file
@@ -0,0 +1,185 @@
|
||||
package dev.lions.user.manager.service;
|
||||
|
||||
import dev.lions.user.manager.dto.user.UserDTO;
|
||||
import dev.lions.user.manager.dto.user.UserSearchCriteriaDTO;
|
||||
import dev.lions.user.manager.dto.user.UserSearchResultDTO;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Service de gestion des utilisateurs Keycloak
|
||||
* Utilise uniquement l'API Admin Keycloak (AUCUN accès direct à la DB)
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* Recherche des utilisateurs selon des critères
|
||||
* @param criteria critères de recherche
|
||||
* @return résultat paginé
|
||||
*/
|
||||
UserSearchResultDTO searchUsers(@Valid @NotNull UserSearchCriteriaDTO criteria);
|
||||
|
||||
/**
|
||||
* Récupère un utilisateur par son ID
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @return utilisateur ou Optional vide
|
||||
*/
|
||||
Optional<UserDTO> getUserById(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Récupère un utilisateur par son username
|
||||
* @param username username
|
||||
* @param realmName nom du realm
|
||||
* @return utilisateur ou Optional vide
|
||||
*/
|
||||
Optional<UserDTO> getUserByUsername(@NotBlank String username, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Récupère un utilisateur par son email
|
||||
* @param email email
|
||||
* @param realmName nom du realm
|
||||
* @return utilisateur ou Optional vide
|
||||
*/
|
||||
Optional<UserDTO> getUserByEmail(@NotBlank String email, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Crée un nouvel utilisateur
|
||||
* @param user données de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @return utilisateur créé avec son ID
|
||||
*/
|
||||
UserDTO createUser(@Valid @NotNull UserDTO user, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Met à jour un utilisateur existant
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param user données modifiées
|
||||
* @param realmName nom du realm
|
||||
* @return utilisateur mis à jour
|
||||
*/
|
||||
UserDTO updateUser(@NotBlank String userId, @Valid @NotNull UserDTO user, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Supprime un utilisateur (soft ou hard delete selon configuration)
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @param hardDelete true pour suppression définitive, false pour soft delete
|
||||
*/
|
||||
void deleteUser(@NotBlank String userId, @NotBlank String realmName, boolean hardDelete);
|
||||
|
||||
/**
|
||||
* Active un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
*/
|
||||
void activateUser(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Désactive un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @param raison raison de la désactivation
|
||||
*/
|
||||
void deactivateUser(@NotBlank String userId, @NotBlank String realmName, String raison);
|
||||
|
||||
/**
|
||||
* Suspend un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @param raison raison de la suspension
|
||||
* @param duree durée de la suspension en jours (0 = indéfinie)
|
||||
*/
|
||||
void suspendUser(@NotBlank String userId, @NotBlank String realmName, String raison, int duree);
|
||||
|
||||
/**
|
||||
* Déverrouille un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
*/
|
||||
void unlockUser(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Réinitialise le mot de passe d'un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @param temporaryPassword mot de passe temporaire
|
||||
* @param temporary true si le mot de passe doit être changé à la prochaine connexion
|
||||
*/
|
||||
void resetPassword(@NotBlank String userId, @NotBlank String realmName,
|
||||
@NotBlank String temporaryPassword, boolean temporary);
|
||||
|
||||
/**
|
||||
* Envoie un email de vérification
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
*/
|
||||
void sendVerificationEmail(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Force la déconnexion de toutes les sessions d'un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @return nombre de sessions révoquées
|
||||
*/
|
||||
int logoutAllSessions(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Récupère les sessions actives d'un utilisateur
|
||||
* @param userId ID de l'utilisateur
|
||||
* @param realmName nom du realm
|
||||
* @return liste des informations de session
|
||||
*/
|
||||
List<String> getActiveSessions(@NotBlank String userId, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Compte le nombre d'utilisateurs selon des critères
|
||||
* @param criteria critères de recherche
|
||||
* @return nombre d'utilisateurs
|
||||
*/
|
||||
long countUsers(@NotNull UserSearchCriteriaDTO criteria);
|
||||
|
||||
/**
|
||||
* Récupère tous les utilisateurs d'un realm (avec pagination)
|
||||
* @param realmName nom du realm
|
||||
* @param page numéro de page
|
||||
* @param pageSize taille de la page
|
||||
* @return liste paginée d'utilisateurs
|
||||
*/
|
||||
UserSearchResultDTO getAllUsers(@NotBlank String realmName, int page, int pageSize);
|
||||
|
||||
/**
|
||||
* Vérifie si un username existe déjà
|
||||
* @param username username à vérifier
|
||||
* @param realmName nom du realm
|
||||
* @return true si existe
|
||||
*/
|
||||
boolean usernameExists(@NotBlank String username, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Vérifie si un email existe déjà
|
||||
* @param email email à vérifier
|
||||
* @param realmName nom du realm
|
||||
* @return true si existe
|
||||
*/
|
||||
boolean emailExists(@NotBlank String email, @NotBlank String realmName);
|
||||
|
||||
/**
|
||||
* Exporte les utilisateurs au format CSV
|
||||
* @param criteria critères de recherche
|
||||
* @return contenu CSV
|
||||
*/
|
||||
String exportUsersToCSV(@NotNull UserSearchCriteriaDTO criteria);
|
||||
|
||||
/**
|
||||
* Importe des utilisateurs depuis un CSV
|
||||
* @param csvContent contenu CSV
|
||||
* @param realmName nom du realm
|
||||
* @return nombre d'utilisateurs importés
|
||||
*/
|
||||
int importUsersFromCSV(@NotBlank String csvContent, @NotBlank String realmName);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package dev.lions.user.manager.validation;
|
||||
|
||||
/**
|
||||
* Constantes de validation pour les DTOs
|
||||
* Centralise les règles de validation communes
|
||||
*/
|
||||
public final class ValidationConstants {
|
||||
|
||||
private ValidationConstants() {
|
||||
// Classe utilitaire, pas d'instanciation
|
||||
}
|
||||
|
||||
// Username
|
||||
public static final int USERNAME_MIN_LENGTH = 3;
|
||||
public static final int USERNAME_MAX_LENGTH = 100;
|
||||
public static final String USERNAME_PATTERN = "^[a-zA-Z0-9._-]+$";
|
||||
public static final String USERNAME_PATTERN_MESSAGE = "Le nom d'utilisateur ne peut contenir que des lettres, chiffres, points, tirets et underscores";
|
||||
|
||||
// Email
|
||||
public static final String EMAIL_PATTERN = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
||||
public static final String EMAIL_PATTERN_MESSAGE = "Format d'email invalide";
|
||||
|
||||
// Nom et Prénom
|
||||
public static final int NAME_MIN_LENGTH = 2;
|
||||
public static final int NAME_MAX_LENGTH = 100;
|
||||
public static final String NAME_PATTERN = "^[a-zA-ZÀ-ÿ\\s'-]+$";
|
||||
public static final String NAME_PATTERN_MESSAGE = "Le nom ne peut contenir que des lettres, espaces, apostrophes et tirets";
|
||||
|
||||
// Téléphone
|
||||
public static final String PHONE_PATTERN = "^\\+?[0-9\\s.-]{8,20}$";
|
||||
public static final String PHONE_PATTERN_MESSAGE = "Format de téléphone invalide";
|
||||
|
||||
// Mot de passe
|
||||
public static final int PASSWORD_MIN_LENGTH = 8;
|
||||
public static final int PASSWORD_MAX_LENGTH = 100;
|
||||
public static final String PASSWORD_PATTERN = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
|
||||
public static final String PASSWORD_PATTERN_MESSAGE = "Le mot de passe doit contenir au moins 8 caractères, une majuscule, une minuscule, un chiffre et un caractère spécial";
|
||||
|
||||
// Role
|
||||
public static final int ROLE_NAME_MIN_LENGTH = 2;
|
||||
public static final int ROLE_NAME_MAX_LENGTH = 100;
|
||||
public static final String ROLE_NAME_PATTERN = "^[a-zA-Z0-9_-]+$";
|
||||
public static final String ROLE_NAME_PATTERN_MESSAGE = "Le nom du rôle ne peut contenir que des lettres, chiffres, underscores et tirets";
|
||||
|
||||
// Realm
|
||||
public static final String REALM_NAME_PATTERN = "^[a-zA-Z0-9_-]+$";
|
||||
public static final String REALM_NAME_PATTERN_MESSAGE = "Le nom du realm ne peut contenir que des lettres, chiffres, underscores et tirets";
|
||||
|
||||
// UUID
|
||||
public static final String UUID_PATTERN = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
|
||||
public static final String UUID_PATTERN_MESSAGE = "Format UUID invalide";
|
||||
|
||||
// Messages d'erreur génériques
|
||||
public static final String REQUIRED_FIELD = "Ce champ est obligatoire";
|
||||
public static final String INVALID_FORMAT = "Format invalide";
|
||||
public static final String TOO_SHORT = "Valeur trop courte";
|
||||
public static final String TOO_LONG = "Valeur trop longue";
|
||||
|
||||
// Pagination
|
||||
public static final int DEFAULT_PAGE_SIZE = 20;
|
||||
public static final int MAX_PAGE_SIZE = 100;
|
||||
public static final int MIN_PAGE_SIZE = 1;
|
||||
|
||||
// Audit
|
||||
public static final int MAX_DESCRIPTION_LENGTH = 500;
|
||||
public static final int MAX_COMMENT_LENGTH = 1000;
|
||||
public static final int MAX_ERROR_MESSAGE_LENGTH = 2000;
|
||||
|
||||
// IP Address
|
||||
public static final String IP_ADDRESS_PATTERN = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
|
||||
public static final String IP_ADDRESS_PATTERN_MESSAGE = "Format d'adresse IP invalide";
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/dto/base/BaseDTO.class
Normal file
BIN
target/classes/dev/lions/user/manager/dto/base/BaseDTO.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/dto/role/RoleDTO.class
Normal file
BIN
target/classes/dev/lions/user/manager/dto/role/RoleDTO.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/dto/user/UserDTO.class
Normal file
BIN
target/classes/dev/lions/user/manager/dto/user/UserDTO.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/enums/role/TypeRole.class
Normal file
BIN
target/classes/dev/lions/user/manager/enums/role/TypeRole.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/service/AuditService.class
Normal file
BIN
target/classes/dev/lions/user/manager/service/AuditService.class
Normal file
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/service/RoleService.class
Normal file
BIN
target/classes/dev/lions/user/manager/service/RoleService.class
Normal file
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/service/SyncService.class
Normal file
BIN
target/classes/dev/lions/user/manager/service/SyncService.class
Normal file
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/service/UserService.class
Normal file
BIN
target/classes/dev/lions/user/manager/service/UserService.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/lions-user-manager-server-api-1.0.0.jar
Normal file
BIN
target/lions-user-manager-server-api-1.0.0.jar
Normal file
Binary file not shown.
3
target/maven-archiver/pom.properties
Normal file
3
target/maven-archiver/pom.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
artifactId=lions-user-manager-server-api
|
||||
groupId=dev.lions.user.manager
|
||||
version=1.0.0
|
||||
@@ -0,0 +1,31 @@
|
||||
dev\lions\user\manager\dto\role\RoleDTO$RoleDTOBuilder.class
|
||||
dev\lions\user\manager\dto\audit\AuditLogDTO.class
|
||||
dev\lions\user\manager\dto\user\UserSearchCriteriaDTO$UserSearchCriteriaDTOBuilder.class
|
||||
dev\lions\user\manager\dto\audit\AuditLogDTO$AuditLogDTOBuilder.class
|
||||
dev\lions\user\manager\dto\role\RoleAssignmentDTO.class
|
||||
dev\lions\user\manager\dto\base\BaseDTO$BaseDTOBuilder.class
|
||||
dev\lions\user\manager\enums\role\TypeRole.class
|
||||
dev\lions\user\manager\dto\base\BaseDTO.class
|
||||
dev\lions\user\manager\dto\user\UserSearchResultDTO.class
|
||||
dev\lions\user\manager\enums\user\StatutUser.class
|
||||
dev\lions\user\manager\dto\role\RoleAssignmentDTO$RoleAssignmentDTOBuilder.class
|
||||
dev\lions\user\manager\dto\role\RoleDTO$RoleDTOBuilderImpl.class
|
||||
dev\lions\user\manager\service\SyncService.class
|
||||
dev\lions\user\manager\service\UserService.class
|
||||
dev\lions\user\manager\service\AuditService.class
|
||||
dev\lions\user\manager\dto\role\RoleDTO$RoleCompositeDTO.class
|
||||
dev\lions\user\manager\dto\user\UserDTO$FederatedIdentityDTO.class
|
||||
dev\lions\user\manager\dto\user\UserDTO$FederatedIdentityDTO$FederatedIdentityDTOBuilderImpl.class
|
||||
dev\lions\user\manager\dto\user\UserSearchResultDTO$UserSearchResultDTOBuilder.class
|
||||
dev\lions\user\manager\dto\role\RoleDTO.class
|
||||
dev\lions\user\manager\dto\user\UserDTO$FederatedIdentityDTO$FederatedIdentityDTOBuilder.class
|
||||
dev\lions\user\manager\dto\role\RoleDTO$RoleCompositeDTO$RoleCompositeDTOBuilder.class
|
||||
dev\lions\user\manager\dto\user\UserDTO$UserDTOBuilder.class
|
||||
dev\lions\user\manager\dto\user\UserDTO$UserDTOBuilderImpl.class
|
||||
dev\lions\user\manager\dto\user\UserSearchCriteriaDTO.class
|
||||
dev\lions\user\manager\enums\audit\TypeActionAudit.class
|
||||
dev\lions\user\manager\dto\role\RoleDTO$RoleCompositeDTO$RoleCompositeDTOBuilderImpl.class
|
||||
dev\lions\user\manager\dto\user\UserDTO.class
|
||||
dev\lions\user\manager\validation\ValidationConstants.class
|
||||
dev\lions\user\manager\service\RoleService.class
|
||||
dev\lions\user\manager\dto\audit\AuditLogDTO$AuditLogDTOBuilderImpl.class
|
||||
@@ -0,0 +1,15 @@
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\service\RoleService.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\enums\role\TypeRole.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\enums\user\StatutUser.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\user\UserSearchCriteriaDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\audit\AuditLogDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\service\AuditService.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\base\BaseDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\service\SyncService.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\validation\ValidationConstants.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\role\RoleAssignmentDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\user\UserSearchResultDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\enums\audit\TypeActionAudit.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\user\UserDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\dto\role\RoleDTO.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-api\src\main\java\dev\lions\user\manager\service\UserService.java
|
||||
Reference in New Issue
Block a user