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
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 admittedensure-groupsinvocation, the exact case bytes, an injected provisioner, ownerpublish/authenticateevidence capabilities andrevoke_session(invocation) -> identifier.- The descriptor declares revocation scope
parent-session(the Warden v1 kernel refuses an empty scope set).revoke_sessionruns in afinallyblock 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 assession_revocation_evidencewithrevoked=True. - Group creation binds to the T14/T21 journal seam. The provisioner's
group_provenancemust be aPostgresGroupProvenance. ItsGroupJournalBindingmust 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.2is accepted only whenbuildis called with an explicitversion_root='1.2'(as inhandover-adapter.py), and successor@1.3only withversion_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 itsaccess_groups/initial_membersparameters unchanged, so both roots share one parser.access_groupsandinitial_membersare parsed exactly, and members outsideaccess_groupsare 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 privateLLDAPProvisioner._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_changeand never recreates removed state. - The source digest pins the adapter plus
provisioner.py,group_provenance.pyandgroup_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
GroupJournalBindingconstruction (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