Merge RB-11 — keep the dev hatches out of production builds

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

# Conflicts:
#	libs/shared/docs/behaviour-spec.mdx
This commit is contained in:
eho
2026-08-27 14:21:51 +02:00
14 changed files with 632 additions and 37 deletions
+19 -1
View File
@@ -20,7 +20,15 @@ acting role to exercise the drafter/approver/admin flows.
## How to switch role (dev only)
Both are wired only under `isDevMode()` — they do not exist in a production build.
Both are wired only under `isDevMode()` — they do not exist in a production build. That
gate lives in two places: the `roleInterceptor` registration (`app.config.ts`) for every
`HttpClient` request, **and** inside `role.ts`'s `currentRole()` itself, because three
hand-written `fetch` calls (`reveal-bignummer.adapter.ts`, `letter-preview.adapter.ts`,
`org-template.adapter.ts`'s proefbrief) read the role directly and bypass the
interceptor entirely (RB-11/BIO-012). Before RB-11, `currentRole()` had no such gate, so
`?role=` kept working through those three calls in a production build even though this
page said otherwise; the same defect applied to `?subject=` and `subject.ts`, which is
how a BSN reached `sessionStorage` in any build.
- **Dev switcher (easiest):** open the `⚙ state` panel (bottom-right in a dev build) and pick a
role from the **role** dropdown. The page reloads with the new role.
@@ -66,6 +74,16 @@ The admin pages appear in the header nav and in the dashboard **"Beheer"** secti
matching capability is present — otherwise they are reachable only by URL (and the route guard
redirects a user who lacks the capability back to `/dashboard`).
**`drafter` is also the backend's fallback identity (BIO-006).** It is not only the dev
switcher's initial selection — `StubIdentityProvider`'s role switch resolves **any**
request with no `X-Role` header at all (or an unrecognised one) to `drafter` too. Because
`drafter` is also the _only_ role that may reveal a BIG-nummer, the least-privilege
consequence is real: an unauthenticated or misconfigured caller inherits the PII-reveal
capability by default, rather than the weakest one. This is acceptable only because the
POC has no real identity or step-up yet (see the pre-production compliance checklist —
binding the reveal to an app-overlay attribute instead of the coarse role is a named,
not-yet-built item); it must not survive real identity and step-up.
## The one principle
Identity (AD/OIDC, faked here) supplies **coarse roles**; the app owns a **fine-grained capability**