package fr.titionfire.ffsaf.domain.service; import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.repository.CompetitionRepository; import fr.titionfire.ffsaf.data.repository.RegisterRepository; import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleCompet; import fr.titionfire.ffsaf.net2.request.SReqCompet; import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.RegisterMode; import fr.titionfire.ffsaf.utils.SecurityCtx; import io.quarkus.cache.Cache; import io.quarkus.cache.CacheName; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @ApplicationScoped public class CompetPermService { @Inject ServerCustom serverCustom; @Inject CompetitionRepository competitionRepository; @Inject @CacheName("safca-config") Cache cache; @Inject @CacheName("safca-have-access") Cache cacheAccess; @Inject @CacheName("have-access") Cache cacheNoneAccess; @Inject RegisterRepository registerRepository; public Uni getSafcaConfig(long id) { return cache.get(id, k -> { CompletableFuture f = new CompletableFuture<>(); SReqCompet.getConfig(serverCustom.clients, id, f); try { return f.get(500, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new RuntimeException(e); } }); } public Uni> getAllHaveAdminAccess(SecurityCtx securityCtx) { ArrayList out = new ArrayList<>(); Uni> safca = cacheAccess.getAsync(securityCtx.getSubject(), k -> competitionRepository.list("system = ?1", CompetitionSystem.SAFCA) .chain(competitionModels -> { CompletableFuture> f = new CompletableFuture<>(); SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f); return Uni.createFrom().future(f, Duration.ofMillis(500)) .onFailure().recoverWithItem(new HashMap<>()) .map(map_ -> { HashMap map = new HashMap<>(); map_.forEach((key, value) -> map.put(Long.parseLong(key), value)); for (CompetitionModel model : competitionModels) { if (model.getOwner().equals(securityCtx.getSubject())) map.putIfAbsent(model.getId(), "owner"); else if (securityCtx.roleHas("federation_admin") || securityCtx.roleHas("safca_super_admin")) map.putIfAbsent(model.getId(), "admin"); } return map; }); })) .onFailure().call(throwable -> cacheAccess.invalidate(securityCtx.getSubject())); Uni> none = cacheNoneAccess.getAsync(securityCtx.getSubject(), k -> competitionRepository.list("system = ?1", CompetitionSystem.INTERNAL) .map(competitionModels -> { HashMap map = new HashMap<>(); for (CompetitionModel model : competitionModels) { if (model.getOwner().equals(securityCtx.getSubject())) map.putIfAbsent(model.getId(), "owner"); else if (securityCtx.roleHas("federation_admin")) map.putIfAbsent(model.getId(), "admin"); else if (securityCtx.isInClubGroup(model.getClub().getId()) && (securityCtx.roleHas( "club_president") || securityCtx.roleHas("club_respo_intra") || securityCtx.roleHas( "club_secretaire") || securityCtx.roleHas("club_tresorier"))) map.putIfAbsent(model.getId(), "admin"); } return map; })); return safca.invoke(map -> map.forEach((k, v) -> { if (v.equals("owner") || v.equals("admin")) out.add(k); }) ) .call(__ -> none.invoke(map -> map.forEach((k, v) -> { if (v.equals("owner") || v.equals("admin")) out.add(k); }) )) .map(__ -> out.stream().distinct().toList()); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm */ public Uni hasViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { return hasViewPerm(securityCtx, Uni.createFrom().item(competitionModel)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm */ public Uni hasViewPerm(SecurityCtx securityCtx, long id) { return hasViewPerm(securityCtx, competitionRepository.findById(id)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm */ public Uni hasViewPerm(SecurityCtx securityCtx, Uni in) { return in.call(cm -> (cm.isPublicVisible() || cm.getRegisterMode() == RegisterMode.FREE || cm.getRegisterMode() == RegisterMode.HELLOASSO || (cm.getRegisterMode() == RegisterMode.CLUB_ADMIN && securityCtx.isClubAdmin())) ? Uni.createFrom().nullItem() : hasAdminViewPerm(securityCtx, cm).onFailure() .recoverWithUni(__ -> registerRepository.count("membre.userId = ?1 AND competition = ?2", securityCtx.getSubject(), cm).map(Unchecked.function(c -> { if (c == 0) throw new DForbiddenException(); return cm; })) )); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm */ public Uni hasAdminViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { return hasAdminViewPerm(securityCtx, Uni.createFrom().item(competitionModel)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm */ public Uni hasAdminViewPerm(SecurityCtx securityCtx, long id) { return hasAdminViewPerm(securityCtx, competitionRepository.findById(id)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm */ public Uni hasAdminViewPerm(SecurityCtx securityCtx, Uni in) { return in.call(Unchecked.function(o -> { if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) return Uni.createFrom().nullItem(); if (o.getSystem() == CompetitionSystem.SAFCA) return hasSafcaViewPerm(securityCtx, o.getId()); if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here throw new DForbiddenException(); if (o.getSystem() == CompetitionSystem.INTERNAL) if (securityCtx.roleHas("club_president") || securityCtx.roleHas("club_respo_intra") || securityCtx.roleHas("club_secretaire") || securityCtx.roleHas("club_tresorier")) return Uni.createFrom().nullItem(); throw new DForbiddenException(); }) ); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm */ public Uni hasEditPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { return hasEditPerm(securityCtx, Uni.createFrom().item(competitionModel)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm */ public Uni hasEditPerm(SecurityCtx securityCtx, long id) { return hasEditPerm(securityCtx, competitionRepository.findById(id)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm */ public Uni hasEditPerm(SecurityCtx securityCtx, Uni in) { return in.call(Unchecked.function(o -> { if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) return Uni.createFrom().nullItem(); if (o.getSystem() == CompetitionSystem.SAFCA) return hasSafcaEditPerm(securityCtx, o.getId()); if (o.getSystem() == CompetitionSystem.INTERNAL) { if (securityCtx.isInClubGroup(o.getClub().getId()) && securityCtx.isClubAdmin()) return Uni.createFrom().nullItem(); if (o.getAdmin().contains(securityCtx.getSubject())) return Uni.createFrom().nullItem(); throw new DForbiddenException(); } throw new DForbiddenException(); }) ); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm */ public Uni hasTablePerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { return hasTablePerm(securityCtx, Uni.createFrom().item(competitionModel)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm */ public Uni hasTablePerm(SecurityCtx securityCtx, long id) { return hasTablePerm(securityCtx, competitionRepository.findById(id)); } /** * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm */ public Uni hasTablePerm(SecurityCtx securityCtx, Uni in) { return in.call(Unchecked.function(o -> { if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) return Uni.createFrom().nullItem(); if (o.getSystem() == CompetitionSystem.SAFCA) return hasSafcaTablePerm(securityCtx, o.getId()); if (o.getSystem() == CompetitionSystem.INTERNAL) { if (securityCtx.isInClubGroup(o.getClub().getId()) && securityCtx.isClubAdmin()) return Uni.createFrom().nullItem(); if (o.getAdmin().contains(securityCtx.getSubject())) return Uni.createFrom().nullItem(); if (o.getTable().contains(securityCtx.getSubject())) return Uni.createFrom().nullItem(); throw new DForbiddenException(); } throw new DForbiddenException(); }) ); } private Uni hasSafcaViewPerm(SecurityCtx securityCtx, long id) { return securityCtx.roleHas("safca_super_admin") ? Uni.createFrom().nullItem() : getSafcaConfig(id).chain(Unchecked.function(o -> { if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) && !o.table().contains(UUID.fromString(securityCtx.getSubject()))) throw new DForbiddenException(); return Uni.createFrom().nullItem(); })); } private Uni hasSafcaEditPerm(SecurityCtx securityCtx, long id) { return securityCtx.roleHas("safca_super_admin") ? Uni.createFrom().nullItem() : getSafcaConfig(id).chain(Unchecked.function(o -> { if (!o.admin().contains(UUID.fromString(securityCtx.getSubject()))) throw new DForbiddenException(); return Uni.createFrom().nullItem(); })); } private Uni hasSafcaTablePerm(SecurityCtx securityCtx, long id) { return securityCtx.roleHas("safca_super_admin") ? Uni.createFrom().nullItem() : getSafcaConfig(id).chain(Unchecked.function(o -> { if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) && !o.table().contains(UUID.fromString(securityCtx.getSubject()))) throw new DForbiddenException(); return Uni.createFrom().nullItem(); })); } }