docs(adr-0025): promote to accepted + sync persona matrix with test tenant (#205)
CI / check (push) Successful in 2m0s
CI / commits (push) Has been skipped
CI / scan (push) Failing after 1m54s
CI / a11y (push) Successful in 1m44s
CI / perf (push) Successful in 3m49s
Docs site / build (push) Successful in 2m45s

## Summary

ADR-0025 was merged as `proposed` in #201. The test tenant (`apfrd.onmicrosoft.com`) has since been provisioned with the full role / user matrix — 4 privileges + 24 security groups + 19 users with all assignments per the persona table. The ADR is now the implementation reference, so this PR:

1. Promotes the ADR from `proposed` to `accepted`.
2. Syncs the document with what is actually in place — the privilege catalogue grows from 1 to 4 entries (the previously "anticipated future" privileges that the test tenant already has), and the persona matrix grows from 10 to 19 entries (so every one of the 24 functional-role groups has at least one member, closing the gap that prompted `notes/test-tenant-role-assignments.md` and `notes/entra-group-members.md`).
3. Records the Entra app-role GUIDs in a new "Provisioned in the test tenant" subsection for traceability — the GUIDs are stable IDs the implementation will need.
4. Updates the index + the `CLAUDE.md` roll-up.

No code changes. No implementation skeleton — that lands in the next PR (proposed: `feat(libs/feature/auth): authorization types + Principal builder skeleton`).

## What lands

| File | Change |
|---|---|
| `docs/decisions/0025-authorization-model-privileges-roles-scopes.md` | Frontmatter `status: proposed → accepted`; privilege catalogue extended from 1 to 4 entries; persona matrix rewritten from 10 to 19 entries; new `Provisioned in the test tenant (2026-05-20)` subsection capturing the four app-role GUIDs. |
| `docs/decisions/README.md` | 0025 row: `proposed → accepted`. |
| `CLAUDE.md` | Roll-up `0001 → 0024 accepted` → `0001 → 0025 accepted`; Architecture list grows an "Authorization model" bullet. |

The two operator-facing notes files (`notes/test-tenant-role-assignments.md`, `notes/entra-group-members.md`) are gitignored and unchanged — they served their purpose during tenant provisioning and remain as runbooks.

## Notes for the reviewer

- **Why promote now rather than couple with the first implementation PR (the pattern from #194#195#196).** The Entra-side provisioning *is* the implementation for this ADR — the next PR is a portal-side reflection of decisions that are already concrete in the tenant. Promoting now keeps the document honest about what the test tenant runs against.
- **Why all 4 privileges enter the v1 catalogue.** The ADR originally shipped `Portal.Admin` as the sole v1 entry and listed the other three under "anticipated near-future entries". The test tenant has all four; the catalogue should match. The three new entries are explicitly marked "provisioned; consumer surface deferred" so a reader does not look for non-existent surfaces.
- **Why 19 personas, not the cleaner 24 (one per role).** Several APF jobs genuinely combine multiple roles (RH siège often handles paie + compta; DPO often wears the quality officer hat; local delegates often grow out of volunteer roles). Densifying these existing personas is more faithful to real APF org structure than inventing 14 single-role test users. Distinct personas were created where the *scenario* is distinct (scope variations, governance positions) — see the matrix in the ADR for the breakdown.
- **Why `apf-role-partenaire` stays empty in v1.** Placeholder per the original ADR; no consuming surface to test against. The group exists in Entra so the schema is locked, but a user assignment without a guard to exercise would be theatre. The first partner-facing feature adds the user.
- **GUIDs in the ADR.** The four app-role GUIDs are repo-stable identifiers; recording them in the ADR keeps the document self-sufficient when a future contributor opens it without access to the Entra portal. The 24 functional-role group GUIDs are tenant-specific and stay in a gitignored `infra/test-tenant.entra.json` once the implementation PR creates it — referenced by name only in `libs/feature/auth/src/lib/entra-group-to-role.ts`.
- **No `prettier --write` damage.** The persona matrix is a wide table; Prettier sometimes reflows wide markdown tables. The diff is clean — Prettier left the table intact on this run.

## Test plan

- [x] `prettier --check docs/decisions/0025-authorization-model-privileges-roles-scopes.md` — passes (hook ran on commit).
- [x] Markdown links inside the ADR still resolve (`0020`, references to other ADRs unchanged).
- [x] Status row in `docs/decisions/README.md` reflects `accepted`.
- [x] `CLAUDE.md` roll-up line + Architecture list updated; no other instances of "0024" needed bumping.
- [ ] **Review focus** — the expanded privilege catalogue (4 entries, one of them already had a guard, three new ones documented), the 19-entry persona matrix, the "Provisioned in the test tenant" subsection (especially the GUIDs — make sure none was mistyped from `notes/role-user.txt`).

## What's next

With ADR-0025 accepted, the implementation phasing recorded in its `§More Information` opens:

1. **PR — `libs/feature/auth` extension** : `authorization.types.ts` (catalogue constants for the 4 privileges + 24 functional roles + 6 scope kinds), `entra-group-to-role.ts` (slug map skeleton with placeholder GUIDs ; the operator drops real GUIDs into `infra/test-tenant.entra.json` separately), `Principal` builder hook on the OIDC callback, no new guards yet.
2. **PR — `@RequireRole` + `@RequireScope` decorators + guard tests** : real composition tests against the 19 personas.
3. **PR — drift CI gate** : ESLint custom rule asserting every `@RequireRole('...')` literal in code is in the catalogue.
4. **PR — `prisma/seed.ts` for `user_scopes`** : depends on the `Person` + `User` schema (proposed ADR-0026).

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #205
This commit was merged in pull request #205.
This commit is contained in:
2026-05-23 17:42:57 +02:00
parent 82d4245ab7
commit c9a1e195fe
3 changed files with 47 additions and 30 deletions
+1 -1
View File
@@ -68,4 +68,4 @@ ADRs are listed in numerical order. To slice by topic, filter on the `Tags` colu
| [0022](0022-docs-site-vitepress.md) | Documentation site — VitePress + Mermaid plugin, separate static deployment | accepted | `process`, `infrastructure` | 2026-05-15 |
| [0023](0023-charts-d3-observable-plot.md) | Charts + dashboards — D3 + Observable Plot wrapped in `libs/shared/charts` | accepted | `frontend`, `accessibility`, `performance` | 2026-05-16 |
| [0024](0024-ai-service-relay-grpc-sse-bridge.md) | AI service relay — vendored gRPC protos, NestJS gRPC client, SSE bridge to the SPA, POC unsigned principal | accepted | `backend`, `security`, `observability` | 2026-05-19 |
| [0025](0025-authorization-model-privileges-roles-scopes.md) | Authorization model — three orthogonal axes (privileges × functional roles × scopes), Entra-backed | proposed | `security`, `backend`, `data` | 2026-05-20 |
| [0025](0025-authorization-model-privileges-roles-scopes.md) | Authorization model — three orthogonal axes (privileges × functional roles × scopes), Entra-backed | accepted | `security`, `backend`, `data` | 2026-05-20 |