feat(server-impl): refactoring resources JAX-RS, corrections AuditService/SyncService/UserService, ajout entites Sync et scripts Docker
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,139 +1,62 @@
|
||||
package dev.lions.user.manager.resource;
|
||||
|
||||
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 dev.lions.user.manager.api.UserResourceApi;
|
||||
import dev.lions.user.manager.dto.common.ApiErrorDTO;
|
||||
import dev.lions.user.manager.dto.importexport.ImportResultDTO;
|
||||
import dev.lions.user.manager.dto.user.*;
|
||||
import dev.lions.user.manager.service.UserService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion des utilisateurs
|
||||
* Endpoints exposés pour les opérations CRUD sur les utilisateurs Keycloak
|
||||
* Implémente l'interface API commune.
|
||||
*/
|
||||
@Path("/api/users")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Users", description = "Gestion des utilisateurs Keycloak")
|
||||
@PermitAll // DEV: Permet l'accès sans authentification (écrasé par @RolesAllowed sur les méthodes en PROD)
|
||||
@Slf4j
|
||||
public class UserResource {
|
||||
@jakarta.enterprise.context.ApplicationScoped
|
||||
@jakarta.ws.rs.Path("/api/users")
|
||||
public class UserResource implements UserResourceApi {
|
||||
|
||||
@Inject
|
||||
UserService userService;
|
||||
|
||||
@POST
|
||||
@Path("/search")
|
||||
@Operation(summary = "Rechercher des utilisateurs", description = "Recherche d'utilisateurs selon des critères")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Résultats de recherche",
|
||||
content = @Content(schema = @Schema(implementation = UserSearchResultDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Critères invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response searchUsers(@Valid @NotNull UserSearchCriteriaDTO criteria) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public UserSearchResultDTO searchUsers(@Valid @NotNull UserSearchCriteriaDTO criteria) {
|
||||
log.info("POST /api/users/search - Recherche d'utilisateurs");
|
||||
|
||||
try {
|
||||
UserSearchResultDTO result = userService.searchUsers(criteria);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la recherche d'utilisateurs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return userService.searchUsers(criteria);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{userId}")
|
||||
@Operation(summary = "Récupérer un utilisateur par ID", description = "Récupère les détails d'un utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Utilisateur trouvé",
|
||||
content = @Content(schema = @Schema(implementation = UserDTO.class))),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager", "user_viewer"})
|
||||
public Response getUserById(
|
||||
@Parameter(description = "ID de l'utilisateur") @PathParam("userId") @NotBlank String userId,
|
||||
@Parameter(description = "Nom du realm") @QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager", "user_viewer" })
|
||||
public UserDTO getUserById(String userId, String realmName) {
|
||||
log.info("GET /api/users/{} - realm: {}", userId, realmName);
|
||||
|
||||
try {
|
||||
return userService.getUserById(userId, realmName)
|
||||
.map(user -> Response.ok(user).build())
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Utilisateur non trouvé"))
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération de l'utilisateur {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return userService.getUserById(userId, realmName)
|
||||
.orElseThrow(() -> new RuntimeException("Utilisateur non trouvé")); // ExceptionMapper should handle/map
|
||||
// to 404
|
||||
}
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Lister tous les utilisateurs", description = "Liste paginée de tous les utilisateurs")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des utilisateurs",
|
||||
content = @Content(schema = @Schema(implementation = UserSearchResultDTO.class))),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager", "user_viewer"})
|
||||
public Response getAllUsers(
|
||||
@QueryParam("realm") @NotBlank String realmName,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("pageSize") @DefaultValue("20") int pageSize
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager", "user_viewer" })
|
||||
public UserSearchResultDTO getAllUsers(String realmName, int page, int pageSize) {
|
||||
log.info("GET /api/users - realm: {}, page: {}, pageSize: {}", realmName, page, pageSize);
|
||||
|
||||
try {
|
||||
UserSearchResultDTO result = userService.getAllUsers(realmName, page, pageSize);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des utilisateurs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return userService.getAllUsers(realmName, page, pageSize);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Créer un utilisateur", description = "Crée un nouvel utilisateur dans Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "201", description = "Utilisateur créé",
|
||||
content = @Content(schema = @Schema(implementation = UserDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "409", description = "Utilisateur existe déjà"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response createUser(
|
||||
@Valid @NotNull UserDTO user,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public Response createUser(@Valid @NotNull UserDTO user, String realmName) {
|
||||
log.info("POST /api/users - Création d'un utilisateur: {}", user.getUsername());
|
||||
|
||||
try {
|
||||
@@ -142,380 +65,97 @@ public class UserResource {
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Données invalides lors de la création: {}", e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
.entity(new ApiErrorDTO(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la création de l'utilisateur", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{userId}")
|
||||
@Operation(summary = "Mettre à jour un utilisateur", description = "Met à jour les informations d'un utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Utilisateur mis à jour",
|
||||
content = @Content(schema = @Schema(implementation = UserDTO.class))),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response updateUser(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@NotNull UserDTO user,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public UserDTO updateUser(String userId, @Valid @NotNull UserDTO user, String realmName) {
|
||||
log.info("PUT /api/users/{} - Mise à jour", userId);
|
||||
|
||||
try {
|
||||
// Validation manuelle des champs obligatoires
|
||||
if (user.getPrenom() == null || user.getPrenom().trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Le prénom est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
if (user.getPrenom().length() < 2 || user.getPrenom().length() > 100) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Le prénom doit contenir entre 2 et 100 caractères"))
|
||||
.build();
|
||||
}
|
||||
if (user.getNom() == null || user.getNom().trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Le nom est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
if (user.getNom().length() < 2 || user.getNom().length() > 100) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Le nom doit contenir entre 2 et 100 caractères"))
|
||||
.build();
|
||||
}
|
||||
if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("L'email est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
if (!user.getEmail().matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Format d'email invalide"))
|
||||
.build();
|
||||
}
|
||||
|
||||
UserDTO updatedUser = userService.updateUser(userId, user, realmName);
|
||||
return Response.ok(updatedUser).build();
|
||||
} catch (RuntimeException e) {
|
||||
if (e.getMessage().contains("non trouvé")) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
log.error("Erreur lors de la mise à jour de l'utilisateur {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return userService.updateUser(userId, user, realmName);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{userId}")
|
||||
@Operation(summary = "Supprimer un utilisateur", description = "Supprime un utilisateur (soft ou hard delete)")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Utilisateur supprimé"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin"})
|
||||
public Response deleteUser(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName,
|
||||
@QueryParam("hardDelete") @DefaultValue("false") boolean hardDelete
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin" })
|
||||
public void deleteUser(String userId, String realmName, boolean hardDelete) {
|
||||
log.info("DELETE /api/users/{} - realm: {}, hardDelete: {}", userId, realmName, hardDelete);
|
||||
|
||||
try {
|
||||
userService.deleteUser(userId, realmName, hardDelete);
|
||||
return Response.noContent().build();
|
||||
} catch (RuntimeException e) {
|
||||
if (e.getMessage().contains("non trouvé")) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
log.error("Erreur lors de la suppression de l'utilisateur {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
userService.deleteUser(userId, realmName, hardDelete);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{userId}/activate")
|
||||
@Operation(summary = "Activer un utilisateur", description = "Active un utilisateur désactivé")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Utilisateur activé"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response activateUser(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public void activateUser(String userId, String realmName) {
|
||||
log.info("POST /api/users/{}/activate", userId);
|
||||
|
||||
try {
|
||||
userService.activateUser(userId, realmName);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'activation de l'utilisateur {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
userService.activateUser(userId, realmName);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{userId}/deactivate")
|
||||
@Operation(summary = "Désactiver un utilisateur", description = "Désactive un utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Utilisateur désactivé"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response deactivateUser(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName,
|
||||
@QueryParam("raison") String raison
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public void deactivateUser(String userId, String realmName, String raison) {
|
||||
log.info("POST /api/users/{}/deactivate - raison: {}", userId, raison);
|
||||
|
||||
try {
|
||||
userService.deactivateUser(userId, realmName, raison);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la désactivation de l'utilisateur {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
userService.deactivateUser(userId, realmName, raison);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{userId}/reset-password")
|
||||
@Operation(summary = "Réinitialiser le mot de passe", description = "Définit un nouveau mot de passe pour l'utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Mot de passe réinitialisé"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response resetPassword(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName,
|
||||
@NotNull PasswordResetRequest request
|
||||
) {
|
||||
log.info("POST /api/users/{}/reset-password - temporary: {}", userId, request.temporary);
|
||||
|
||||
try {
|
||||
userService.resetPassword(userId, realmName, request.password, request.temporary);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la réinitialisation du mot de passe pour {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public void resetPassword(String userId, String realmName, @NotNull PasswordResetRequestDTO request) {
|
||||
log.info("POST /api/users/{}/reset-password - temporary: {}", userId, request.isTemporary());
|
||||
userService.resetPassword(userId, realmName, request.getPassword(), request.isTemporary());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{userId}/send-verification-email")
|
||||
@Operation(summary = "Envoyer email de vérification", description = "Envoie un email de vérification à l'utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Email envoyé"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response sendVerificationEmail(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public void sendVerificationEmail(String userId, String realmName) {
|
||||
log.info("POST /api/users/{}/send-verification-email", userId);
|
||||
|
||||
try {
|
||||
userService.sendVerificationEmail(userId, realmName);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'envoi de l'email de vérification pour {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
userService.sendVerificationEmail(userId, realmName);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{userId}/logout-sessions")
|
||||
@Operation(summary = "Déconnecter toutes les sessions", description = "Révoque toutes les sessions actives de l'utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Sessions révoquées"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
public Response logoutAllSessions(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public SessionsRevokedDTO logoutAllSessions(String userId, String realmName) {
|
||||
log.info("POST /api/users/{}/logout-sessions", userId);
|
||||
|
||||
try {
|
||||
int count = userService.logoutAllSessions(userId, realmName);
|
||||
return Response.ok(new SessionsRevokedResponse(count)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la déconnexion des sessions pour {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
int count = userService.logoutAllSessions(userId, realmName);
|
||||
return new SessionsRevokedDTO(count);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{userId}/sessions")
|
||||
@Operation(summary = "Récupérer les sessions actives", description = "Liste les sessions actives de l'utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des sessions"),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager", "user_viewer"})
|
||||
public Response getActiveSessions(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
@Override
|
||||
@RolesAllowed({ "admin", "user_manager", "user_viewer" })
|
||||
public List<String> getActiveSessions(String userId, String realmName) {
|
||||
log.info("GET /api/users/{}/sessions", userId);
|
||||
|
||||
try {
|
||||
List<String> sessions = userService.getActiveSessions(userId, realmName);
|
||||
return Response.ok(sessions).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des sessions pour {}", userId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return userService.getActiveSessions(userId, realmName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporter les utilisateurs en CSV
|
||||
*/
|
||||
@Override
|
||||
@GET
|
||||
@Path("/export/csv")
|
||||
@Operation(summary = "Exporter les utilisateurs en CSV")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Fichier CSV généré avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Realm manquant ou invalide"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager", "user_viewer"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public Response exportUsersToCSV(@QueryParam("realm") @NotBlank String realmName) {
|
||||
@jakarta.ws.rs.Path("/export/csv")
|
||||
@jakarta.ws.rs.Produces("text/csv")
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public Response exportUsersToCSV(@QueryParam("realm") String realmName) {
|
||||
log.info("GET /api/users/export/csv - realm: {}", realmName);
|
||||
|
||||
try {
|
||||
UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
|
||||
UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
|
||||
.realmName(realmName)
|
||||
.pageSize(10000) // Export complet sans pagination
|
||||
.page(0)
|
||||
.pageSize(10_000)
|
||||
.build();
|
||||
|
||||
String csvContent = userService.exportUsersToCSV(criteria);
|
||||
|
||||
String filename = "users_export_" +
|
||||
LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmmss")) +
|
||||
".csv";
|
||||
|
||||
return Response.ok(csvContent)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
|
||||
String csv = userService.exportUsersToCSV(criteria);
|
||||
return Response.ok(csv)
|
||||
.type(MediaType.valueOf("text/csv"))
|
||||
.header("Content-Disposition", "attachment; filename=\"users-" + (realmName != null ? realmName : "export") + ".csv\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'export CSV des utilisateurs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Importer des utilisateurs depuis CSV avec rapport détaillé
|
||||
*/
|
||||
@Override
|
||||
@POST
|
||||
@Path("/import/csv")
|
||||
@Operation(summary = "Importer des utilisateurs depuis un fichier CSV")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Import terminé avec rapport détaillé"),
|
||||
@APIResponse(responseCode = "400", description = "Fichier CSV vide ou invalide"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "user_manager"})
|
||||
@Consumes(MediaType.TEXT_PLAIN)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response importUsersFromCSV(
|
||||
@QueryParam("realm") @NotBlank String realmName,
|
||||
String csvContent) {
|
||||
@jakarta.ws.rs.Path("/import/csv")
|
||||
@jakarta.ws.rs.Consumes(MediaType.TEXT_PLAIN)
|
||||
@RolesAllowed({ "admin", "user_manager" })
|
||||
public ImportResultDTO importUsersFromCSV(@QueryParam("realm") String realmName, String csvContent) {
|
||||
log.info("POST /api/users/import/csv - realm: {}", realmName);
|
||||
|
||||
try {
|
||||
if (csvContent == null || csvContent.trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Le contenu CSV est vide"))
|
||||
.build();
|
||||
}
|
||||
|
||||
dev.lions.user.manager.dto.importexport.ImportResultDTO result = userService.importUsersFromCSV(csvContent, realmName);
|
||||
|
||||
log.info("{} utilisateur(s) importé(s) dans le realm {} ({} erreur(s))",
|
||||
result.getSuccessCount(), realmName, result.getErrorCount());
|
||||
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'import CSV des utilisateurs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== DTOs internes ====================
|
||||
|
||||
@Schema(description = "Requête de réinitialisation de mot de passe")
|
||||
public static class PasswordResetRequest {
|
||||
@Schema(description = "Nouveau mot de passe", required = true)
|
||||
public String password;
|
||||
|
||||
@Schema(description = "Indique si le mot de passe est temporaire", defaultValue = "true")
|
||||
public boolean temporary = true;
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse de révocation de sessions")
|
||||
public static class SessionsRevokedResponse {
|
||||
@Schema(description = "Nombre de sessions révoquées")
|
||||
public int count;
|
||||
|
||||
public SessionsRevokedResponse(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse d'erreur")
|
||||
public static class ErrorResponse {
|
||||
@Schema(description = "Message d'erreur")
|
||||
public String message;
|
||||
|
||||
public ErrorResponse(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
return userService.importUsersFromCSV(csvContent, realmName);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user