net-kingdom/sso-mfa/k8s/keycape/ensure-groups-adapter.md
tegwick 835680ceb9 Source support for oidc-browser-relying-party@1.3 successor selection
ensure-groups and hand-over accept 1.3 via explicit version_root only (1.1
stays default; hand-over applies 1.2 owner handling to 1.3).
deliver-consumer-secret accepts exactly {1.2, 1.3}. compose-1.2-descriptors
gains --version {1.2,1.3} (default 1.2) and refuses cleanly when
security-case-type-revisions/<version> is absent. The committed 1.2
composition artifact is regenerated with its recorded args: only the adapter
source and descriptor digests of the three edited adapters change.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 903097@bnt-lap001
Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23
2026-10-17 11:21:15 +02:00

3.5 KiB

Ensure-groups adapter source candidate (NK-WP-0048-T24)

Source candidate only, modelled on the T13 registration adapter. It is unexercised against any live system and is not pinned, admitted or reviewed.

Binding

  • build() takes an admitted ensure-groups invocation, the exact case bytes, an injected provisioner, owner publish/authenticate evidence capabilities and revoke_session(invocation) -> identifier.
  • The descriptor declares revocation scope parent-session (the Warden v1 kernel refuses an empty scope set). revoke_session runs in a finally block on every run and rollback, including failed ones, so the parent session always ends; a missing or malformed revocation identifier refuses before publication. The identifier is recorded in the evidence payload and returned as session_revocation_evidence with revoked=True.
  • Group creation binds to the T14/T21 journal seam. The provisioner's group_provenance must be a PostgresGroupProvenance. Its GroupJournalBinding must name this case pin, the reviewed adapter source digest and the admitted principal. A missing journal, a local SQLite journal or a differing binding is refused before any call.
  • By default the case must be canonical oidc-browser-relying-party@1.1. oidc-browser-relying-party@1.2 is accepted only when build is called with an explicit version_root='1.2' (as in handover-adapter.py), and successor @1.3 only with version_root='1.3'; an unknown root or a case type that differs from the selected root is refused. The 1.2 revision leaves the ensure-groups step and its access_groups / initial_members parameters unchanged, so both roots share one parser. access_groups and initial_members are parsed exactly, and members outside access_groups are refused. This is a source path only; it admits nothing.
  • The journal binding is computed by the adapter's public group_binding, which a test pins byte-identical to the provisioner's private LLDAPProvisioner._group_binding; the adapter no longer calls the private method. Promoting it to a public provisioner API is an identity-provisioner owner change, not made here.
  • The idempotency key and correlation id are derived from the case pin, so every attempt resumes the same journal receipt. A replay returns no_change and never recreates removed state.
  • The source digest pins the adapter plus provisioner.py, group_provenance.py and group_provenance_postgres.py.

Rollback

Rollback calls revoke_service_groups against the original journal receipt. It accepts only service-group/service-group-membership created objects. The journal, not the caller, decides what was created, and preexisting or shared state is preserved. delete_created_if_empty defaults to false. Enabling it requires an owner fact that no other case uses the groups. This adapter does not supply that fact.

verify requires the authenticated owner evidence and a journal readback state (ensured / revoked) matching the outcome. It fails closed on any journal error.

Not supplied here

  • NetKingdom (directory) owner source review before pinning.
  • Native CNPG journal admission, trusted continuity/checkpoint callbacks and protected GroupJournalBinding construction (T14).
  • Writer isolation or CAS against external LDAP writers.
  • Any native action.

Tests use synthetic fakes only (no SQL, no LLDAP): PYTHONPATH=~/ops-warden/src ~/ops-warden/.venv/bin/python -m pytest sso-mfa/k8s/keycape/test_ensure_groups_adapter.py