ADR-0006 — Personas staff cross-tenant (Jalon 3, audit en chaîne)¶
| Statut | ✅ Accepté — 2026-06-11 (validé par la Direction, avec ADR-0003) |
| Portée | backend/ (RBAC, scoping), workflow des demandes, Jalon 3 |
| Décision | Les personas staff (actuaire_junior, actuaire_senior, actuaire_externe) traversent les tenants clients, chacun selon son périmètre ; le consultant_client reste strictement scopé à son tenant. Tout accès staff à un tenant tiers est journalisé. |
| Réf. | ADR-0003 (Levier C), ADR-0005 (tenant = groupe), Quantis-app/docs/IDENTITE_FEDEREE.md §5 |
🎯 À retenir
Les personas staff (actuaire_junior, actuaire_senior, actuaire_externe) traversent les tenants clients, chacun selon son périmètre de workflow ; le consultant_client reste scopé à son tenant (404 sinon). Le filtre des listes backend passe de tenant_schema à périmètre(persona, session), centralisé dans rbac.py. Tout accès staff à un tenant tiers est journalisé.
Contexte¶
Depuis le Jalon 0, la règle d'isolation est absolue : toute ressource est scopée
tenant_schema, un accès cross-tenant répond 404. Cette règle est correcte
pour les utilisateurs clients — mais le Jalon 3 introduit le workflow d'audit
en chaîne, opéré par les actuaires du cabinet (junior, senior) et un
actuaire externe mandaté.
Or ces utilisateurs arrivent par la chaîne SSO avec leur tenant (les
internes BFEV portent un tenant_schema BFEV, pas celui du client). Si la
règle « tout est scopé au tenant de session » restait absolue, un Senior ne
verrait jamais les demandes des clients : le workflow serait impossible.
C'est le problème déjà rencontré (et résolu) par EmpreinteCarbone : ses
personas Expert/Admin sont globaux au module, non scopés tenant — seule
l'élévation par match d'email (jamais le claim role) y donne accès (§5.1).
Options considérées¶
- A — Personas staff cross-tenant, consultant scopé : le RBAC du module donne au staff un périmètre défini par le workflow (assignation, transmission), pas par le tenant de session.
- B — Inscrire les actuaires comme membres de chaque tenant client (côté Quantis) : l'isolation 404 resterait absolue.
- C — Tenant « cabinet » miroir : dupliquer les demandes des clients dans un tenant BFEV de travail.
| Critère | A — Staff cross-tenant | B — Membre de chaque tenant | C — Miroir cabinet |
|---|---|---|---|
| Vérité contractuelle | ✅ l'actuaire n'est PAS un employé du client | ❌ fausse appartenance | ⚠️ ambiguë |
| Charge d'administration | ✅ nulle (le workflow porte le périmètre) | ❌ N tenants × M actuaires à maintenir | ⚠️ synchronisation |
| Confidentialité | ✅ périmètre par rôle + journalisation | ⚠️ l'actuaire devient un user du client | ❌ données dupliquées |
| Cohérence écosystème | ✅ même modèle qu'EC (Expert/Admin globaux) | ❌ inédit | ❌ inédit |
| Risque de fuite cross-tenant | ⚠️ à contenir par le RBAC (cf. garde-fous) | ✅ mécanique | ⚠️ via le miroir |
Décision¶
Option A. Le périmètre de visibilité est défini par persona, le RBAC du module fait foi :
| Persona | Périmètre | Source du droit |
|---|---|---|
consultant_client |
son tenant uniquement (404 sinon — inchangé) | tenant de session |
actuaire_junior |
les demandes qui lui sont assignées | assignation par un senior |
actuaire_senior |
toutes les demandes, tous tenants | pré-création par email (équipe) |
actuaire_externe |
les demandes qui lui sont transmises pour audit | transmission par un senior |
Garde-fous (font partie de la décision)¶
⚠️ Piège
Donner au staff un périmètre cross-tenant ouvre un risque de fuite : il se contient par le RBAC, pas par l'isolation tenant. Élévation staff par match d'email uniquement (jamais le claim role), périmètre minimal (junior/externe ne voient que ce qui leur est assigné/transmis), et journalisation immuable de toute action sur un tenant tiers.
- L'élévation vers un persona staff passe UNIQUEMENT par match d'email
avec un compte pré-créé (table équipe) — jamais par le claim
roledu SLT (ADR-0003 §5.1). Un inconnu resteconsultant_clientde son tenant. - Journalisation : toute action staff sur une demande d'un tenant tiers
écrit un
EvenementDemande(acteur, persona, action, horodatage). Le journal est immuable (jamais de DELETE — cohérent vision étage 1). - Périmètre minimal : junior et externe ne reçoivent jamais « tout » ; leur visibilité découle d'un acte explicite d'un senior (assignation / transmission), lui-même journalisé.
- Le client ne voit pas la cuisine interne : les événements portent un
drapeau
visible_client; les critiques de revue restent internes.
Conséquences¶
- Les listes backend ne filtrent plus uniquement par
tenant_schema: le filtre devientpérimètre(persona, session)— centralisé dansrbac.py(une seule implémentation, testée). - Le pattern
sso_link(ADR-0003) passe de direction à code : tableSSOLink+ résolution au login (lien existant → routage ; email pré-créé → élévation ; défaut →consultant_client). - Bootstrap : le(s) premier(s) senior(s) sont seedés par variable d'env
(
SENIOR_EMAILS) ; ensuite l'écran « Équipe » (réservé senior) pré-crée juniors et externes.
Déclencheur de réexamen¶
Si un client exige contractuellement que ses données ne soient visibles que
d'une liste nominative d'actuaires (et non « tout senior »), introduire une
ACL par tenant (liste d'accès) par-dessus ce modèle — le RBAC centralisé de
rbac.py est le point d'insertion prévu.