ADR-0031 — Habilitation projet : affectation métier, traduction en droits, projection locale et enforcement¶
Date : 2026-08-29
Catégorie : B (sécurité / autorisation plateforme)
Décideur : Lead architecte DYNORS — à valider CTO
Statut : Proposée — validée architecte le 2026-08-29 ; validation CTO à programmer
Amende : [[0029-sirrat-auth-ldap-autorisation-operateur-projet]] — remplace sa décision 2
S'appuie sur : ADR « Bounded contexts de l'écosystème DYNORS » (contexte Delivery & Staffing → TAKKU) ; docs/architecture/GOUVERNANCE_OFFRES_CAPACITES_BILLING_ENTITLEMENTS.md (famille F4) ; ADR-0016 (cercles) ; ADR-0019 (couloirs)
Sources : applications/sirrat/backend/.../security/SirratCaller.java, .../SirratAccessGuard.java, .../model/SirratProjectPerimeter.java ; applications/takku/backend/.../model/TakkuAffectation.java, .../service/HabilitationPublisher.java ; dynors-platform/entitlements/
Contexte¶
L'ADR-0029 a posé, en août 2026, que l'accès-projet dans SIRRAT aurait pour source de vérité les
groupes LDAP, avec un mapping groupe → projet(s) « tenu côté plateforme (Forge/SIRRAT) ».
Trois faits, vérifiés depuis, rendent cette décision inapplicable telle quelle.
- La question ouverte n° 1 de la validation CTO est tranchée : GitLab n'est pas synchronisé sur LDAP. Aucune configuration d'authentification GitLab n'existe dans le dépôt. Par les termes mêmes de l'ADR-0029, la réconciliation GitLab est donc différée.
- Le mapping
groupe → projetn'a jamais existé — aucune table, aucun champ, aucun code. Le défaut que l'ADR corrigeait est donc resté actif : un opérateur authentifié pouvait demander une autorisation de déploiement pour n'importe quel projet. - L'ADR « Bounded contexts » attribue
Projet,missionetaffectationà TAKKU (contexte Delivery & Staffing), et la gouvernance F4 désigne « Back-office / TAKKU admin » comme émetteur des octrois. Deux textes indépendants placent donc la source ailleurs que dans l'annuaire.
À quoi s'ajoute une contrainte propre à SIRRAT : c'est l'outil avec lequel on répare la
plateforme. S'il faut que la plateforme fonctionne pour entrer dans SIRRAT, on ne répare rien.
Or dynors-entitlements est joignable uniquement à travers SLY (SlyTransitFilter sur /*),
par conception.
Décision¶
1. La chaîne de responsabilité¶
TAKKU affectation métier — qui travaille où, en quelle qualité, jusqu'à quand
↓
dynors-entitlements traduction en droits
↓
┌─────────────┬─────────────┐
↓ ↓
GitLab SIRRAT
accès au enforcement + projection locale
code
| Brique | Répond à | N'est pas |
|---|---|---|
| TAKKU | qui travaille où et en quelle qualité | l'autorité des habilitations |
| entitlements | quels droits en résultent | un référentiel de staffing |
| SIRRAT | applique le droit au plan de contrôle | un annuaire |
| GitLab | applique l'accès au code | une source d'habilitation |
Entitlements entre dans la chaîne cible dès maintenant. Aucun lien direct TAKKU → SIRRAT n'est retenu comme architecture de production. Le pont transitoire actuellement désactivé par défaut sert uniquement à valider le format de projection ; il doit disparaître avant activation RMOA/PROD au profit du flux TAKKU → entitlements → SIRRAT.
2. Le rôle est strictement attaché au projet¶
Il n'existe pas de « rôle de la personne ». Un rôle n'existe que dans le couple (personne, projet).
require(projet, cercle, action)
↓
rôle = perimetre[projet] ← absent ⇒ refus, sans repli
↓
action permise pour ce rôle dans ce cercle ?
Interdit : max(rôle sur A, rôle sur B). Une élévation sur un projet ne doit jamais produire
une élévation ailleurs — sinon, à 10 000 projets, une seule affectation privilégiée devient une
élévation transverse, ce que le périmètre projet existe précisément pour empêcher.
| Rôle projet | Voir | Composer | Déployer (hors production) |
|---|---|---|---|
DEVELOPER |
oui | oui | non |
OPS |
oui | oui | oui |
MANAGER |
non | non | non |
DEVELOPER peut composer sans déclencher un déploiement ; OPS peut composer et déployer.
La distinction est portée par une politique déclarative, jamais par le front ni par TAKKU.
MANAGER n'obtient aucune capacité SIRRAT. Il appartient au projet sans besoin opérationnel.
Aucun rôle de consultation n'est créé par anticipation ; le jour où un besoin réel apparaît
(consulter les versions déployées, l'état des couloirs), une capacité de lecture sera introduite
et justifiée.
Conséquence sur la résolution (ADR-0032 §3) : n'accordant aucune capacité, MANAGER ne
participe pas au calcul du rôle SIRRAT. Le faire entrer dans l'ordre de restriction
transformerait un fait de staffing non opérationnel en refus d'une habilitation légitime.
Un rôle qui n'accorde rien dans une application ne décide rien dans cette application.
3. Aucun joker projet, nulle part¶
La dimension projet n'admet pas de valeur *. Un accès transverse est une capacité
transverse explicitement nommée, jamais un périmètre projet élargi.
| Capacité transverse | Usage |
|---|---|
SIRRAT_PLATFORM_ENGINEER |
administration plateforme et Forge |
SIRRAT_BREAK_GLASS |
accès exceptionnel transverse du compte de secours |
SIRRAT_BREAK_GLASS est nominative, journalisée à chaque usage, et autorise le serveur à
court-circuiter le contrôle projet. C'est un concept entièrement distinct d'un rôle projet :
OPS sur DAWALALE et BREAK_GLASS transverse ne se rencontrent jamais.
SIRRAT_PLATFORM_OPS n'est pas créé. Tant qu'aucune équipe d'exploitation transverse
n'existe réellement, on ne crée pas le rôle qui lui correspondrait.
4. Accès à l'application ≠ accès aux données projet¶
Identité DYNORS authentifiée
→ SIRRAT NON-PROD s'ouvre : coquille, profil, messages de console
→ « Mes projets » : vide
→ aucune donnée projet lue
→ aucune écriture, aucun déploiement
Entrer dans SIRRAT NON-PROD n'est pas un droit à accorder. Ce qui est gardé, c'est la donnée projet. Les écrans qui en affichent filtrent (liste vide) plutôt qu'ils ne refusent (403) : une personne sans habilitation doit voir une application vide, pas une application cassée.
5. Projection locale dans SIRRAT — invariants¶
SIRRAT conserve une projection locale du périmètre (sirrat_project_perimeter), lue à chaque
requête. C'est ce qui lui permet d'appliquer un droit sans aucun appel sortant — condition de
sa disponibilité quand la plateforme est en panne.
Cette table n'est jamais source de vérité. Elle peut être supprimée intégralement et reconstruite depuis la source sans aucune perte d'information métier.
| Invariant | |
|---|---|
| ❌ | Aucune création manuelle |
| ❌ | Aucune édition depuis l'interface de SIRRAT |
| ❌ | Aucune autorité métier |
| ❌ | Aucune donnée éternelle |
| ✅ | source, sourceRef, expiresAt obligatoires |
| ✅ | Remplacement atomique par sujet |
| ✅ | Projection périmée ⇒ aucun droit |
| ✅ | Aucun repli permissif, jamais |
| ✅ | Reconstructible intégralement depuis la source |
Le dernier invariant impose deux opérations distinctes côté amont : la republication périodique (filet courant) et la reconstruction complète — qui doit couvrir toutes les affectations en cours, y compris celles à reconduction manuelle. Sans cette distinction, la phrase encadrée est fausse et la table redevient un membership de fait.
6. Cycle session / projection¶
La session ne porte plus le périmètre projet.
connexion
↓ la session porte : identité + capacités transverses
↓
chaque requête sur une donnée projet
↓ lecture de la projection LOCALE (base SIRRAT — aucun appel sortant)
↓
fraîche → périmètre appliqué
périmée → périmètre VIDE, utilisateur toujours authentifié
absente → périmètre VIDE, utilisateur toujours authentifié
| Valeur | Nature | |
|---|---|---|
| TTL de projection | 15 minutes | la borne de révocation |
| Durée de session | 8 heures maximum, expiration absolue | confort d'authentification |
La session n'est pas prolongeable par activité : au terme des 8 heures, il faut se reconnecter. Le coût de lecture reste faible — SIRRAT relit sa propre base, ce qui n'est pas une dépendance réseau.
7. Preuve fraîche — liste courte¶
| Acte | Exigence |
|---|---|
| Déploiement RMOA | projection fraîche + capacité requise |
Résolution / révélation d'un secret (vault://) |
projection fraîche + capacité explicite |
| Opération destructrice critique | réservé à une catégorie future si le besoin apparaît |
Pas de ré-authentification systématique : exiger une reconnexion à chaque déploiement RMOA serait vite pénible et donc contourné.
Explicitement hors liste : écriture sur DEV, consultation, édition courante, ajout d'un template à une recette. Y faire entrer les gestes ordinaires reviendrait à interroger la plateforme à chaque clic, et l'indépendance de SIRRAT disparaîtrait.
8. Minimisation — l'API rend la décision, pas le mécanisme¶
{
"projectKey": "dawalale",
"name": "DAWALALE",
"circles": ["dev", "int"],
"capabilities": { "compose": true, "deploy": true }
}
Le front ne code jamais la règle DEVELOPER ⇒ compose + deploy. La matrice
rôle × projet × cercle × action reste entièrement serveur. Le rôle n'est exposé que s'il
doit être affiché à l'utilisateur pour une raison fonctionnelle — pas pour piloter des
boutons.
Ne sortent jamais sans besoin démontré : clé primaire, UUID interne, apiToken, identifiant
d'entitlement, DN LDAP, sourceRef interne, règle d'autorisation brute. La même règle vaut pour
les messages d'erreur et les journaux renvoyés.
Ce que cette ADR amende dans l'ADR-0029¶
| Décision 0029 | Sort |
|---|---|
§1 — identité = LDAP, déléguée à dynors-auth ; SIRRAT ne se connecte jamais à l'annuaire |
conservée |
| §2 — « source de vérité de l'accès-projet = groupes LDAP » | remplacée — source métier = affectation TAKKU, traduite par entitlements |
§2 — mapping groupe LDAP → projet « côté plateforme (Forge/SIRRAT) » |
caduque — la relation personne↔projet est directe |
| §2 — « en cas de divergence, LDAP fait foi » | remplacée — l'affectation fait foi |
| §3 — enforcement dans SIRRAT, dashboard scopé | conservée |
| §4 — séparation opérateur / Tech Lead / CTO | conservée |
| §5 — SIRRAT n'est ni un annuaire ni un miroir de GitLab | conservée — elle fonde les invariants du §5 ci-dessus |
| Question ouverte n° 1 — GitLab↔LDAP synchronisé ? | tranchée : non (vérifié 2026-08-29) |
Conséquences¶
Positif. Moindre privilège réel et borné au projet ; aucune élévation transverse implicite ; révocation bornée à 15 minutes ; SIRRAT reste utilisable quand la plateforme est tombée ; chaque droit porte sa provenance et son échéance ; aucun annuaire parallèle.
Coût. Entitlements doit acquérir les scopes project:* et un chemin d'émission ; TAKKU doit
exposer l'affectation ; SIRRAT doit cesser de figer le périmètre dans la session ; la
republication doit se doubler d'une reconstruction complète.
Rupture à préparer. Aucun rôle n'étant plus accordé d'office, un déploiement qui n'en déclare
aucun donne une session sans capacité. La migration se fait sans jamais poser de rôle global :
un compte de secours nominatif portant SIRRAT_BREAK_GLASS, puis bascule sur la projection, puis
retrait de l'échafaudage.
Composants impactés. SirratCaller (périmètre par projet), SirratAccessGuard
(require(projet, cercle, action)), SirratHumanSessionService (la session cesse de porter le
périmètre), ProjectPerimeterService (TTL, reconstruction), DTO projet (capacités au lieu du
rôle), TakkuAffectation / HabilitationPublisher (émission vers entitlements),
dynors-entitlements (scopes projet, émission).
Points ouverts — tranchés le 2026-08-29¶
Les trois points sont résolus et détaillés dans [[0032-resolution-habilitation-user-group]].
| Décision | |
|---|---|
dynors-entitlements |
SubjectType.USER et SubjectType.GROUP dès maintenant — un GROUP représentant une équipe métier réelle, jamais une combinaison projet × rôle. Résolution, surcharge et conflits : ADR-0032 §3. |
| Chemin d'émission | Push entitlements → SIRRAT, instantané aplati (SIRRAT ne connaît ni les groupes ni les appartenances). SIRRAT n'émet toujours aucun appel sortant pour autoriser. ADR-0032 §5. |
| Recouvrement JARAAF ↔ TAKKU | TAKKU est propriétaire de la relation opérationnelle personne ↔ projet / mission ; JARAAF consomme. ADR-0032 §8. |
Deux compléments imposés par la revue, également portés par l'ADR-0032 :
- Ordre et idempotence des projections —
sourceRevisionmonotone ; une projection plus ancienne ne peut jamais écraser une plus récente (§6). Indispensable dès lors que push, republication et reconstruction coexistent. - Une seule horloge d'autorisation projet —
expiresAt, TTL 15 min. Aucun second seuil de fraîcheur (§7).