Aller au contenu

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.

  1. L'élévation vers un persona staff passe UNIQUEMENT par match d'email avec un compte pré-créé (table équipe) — jamais par le claim role du SLT (ADR-0003 §5.1). Un inconnu reste consultant_client de son tenant.
  2. 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).
  3. 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é.
  4. 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 devient périmètre(persona, session) — centralisé dans rbac.py (une seule implémentation, testée).
  • Le pattern sso_link (ADR-0003) passe de direction à code : table SSOLink + 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.