feat(openzaak): least-privilege client scopes (WP-57)
setup_configuration has no YAML field for granular autorisaties, so bigregister-test now starts at heeft_alle_autorisaties: false (dev + prod template) and bootstrap-catalogus.sh grants exactly the ztc/zrc scopes the harness needs via the Django ORM, sidestepping the zero-scope chicken-and-egg with the JWT-authenticated Autorisaties REST API. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# WP-57 — Least-privilege client scopes
|
||||
|
||||
Status: todo
|
||||
Status: done
|
||||
Phase: 10 — OpenZaak production hardening
|
||||
|
||||
## Why
|
||||
@@ -39,8 +39,51 @@ exercises.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Client config has no wildcard/all-scopes grant.
|
||||
- [ ] `OpenZaakIntegrationTests` (WP-54) pass unchanged against the narrowed client.
|
||||
- [x] Client config has no wildcard/all-scopes grant.
|
||||
- [x] `OpenZaakIntegrationTests` (WP-54) pass unchanged against the narrowed client.
|
||||
|
||||
## What actually happened
|
||||
|
||||
`vng_api_common`'s `ApplicatieConfigurationModel` (the class backing
|
||||
`setup_configuration`'s `vng_api_common_applicaties` step, read from the installed package
|
||||
inside the `openzaak/open-zaak:1.29.1` image) only has fields for
|
||||
`uuid`/`client_ids`/`label`/`heeft_alle_autorisaties` — there is no YAML field for granular
|
||||
`autorisaties` at all. So `data.yaml` now sets `heeft_alle_autorisaties: false` (both the dev
|
||||
harness and the prod template), which leaves `bigregister-test` with **zero** Autorisaties
|
||||
until something else grants them.
|
||||
|
||||
That "something else" can't be the JWT-authenticated Autorisaties REST API — a zero-scope
|
||||
client can't grant itself scope over an API gated by scope (confirmed from
|
||||
`ApplicatieViewSet.required_scopes`: `update`/`partial_update` need
|
||||
`autorisaties.bijwerken`). `bootstrap-catalogus.sh` grants the scopes directly via the ORM
|
||||
instead (`docker compose exec web python manage.py shell`, workdir `/app/src`) — no
|
||||
JWT/REST layer involved, so no circularity. Two grants, both idempotent (delete-then-create):
|
||||
|
||||
- `ztc`: `catalogi.lezen` + `catalogi.schrijven` — granted up front (no zaaktype dependency).
|
||||
Only `catalogi.schrijven` is provisioning-only; the BFF itself only ever reads Catalogi.
|
||||
- `zrc`: `zaken.aanmaken` + `zaken.bijwerken` + `zaken.lezen`, scoped to the one zaaktype
|
||||
(`zaaktype=<ZT-HERREG url>`, `max_vertrouwelijkheidaanduiding=openbaar` — both fields are
|
||||
*required* by OpenZaak's `AutorisatieValidator` for any `zaken.*` scope) — granted once
|
||||
`zaaktype_url` is known, right after the zaaktype is created/resolved.
|
||||
|
||||
Reading the actual `RolViewSet`/`StatusViewSet`/`ZaakInformatieObjectViewSet`
|
||||
`required_scopes` (not just the scope docstrings, which are aspirational/descriptive) showed
|
||||
the decision text's "statussen (aanmaken), rollen (aanmaken)" don't map to separate OpenZaak
|
||||
scopes — `zaken.aanmaken` alone (OR'd against alternatives) already covers the first status
|
||||
and the initiator rol; there is no `rollen.aanmaken` scope. `documenten`/`zaakinformatieobjecten`
|
||||
scope was **not** granted: `Zgw:InformatieobjecttypeUrls` is empty in `appsettings.json`, so
|
||||
`OpenZaakDocumentSource.Upload` can't function in this harness regardless of scope (throws
|
||||
before any HTTP call) — nothing to scope precisely to yet. Left as a documented follow-up
|
||||
(the script would also need to seed an `informatieobjecttype` to have something concrete to
|
||||
scope `documenten.aanmaken` to).
|
||||
|
||||
Verified for real: `down -v` fresh volume → `up -d` → `bootstrap-catalogus.sh` (all
|
||||
"created", scopes granted, `heeft_alle_autorisaties: False` confirmed via `manage.py shell`)
|
||||
→ `dotnet test --filter Category=Integration` green → reran `bootstrap-catalogus.sh` again
|
||||
under the now-narrowed client (all "exists", scopes re-granted idempotently, no 403s) →
|
||||
confirmed the narrowing is real, not just untested, by DELETEing the seeded zaak with a
|
||||
hand-rolled JWT for this client: 403 `permission_denied` (zaak deletion needs
|
||||
`zaken.verwijderen`/`zaken.geforceerd-bijwerken`, neither granted).
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
Reference in New Issue
Block a user