Aller au contenu

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.

  1. 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.
  2. Le mapping groupe → projet n'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.
  3. L'ADR « Bounded contexts » attribue Projet, mission et affectation à 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 projectionssourceRevision monotone ; 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 projetexpiresAt, TTL 15 min. Aucun second seuil de fraîcheur (§7).