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:
@@ -107,14 +107,24 @@ app change.
|
||||
**rolls back the whole create**) on any notified resource — see the compose file's comment.
|
||||
- `setup_configuration/data.yaml` — the declarative, scripted alternative to clicking through
|
||||
the Django admin (upstream's own documented `setup_configuration` CLI mechanism): creates the
|
||||
one `bigregister-test` client (`heeft_alle_autorisaties: true` — this instance never exists
|
||||
for anything but this harness, so there's no least-privilege boundary worth modeling).
|
||||
one `bigregister-test` client with `heeft_alle_autorisaties: false` — this YAML mechanism
|
||||
(`vng_api_common`'s `ApplicatieConfigurationModel`) has no field for granular scopes at all,
|
||||
so the client starts with zero Autorisaties; `bootstrap-catalogus.sh` grants the exact ones
|
||||
it needs (WP-57).
|
||||
- `bootstrap-catalogus.sh` — the business content (catalogus/zaaktype/zaak/…) `setup_configuration`
|
||||
has no YAML for; every field value here was checked against OpenZaak's own OpenAPI spec and a
|
||||
live run of this exact script, not guessed (two OpenZaak quirks it works around: a zaaktype
|
||||
needs ≥1 resultaattype and 2 statustypen before it can be published, and its
|
||||
`selectielijstklasse` and the zaaktype's `selectielijstProcestype` must reference the same
|
||||
`procesType` on the public VNG selectielijst API). Idempotent (WP-56) — see "Bring it up" above.
|
||||
Also grants `bigregister-test`'s Autorisaties via `manage.py shell` (WP-57, see the script's
|
||||
top comment): `ztc` scopes (`catalogi.lezen`/`catalogi.schrijven`, this script's own
|
||||
content-creation needs) up front, `zrc` scopes (`zaken.aanmaken`/`zaken.bijwerken`/
|
||||
`zaken.lezen`, scoped to the one zaaktype the BFF and this script both use) once that
|
||||
zaaktype exists. No `documenten`/DRC grant — `Zgw:InformatieobjecttypeUrls` is empty in this
|
||||
harness's `appsettings.json`, so `OpenZaakDocumentSource` isn't reachable here yet; add the
|
||||
grant (scoped to a real `informatieobjecttype`, which this script would also need to seed)
|
||||
when a later WP wires DRC content into this harness.
|
||||
- **Not here**: Documenten (DRC) / Notificaties (NRC) content — add if a later WP needs to prove
|
||||
those round-trips against a live instance too (WP-51/52 are fixture-tested today).
|
||||
- `docker-compose.openzaak.prod.yml` (WP-55) — production overrides layered on top of
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
# repeatedly against a long-lived instance, not just once per fresh volume. Prints the seeded
|
||||
# zaak's `identificatie` + `url` on success; also writes them to seeded.env (repo-ignored) for
|
||||
# OpenZaakIntegrationTests.cs to assert against.
|
||||
#
|
||||
# WP-57: `bigregister-test` starts with ZERO Autorisaties (data.yaml sets
|
||||
# heeft_alle_autorisaties: false) — the setup_configuration YAML has no field for granular
|
||||
# scopes at all (confirmed from vng_api_common's own ApplicatieConfigurationModel), so this
|
||||
# script grants them itself via `manage.py shell` (Django ORM, inside the `web` container) at
|
||||
# the two points they become grantable: ztc scopes up front (no zaaktype dependency), zrc
|
||||
# scopes once `zaaktype_url` exists below. Going through the ORM instead of the
|
||||
# JWT-authenticated Autorisaties REST API sidesteps a real chicken-and-egg: a client with zero
|
||||
# scopes cannot grant itself any scope over that API. Re-running this script re-grants the same
|
||||
# scopes (idempotent, like everything else here).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
@@ -61,6 +71,27 @@ oz() {
|
||||
echo "$json"
|
||||
}
|
||||
|
||||
# Grant (replace) an Autorisatie for $CLIENT_ID directly via the ORM (see the WP-57 note up
|
||||
# top for why this bypasses the REST Autorisaties API). $1 = component, $2 = python list
|
||||
# literal of scopes, $3.. = extra `Autorisatie(...)` kwargs as `name=value` (value already a
|
||||
# valid Python literal, e.g. a quoted URL).
|
||||
grant_scopes() {
|
||||
local component="$1" scopes="$2"
|
||||
shift 2
|
||||
local extra="" kv
|
||||
for kv in "$@"; do extra+=" $kv,"$'\n'; done
|
||||
docker compose -f docker-compose.openzaak.yml exec -T --workdir /app/src web python manage.py shell <<PY
|
||||
from vng_api_common.authorizations.models import Applicatie
|
||||
|
||||
app = Applicatie.objects.get(client_ids__contains=["$CLIENT_ID"])
|
||||
app.autorisaties.filter(component="$component").delete()
|
||||
app.autorisaties.create(
|
||||
component="$component",
|
||||
scopes=$scopes,
|
||||
$extra)
|
||||
PY
|
||||
}
|
||||
|
||||
# $1 = list path+query (server-side-filtered to the natural key). Prints the first result's
|
||||
# `url`, or nothing if the list is empty — the GET-before-POST idempotency check.
|
||||
existing_url() {
|
||||
@@ -86,6 +117,9 @@ until curl -sS -o /dev/null -w '%{http_code}' "$BASE/catalogi/api/v1/catalogusse
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "Granting ztc scopes (catalogi.lezen, catalogi.schrijven — this script's own content-creation needs; the BFF only ever reads Catalogi)..."
|
||||
grant_scopes ztc '["catalogi.lezen", "catalogi.schrijven"]'
|
||||
|
||||
echo "Catalogus..."
|
||||
catalogus_url=$(existing_url "/catalogi/api/v1/catalogussen?domein=BIGR&rsin=$RSIN")
|
||||
if [ -n "$catalogus_url" ]; then
|
||||
@@ -134,6 +168,11 @@ print(json.dumps({
|
||||
echo " created: $zaaktype_url"
|
||||
fi
|
||||
|
||||
echo "Granting zrc scopes (zaken.aanmaken, zaken.bijwerken, zaken.lezen), scoped to $zaaktype_url — the one zaaktype this harness (and the BFF's Zgw:ZaaktypeUrls config) ever uses..."
|
||||
grant_scopes zrc '["zaken.aanmaken", "zaken.bijwerken", "zaken.lezen"]' \
|
||||
"zaaktype=\"$zaaktype_url\"" \
|
||||
'max_vertrouwelijkheidaanduiding="openbaar"'
|
||||
|
||||
echo "Statustypen (publish needs a begin AND an end status)..."
|
||||
statustype_url=$(existing_statustype_url "$zaaktype_url" 1)
|
||||
if [ -n "$statustype_url" ]; then
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
# into it to produce the gitignored data.prod.yaml that docker-compose.openzaak.prod.yml
|
||||
# mounts over the container's data.yaml.
|
||||
#
|
||||
# Least-privilege client scopes (heeft_alle_autorisaties: true below) are WP-57's job, not
|
||||
# this WP's — left matching the dev harness on purpose.
|
||||
# Least-privilege client scopes (WP-57): heeft_alle_autorisaties is false, matching the dev
|
||||
# harness (setup_configuration has no YAML field for granular `autorisaties` — see
|
||||
# data.yaml's comment). This template only covers infra config; a real deploy must grant this
|
||||
# client's Autorisaties the same way bootstrap-catalogus.sh does for the dev harness — via
|
||||
# `manage.py shell` (or the Autorisaties REST API from an already-privileged caller) against
|
||||
# the production catalogus/zaaktype URLs, once, as part of standing up that environment.
|
||||
sites_config_enable: true
|
||||
sites_config:
|
||||
items:
|
||||
@@ -25,4 +29,4 @@ vng_api_common_applicaties:
|
||||
client_ids:
|
||||
- ${OPENZAAK_CLIENT_ID}
|
||||
label: BIG-register BFF (production)
|
||||
heeft_alle_autorisaties: true
|
||||
heeft_alle_autorisaties: false
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
# documented CLI config mechanism — see docker-compose.openzaak.yml) instead of the Django
|
||||
# admin. Creates the ONE application the bootstrap script + integration test authenticate as.
|
||||
#
|
||||
# ponytail: heeft_alle_autorisaties (all scopes) rather than a granular per-component/scope
|
||||
# list — this instance only ever exists for this harness/test, never a shared or prod
|
||||
# OpenZaak, so there's no least-privilege boundary worth modeling here.
|
||||
# heeft_alle_autorisaties is false (WP-57, least privilege) — but
|
||||
# `ApplicatieConfigurationModel` (vng_api_common's setup_configuration step) has no field for
|
||||
# granular `autorisaties` at all, only this boolean. So this client starts with ZERO scopes;
|
||||
# bootstrap-catalogus.sh grants the exact ones it needs via `manage.py shell` (Django ORM,
|
||||
# not the JWT-authenticated Autorisaties REST API — a zero-scope client can't grant itself
|
||||
# anything over REST, so this sidesteps that bootstrap chicken-and-egg entirely).
|
||||
sites_config_enable: true
|
||||
sites_config:
|
||||
items:
|
||||
@@ -24,4 +27,4 @@ vng_api_common_applicaties:
|
||||
client_ids:
|
||||
- bigregister-test
|
||||
label: BIG-register BFF (WP-54 test harness)
|
||||
heeft_alle_autorisaties: true
|
||||
heeft_alle_autorisaties: false
|
||||
|
||||
Reference in New Issue
Block a user