Initial commit

This commit is contained in:
dahoud
2025-10-01 01:37:34 +00:00
commit f2bb633142
310 changed files with 86051 additions and 0 deletions

View File

@@ -0,0 +1,495 @@
package dev.lions.btpxpress.adapter.http;
import dev.lions.btpxpress.application.service.UserService;
import dev.lions.btpxpress.domain.core.entity.User;
import dev.lions.btpxpress.domain.core.entity.UserRole;
import dev.lions.btpxpress.domain.core.entity.UserStatus;
import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Resource REST pour la gestion des utilisateurs - Architecture 2025 SÉCURITÉ: Accès restreint aux
* administrateurs
*/
@Path("/api/v1/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Utilisateurs", description = "Gestion des utilisateurs du système")
@SecurityRequirement(name = "JWT")
// @Authenticated - Désactivé pour les tests
public class UserResource {
private static final Logger logger = LoggerFactory.getLogger(UserResource.class);
@Inject UserService userService;
// === ENDPOINTS DE CONSULTATION ===
@GET
@Operation(summary = "Récupérer tous les utilisateurs")
@APIResponse(responseCode = "200", description = "Liste des utilisateurs récupérée avec succès")
@APIResponse(responseCode = "401", description = "Non authentifié")
@APIResponse(responseCode = "403", description = "Accès refusé - droits administrateur requis")
public Response getAllUsers(
@Parameter(description = "Numéro de page (0-indexed)") @QueryParam("page") @DefaultValue("0")
int page,
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
int size,
@Parameter(description = "Terme de recherche") @QueryParam("search") String search,
@Parameter(description = "Filtrer par rôle") @QueryParam("role") String role,
@Parameter(description = "Filtrer par statut") @QueryParam("status") String status,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
List<User> users;
if (search != null && !search.isEmpty()) {
users = userService.searchUsers(search, page, size);
} else if (role != null && !role.isEmpty()) {
UserRole userRole = UserRole.valueOf(role.toUpperCase());
users = userService.findByRole(userRole, page, size);
} else if (status != null && !status.isEmpty()) {
UserStatus userStatus = UserStatus.valueOf(status.toUpperCase());
users = userService.findByStatus(userStatus, page, size);
} else {
users = userService.findAll(page, size);
}
// Convertir en DTO pour éviter d'exposer les données sensibles
List<UserResponse> userResponses = users.stream().map(this::toUserResponse).toList();
return Response.ok(userResponses).build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la récupération des utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la récupération des utilisateurs: " + e.getMessage())
.build();
}
}
@GET
@Path("/{id}")
@Operation(summary = "Récupérer un utilisateur par ID")
@APIResponse(responseCode = "200", description = "Utilisateur récupéré avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response getUserById(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
return userService
.findById(userId)
.map(user -> Response.ok(toUserResponse(user)).build())
.orElse(
Response.status(Response.Status.NOT_FOUND)
.entity("Utilisateur non trouvé avec l'ID: " + id)
.build());
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("ID d'utilisateur invalide: " + id)
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la récupération de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la récupération de l'utilisateur: " + e.getMessage())
.build();
}
}
@GET
@Path("/count")
@Operation(summary = "Compter le nombre d'utilisateurs")
@APIResponse(responseCode = "200", description = "Nombre d'utilisateurs retourné avec succès")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response countUsers(
@Parameter(description = "Filtrer par statut") @QueryParam("status") String status,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
long count;
if (status != null && !status.isEmpty()) {
UserStatus userStatus = UserStatus.valueOf(status.toUpperCase());
count = userService.countByStatus(userStatus);
} else {
count = userService.count();
}
return Response.ok(new CountResponse(count)).build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors du comptage des utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors du comptage des utilisateurs: " + e.getMessage())
.build();
}
}
@GET
@Path("/pending")
@Operation(summary = "Récupérer les utilisateurs en attente de validation")
@APIResponse(
responseCode = "200",
description = "Liste des utilisateurs en attente récupérée avec succès")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response getPendingUsers(
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
List<User> pendingUsers = userService.findByStatus(UserStatus.PENDING, 0, 100);
List<UserResponse> userResponses = pendingUsers.stream().map(this::toUserResponse).toList();
return Response.ok(userResponses).build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la récupération des utilisateurs en attente", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la récupération des utilisateurs en attente: " + e.getMessage())
.build();
}
}
@GET
@Path("/stats")
@Operation(summary = "Obtenir les statistiques des utilisateurs")
@APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response getUserStats(
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
Object stats = userService.getStatistics();
return Response.ok(stats).build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la génération des statistiques utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la génération des statistiques: " + e.getMessage())
.build();
}
}
// === ENDPOINTS DE GESTION ===
@POST
@Operation(summary = "Créer un nouvel utilisateur")
@APIResponse(responseCode = "201", description = "Utilisateur créé avec succès")
@APIResponse(responseCode = "400", description = "Données invalides")
@APIResponse(responseCode = "409", description = "Email déjà utilisé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response createUser(
@Parameter(description = "Données du nouvel utilisateur") @Valid @NotNull
CreateUserRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
User user =
userService.createUser(
request.email,
request.password,
request.nom,
request.prenom,
request.role,
request.status);
return Response.status(Response.Status.CREATED).entity(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la création de l'utilisateur", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la création de l'utilisateur: " + e.getMessage())
.build();
}
}
@PUT
@Path("/{id}")
@Operation(summary = "Modifier un utilisateur")
@APIResponse(responseCode = "200", description = "Utilisateur modifié avec succès")
@APIResponse(responseCode = "400", description = "Données invalides")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response updateUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Nouvelles données utilisateur") @Valid @NotNull
UpdateUserRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
User user = userService.updateUser(userId, request.nom, request.prenom, request.email);
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la modification de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la modification de l'utilisateur: " + e.getMessage())
.build();
}
}
@PUT
@Path("/{id}/status")
@Operation(summary = "Modifier le statut d'un utilisateur")
@APIResponse(responseCode = "200", description = "Statut modifié avec succès")
@APIResponse(responseCode = "400", description = "Statut invalide")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response updateUserStatus(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Nouveau statut") @Valid @NotNull UpdateStatusRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
UserStatus status = UserStatus.valueOf(request.status.toUpperCase());
User user = userService.updateStatus(userId, status);
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Statut invalide: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la modification du statut utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la modification du statut: " + e.getMessage())
.build();
}
}
@PUT
@Path("/{id}/role")
@Operation(summary = "Modifier le rôle d'un utilisateur")
@APIResponse(responseCode = "200", description = "Rôle modifié avec succès")
@APIResponse(responseCode = "400", description = "Rôle invalide")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response updateUserRole(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Nouveau rôle") @Valid @NotNull UpdateRoleRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
UserRole role = UserRole.valueOf(request.role.toUpperCase());
User user = userService.updateRole(userId, role);
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Rôle invalide: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la modification du rôle utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la modification du rôle: " + e.getMessage())
.build();
}
}
@POST
@Path("/{id}/approve")
@Operation(summary = "Approuver un utilisateur en attente")
@APIResponse(responseCode = "200", description = "Utilisateur approuvé avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response approveUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
User user = userService.approveUser(userId);
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de l'approbation de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de l'approbation de l'utilisateur: " + e.getMessage())
.build();
}
}
@POST
@Path("/{id}/reject")
@Operation(summary = "Rejeter un utilisateur en attente")
@APIResponse(responseCode = "200", description = "Utilisateur rejeté avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response rejectUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Raison du rejet") @Valid @NotNull RejectUserRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
userService.rejectUser(userId, request.reason);
return Response.ok().entity("Utilisateur rejeté avec succès").build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors du rejet de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors du rejet de l'utilisateur: " + e.getMessage())
.build();
}
}
@DELETE
@Path("/{id}")
@Operation(summary = "Supprimer un utilisateur")
@APIResponse(responseCode = "204", description = "Utilisateur supprimé avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response deleteUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
try {
UUID userId = UUID.fromString(id);
userService.deleteUser(userId);
return Response.noContent().build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("ID invalide: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la suppression de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la suppression de l'utilisateur: " + e.getMessage())
.build();
}
}
// === MÉTHODES UTILITAIRES ===
private UserResponse toUserResponse(User user) {
return new UserResponse(
user.getId(),
user.getEmail(),
user.getNom(),
user.getPrenom(),
user.getRole().toString(),
user.getStatus().toString(),
user.getDateCreation(),
user.getDateModification(),
user.getDerniereConnexion(),
user.getActif());
}
// === CLASSES UTILITAIRES ===
public static record CountResponse(long count) {}
public static record CreateUserRequest(
@Parameter(description = "Email de l'utilisateur") String email,
@Parameter(description = "Mot de passe") String password,
@Parameter(description = "Nom de famille") String nom,
@Parameter(description = "Prénom") String prenom,
@Parameter(description = "Rôle (USER, ADMIN, MANAGER)") String role,
@Parameter(description = "Statut (ACTIF, INACTIF, SUSPENDU)") String status) {}
public static record UpdateUserRequest(
@Parameter(description = "Nouveau nom") String nom,
@Parameter(description = "Nouveau prénom") String prenom,
@Parameter(description = "Nouvel email") String email) {}
public static record UpdateStatusRequest(
@Parameter(description = "Nouveau statut") String status) {}
public static record UpdateRoleRequest(@Parameter(description = "Nouveau rôle") String role) {}
public static record RejectUserRequest(
@Parameter(description = "Raison du rejet") String reason) {}
public static record UserResponse(
UUID id,
String email,
String nom,
String prenom,
String role,
String status,
LocalDateTime dateCreation,
LocalDateTime dateModification,
LocalDateTime derniereConnexion,
Boolean actif) {}
}