feat(auth): authorization catalogues + Principal builder skeleton (ADR-0025)
CI / check (pull_request) Failing after 28s
CI / commits (pull_request) Failing after 25s
CI / scan (pull_request) Failing after 56s
CI / a11y (pull_request) Failing after 33s
CI / perf (pull_request) Failing after 47s
Docs site / build (pull_request) Failing after 26s
CI / check (pull_request) Failing after 28s
CI / commits (pull_request) Failing after 25s
CI / scan (pull_request) Failing after 56s
CI / a11y (pull_request) Failing after 33s
CI / perf (pull_request) Failing after 47s
Docs site / build (pull_request) Failing after 26s
Per ADR-0025's implementation phasing, lands the closed-set catalogues + the OIDC-callback hook that composes the session-resident Principal. No new guards yet. - libs/shared/auth: framework-agnostic lib hosting authorization.types.ts (4 privileges + 24 functional roles + 6 scope kinds + Principal type) and entra-group-to-role.ts (validated GUID -> slug resolver). 17 unit tests against the catalogues + resolver contracts. - BFF: PrincipalBuilder composes the three axes once at sign-in; ScopeResolver seam (StubScopeResolver returns unrestricted in v1, replaced by a Prisma-backed implementation when ADR-0026's user_scopes table lands). 19 persona-driven tests covering the test tenant's full provisioning + edge cases (unknown privilege drift, unknown group GUID, empty permissions). - AuthService extracts the Entra "groups" claim; AuthenticatedUser grows the groups: readonly string[] field. - SessionEstablisher builds + persists the principal alongside the legacy user shape (additive — guards consuming user.oid unchanged). - ENTRA_GROUP_MAP_PATH env var + load-entra-group-map.ts read the tenant-private map from infra/<env>-tenant.entra.json (gitignored; .example.json committed with the schema). - ADR-0025 path references updated from libs/feature/auth (Angular-scoped, scope:portal-shell) to libs/shared/auth (consumable by both BFF and SPA). - notes/entra-groups-claim-activation.md: operator runbook for the "Token configuration -> Add groups claim" step in the Entra admin centre. Test plan: - pnpm nx affected -t lint test build : 13 projects green (497 BFF tests + 17 shared-auth tests + 1 shared-util test + full lint + full build).
This commit is contained in:
+41
-8
@@ -2,14 +2,15 @@
|
||||
|
||||
Infrastructure-as-code artefacts for the project. Separate from application code and from documentation: this folder contains the recipes and configs that the team and ops use to stand up running infrastructure (CI runners, future local-dev databases, future on-prem deploy assets).
|
||||
|
||||
| Subject | File / Folder | ADR / Reference |
|
||||
| -------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Self-hosted CI runners (Gitea Actions) | [`ci-runners.compose.yml`](ci-runners.compose.yml) | [ADR-0015 §"Runners"](../docs/decisions/0015-cicd-gitea-actions.md) |
|
||||
| Shared `act_runner` configuration | [`runner-config.yaml`](runner-config.yaml) | [ADR-0015 §"Runners"](../docs/decisions/0015-cicd-gitea-actions.md) |
|
||||
| CI runners convenience script | [`ci-runners.sh`](ci-runners.sh) | See "Convenience script" below |
|
||||
| Runtime state of the runners | `data/` (git-ignored after `.gitignore`) | — |
|
||||
| Env-vars template for the runners | `.env.example` (`.env` is git-ignored) | — |
|
||||
| Local-dev runtime stack | [`local/`](local/) | [ADR-0006](../docs/decisions/0006-persistence-postgresql-prisma.md), [ADR-0010](../docs/decisions/0010-session-management-redis.md), [ADR-0012](../docs/decisions/0012-observability-pino-opentelemetry.md), [ADR-0013](../docs/decisions/0013-audit-trail-separated-postgres-append-only.md) |
|
||||
| Subject | File / Folder | ADR / Reference |
|
||||
| -------------------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Self-hosted CI runners (Gitea Actions) | [`ci-runners.compose.yml`](ci-runners.compose.yml) | [ADR-0015 §"Runners"](../docs/decisions/0015-cicd-gitea-actions.md) |
|
||||
| Shared `act_runner` configuration | [`runner-config.yaml`](runner-config.yaml) | [ADR-0015 §"Runners"](../docs/decisions/0015-cicd-gitea-actions.md) |
|
||||
| CI runners convenience script | [`ci-runners.sh`](ci-runners.sh) | See "Convenience script" below |
|
||||
| Runtime state of the runners | `data/` (git-ignored after `.gitignore`) | — |
|
||||
| Env-vars template for the runners | `.env.example` (`.env` is git-ignored) | — |
|
||||
| Local-dev runtime stack | [`local/`](local/) | [ADR-0006](../docs/decisions/0006-persistence-postgresql-prisma.md), [ADR-0010](../docs/decisions/0010-session-management-redis.md), [ADR-0012](../docs/decisions/0012-observability-pino-opentelemetry.md), [ADR-0013](../docs/decisions/0013-audit-trail-separated-postgres-append-only.md) |
|
||||
| Entra group GUID → role slug map | [`test-tenant.entra.example.json`](test-tenant.entra.example.json) (`*-tenant.entra.json` is git-ignored) | [ADR-0025 §"Sources of truth — Entra-side configuration"](../docs/decisions/0025-authorization-model-privileges-roles-scopes.md) |
|
||||
|
||||
Future folders / files that will land here as the corresponding ADRs ship:
|
||||
|
||||
@@ -230,6 +231,38 @@ This stack is **dev-only**. The corresponding production layout (HA Postgres, Re
|
||||
|
||||
---
|
||||
|
||||
## Entra group map — `test-tenant.entra.example.json`
|
||||
|
||||
Pure JSON object keyed on Entra security-group GUID (lower-case), valued by an `apf-role-<slug>` slug from the ADR-0025 functional-role catalogue. The BFF loads it at boot through `EntraGroupToRoleResolver` (from `shared-auth`) and uses it on every sign-in to translate the `groups` claim into the 24-entry catalogue's role slugs.
|
||||
|
||||
The 24 entries below cover the entire v1 catalogue — including `partenaire`, which ships empty in the test tenant by design but is kept in the schema so a typo or omission fails the parser at boot rather than silently dropping the role.
|
||||
|
||||
### Provisioning a real file
|
||||
|
||||
```bash
|
||||
cp infra/test-tenant.entra.example.json infra/test-tenant.entra.json
|
||||
|
||||
# Then for each role replace the placeholder GUID with the real one
|
||||
# from Entra:
|
||||
# Microsoft Entra admin centre → Groups → <apf-role-*> → Object ID.
|
||||
# Point the BFF at the file via apps/portal-bff/.env:
|
||||
# ENTRA_GROUP_MAP_PATH=infra/test-tenant.entra.json
|
||||
```
|
||||
|
||||
The real file (`infra/<env>-tenant.entra.json`) is git-ignored because the group GUIDs are tenant-private — leaking them does not authorize anything by itself, but it does reveal the tenant's internal authorization topology. Each environment (test / preprod / prod) carries its own file; the slugs are stable across environments, the GUIDs are not.
|
||||
|
||||
If `ENTRA_GROUP_MAP_PATH` is unset, the resolver runs with an empty map: every user signs in successfully but receives an empty `roles[]` (and consequently no `apf-role-*` UI). The BFF logs a WARN at boot so an operator can spot the missing config; this is a deliberate fail-soft posture so a fresh dev environment is not blocked by an Entra-side dependency.
|
||||
|
||||
Validation rules enforced at boot by `parseEntraGroupMap` (in `libs/shared/auth/`):
|
||||
|
||||
- keys must look like a GUID (`8-4-4-4-12` hex);
|
||||
- values must be members of `FUNCTIONAL_ROLES`;
|
||||
- the same GUID cannot map to two different slugs (case-insensitive).
|
||||
|
||||
A malformed file crashes the BFF at startup. The error message names the offending key / value.
|
||||
|
||||
---
|
||||
|
||||
## Future infra concerns — placeholders
|
||||
|
||||
These are listed here so a contributor knows where to expect related files; they don't exist yet.
|
||||
|
||||
Reference in New Issue
Block a user