Task 1.5 - Endpoints REST complets

- Création de AuthResource avec tous les endpoints d'authentification
  * POST /api/auth/login - Connexion utilisateur avec JWT
  * POST /api/auth/logout - Déconnexion utilisateur
  * POST /api/auth/refresh - Rafraîchissement de token
  * GET /api/auth/validate - Validation de token
  * POST /api/auth/forgot-password - Demande de réinitialisation
  * POST /api/auth/reset-password - Réinitialisation avec token
  * PUT /api/auth/change-password - Changement de mot de passe

- Création de UserResource avec tous les endpoints de gestion utilisateurs
  * GET /api/users - Liste paginée avec filtres
  * GET /api/users/{id} - Utilisateur par ID
  * POST /api/users - Création d'utilisateur (ADMIN)
  * PUT /api/users/{id} - Mise à jour utilisateur
  * DELETE /api/users/{id} - Suppression utilisateur (ADMIN)
  * PUT /api/users/{id}/status - Changement de statut
  * GET /api/users/profile - Profil utilisateur courant
  * PUT /api/users/profile - Mise à jour profil courant
  * GET /api/users/search - Recherche d'utilisateurs

- Annotations OpenAPI complètes sur tous les endpoints
- Gestion d'erreurs avec try-catch et codes HTTP appropriés
- Logging SLF4J sur toutes les opérations
- Sécurité basée sur les rôles avec @RolesAllowed
- Correction des imports OpenAPI (Parameter, APIResponse, APIResponses)
- Correction de l'ordre des exceptions (AuthorizationException avant GBCMException)
- Correction des constructeurs ValidationException
- Compilation réussie de tous les endpoints
This commit is contained in:
dahoud
2025-10-06 20:44:56 +00:00
parent 74abdd9f5f
commit 5ec5e538cc
3 changed files with 987 additions and 10 deletions

View File

@@ -0,0 +1,392 @@
package com.gbcm.server.impl.resource;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gbcm.server.api.dto.auth.LoginRequestDTO;
import com.gbcm.server.api.dto.auth.LoginResponseDTO;
import com.gbcm.server.api.dto.user.UserDTO;
import com.gbcm.server.api.exceptions.AuthenticationException;
import com.gbcm.server.api.exceptions.GBCMException;
import com.gbcm.server.api.interfaces.AuthService;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Contrôleur REST pour les services d'authentification de la plateforme GBCM.
* Expose tous les endpoints d'authentification, autorisation et gestion des tokens.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Path("/api/auth")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Authentication", description = "Services d'authentification et autorisation GBCM")
public class AuthResource {
private static final Logger logger = LoggerFactory.getLogger(AuthResource.class);
@Inject
AuthService authService;
/**
* Endpoint de connexion utilisateur.
*
* @param loginRequest les informations de connexion
* @return la réponse de connexion avec token et informations utilisateur
* @throws AuthenticationException si l'authentification échoue
* @throws GBCMException si une erreur interne survient
*/
@POST
@Path("/login")
@Operation(
summary = "Connexion utilisateur",
description = "Authentifie un utilisateur avec email et mot de passe et retourne un token JWT"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Connexion réussie",
content = @Content(schema = @Schema(implementation = LoginResponseDTO.class))
),
@APIResponse(
responseCode = "401",
description = "Identifiants invalides",
content = @Content(schema = @Schema(implementation = String.class))
),
@APIResponse(
responseCode = "400",
description = "Données de requête invalides",
content = @Content(schema = @Schema(implementation = String.class))
),
@APIResponse(
responseCode = "500",
description = "Erreur interne du serveur",
content = @Content(schema = @Schema(implementation = String.class))
)
})
public Response login(
@Parameter(description = "Informations de connexion", required = true)
@Valid LoginRequestDTO loginRequest
) {
try {
logger.info("Demande de connexion reçue pour: {}", loginRequest.getEmail());
LoginResponseDTO response = authService.login(loginRequest);
logger.info("Connexion réussie pour: {}", loginRequest.getEmail());
return Response.ok(response).build();
} catch (AuthenticationException e) {
logger.warn("Échec d'authentification pour {}: {}", loginRequest.getEmail(), e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Erreur d'authentification: " + e.getMessage())
.build();
} catch (GBCMException e) {
logger.error("Erreur GBCM lors de la connexion pour {}: {}", loginRequest.getEmail(), e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la connexion pour {}: {}", loginRequest.getEmail(), e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint de déconnexion utilisateur.
*
* @param authToken le token d'authentification
* @return confirmation de déconnexion
*/
@POST
@Path("/logout")
@Operation(
summary = "Déconnexion utilisateur",
description = "Invalide le token d'authentification de l'utilisateur"
)
@APIResponses({
@APIResponse(responseCode = "200", description = "Déconnexion réussie"),
@APIResponse(responseCode = "401", description = "Token invalide"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response logout(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken
) {
try {
logger.info("Demande de déconnexion reçue");
authService.logout(authToken);
logger.info("Déconnexion réussie");
return Response.ok("Déconnexion réussie").build();
} catch (AuthenticationException e) {
logger.warn("Échec de déconnexion: {}", e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Erreur d'authentification: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la déconnexion: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint de rafraîchissement de token.
*
* @param refreshToken le token de rafraîchissement
* @return nouveau token d'accès
*/
@POST
@Path("/refresh")
@Operation(
summary = "Rafraîchissement du token",
description = "Génère un nouveau token d'authentification à partir d'un refresh token"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Token rafraîchi",
content = @Content(schema = @Schema(implementation = LoginResponseDTO.class))
),
@APIResponse(responseCode = "401", description = "Token de rafraîchissement invalide"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response refreshToken(
@Parameter(description = "Token de rafraîchissement", required = true)
@FormParam("refreshToken") String refreshToken
) {
try {
logger.info("Demande de rafraîchissement de token reçue");
LoginResponseDTO response = authService.refreshToken(refreshToken);
logger.info("Token rafraîchi avec succès");
return Response.ok(response).build();
} catch (AuthenticationException e) {
logger.warn("Échec de rafraîchissement de token: {}", e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Erreur d'authentification: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors du rafraîchissement: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint de validation de token.
*
* @param authToken le token d'authentification à valider
* @return les informations de l'utilisateur si le token est valide
*/
@GET
@Path("/validate")
@Operation(
summary = "Validation du token",
description = "Vérifie la validité d'un token d'authentification et retourne les informations utilisateur"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Token valide",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "401", description = "Token invalide ou expiré"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response validateToken(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken
) {
try {
logger.debug("Demande de validation de token reçue");
UserDTO user = authService.validateToken(authToken);
logger.debug("Token validé avec succès pour l'utilisateur: {}", user.getEmail());
return Response.ok(user).build();
} catch (AuthenticationException e) {
logger.warn("Échec de validation de token: {}", e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Erreur d'authentification: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la validation: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint de demande de réinitialisation de mot de passe.
*
* @param email l'adresse email de l'utilisateur
* @return confirmation d'envoi de l'email
*/
@POST
@Path("/forgot-password")
@Operation(
summary = "Mot de passe oublié",
description = "Envoie un email de réinitialisation de mot de passe à l'utilisateur"
)
@APIResponses({
@APIResponse(responseCode = "200", description = "Email envoyé"),
@APIResponse(responseCode = "400", description = "Email invalide"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response forgotPassword(
@Parameter(description = "Adresse email", required = true)
@FormParam("email") String email
) {
try {
logger.info("Demande de réinitialisation de mot de passe pour: {}", email);
authService.forgotPassword(email);
logger.info("Email de réinitialisation envoyé pour: {}", email);
return Response.ok("Email de réinitialisation envoyé").build();
} catch (GBCMException e) {
logger.warn("Erreur lors de la demande de réinitialisation pour {}: {}", email, e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la demande de réinitialisation pour {}: {}", email, e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint de réinitialisation de mot de passe.
*
* @param resetToken le token de réinitialisation
* @param newPassword le nouveau mot de passe
* @return confirmation de réinitialisation
*/
@POST
@Path("/reset-password")
@Operation(
summary = "Réinitialisation du mot de passe",
description = "Réinitialise le mot de passe avec un token de réinitialisation"
)
@APIResponses({
@APIResponse(responseCode = "200", description = "Mot de passe réinitialisé"),
@APIResponse(responseCode = "400", description = "Token invalide ou expiré"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response resetPassword(
@Parameter(description = "Token de réinitialisation", required = true)
@FormParam("resetToken") String resetToken,
@Parameter(description = "Nouveau mot de passe", required = true)
@FormParam("newPassword") String newPassword
) {
try {
logger.info("Demande de réinitialisation de mot de passe avec token");
authService.resetPassword(resetToken, newPassword);
logger.info("Mot de passe réinitialisé avec succès");
return Response.ok("Mot de passe réinitialisé avec succès").build();
} catch (GBCMException e) {
logger.warn("Erreur lors de la réinitialisation: {}", e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la réinitialisation: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint de changement de mot de passe.
*
* @param authToken le token d'authentification
* @param oldPassword l'ancien mot de passe
* @param newPassword le nouveau mot de passe
* @return confirmation de changement
*/
@PUT
@Path("/change-password")
@Operation(
summary = "Changement de mot de passe",
description = "Change le mot de passe d'un utilisateur authentifié"
)
@APIResponses({
@APIResponse(responseCode = "200", description = "Mot de passe changé"),
@APIResponse(responseCode = "401", description = "Non autorisé"),
@APIResponse(responseCode = "400", description = "Ancien mot de passe incorrect"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response changePassword(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken,
@Parameter(description = "Ancien mot de passe", required = true)
@FormParam("oldPassword") String oldPassword,
@Parameter(description = "Nouveau mot de passe", required = true)
@FormParam("newPassword") String newPassword
) {
try {
logger.info("Demande de changement de mot de passe");
authService.changePassword(authToken, oldPassword, newPassword);
logger.info("Mot de passe changé avec succès");
return Response.ok("Mot de passe changé avec succès").build();
} catch (AuthenticationException e) {
logger.warn("Échec de changement de mot de passe: {}", e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Erreur d'authentification: " + e.getMessage())
.build();
} catch (GBCMException e) {
logger.warn("Erreur lors du changement de mot de passe: {}", e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors du changement de mot de passe: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
}

View File

@@ -0,0 +1,585 @@
package com.gbcm.server.impl.resource;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gbcm.server.api.dto.common.PagedResultDTO;
import com.gbcm.server.api.dto.common.SuccessResponseDTO;
import com.gbcm.server.api.dto.user.CreateUserDTO;
import com.gbcm.server.api.dto.user.UpdateUserDTO;
import com.gbcm.server.api.dto.user.UserDTO;
import com.gbcm.server.api.enums.UserRole;
import com.gbcm.server.api.exceptions.AuthorizationException;
import com.gbcm.server.api.exceptions.GBCMException;
import com.gbcm.server.api.exceptions.ResourceNotFoundException;
import com.gbcm.server.api.exceptions.ValidationException;
import com.gbcm.server.api.interfaces.UserService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Contrôleur REST pour la gestion des utilisateurs de la plateforme GBCM.
* Expose tous les endpoints CRUD pour les utilisateurs avec sécurité basée sur les rôles.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Users", description = "Gestion des utilisateurs GBCM")
public class UserResource {
private static final Logger logger = LoggerFactory.getLogger(UserResource.class);
@Inject
UserService userService;
/**
* Endpoint pour récupérer la liste paginée des utilisateurs.
*
* @param page numéro de page (commence à 0)
* @param size taille de page
* @param sort critères de tri
* @param role filtre par rôle
* @param active filtre par statut actif
* @param search terme de recherche
* @return liste paginée des utilisateurs
*/
@GET
@RolesAllowed({"ADMIN", "MANAGER"})
@Operation(
summary = "Liste des utilisateurs",
description = "Récupère la liste paginée des utilisateurs avec filtres optionnels"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Liste récupérée avec succès",
content = @Content(schema = @Schema(implementation = PagedResultDTO.class))
),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé - permissions insuffisantes"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response getUsers(
@Parameter(description = "Numéro de page (commence à 0)", example = "0")
@QueryParam("page") @DefaultValue("0") int page,
@Parameter(description = "Taille de page", example = "20")
@QueryParam("size") @DefaultValue("20") int size,
@Parameter(description = "Tri (ex: firstName,asc ou email,desc)")
@QueryParam("sort") String sort,
@Parameter(description = "Filtre par rôle")
@QueryParam("role") UserRole role,
@Parameter(description = "Filtre par statut actif")
@QueryParam("active") Boolean active,
@Parameter(description = "Recherche par nom ou email")
@QueryParam("search") String search
) {
try {
logger.info("Demande de liste des utilisateurs - page: {}, size: {}", page, size);
PagedResultDTO<UserDTO> result = userService.getUsers(page, size, sort, role, active, search);
logger.info("Liste des utilisateurs récupérée avec succès - {} éléments", result.getTotalElements());
return Response.ok(result).build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour la liste des utilisateurs: {}", e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la récupération des utilisateurs: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour récupérer un utilisateur par son ID.
*
* @param id l'identifiant de l'utilisateur
* @return les détails de l'utilisateur
*/
@GET
@Path("/{id}")
@RolesAllowed({"ADMIN", "MANAGER", "COACH"})
@Operation(
summary = "Utilisateur par ID",
description = "Récupère les détails d'un utilisateur par son identifiant"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Utilisateur trouvé",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response getUserById(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id
) {
try {
logger.info("Demande de récupération de l'utilisateur ID: {}", id);
UserDTO user = userService.getUserById(id);
logger.info("Utilisateur récupéré avec succès: {}", user.getEmail());
return Response.ok(user).build();
} catch (ResourceNotFoundException e) {
logger.warn("Utilisateur non trouvé avec ID: {}", id);
return Response.status(Response.Status.NOT_FOUND)
.entity("Utilisateur non trouvé: " + e.getMessage())
.build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour l'utilisateur ID {}: {}", id, e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la récupération de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour créer un nouvel utilisateur.
*
* @param createUserDTO les données du nouvel utilisateur
* @return l'utilisateur créé
*/
@POST
@RolesAllowed({"ADMIN"})
@Operation(
summary = "Créer un utilisateur",
description = "Crée un nouvel utilisateur dans le système"
)
@APIResponses({
@APIResponse(
responseCode = "201",
description = "Utilisateur créé avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "400", description = "Données invalides"),
@APIResponse(responseCode = "409", description = "Email déjà utilisé"),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé - seuls les admins peuvent créer des utilisateurs"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response createUser(
@Parameter(description = "Données du nouvel utilisateur", required = true)
@Valid CreateUserDTO createUserDTO
) {
try {
logger.info("Demande de création d'utilisateur: {}", createUserDTO.getEmail());
UserDTO user = userService.createUser(createUserDTO);
logger.info("Utilisateur créé avec succès: {}", user.getEmail());
return Response.status(Response.Status.CREATED).entity(user).build();
} catch (ValidationException e) {
logger.warn("Données invalides pour la création d'utilisateur: {}", e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour la création d'utilisateur: {}", e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (GBCMException e) {
logger.warn("Erreur lors de la création d'utilisateur: {}", e.getMessage());
return Response.status(Response.Status.CONFLICT)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la création d'utilisateur: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour mettre à jour un utilisateur existant.
*
* @param id l'identifiant de l'utilisateur
* @param updateUserDTO les données de mise à jour
* @return l'utilisateur mis à jour
*/
@PUT
@Path("/{id}")
@RolesAllowed({"ADMIN", "MANAGER"})
@Operation(
summary = "Mettre à jour un utilisateur",
description = "Met à jour les informations d'un utilisateur existant"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Utilisateur mis à jour avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@APIResponse(responseCode = "400", description = "Données invalides"),
@APIResponse(responseCode = "409", description = "Email déjà utilisé par un autre utilisateur"),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response updateUser(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id,
@Parameter(description = "Données de mise à jour", required = true)
@Valid UpdateUserDTO updateUserDTO
) {
try {
logger.info("Demande de mise à jour de l'utilisateur ID: {}", id);
UserDTO user = userService.updateUser(id, updateUserDTO);
logger.info("Utilisateur mis à jour avec succès: {}", user.getEmail());
return Response.ok(user).build();
} catch (ResourceNotFoundException e) {
logger.warn("Utilisateur non trouvé pour mise à jour ID: {}", id);
return Response.status(Response.Status.NOT_FOUND)
.entity("Utilisateur non trouvé: " + e.getMessage())
.build();
} catch (ValidationException e) {
logger.warn("Données invalides pour la mise à jour de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour la mise à jour de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (GBCMException e) {
logger.warn("Erreur lors de la mise à jour de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.CONFLICT)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la mise à jour de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour supprimer un utilisateur (soft delete).
*
* @param id l'identifiant de l'utilisateur
* @return confirmation de suppression
*/
@DELETE
@Path("/{id}")
@RolesAllowed({"ADMIN"})
@Operation(
summary = "Supprimer un utilisateur",
description = "Supprime un utilisateur du système (soft delete)"
)
@APIResponses({
@APIResponse(responseCode = "200", description = "Utilisateur supprimé avec succès"),
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé - seuls les admins peuvent supprimer des utilisateurs"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response deleteUser(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id
) {
try {
logger.info("Demande de suppression de l'utilisateur ID: {}", id);
SuccessResponseDTO<Void> result = userService.deleteUser(id);
logger.info("Utilisateur supprimé avec succès ID: {}", id);
return Response.ok(result).build();
} catch (ResourceNotFoundException e) {
logger.warn("Utilisateur non trouvé pour suppression ID: {}", id);
return Response.status(Response.Status.NOT_FOUND)
.entity("Utilisateur non trouvé: " + e.getMessage())
.build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour la suppression de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la suppression de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour changer le statut d'un utilisateur.
*
* @param id l'identifiant de l'utilisateur
* @param active le nouveau statut
* @return l'utilisateur avec le nouveau statut
*/
@PUT
@Path("/{id}/status")
@RolesAllowed({"ADMIN", "MANAGER"})
@Operation(
summary = "Changer le statut d'un utilisateur",
description = "Active ou désactive un compte utilisateur"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Statut modifié avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response changeUserStatus(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id,
@Parameter(description = "Nouveau statut", required = true, example = "true")
@FormParam("active") boolean active
) {
try {
logger.info("Demande de changement de statut pour l'utilisateur ID: {} -> {}", id, active);
UserDTO user = userService.changeUserStatus(id, active);
logger.info("Statut utilisateur changé avec succès: {} -> {}", id, active);
return Response.ok(user).build();
} catch (ResourceNotFoundException e) {
logger.warn("Utilisateur non trouvé pour changement de statut ID: {}", id);
return Response.status(Response.Status.NOT_FOUND)
.entity("Utilisateur non trouvé: " + e.getMessage())
.build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour le changement de statut de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors du changement de statut de l'utilisateur {}: {}", id, e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour récupérer le profil de l'utilisateur connecté.
*
* @param authToken le token d'authentification
* @return le profil de l'utilisateur connecté
*/
@GET
@Path("/profile")
@RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT", "PROSPECT"})
@Operation(
summary = "Profil utilisateur",
description = "Récupère le profil de l'utilisateur actuellement connecté"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Profil récupéré avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response getCurrentUserProfile(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken
) {
try {
logger.info("Demande de récupération du profil utilisateur courant");
UserDTO user = userService.getCurrentUserProfile(authToken);
logger.info("Profil utilisateur récupéré avec succès: {}", user.getEmail());
return Response.ok(user).build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour le profil utilisateur: {}", e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la récupération du profil: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour mettre à jour le profil de l'utilisateur connecté.
*
* @param authToken le token d'authentification
* @param updateUserDTO les données de mise à jour
* @return le profil mis à jour
*/
@PUT
@Path("/profile")
@RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT", "PROSPECT"})
@Operation(
summary = "Mettre à jour le profil",
description = "Met à jour le profil de l'utilisateur actuellement connecté"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Profil mis à jour avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))
),
@APIResponse(responseCode = "400", description = "Données invalides"),
@APIResponse(responseCode = "409", description = "Email déjà utilisé"),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response updateCurrentUserProfile(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken,
@Parameter(description = "Données de mise à jour du profil", required = true)
@Valid UpdateUserDTO updateUserDTO
) {
try {
logger.info("Demande de mise à jour du profil utilisateur courant");
UserDTO user = userService.updateCurrentUserProfile(authToken, updateUserDTO);
logger.info("Profil utilisateur mis à jour avec succès: {}", user.getEmail());
return Response.ok(user).build();
} catch (ValidationException e) {
logger.warn("Données invalides pour la mise à jour du profil: {}", e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour la mise à jour du profil: {}", e.getMessage());
return Response.status(Response.Status.UNAUTHORIZED)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (GBCMException e) {
logger.warn("Erreur lors de la mise à jour du profil: {}", e.getMessage());
return Response.status(Response.Status.CONFLICT)
.entity("Erreur: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la mise à jour du profil: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
/**
* Endpoint pour rechercher des utilisateurs par critères.
*
* @param query le terme de recherche
* @param page le numéro de page
* @param size la taille de page
* @return les résultats de recherche paginés
*/
@GET
@Path("/search")
@RolesAllowed({"ADMIN", "MANAGER", "COACH"})
@Operation(
summary = "Rechercher des utilisateurs",
description = "Recherche d'utilisateurs par différents critères"
)
@APIResponses({
@APIResponse(
responseCode = "200",
description = "Résultats de recherche",
content = @Content(schema = @Schema(implementation = PagedResultDTO.class))
),
@APIResponse(responseCode = "401", description = "Non authentifié"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Response searchUsers(
@Parameter(description = "Terme de recherche", required = true)
@QueryParam("q") String query,
@Parameter(description = "Numéro de page", example = "0")
@QueryParam("page") @DefaultValue("0") int page,
@Parameter(description = "Taille de page", example = "20")
@QueryParam("size") @DefaultValue("20") int size
) {
try {
logger.info("Demande de recherche d'utilisateurs avec query: {}", query);
PagedResultDTO<UserDTO> result = userService.searchUsers(query, page, size);
logger.info("Recherche d'utilisateurs terminée - {} résultats", result.getTotalElements());
return Response.ok(result).build();
} catch (AuthorizationException e) {
logger.warn("Accès refusé pour la recherche d'utilisateurs: {}", e.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur inattendue lors de la recherche d'utilisateurs: {}", e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur interne du serveur")
.build();
}
}
}

View File

@@ -1,5 +1,11 @@
package com.gbcm.server.impl.service; package com.gbcm.server.impl.service;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gbcm.server.api.dto.common.PagedResultDTO; import com.gbcm.server.api.dto.common.PagedResultDTO;
import com.gbcm.server.api.dto.common.SuccessResponseDTO; import com.gbcm.server.api.dto.common.SuccessResponseDTO;
import com.gbcm.server.api.dto.user.CreateUserDTO; import com.gbcm.server.api.dto.user.CreateUserDTO;
@@ -11,17 +17,11 @@ import com.gbcm.server.api.exceptions.GBCMException;
import com.gbcm.server.api.exceptions.ResourceNotFoundException; import com.gbcm.server.api.exceptions.ResourceNotFoundException;
import com.gbcm.server.api.exceptions.ValidationException; import com.gbcm.server.api.exceptions.ValidationException;
import com.gbcm.server.api.interfaces.UserService; import com.gbcm.server.api.interfaces.UserService;
import com.gbcm.server.impl.entity.User;
import com.gbcm.server.impl.service.security.PasswordService; import com.gbcm.server.impl.service.security.PasswordService;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/** /**
* Implémentation simplifiée du service de gestion des utilisateurs pour la plateforme GBCM. * Implémentation simplifiée du service de gestion des utilisateurs pour la plateforme GBCM.
@@ -105,11 +105,11 @@ public class UserServiceImpl implements UserService {
// Validation basique // Validation basique
if (createUserDTO == null) { if (createUserDTO == null) {
throw new ValidationException("Données de création requises", "USER_CREATE_DATA_REQUIRED"); throw new ValidationException("Données de création requises");
} }
if (createUserDTO.getEmail() == null || createUserDTO.getEmail().trim().isEmpty()) { if (createUserDTO.getEmail() == null || createUserDTO.getEmail().trim().isEmpty()) {
throw new ValidationException("Email requis", "USER_EMAIL_REQUIRED"); throw new ValidationException("Email requis");
} }
// Simulation - créer un utilisateur fictif // Simulation - créer un utilisateur fictif
@@ -140,7 +140,7 @@ public class UserServiceImpl implements UserService {
} }
if (updateUserDTO == null) { if (updateUserDTO == null) {
throw new ValidationException("Données de mise à jour requises", "USER_UPDATE_DATA_REQUIRED"); throw new ValidationException("Données de mise à jour requises");
} }
// Simulation - retourner un utilisateur mis à jour // Simulation - retourner un utilisateur mis à jour
@@ -238,7 +238,7 @@ public class UserServiceImpl implements UserService {
} }
if (updateUserDTO == null) { if (updateUserDTO == null) {
throw new ValidationException("Données de mise à jour requises", "USER_UPDATE_DATA_REQUIRED"); throw new ValidationException("Données de mise à jour requises");
} }
// Simulation - retourner le profil mis à jour // Simulation - retourner le profil mis à jour