Files
lions-user-manager-server-i…/src/main/java/dev/lions/user/manager/resource/UserResource.java
dahoud 8ab1513bf5 feat(lum): KeycloakRealmSetupService + rôles RBAC UnionFlow + Jacoco 100%
- Ajoute KeycloakRealmSetupService : auto-initialisation des rôles realm
  (admin, user_manager, user_viewer, role_manager...) et assignation du rôle
  user_manager au service account unionflow-server au démarrage (idempotent,
  retries, thread séparé pour ne pas bloquer le démarrage)
  → Corrige le 403 sur resetPassword / changement de mot de passe premier login

- UserResource : étend les @RolesAllowed avec ADMIN/SUPER_ADMIN/USER pour
  permettre aux appels inter-services unionflow-server d'accéder aux endpoints
  sans être bloqués par le RBAC LUM ; corrige sendVerificationEmail (retourne Response)

- application-dev.properties : service-accounts.user-manager-clients=unionflow-server
- application-prod.properties : client-id, credentials.secret, token.audience, auto-setup
- application-test.properties : H2 in-memory (plus besoin de Docker pour les tests)
- pom.xml : H2 scope test, Jacoco 100% enforcement (exclusions MapStruct/repos/setup),
  annotation processors MapStruct+Lombok explicites
- .gitignore + .env ajouté (.env exclu du commit)
- script/docker/.env.example : variables KEYCLOAK_ADMIN_USERNAME/PASSWORD documentées
2026-04-12 15:04:23 +00:00

163 lines
6.7 KiB
Java

package dev.lions.user.manager.resource;
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.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
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 java.util.List;
/**
* REST Resource pour la gestion des utilisateurs
* Implémente l'interface API commune.
*/
@Slf4j
@jakarta.enterprise.context.ApplicationScoped
@jakarta.ws.rs.Path("/api/users")
public class UserResource implements UserResourceApi {
@Inject
UserService userService;
@Override
@RolesAllowed({ "admin", "user_manager", "ADMIN", "SUPER_ADMIN" })
public UserSearchResultDTO searchUsers(@Valid @NotNull UserSearchCriteriaDTO criteria) {
log.info("POST /api/users/search - Recherche d'utilisateurs");
return userService.searchUsers(criteria);
}
@Override
@RolesAllowed({ "admin", "user_manager", "user_viewer", "ADMIN", "SUPER_ADMIN", "USER" })
public UserDTO getUserById(String userId, String realmName) {
log.info("GET /api/users/{} - realm: {}", userId, realmName);
return userService.getUserById(userId, realmName)
.orElseThrow(() -> new RuntimeException("Utilisateur non trouvé")); // ExceptionMapper should handle/map
// to 404
}
@Override
@RolesAllowed({ "admin", "user_manager", "user_viewer", "ADMIN", "SUPER_ADMIN", "USER" })
public UserSearchResultDTO getAllUsers(String realmName, int page, int pageSize) {
log.info("GET /api/users - realm: {}, page: {}, pageSize: {}", realmName, page, pageSize);
return userService.getAllUsers(realmName, page, pageSize);
}
@Override
@RolesAllowed({ "admin", "user_manager", "ADMIN", "SUPER_ADMIN" })
public Response createUser(@Valid @NotNull UserDTO user, String realmName) {
log.info("POST /api/users - Création d'un utilisateur: {}", user.getUsername());
try {
UserDTO createdUser = userService.createUser(user, realmName);
return Response.status(Response.Status.CREATED).entity(createdUser).build();
} catch (IllegalArgumentException e) {
log.warn("Données invalides lors de la création: {}", e.getMessage());
return Response.status(Response.Status.CONFLICT)
.entity(new ApiErrorDTO(e.getMessage()))
.build();
} catch (Exception e) {
log.error("Erreur lors de la création de l'utilisateur", e);
throw new RuntimeException(e);
}
}
@Override
@RolesAllowed({ "admin", "user_manager", "ADMIN", "SUPER_ADMIN" })
public UserDTO updateUser(String userId, @Valid @NotNull UserDTO user, String realmName) {
log.info("PUT /api/users/{} - Mise à jour", userId);
return userService.updateUser(userId, user, realmName);
}
@Override
@RolesAllowed({ "admin", "ADMIN", "SUPER_ADMIN" })
public void deleteUser(String userId, String realmName, boolean hardDelete) {
log.info("DELETE /api/users/{} - realm: {}, hardDelete: {}", userId, realmName, hardDelete);
userService.deleteUser(userId, realmName, hardDelete);
}
@Override
@RolesAllowed({ "admin", "user_manager", "ADMIN", "SUPER_ADMIN" })
public void activateUser(String userId, String realmName) {
log.info("POST /api/users/{}/activate", userId);
userService.activateUser(userId, realmName);
}
@Override
@RolesAllowed({ "admin", "user_manager", "ADMIN", "SUPER_ADMIN" })
public void deactivateUser(String userId, String realmName, String raison) {
log.info("POST /api/users/{}/deactivate - raison: {}", userId, raison);
userService.deactivateUser(userId, realmName, raison);
}
@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());
}
@Override
@RolesAllowed({ "admin", "user_manager" })
public Response sendVerificationEmail(String userId, String realmName) {
log.info("POST /api/users/{}/send-verification-email", userId);
userService.sendVerificationEmail(userId, realmName);
return Response.accepted().build();
}
@Override
@RolesAllowed({ "admin", "user_manager" })
public SessionsRevokedDTO logoutAllSessions(String userId, String realmName) {
log.info("POST /api/users/{}/logout-sessions", userId);
int count = userService.logoutAllSessions(userId, realmName);
return new SessionsRevokedDTO(count);
}
@Override
@RolesAllowed({ "admin", "user_manager", "user_viewer" })
public List<String> getActiveSessions(String userId, String realmName) {
log.info("GET /api/users/{}/sessions", userId);
return userService.getActiveSessions(userId, realmName);
}
@Override
@GET
@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);
UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
.realmName(realmName)
.page(0)
.pageSize(10_000)
.build();
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();
}
@Override
@POST
@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);
return userService.importUsersFromCSV(csvContent, realmName);
}
}