Commit Graph
2 Commits
Author SHA1 Message Date
not d0fb2b3e8c S-15c · Enforce MFA on the medewerker (Keycloak) realm (#158)
CI / build (push) Successful in 1m7s
CI / lint (push) Successful in 1m22s
CI / unit (push) Successful in 1m24s
CI / frontend (push) Successful in 3m5s
CI / mutation (push) Successful in 6m13s
CI / verify-stack (push) Successful in 8m39s
Closes #132.

Staff logins (behandel + beheer portals) now need a second factor; the citizen realms are unchanged.

**How:** every seeded medewerker carries a TOTP credential, which activates Keycloak's stock *conditional OTP* step in both the browser flow and the direct grant — no custom browser-flow JSON in the export. `CONFIGURE_TOTP` is a default required action so a medewerker added later must enrol first. ADR-0031 records the choice and, explicitly, that the shared fixture secret is a demo posture only.

**Tests (red first, 30c5279):**
- `check_realms.py` asserts the medewerker password-only grant is **refused**, then that password + TOTP succeeds and still carries the `behandelaar` role. It failed with `[MFA NOT ENFORCED]` against the old export.
- The three medewerker e2e logins move to `loginMedewerker()` (`tests/e2e/medewerker-login.ts`), which submits Keycloak's OTP prompt. Both TOTP implementations (Python `hmac`, Node `crypto`) are ~6 lines of RFC 6238 — no new dependency.

Verified locally against Keycloak 26.1: password-only → `invalid_grant`, password + code → 200, and the browser flow's `#otp` prompt accepts a computed code and issues an auth code.

## Definition of Done
- [x] Failing test/verify committed first; implementation makes it pass.
- [x] Conventional Commits referencing the issue (`refs #132`).
- [ ] CI green (verify-stack compose smoke + relevant checks).
- [x] `docker compose up` reaches green health within 3 minutes (Keycloak change is import-time only).
- [x] Docs touched (runbook, synthetic-data, demo-script) + ADR-0031 + demo note.
- [x] Closed by the merging PR (`closes #132`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #158
2026-09-04 08:27:52 +00:00
not 4698c869f3 feat(portal-beheer): beheer portal + read-only catalogus viewer (closes #130) (#133)
CI / build (push) Successful in 4m31s
CI / lint (push) Successful in 4m48s
CI / unit (push) Successful in 1m50s
CI / frontend (push) Successful in 4m44s
CI / mutation (push) Successful in 7m12s
CI / verify-stack (push) Canceled after 0s
## What & why

S-15a, the first of the S-15 (#16) split. A new **beheer** portal (medewerker realm, like behandel) shows the ZTC catalogus — the published zaaktypen — **read-only**. A beheerder logs in and sees the seeded BIG-REGISTRATIE zaaktype.

Closes #130

### The vertical

portal → BFF `GET /beheer/catalogi/zaaktypen` (medewerker realm + `beheerder` role) → ACL `GET /catalogi/zaaktypen` → ZGW Catalogi API.

- **ACL**: new read-only `GET /catalogi/zaaktypen` listing published zaaktypen (reuses the ADR-0021 Catalogi client; public-safe `identificatie`/`omschrijving`).
- **BFF**: new typed `IAclClient` + `Downstream:Acl:BaseUrl`, and `GET /beheer/catalogi/zaaktypen` behind a new `beheerder` policy (reuses the medewerker bearer scheme + realm-role lifting). OpenAPI spec + generated Angular client regenerated.
- **Keycloak**: `beheerder` realm role + `bram-beheerder` test user in the medewerker realm.
- **Frontend**: new `apps/beheer` Angular app (copied from behandel) with a read-only catalogus page; `SECURE_API_ROUTES=['/beheer/']`.
- **Infra**: `beheer` compose service (port 8143), added to `WAIT_SVCS` + CI log-dump; a Playwright e2e (beheerder login → catalogus shows BIG-REGISTRATIE).

### New boundary → ADR-0025

The BFF now reaches the **ACL directly** for the catalogus read — a new service-to-service edge (§14). The catalogus is neither a domain nor a projection concern, and §8.1 means only the ACL may read ZGW; routing through the domain would pollute it with a non-domain passthrough. §8.1/§8.3 stay intact. Recorded in **ADR-0025**.

## Definition of Done

- [x] Failing test committed before each implementation (red→green per layer: ACL, BFF, frontend).
- [x] Conventional Commits referencing #130.
- [ ] CI green — pending Gitea Actions run.
- [x] `docker compose up` brings up `beheer` (health-gated in `WAIT_SVCS`).
- [x] Docs — ADR-0025 + demo-script S-15a note.
- [x] Demo note in `docs/demo-script.md`.

## Verified locally

lint (`dotnet format`) ✓ · .NET unit (Acl 57 / Big 152 / EventSubscriber 19 / Bff 40) ✓ · frontend lint+test (8 projects) ✓ · frontend build (4 apps) ✓. Mutation ratchet: added a gateway unit test for the new `ListZaaktypenAsync` mapping so the ACL score holds. verify-stack (compose smoke + e2e) runs in CI.

## Notes for reviewers

- The BFF drops the ZGW URL from `BeheerZaaktype` (public-safe: identificatie + omschrijving only).
- The catalogus e2e asserts on the stable seeded `BIG-REGISTRATIE` (not a per-test reference), safe on the shared verify stack.
- Follow-ups: **S-15b** (#131) default-fill CRUD, **S-15c** (#132) medewerker-realm MFA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #133
2026-07-24 11:08:17 +00:00