diff --git a/.forgejo/workflows/acceptance.yaml b/.forgejo/workflows/acceptance.yaml index fc0d0df..a3d1cb3 100644 --- a/.forgejo/workflows/acceptance.yaml +++ b/.forgejo/workflows/acceptance.yaml @@ -2,7 +2,7 @@ name: Authentication acceptance on: push: branches: [main] - paths: ["src/**", ".forgejo/workflows/acceptance.yaml"] + paths: ["src/**", "scripts/provider-onboarding-contract.py", ".forgejo/workflows/acceptance.yaml"] workflow_dispatch: jobs: acceptance: @@ -18,3 +18,23 @@ jobs: tar xzf /tmp/keycape-source.tar.gz -C /tmp/keycape-acceptance --strip-components=1 cd /tmp/keycape-acceptance/src /usr/local/go/bin/go test ./... + + provider-contract: + runs-on: ubuntu-latest + container: + image: ghcr.io/gpappsoft/privacyidea-docker@sha256:af7841adad262f129e0c1d4f553af13f21cb2f4dc713533f316cfe43ed0b4473 + steps: + - name: Exercise isolated provider enrollment and recovery + run: | + python3 - <<'PYCODE' + import os,tarfile,tempfile,urllib.request,subprocess + from pathlib import Path + with tempfile.TemporaryDirectory(prefix='keycape-provider-ci-') as directory: + root=Path(directory) + url='https://forgejo.coulomb.social/'+os.environ['GITHUB_REPOSITORY']+'/archive/'+os.environ['GITHUB_SHA']+'.tar.gz' + with urllib.request.urlopen(url,timeout=30) as response,tempfile.TemporaryFile() as archive: + archive.write(response.read());archive.seek(0) + with tarfile.open(fileobj=archive,mode='r:gz') as tar:tar.extractall(root,filter='data') + script=next(root.glob('*/scripts/provider-onboarding-contract.py')) + subprocess.run(['python3',str(script)],check=True,timeout=90) + PYCODE diff --git a/docs/enrollment-assurance-release-2026-09-13.md b/docs/enrollment-assurance-release-2026-09-13.md new file mode 100644 index 0000000..7171b47 --- /dev/null +++ b/docs/enrollment-assurance-release-2026-09-13.md @@ -0,0 +1,35 @@ +# Enrollment and assurance release — 2026-09-13 + +Source 113f3a6 includes the confirmed-enrollment/AAL2 fix from 122a0d1 and the +reader-scope safeguard. Final image: +`forgejo.coulomb.social/coulomb/key-cape@sha256:75bdbe1cd8181949c9d2759506629f19bd5d62a7cf81293811affa76c6a43dd6`. + +CI acceptance run 54 and image run 55 passed for 113f3a6; the full Go suite +passed locally. Tests cover pending/confirmed enrollment, cancellation state, +existing factors alongside pending enrollment, malformed/unknown states, +incomplete pagination, password passthrough and static-password token rejection, +missing scope proof, lost realm visibility, probe assignment and realm mismatch. + +The installed privacyIDEA image was tested in an isolated Job with in-memory +SQLite, synthetic accounts, no production config/credentials, no service-account +token and no production data volumes. `provider-onboarding-contract-r7` passed: +password-only passthrough produces no OTP evidence; verification-pending TOTP +remains active; cancellation removes the pending token; explicit type=totp plus +OTP confirms enrollment; policy withdrawal gives a successful empty list; +restoration returns factor visibility; a genuinely provider-issued two-second +JWT expires and is rejected. The test source is retained under scripts and +wired into exact-commit CI with the tested provider image pinned by digest. + +The production reader-scope probe KCFACTORSCOPE01 was generated provider-side, +verified disabled/unassigned in coulomb, and checked with the real dedicated +reader role. Native Job keycape-factor-scope-proof passed visibility, inert-state +and temporary-session revocation checks. Its key never leaves privacyIDEA. +KeyCape's readProbeSerial was set using Secret UID/resource-version CAS; exact +readback preserved every other config byte and Secret field. Image rollouts used +resource-version and expected-image guards. Canonical deployment pin is updated. + +Mandatory and explicit AAL2 policies remain mandatory. No optional client policy +or production self-service enrollment policy was changed by this release. The +remaining T02/T03 work is actual self-service browser acceptance, scoped optional +client activation and privileged portal assurance. P04 audited lost-factor +recovery is still separate. Isolated provider tests do not claim those complete. diff --git a/workplans/KEY-WP-0035-optional-mfa.md b/workplans/KEY-WP-0035-optional-mfa.md index 9441059..687fd38 100644 --- a/workplans/KEY-WP-0035-optional-mfa.md +++ b/workplans/KEY-WP-0035-optional-mfa.md @@ -81,7 +81,7 @@ Consumer source 632b1f1 deployed and Ready 1/1; CI, four provider HTTP checks an ```task id: KEY-WP-0035-T05 -status: progress +status: done priority: high ``` @@ -91,4 +91,12 @@ factors. Distinguish pending enrollment from completed enrollment and require TOTP/HOTP serial/type evidence before AAL2. Cover pending/confirmed/cancelled, existing-factor plus pending enrollment, missing/unknown state, incomplete pages, password passthrough and static-password token rejection. All Go suites pass -locally; publish and verify the guarded issuer replacement before completion. +locally; source 113f3a6 was published, CI acceptance/build passed, and the guarded issuer replacement is live. See docs/enrollment-assurance-release-2026-09-13.md. + +Provider recovery testing additionally proved that withdrawing reader policy +returns HTTP 200 with an empty list. Added and deployed a same-realm disabled, +unassigned visibility probe before no-factor decisions, with missing visibility +failing closed. Native scope proof passed. Isolated installed-provider tests +passed possession confirmation/cancellation, permission withdrawal/recovery and +provider-issued JWT expiry; these are now included in CI. T02/T03 remain the +actual optional-client and self-service browser activation gates.