From 47daa459e2b2ea1e1c55f4caeceb4aa92ce6925e Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 19 Jul 2024 14:52:05 +0200 Subject: [PATCH] feat: better error message --- .../domain/service/AffiliationService.java | 29 ++++++++++--------- .../ffsaf/domain/service/ClubService.java | 18 ++++++------ .../ffsaf/domain/service/KeycloakService.java | 3 +- .../ffsaf/domain/service/LicenceService.java | 4 +-- .../ffsaf/domain/service/MembreService.java | 14 ++++----- .../ffsaf/rest/AffiliationEndpoints.java | 7 +++-- .../titionfire/ffsaf/rest/AssoEndpoints.java | 3 +- .../titionfire/ffsaf/rest/ClubEndpoints.java | 12 +++++--- .../titionfire/ffsaf/rest/CombEndpoints.java | 13 ++++++--- .../ffsaf/rest/CompteEndpoints.java | 3 +- .../ffsaf/rest/LicenceEndpoints.java | 3 +- .../rest/exception/DBadRequestException.java | 14 +++++++++ .../rest/exception/DForbiddenException.java | 18 ++++++++++++ .../ffsaf/rest/exception/DInternalError.java | 15 ++++++++++ .../rest/exception/DNotFoundException.java | 15 ++++++++++ .../ffsaf/rest/exception/DetailException.java | 20 +++++++++++++ .../rest/exception/DetailExceptionMapper.java | 19 ++++++++++++ .../exception/KeycloakExceptionMapper.java | 20 +++++++++++++ src/main/webapp/src/App.jsx | 8 +++++ src/main/webapp/src/hooks/useAuth.jsx | 2 ++ src/main/webapp/src/hooks/useCountries.jsx | 2 -- src/main/webapp/src/pages/DemandeAff.jsx | 26 +++++++++++++---- src/main/webapp/src/pages/MemberList.jsx | 8 +++-- .../admin/affiliation/AffiliationReqPage.jsx | 20 ++++++++++--- .../src/pages/admin/club/AffiliationCard.jsx | 14 +++++++-- .../webapp/src/pages/admin/club/ClubList.jsx | 8 +++-- .../webapp/src/pages/admin/club/ClubPage.jsx | 14 +++++++-- .../src/pages/admin/club/NewClubPage.jsx | 8 +++-- .../src/pages/admin/member/CompteInfo.jsx | 19 +++++++----- .../src/pages/admin/member/LicenceCard.jsx | 14 +++++++-- .../src/pages/admin/member/MemberPage.jsx | 8 +++-- .../webapp/src/pages/club/club/MyClubPage.jsx | 8 +++-- .../src/pages/club/member/LicenceCard.jsx | 14 +++++++-- .../src/pages/club/member/MemberPage.jsx | 8 +++-- src/main/webapp/src/utils/Tools.js | 5 ++++ 35 files changed, 327 insertions(+), 89 deletions(-) create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/DBadRequestException.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/DForbiddenException.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/DInternalError.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/DNotFoundException.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/DetailException.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/DetailExceptionMapper.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/exception/KeycloakExceptionMapper.java diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java index c380c44..09f864e 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java @@ -4,6 +4,8 @@ import fr.titionfire.ffsaf.data.model.*; import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation; +import fr.titionfire.ffsaf.rest.exception.DBadRequestException; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm; import fr.titionfire.ffsaf.utils.SequenceType; @@ -14,7 +16,6 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.ws.rs.NotFoundException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.hibernate.reactive.mutiny.Mutiny; @@ -60,42 +61,42 @@ public class AffiliationService { return Uni.createFrom().item(affModel) .invoke(Unchecked.consumer(model -> { if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) { - throw new IllegalArgumentException("Saison not valid"); + throw new DBadRequestException("Saison non valid"); } })) .chain(() -> repositoryRequest.count("siret = ?1 and saison = ?2", affModel.getSiret(), affModel.getSaison())) .onItem().invoke(Unchecked.consumer(count -> { if (count != 0 && unique) { - throw new IllegalArgumentException("Affiliation request already exists"); + throw new DBadRequestException("Demande d'affiliation déjà existante"); } })) .chain(() -> clubRepository.find("SIRET = ?1", affModel.getSiret()).firstResult().chain(club -> repository.count("club = ?1 and saison = ?2", club, affModel.getSaison()))) .onItem().invoke(Unchecked.consumer(count -> { if (count != 0) { - throw new IllegalArgumentException("Affiliation already exists"); + throw new DBadRequestException("Affiliation déjà existante"); } })) .map(o -> affModel) .call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence", model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> { if (count == 0) { - throw new IllegalArgumentException("Licence membre n°1 inconnue"); + throw new DBadRequestException("Licence membre n°1 inconnue"); } })) : Uni.createFrom().nullItem()) ) .call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence", model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> { if (count == 0) { - throw new IllegalArgumentException("Licence membre n°2 inconnue"); + throw new DBadRequestException("Licence membre n°2 inconnue"); } })) : Uni.createFrom().nullItem()) ) .call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence", model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> { if (count == 0) { - throw new IllegalArgumentException("Licence membre n°3 inconnue"); + throw new DBadRequestException("Licence membre n°3 inconnue"); } })) : Uni.createFrom().nullItem()) ); @@ -104,7 +105,7 @@ public class AffiliationService { public Uni saveEdit(AffiliationRequestForm form) { return pre_save(form, false) .chain(model -> repositoryRequest.findById(form.getId()) - .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .chain(origine -> { origine.setName(model.getName()); origine.setRNA(model.getRNA()); @@ -144,7 +145,7 @@ public class AffiliationService { public Uni saveAdmin(AffiliationRequestSaveForm form) { return repositoryRequest.findById(form.getId()) - .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .map(model -> { model.setName(form.getName()); model.setSiret(form.getSiret()); @@ -237,7 +238,7 @@ public class AffiliationService { public Uni accept(AffiliationRequestSaveForm form) { return repositoryRequest.findById(form.getId()) - .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .chain(req -> clubRepository.find("SIRET = ?1", form.getSiret()).firstResult() .chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model)) @@ -298,7 +299,7 @@ public class AffiliationService { public Uni getRequest(long id) { return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel) - .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .call(out -> clubRepository.find("SIRET = ?1", out.getSiret()).firstResult().invoke(c -> { if (c != null) { out.setClub(c.getId()); @@ -322,7 +323,7 @@ public class AffiliationService { public Uni> getAffiliation(long id) { return clubRepository.findById(id) - .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .onItem().ifNull().failWith(new DNotFoundException("Club non trouvé")) .call(model -> Mutiny.fetch(model.getAffiliations())) .chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET()) .map(reqs -> reqs.stream().map(req -> @@ -334,10 +335,10 @@ public class AffiliationService { public Uni setAffiliation(long id, int saison) { return clubRepository.findById(id) - .onItem().ifNull().failWith(new NotFoundException("Club non trouver")) + .onItem().ifNull().failWith(new DNotFoundException("Club non trouvé")) .invoke(Unchecked.consumer(club -> { if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) { - throw new IllegalArgumentException("Affiliation deja existante"); + throw new DBadRequestException("Affiliation déjà existante"); } })) .chain(club -> diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java index c624945..61aed55 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java @@ -13,6 +13,9 @@ import fr.titionfire.ffsaf.net2.request.SReqClub; import fr.titionfire.ffsaf.rest.data.DeskMember; import fr.titionfire.ffsaf.rest.data.RenewAffData; import fr.titionfire.ffsaf.rest.data.SimpleClubList; +import fr.titionfire.ffsaf.rest.exception.DBadRequestException; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.from.FullClubForm; import fr.titionfire.ffsaf.rest.from.PartClubForm; import fr.titionfire.ffsaf.utils.*; @@ -26,9 +29,6 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.ws.rs.BadRequestException; -import jakarta.ws.rs.ForbiddenException; -import jakarta.ws.rs.NotFoundException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.JsonWebToken; import org.hibernate.reactive.mutiny.Mutiny; @@ -105,7 +105,7 @@ public class ClubService { .call(result -> query.count().invoke(result::setResult_count)) .call(result -> query.pageCount() .invoke(Unchecked.consumer(pages -> { - if (page > pages) throw new BadRequestException(); + if (page > pages) throw new DBadRequestException("Page out of range"); })) .invoke(result::setPage_count)) .call(result -> query.page(Page.of(page, limit)).list() @@ -124,7 +124,7 @@ public class ClubService { public Uni getOfUser(JsonWebToken idToken) { return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> { if (m == null || m.getClub() == null) - throw new NotFoundException("Club not found"); + throw new DNotFoundException("Club non trouvé"); })) .map(MembreModel::getClub) .call(club -> Mutiny.fetch(club.getContact())); @@ -146,9 +146,9 @@ public class ClubService { return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> { if (m == null || m.getClub() == null) - throw new NotFoundException("Club not found"); + throw new DNotFoundException("Club non trouvé"); if (!GroupeUtils.isInClubGroup(m.getClub().getId(), idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); })) .map(MembreModel::getClub) .call(club -> Mutiny.fetch(club.getContact())) @@ -159,7 +159,7 @@ public class ClubService { try { club.setContact(MAPPER.readValue(form.getContact(), typeRef)); } catch (JsonProcessingException e) { - throw new BadRequestException(); + throw new DBadRequestException("Erreur de format des contacts"); } club.setTraining_location(form.getTraining_location()); @@ -191,7 +191,7 @@ public class ClubService { try { m.setContact(MAPPER.readValue(input.getContact(), typeRef)); } catch (JsonProcessingException e) { - throw new BadRequestException(); + throw new DBadRequestException("Erreur de format des contacts"); } } return Panache.withTransaction(() -> repository.persist(m)); diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java index cb83f09..ce78f51 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java @@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.domain.service; import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.model.MembreModel; +import fr.titionfire.ffsaf.rest.exception.DInternalError; import fr.titionfire.ffsaf.utils.*; import io.quarkus.runtime.annotations.RegisterForReflection; import io.smallrye.mutiny.Uni; @@ -78,7 +79,7 @@ public class KeycloakService { public Uni getUserFromMember(MembreModel membreModel) { if (membreModel.getUserId() == null) { return Uni.createFrom() - .failure(new NullPointerException("No keycloak user linked to the user id=" + membreModel.getId())); + .failure(new DInternalError("No keycloak user linked to the user id=" + membreModel.getId())); } return Uni.createFrom().item(membreModel::getUserId); } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java index 155a103..8bbccc4 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java @@ -5,6 +5,7 @@ import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.repository.CombRepository; import fr.titionfire.ffsaf.data.repository.LicenceRepository; import fr.titionfire.ffsaf.data.repository.SequenceRepository; +import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.from.LicenceForm; import fr.titionfire.ffsaf.utils.SequenceType; import fr.titionfire.ffsaf.utils.Utils; @@ -14,7 +15,6 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.ws.rs.BadRequestException; import org.eclipse.microprofile.jwt.JsonWebToken; import org.hibernate.reactive.mutiny.Mutiny; @@ -91,7 +91,7 @@ public class LicenceService { return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count() .invoke(Unchecked.consumer(count -> { if (count > 0) - throw new BadRequestException(); + throw new DBadRequestException("Licence déjà demandée"); })).chain(__ -> combRepository.findById(id).chain(combRepository -> { LicenceModel model = new LicenceModel(); model.setMembre(combRepository); diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java index 3b8074a..b09a107 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -11,6 +11,8 @@ import fr.titionfire.ffsaf.net2.request.SReqComb; import fr.titionfire.ffsaf.rest.data.MeData; import fr.titionfire.ffsaf.rest.data.SimpleLicence; import fr.titionfire.ffsaf.rest.data.SimpleMembre; +import fr.titionfire.ffsaf.rest.exception.DBadRequestException; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.from.ClubMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.utils.*; @@ -25,8 +27,6 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.ws.rs.BadRequestException; -import jakarta.ws.rs.ForbiddenException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.JsonWebToken; import org.hibernate.reactive.mutiny.Mutiny; @@ -101,7 +101,7 @@ public class MembreService { .call(result -> query.count().invoke(result::setResult_count)) .call(result -> query.pageCount() .invoke(Unchecked.consumer(pages -> { - if (page > pages) throw new BadRequestException(); + if (page > pages) throw new DBadRequestException("Page out of range"); })) .invoke(result::setPage_count)) .call(result -> query.page(Page.of(page, limit)).list() @@ -155,7 +155,7 @@ public class MembreService { return repository.findById(id) .invoke(Unchecked.consumer(membreModel -> { if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); })) .invoke(Unchecked.consumer(membreModel -> { RoleAsso source = RoleAsso.MEMBRE; @@ -163,7 +163,7 @@ public class MembreService { else if (securityIdentity.getRoles().contains("club_secretaire")) source = RoleAsso.SECRETAIRE; else if (securityIdentity.getRoles().contains("club_respo_intra")) source = RoleAsso.MEMBREBUREAU; if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level) - throw new ForbiddenException(); + throw new DForbiddenException("Permission insuffisante"); })) .onItem().transformToUni(target -> { target.setFname(membre.getFname()); @@ -225,12 +225,12 @@ public class MembreService { return repository.findById(id) .invoke(Unchecked.consumer(membreModel -> { if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); })) .call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count() .invoke(Unchecked.consumer(l -> { if (l > 0) - throw new BadRequestException(); + throw new DBadRequestException("Impossible de supprimer un membre avec des licences"); }))) .call(membreModel -> (membreModel.getUserId() != null) ? keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem()) diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java index a9fc32f..6e6448d 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java @@ -4,6 +4,7 @@ import fr.titionfire.ffsaf.domain.service.AffiliationService; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliationResume; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm; import fr.titionfire.ffsaf.utils.GroupeUtils; @@ -42,7 +43,7 @@ public class AffiliationEndpoints { Consumer checkPerm = Unchecked.consumer(id -> { if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); }); @GET @@ -68,7 +69,7 @@ public class AffiliationEndpoints { public Uni getAffRequest(@PathParam("id") long id) { return service.getRequest(id).invoke(Unchecked.consumer(o -> { if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin")) - throw new ForbiddenException(); + throw new DForbiddenException(); })).invoke(o -> checkPerm.accept(o.getClub())); } @@ -79,7 +80,7 @@ public class AffiliationEndpoints { public Uni getDelAffRequest(@PathParam("id") long id) { return service.getRequest(id).invoke(Unchecked.consumer(o -> { if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin")) - throw new ForbiddenException(); + throw new DForbiddenException(); })).invoke(o -> checkPerm.accept(o.getClub())) .chain(o -> service.deleteReqAffiliation(id)); } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java index 5868f4f..d739ca7 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java @@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.rest; import fr.titionfire.ffsaf.rest.client.SirenService; import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import io.smallrye.mutiny.Uni; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; @@ -20,7 +21,7 @@ public class AssoEndpoints { return sirenService.get_unite(siren).onFailure().transform(throwable -> { if (throwable instanceof WebApplicationException exception) { if (exception.getResponse().getStatus() == 400) - return new BadRequestException("Not found"); + return new DNotFoundException("Siret introuvable"); } return throwable; }); diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java index d7e6555..ef778ed 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java @@ -7,6 +7,8 @@ import fr.titionfire.ffsaf.rest.data.DeskMember; import fr.titionfire.ffsaf.rest.data.RenewAffData; import fr.titionfire.ffsaf.rest.data.SimpleClub; import fr.titionfire.ffsaf.rest.data.SimpleClubList; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.rest.exception.DInternalError; import fr.titionfire.ffsaf.rest.from.FullClubForm; import fr.titionfire.ffsaf.rest.from.PartClubForm; import fr.titionfire.ffsaf.utils.Contact; @@ -50,11 +52,11 @@ public class ClubEndpoints { Consumer checkPerm = Unchecked.consumer(clubModel -> { if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(clubModel.getId(), idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); }); Consumer checkPerm2 = Unchecked.consumer(id -> { if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); }); @GET @@ -112,7 +114,8 @@ public class ClubEndpoints { if (input.getLogo().length > 0) return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub" )).invoke(Unchecked.consumer(out -> { - if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); + if (!out.equals("OK")) + throw new DInternalError("Impossible de reconnaitre le fichier: " + out); })); else return Uni.createFrom().nullItem(); @@ -120,7 +123,8 @@ public class ClubEndpoints { if (input.getStatus().length > 0) return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus" )).invoke(Unchecked.consumer(out -> { - if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); + if (!out.equals("OK")) + throw new DInternalError("Impossible de reconnaitre le fichier: " + out); })); else return Uni.createFrom().nullItem(); diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java index 7d3b8c2..36c3492 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java @@ -4,6 +4,8 @@ import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.domain.service.MembreService; import fr.titionfire.ffsaf.rest.data.MeData; import fr.titionfire.ffsaf.rest.data.SimpleMembre; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.rest.exception.DInternalError; import fr.titionfire.ffsaf.rest.from.ClubMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.utils.GroupeUtils; @@ -46,7 +48,7 @@ public class CombEndpoints { Consumer checkPerm = Unchecked.consumer(membreModel -> { if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup( membreModel.getClub().getId(), idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); }); @GET @@ -110,12 +112,14 @@ public class CombEndpoints { public Uni setAdminMembre(@PathParam("id") long id, FullMemberForm input) { return membreService.update(id, input) .invoke(Unchecked.consumer(out -> { - if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); + if (!out.equals("OK")) + throw new DInternalError("Impossible de reconnaitre le fichier: " + out); })).chain(() -> { if (input.getPhoto_data().length > 0) return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre" )).invoke(Unchecked.consumer(out -> { - if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); + if (!out.equals("OK")) + throw new DInternalError("Impossible de reconnaitre le fichier: " + out); })); else return Uni.createFrom().nullItem(); @@ -160,7 +164,8 @@ public class CombEndpoints { if (input.getPhoto_data().length > 0) return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre" )).invoke(Unchecked.consumer(out -> { - if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); + if (!out.equals("OK")) + throw new DInternalError("Impossible de reconnaitre le fichier: " + out); })); else return Uni.createFrom().nullItem(); diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CompteEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CompteEndpoints.java index d05c38d..f6a5231 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/CompteEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/CompteEndpoints.java @@ -1,6 +1,7 @@ package fr.titionfire.ffsaf.rest; import fr.titionfire.ffsaf.domain.service.KeycloakService; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.from.MemberPermForm; import fr.titionfire.ffsaf.utils.GroupeUtils; import fr.titionfire.ffsaf.utils.Pair; @@ -38,7 +39,7 @@ public class CompteEndpoints { return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> { if (!securityIdentity.getRoles().contains("federation_admin") && pair.getKey().groups().stream().map(GroupRepresentation::getPath) .noneMatch(s -> s.startsWith("/club/") && GroupeUtils.contains(s, accessToken))) - throw new ForbiddenException(); + throw new DForbiddenException(); return pair; })).map(Pair::getValue); } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java index 9e4d069..91dd5fd 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java @@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.rest; import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.domain.service.LicenceService; import fr.titionfire.ffsaf.rest.data.SimpleLicence; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.from.LicenceForm; import fr.titionfire.ffsaf.utils.GroupeUtils; import io.quarkus.oidc.IdToken; @@ -33,7 +34,7 @@ public class LicenceEndpoints { Consumer checkPerm = Unchecked.consumer(membreModel -> { if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken)) - throw new ForbiddenException(); + throw new DForbiddenException(); }); @GET diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/DBadRequestException.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/DBadRequestException.java new file mode 100644 index 0000000..31a9be3 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/DBadRequestException.java @@ -0,0 +1,14 @@ +package fr.titionfire.ffsaf.rest.exception; + +import jakarta.ws.rs.core.Response; + +import java.io.Serial; + +public class DBadRequestException extends DetailException { + @Serial + private static final long serialVersionUID = 7518556311032332135L; + + public DBadRequestException(String message) { + super(Response.Status.BAD_REQUEST, message); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/DForbiddenException.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/DForbiddenException.java new file mode 100644 index 0000000..d95bf5b --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/DForbiddenException.java @@ -0,0 +1,18 @@ +package fr.titionfire.ffsaf.rest.exception; + +import jakarta.ws.rs.core.Response; + +import java.io.Serial; + +public class DForbiddenException extends DetailException { + @Serial + private static final long serialVersionUID = 8408920537659758038L; + + public DForbiddenException() { + this("Accès a la ressource interdite"); + } + + public DForbiddenException(String message) { + super(Response.Status.FORBIDDEN, message); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/DInternalError.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/DInternalError.java new file mode 100644 index 0000000..777fa99 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/DInternalError.java @@ -0,0 +1,15 @@ +package fr.titionfire.ffsaf.rest.exception; + +import jakarta.ws.rs.core.Response; + +import java.io.Serial; + +public class DInternalError extends DetailException { + + @Serial + private static final long serialVersionUID = -3635595157694180842L; + + public DInternalError(String message) { + super(Response.Status.INTERNAL_SERVER_ERROR, message); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/DNotFoundException.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/DNotFoundException.java new file mode 100644 index 0000000..9baab1d --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/DNotFoundException.java @@ -0,0 +1,15 @@ +package fr.titionfire.ffsaf.rest.exception; + +import jakarta.ws.rs.core.Response; + +import java.io.Serial; + +public class DNotFoundException extends DetailException{ + + @Serial + private static final long serialVersionUID = -5193524134675732376L; + + public DNotFoundException(String message) { + super(Response.Status.NOT_FOUND, message); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/DetailException.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/DetailException.java new file mode 100644 index 0000000..0af0f41 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/DetailException.java @@ -0,0 +1,20 @@ +package fr.titionfire.ffsaf.rest.exception; + +import jakarta.ws.rs.core.Response; +import lombok.Getter; + +import java.io.Serial; + +@Getter +public class DetailException extends Exception { + + @Serial + private static final long serialVersionUID = 5349921926328753676L; + + private final Response.Status status; + + public DetailException(Response.Status status, String message) { + super(message); + this.status = status; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/DetailExceptionMapper.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/DetailExceptionMapper.java new file mode 100644 index 0000000..c37f711 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/DetailExceptionMapper.java @@ -0,0 +1,19 @@ +package fr.titionfire.ffsaf.rest.exception; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +@Provider +public class DetailExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(DetailException e) { + return Response.status(e.getStatus()) + .entity(e.getMessage()) + .type(MediaType.TEXT_PLAIN_TYPE) + .build(); + } + +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/exception/KeycloakExceptionMapper.java b/src/main/java/fr/titionfire/ffsaf/rest/exception/KeycloakExceptionMapper.java new file mode 100644 index 0000000..bb40b9d --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/exception/KeycloakExceptionMapper.java @@ -0,0 +1,20 @@ +package fr.titionfire.ffsaf.rest.exception; + +import fr.titionfire.ffsaf.utils.KeycloakException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +@Provider +public class KeycloakExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(KeycloakException e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur du gestionnaire d'identité: " + e.getMessage()) + .type(MediaType.TEXT_PLAIN_TYPE) + .build(); + } + +} \ No newline at end of file diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index a2fe33a..aeee804 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -80,6 +80,14 @@ function Root() { check_validity(data => dispatch({type: 'init', val: data})) }, []); + + useEffect(() => { + const interval = setInterval(() => { + check_validity(data => dispatch({type: 'update', val: data})) + }, 1000 * 60 * 9) + return () => clearInterval(interval) + }, []); + return <>