Files
unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/security/OrganisationContextHolder.java
lionsdev d8006c8425
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m11s
feat(p0-2026-04-25): multi-référentiel comptable + UBO + audit trail + SoD + seuils AML
Sprint 1 P0 (consolidation 2026-04-25, ETAT_PROJET_METIER_2026-04-25.md) :

P0-NEW-9/10/11 — Multi-référentiel comptable
  - enum ReferentielComptable (SYSCOHADA / SYCEBNL / PCSFD_UMOA)
  - Organisation.referentielComptable + mapping defaultFor(typeOrganisation)
  - V43 : colonne + check + index + mapping initial des orgs existantes

P0-NEW-13 — Bénéficiaires effectifs (UBO) — Instruction BCEAO 003-03-2025
  - Entité BeneficiaireEffectif + repository
  - V44 : table beneficiaires_effectifs (FK kyc_dossier, UBO + PEP + sanctions)
  - Conservation 10 ans (directive 02/2015/CM/UEMOA)

P0-NEW-14 — Compliance Officer (Instruction BCEAO 001-03-2025)
  - Organisation.complianceOfficerId + V43 colonne + index

P0-NEW-15 — Seuils AML alignés (Instruction BCEAO 002-03-2025)
  - AmlSeuils : 10M FCFA intra-UEMOA / 5M FCFA entrée-sortie / 1M FCFA espèce
  - Liste pays UEMOA ISO 3166-1
  - Méthodes seuilApplicable() / depasseSeuil() / depasseSeuilEspece()

P0-NEW-17/18 — Rôles PRESIDENT + CONTROLEUR_INTERNE + suppléants
  - V45 seed : PRESIDENT, VICE_PRESIDENT, CONTROLEUR_INTERNE, ANIMATEUR_ZONE, SECRETAIRE_ADJOINT, TRESORIER_ADJOINT
  - Catégories GOUVERNANCE / CONTROLE / OPERATIONNEL

P0-NEW-19 — Audit trail enrichi (SYSCOHADA + AUDSCGIE)
  - V45 : table audit_trail_operations (acteur, action, contexte multi-org, payload JSONB, SoD)
  - Entité AuditTrailOperation + AuditTrailOperationRepository
  - AuditTrailService (log avec contexte automatique depuis OrganisationContextHolder)
  - OrganisationContextHolder enrichi (roleActif, currentUserId, currentUserEmail)

P0-NEW-20 — SoD (Separation of Duties) — SYSCOHADA + AUDSCGIE + BCEAO Circulaire 03-2017
  - SoDPermissionChecker.checkValidationDistinct() (4-eyes principle)
  - .checkRoleCombination() (combinaisons interdites : Trésorier+Président, etc.)
  - .checkComplianceOfficerEligibility() (Instruction BCEAO 001-03-2025)
  - SoDCheckResult record avec audit trail automatique

P0-NEW-24 — Champ numero_cmu sur Membre (Loi 2014-131 CI)
  - Membre.numeroCMU + V43 colonne + check format 11 caractères + index
  - Auto-déclaration (pas d'API publique CNAM disponible)

BUILD SUCCESS.
2026-04-25 01:15:25 +00:00

95 lines
2.5 KiB
Java

package dev.lions.unionflow.server.security;
import dev.lions.unionflow.server.entity.Organisation;
import jakarta.enterprise.context.RequestScoped;
import java.util.UUID;
/**
* Holder request-scoped contenant l'organisation active résolue pour la requête courante.
*
* <p>Peuplé par {@link OrganisationContextFilter} à partir du header
* {@code X-Active-Organisation-Id}. Utilisé par les services métier pour
* scoper toutes les opérations à l'organisation active.
*
* <p>Exemple d'utilisation dans un service :
* <pre>{@code
* @Inject OrganisationContextHolder orgContext;
*
* public List<Tontine> listTontines() {
* UUID orgId = orgContext.getOrganisationId();
* return tontineRepository.findByOrganisationId(orgId);
* }
* }</pre>
*/
@RequestScoped
public class OrganisationContextHolder {
private UUID organisationId;
private Organisation organisation;
private boolean resolved = false;
/** Rôle actif sélectionné par le user pour cette requête (header X-Active-Role). */
private String roleActif;
/** UUID de l'utilisateur courant (sub du JWT). */
private UUID currentUserId;
/** Email de l'utilisateur courant (claim email du JWT). */
private String currentUserEmail;
public String getRoleActif() {
return roleActif;
}
public void setRoleActif(String roleActif) {
this.roleActif = roleActif;
}
public UUID getCurrentUserId() {
return currentUserId;
}
public void setCurrentUserId(UUID currentUserId) {
this.currentUserId = currentUserId;
}
public String getCurrentUserEmail() {
return currentUserEmail;
}
public void setCurrentUserEmail(String currentUserEmail) {
this.currentUserEmail = currentUserEmail;
}
public UUID getOrganisationId() {
return organisationId;
}
public void setOrganisationId(UUID organisationId) {
this.organisationId = organisationId;
}
public Organisation getOrganisation() {
return organisation;
}
public void setOrganisation(Organisation organisation) {
this.organisation = organisation;
}
public boolean isResolved() {
return resolved;
}
public void setResolved(boolean resolved) {
this.resolved = resolved;
}
/**
* Retourne true si un contexte d'organisation est disponible.
*/
public boolean hasContext() {
return resolved && organisationId != null;
}
}