feat: sécuriser endpoints organisations + stats par org + fix IP dev

- PUT /{id}: check appartenance ADMIN_ORGANISATION (403 si pas membre)
- GET /statistiques: stats scoped à l'org active pour ADMIN_ORGANISATION
- OrganisationService.obtenirStatistiquesParOrganisation(): stats mono-org
- MembreOrganisationRepository.findByMembreEmailAndOrganisationId()
- application-dev: IP 192.168.1.145→localhost pour Keycloak
This commit is contained in:
dahoud
2026-04-18 08:07:04 +00:00
parent 9f14c2e345
commit 9a53ce4077
4 changed files with 83 additions and 2 deletions

View File

@@ -17,9 +17,12 @@ import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
import dev.lions.unionflow.server.security.OrganisationContextHolder;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.microprofile.openapi.annotations.Operation;
@@ -59,6 +62,8 @@ public class OrganisationResource {
@Inject
dev.lions.unionflow.server.repository.MembreOrganisationRepository membreOrganisationRepository;
@Inject OrganisationContextHolder organisationContextHolder;
/** Récupère les organisations du membre connecté (pour admin d'organisation) */
@GET
@Path("/mes")
@@ -274,6 +279,27 @@ public class OrganisationResource {
LOG.infof("Mise à jour de l'organisation ID: %s", id);
// Ownership check: ADMIN_ORGANISATION can only update their own org
Set<String> roles = securityIdentity.getRoles();
boolean isOrgAdmin = roles.contains("ADMIN_ORGANISATION")
&& !roles.contains("ADMIN")
&& !roles.contains("SUPER_ADMIN");
if (isOrgAdmin) {
String email = securityIdentity.getPrincipal() != null
? securityIdentity.getPrincipal().getName()
: null;
boolean belongsToOrg = email != null
&& membreOrganisationRepository.findByMembreEmailAndOrganisationId(email, id)
.filter(mo -> StatutMembre.ACTIF.equals(mo.getStatutMembre()))
.isPresent();
if (!belongsToOrg) {
LOG.warnf("ADMIN_ORGANISATION %s attempted to update org %s without membership", email, id);
return Response.status(Response.Status.FORBIDDEN)
.entity(Map.of("error", "Vous n'êtes pas membre actif de cette organisation"))
.build();
}
}
try {
Organisation organisationMiseAJour = organisationService.convertFromUpdateRequest(request);
Organisation organisation =
@@ -487,6 +513,19 @@ public class OrganisationResource {
LOG.info("Récupération des statistiques des organisations");
try {
// ADMIN_ORGANISATION (without ADMIN/SUPER_ADMIN) → scope to their active org
Set<String> roles = securityIdentity.getRoles();
boolean isOrgAdmin = roles.contains("ADMIN_ORGANISATION")
&& !roles.contains("ADMIN")
&& !roles.contains("SUPER_ADMIN");
if (isOrgAdmin && organisationContextHolder.hasContext()) {
UUID orgId = organisationContextHolder.getOrganisationId();
LOG.infof("ADMIN_ORGANISATION: scoping statistiques to org %s", orgId);
Map<String, Object> statistiques = organisationService.obtenirStatistiquesParOrganisation(orgId);
return Response.ok(statistiques).build();
}
Map<String, Object> statistiques = organisationService.obtenirStatistiques();
return Response.ok(statistiques).build();
} catch (Exception e) {