diff --git a/docs/account-journeys.md b/docs/account-journeys.md index e60fc74..eb00cca 100644 --- a/docs/account-journeys.md +++ b/docs/account-journeys.md @@ -69,7 +69,7 @@ headless capability alone does not mean a journey is usable or verified live. | P03 — Find and inspect a tenant | Search exact identifier or disambiguate matching names; clearly state selected scope | Unknown/ambiguous name presents choices without guessing authority | Existing tests for unknown/ambiguous names and tenant selection | | P04 — Recover a tenant administrator | Verify request and affected scope; audited, bounded account/factor recovery returns control | Insufficient proof denies; provider outage yields pending state and named owner | Implemented: recent-MFA platform recovery, exact-factor confirmation, durable provider audit/readback/retry and replacement possession; see evidence/2026-09-13-p04-recovery.md | | P05 — Operate sign-in, factor and notification services | See degraded component and impacted journeys; renew scoped credentials; verify recovery | Expired factor-read credential blocks assurance decision; missing mail uses explicit assisted setup; no hidden MFA bypass | Implemented and deployed (USER-WP-0032): checked services, confirmed single-delivery recovery, durable uncertain-send guard; SMTP/IMAP retry and provider expiry/permission/rotation acceptance pass; live checks send no email | -| P06 — Set authentication policy | Distinguish optional-after-enrollment, mandatory, and application step-up; preview impact and rollback | Unsupported/ambiguous policy rejected; avoid locking out unenrolled users or weakening privileged actions | KeyCape optional policy source tested; scoped rollout and privileged-portal policy acceptance pending | +| P06 — Set authentication policy | Distinguish optional-after-enrollment, mandatory, and application step-up; preview impact and rollback | Unsupported/ambiguous policy rejected; avoid locking out unenrolled users or weakening privileged actions | Implemented and deployed: issuer-owned scoped policy, recent-MFA preview/apply/rollback, privileged portal guards and installed-provider browser acceptance; see P06 evidence | | P07 — Suspend/retire/reactivate tenant | Confirm blast radius and resulting tenant lifecycle; applications reflect effective policy | Stale version or partial propagation provides readback/retry; recovery retains audit | Existing tenant lifecycle APIs; browser confirmation/propagation drill pending | | P08 — Review audit and service recovery | Follow correlation reference without exposing secrets; see actor, tenant, action and result; prove rollback | Missing telemetry or unknown state visible; no success until authoritative readback | Existing service events/references; unified browser audit/support workflow pending | diff --git a/docs/evidence/2026-09-13-p06-authentication-policy.md b/docs/evidence/2026-09-13-p06-authentication-policy.md index 61b3779..0f4e95c 100644 --- a/docs/evidence/2026-09-13-p06-authentication-policy.md +++ b/docs/evidence/2026-09-13-p06-authentication-policy.md @@ -13,6 +13,9 @@ factor replacement requires audited recovery, including from old AAL1 sessions. - Previous interrupted run: full Go suite and 232 database-backed portal tests passed. - Resumed run: all 30 portal Chromium checks and five portal policy tests passed. +- Final platform completeness gate: 33 tests passed, zero skips, all P01–P08 + marked implemented with no unresolved platform journeys. See the adjacent + P06 platform-complete JSON report. - Issuer policy/runtime regression tests passed; two policy rollout and one provider guard deployment tests passed. - Installed privacyIDEA fixture `provider-p06-browser-46713f7e`: native adapter @@ -31,5 +34,46 @@ production account changes. Reproduce using key-cape ## Release -Pending CI image publication and ordered live rollout. Do not infer deployment -or complete platform journey acceptance from these test results. +Completed 2026-09-14 Europe/Berlin (2026-09-13 UTC): + +- KeyCape source `e0b3c25`; CI acceptance run 64 and image run 65 passed. +- Portal source `3bd1827`; image run 178 and journey run 179 passed. Routing-only + smoke run 177 was cancelled; it is not claimed as passed. +- NetKingdom policy/guard tooling: `a549617`. +- Portal image: `forgejo.coulomb.social/coulomb/user-engine@sha256:de39ed717b69ce61c66fe0b12f9fdbe63abbee0ea0cbad9614c2dae6c2c5116c`. +- Issuer image: `forgejo.coulomb.social/coulomb/key-cape@sha256:db2c5a13a47839049349e881c8d19bc39f720ee69d8518f9f2eba2b1f98af9d5`. + +Portal guards rolled out first. Provider guard +`keycape-onboarding-9465e5e0b98f7986` then rolled out to privacyIDEA and recovery. +Provider policy readback confirms TOTP enrollment, pending-only deletion and +active-factor changes through recovery; existing tokens were unchanged. +Only the two reviewed client registrations were migrated with UID/resource-version +CAS and unrelated Secret bytes preserved. Issuer replacement then activated +persistent policy at `/var/lib/keycape-policy/policy.json` on the dedicated PVC. + +Read-only Job `p06-policy-readback-1c0eba21` verified both effective policies are +`optional_after_enrollment`, revision 0, no administrative changes yet. The Job +was removed. Portal health/readiness and issuer readiness return 200; both policy +surfaces reject anonymous access with 403. The migration inspector reports no +remaining change. Receipts are in the adjacent P06 rollout JSON. + +Positive policy changes/replay/rollback are tested in isolated stores and portal +fixtures. No production account or real administrator policy override was used +as a test. Actual invited-person and application sign-in acceptance remains in +USER-WP-0028 / KEY-WP-0034 / VERGABE-WP-0019. + +## Operating policy and rollback + +Open Platform administration → Authentication policy and verify with fresh MFA. +Select a reviewed client, policy and change reference; preview the impact and +acknowledge it before applying. Cancel leaves policy unchanged. A stale review +requires a new preview; an uncertain response should be checked in current state +and history. Rollback is a new reviewed change to that client's previous policy. + +Preserve the policy PVC and receipts across issuer replacement; do not delete +it during rollback. It uses this cluster's local-path storage, not multi-node HA. +Before reverting to a pre-P06 portal or issuer, restore mandatory MFA for both +clients in runtime policy and the baseline client registrations, verify it, then +restore the previous image pins. Keep the provider active-factor guard and +recovery service. Reverting the portal guards while optional login remains active +would weaken administration. Previous exact image pins are in the rollout receipt. diff --git a/docs/evidence/2026-09-13-p06-platform-complete.json b/docs/evidence/2026-09-13-p06-platform-complete.json new file mode 100644 index 0000000..0254835 --- /dev/null +++ b/docs/evidence/2026-09-13-p06-platform-complete.json @@ -0,0 +1,114 @@ +{ + "tests_run": 33, + "test_success": true, + "skipped": 0, + "journeys": [ + { + "id": "P01", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_portal_navigation.PortalNavigationTests.test_operator_can_reach_administration_without_personal_membership", + "test_portal_navigation.PortalNavigationTests.test_existing_tenant_user_navigation_preserves_authority" + ], + "remaining": "", + "automated_result": "passed" + }, + { + "id": "P02", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_journey_roles.PlatformAdminJourneys.test_bootstrap_retry_does_not_duplicate_first_admin", + "test_web.PortalApplicationTests.test_platform_tenant_authority_denial_is_redacted_and_creates_no_admin", + "test_journey_roles.PlatformAdminJourneys.test_partial_first_admin_setup_rolls_back_and_retry_finishes" + ], + "remaining": "Local bootstrap is atomic and retryable; tenant creation remains delegated to its authority.", + "automated_result": "passed" + }, + { + "id": "P03", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_portal_navigation.PortalNavigationTests.test_ambiguous_or_unknown_short_names_do_not_guess_a_tenant", + "test_portal_navigation.PortalNavigationTests.test_existing_tenant_is_selectable_and_short_name_resolves" + ], + "remaining": "", + "automated_result": "passed" + }, + { + "id": "P04", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_journey_roles.PlatformAdminJourneys.test_recovery_uses_tenant_access_and_keeps_global_identity_operations_unused", + "test_platform_support.PlatformSupportJourneys.test_recovery_denied_before_preview_and_operator_sees_factor_boundary", + "test_factor_recovery_journey.FactorRecoveryJourney.test_role_csrf_and_freshness_denied_before_provider", + "test_factor_recovery_journey.FactorRecoveryJourney.test_preview_cancel_apply_retry_and_secret_not_rendered", + "test_factor_recovery_journey.FactorRecoveryJourney.test_stepup_requests_fresh_mfa_and_binds_return" + ], + "remaining": "", + "automated_result": "passed" + }, + { + "id": "P05", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_journey_roles.PlatformAdminJourneys.test_delivery_denial_redaction_retry_and_completed_guard", + "test_platform_support.PlatformSupportJourneys.test_service_capabilities_distinguish_configuration_from_health", + "test_service_operations.ServiceOperations.test_one_confirmed_delivery_and_completed_retry_guard", + "test_service_operations.ServiceOperations.test_role_csrf_and_stale_confirmation_cannot_submit", + "test_service_operations.ServiceOperations.test_failure_releases_claim_and_retry_is_available", + "test_service_operations.ServiceOperations.test_crashed_claim_reconciles_under_event_guard", + "test_service_operations.ServiceOperations.test_missing_mail_lane_never_marks_an_invitation_accepted", + "test_service_operations.ServiceOperations.test_status_is_checked_bounded_and_never_claims_receipt", + "test_service_operations.ServiceOperations.test_selected_mail_evidence_is_safe_and_read_only" + ], + "remaining": "", + "automated_result": "passed" + }, + { + "id": "P06", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_authentication_policy.AuthenticationPolicyJourney.test_aal1_account_access_does_not_grant_administration", + "test_authentication_policy.AuthenticationPolicyJourney.test_role_csrf_and_stale_mfa_deny_before_policy_service", + "test_authentication_policy.AuthenticationPolicyJourney.test_review_explains_lockout_scope_rollback_and_receipts", + "test_authentication_policy.AuthenticationPolicyJourney.test_outage_is_redacted_and_current_status_is_retryable", + "test_authentication_policy.AuthenticationPolicyJourney.test_policy_stepup_binds_only_supported_return_path" + ], + "remaining": "", + "automated_result": "passed" + }, + { + "id": "P07", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_journey_roles.PlatformAdminJourneys.test_tenant_retirement_requires_confirmation_and_stale_confirmation_fails", + "test_web.PortalApplicationTests.test_platform_tenant_lifecycle_is_delegated_to_the_authority" + ], + "remaining": "", + "automated_result": "passed" + }, + { + "id": "P08", + "role": "platform_admin", + "implementation": "implemented", + "tests": [ + "test_journey_roles.PlatformAdminJourneys.test_delivery_denial_redaction_retry_and_completed_guard", + "test_journey_roles.TenantAdminJourneys.test_audit_is_tenant_scoped_and_never_dumps_payload", + "test_platform_support.PlatformSupportJourneys.test_operator_correlates_actions_and_delivery_without_raw_content", + "test_platform_support.PlatformSupportJourneys.test_activity_denies_nonoperators_and_mutations", + "test_platform_support.PlatformSupportJourneys.test_exact_filters_apply_before_display_limit_and_missing_is_explicit", + "test_platform_support.PlatformSupportJourneys.test_filter_values_are_escaped_and_bounded" + ], + "remaining": "", + "automated_result": "passed" + } + ], + "complete": true +} diff --git a/docs/evidence/2026-09-13-p06-rollout.json b/docs/evidence/2026-09-13-p06-rollout.json new file mode 100644 index 0000000..14eac51 --- /dev/null +++ b/docs/evidence/2026-09-13-p06-rollout.json @@ -0,0 +1,74 @@ +{ + "portal": { + "namespace": "user-engine", + "deployment": "user-engine", + "uid": "e1c4274a-4b6d-4ea2-b619-e09630b745b0", + "before_image": "forgejo.coulomb.social/coulomb/user-engine@sha256:8c55d285123c70c453752e19fa1b0ed179cb7fe060e854040fa1c559748a1ebf", + "after_image": "forgejo.coulomb.social/coulomb/user-engine@sha256:de39ed717b69ce61c66fe0b12f9fdbe63abbee0ea0cbad9614c2dae6c2c5116c", + "phase": "portal" + }, + "issuer": { + "namespace": "sso", + "deployment": "keycape", + "uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f", + "before_image": "forgejo.coulomb.social/coulomb/key-cape@sha256:d3c358ec793b59d519ea6b1dbe4172449745a57504474399984de6cd8703024d", + "after_image": "forgejo.coulomb.social/coulomb/key-cape@sha256:db2c5a13a47839049349e881c8d19bc39f720ee69d8518f9f2eba2b1f98af9d5", + "phase": "issuer" + }, + "migration": { + "clients": [ + "user-engine-portal", + "vergabe-demo-company" + ], + "before": { + "uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3", + "resource_version": "60841607" + }, + "change_needed": true, + "other_clients_unchanged": true, + "secret_values_emitted": false, + "mode": "apply", + "after": { + "uid": "2e94519d-1550-41c7-9701-2efe47fe1fd3", + "resource_version": "60979095" + } + }, + "persistent_policy": { + "modes": { + "user-engine-portal": "optional_after_enrollment", + "vergabe-demo-company": "optional_after_enrollment" + }, + "revision": 0, + "history_count": 0, + "read_only": true, + "job": "p06-policy-readback-1c0eba21" + }, + "http": [ + { + "host": "users.coulomb.social", + "path": "/healthz", + "http_status": 200 + }, + { + "host": "users.coulomb.social", + "path": "/readyz", + "http_status": 200 + }, + { + "host": "users.coulomb.social", + "path": "/platform/authentication-policy", + "http_status": 403 + }, + { + "host": "kc.coulomb.social", + "path": "/readyz", + "http_status": 200 + }, + { + "host": "kc.coulomb.social", + "path": "/platform/authentication-policy", + "http_status": 403 + } + ], + "decision_id": "f4f11a4b-bd71-4673-971e-1048fa075cba" +} diff --git a/docs/platform-admin-acceptance.md b/docs/platform-admin-acceptance.md index c0023df..f90e85f 100644 --- a/docs/platform-admin-acceptance.md +++ b/docs/platform-admin-acceptance.md @@ -1,7 +1,7 @@ # Platform administrator acceptance Priority workplan: USER-WP-0030. Platform administration requires the platform -operator role; membership in every tenant is not required. Ordinary tenant +operator role and MFA; policy changes and factor recovery require recent MFA. Membership in every tenant is not required. Ordinary tenant administrators cannot read cross-tenant platform support records. | Journey | Implemented acceptance | Remaining integrated evidence | @@ -10,8 +10,8 @@ administrators cannot read cross-tenant platform support records. | P02 Tenant and first admin | Authority readback, local bootstrap rollback and retry without duplicate admin | Real first-admin sign-in depends on provider | | P03 Tenant inspection | Exact identifier, ambiguous-name handling and selected scope | No new implementation gap | | P04 Administrator recovery | Tenant restoration and recent-MFA lost-factor preview, proof attestation, signed scope confirmation, audited disable/readback/retry and replacement enrollment | Implemented and deployed; evidence: evidence/2026-09-13-p04-recovery.md | -| P05 Service operations | Capability availability, delivery failure/retry/readback, assisted setup guidance | Live factor credential renewal, provider health and actual mail receipt | -| P06 Authentication policy | Existing KeyCape optional/mandatory/step-up source tests; no portal policy-write control | Verified factor lookup, owner-approved effective policy rollout and rollback | +| P05 Service operations | Checked provider/credential/mail status, controlled selected delivery and uncertain-outcome recovery | Deployed; see evidence/2026-09-13-p05-service-operations.md. Actual customer inbox receipt remains a user journey. | +| P06 Authentication policy | Recent-MFA scoped policy preview/apply/audit/rollback; mandatory and optional-after-enrollment; application MFA wins | Deployed and persisted; see evidence/2026-09-13-p06-authentication-policy.md. | | P07 Tenant lifecycle | Authority version/confirmation, stale-state rejection and recovery | Application propagation must be verified at the application | | P08 Support investigation | Exact reference across audit/delivery records, tenant filter, safe metadata, unknown-evidence state and delivery drill-down | Provider actions require provider evidence; portal records alone cannot prove rollback | @@ -43,13 +43,13 @@ make test-browser-journeys make test-journeys JOURNEY_ARGS="--role platform_admin --require-complete" ``` -The platform selection includes the P04 recovery tests; the full regression suite runs -219 tests (all pass when disposable PostgreSQL is enabled). The isolated Chromium suite runs 19 -checks. The final completeness command intentionally fails while P05/P06 -retain external dependencies. The machine-readable matrix is -`tests/journey-coverage.json`; passing portal tests is not live factor acceptance. +The platform completeness selection now includes P04–P06 and must pass with +`--require-complete`. The portal regression suite has 232 tests with disposable +PostgreSQL enabled; 30 isolated Chromium checks pass. Installed-provider native +and browser acceptance is recorded separately in P06 evidence. Customer sign-in +and application access remain USER-WP-0028 / KEY-WP-0034 / VERGABE-WP-0019. -## Historical provider handoff (superseded for P04) +## Historical provider handoff (superseded for P04–P06) KEY-WP-0035/NK-WP-0033 and USER-WP-0030-T03 retain the provider gap. The registered `net-kingdom-privacyidea-admin-token` route was rechecked and remains @@ -64,3 +64,8 @@ P04 recovery now uses the signed platform session and private provider service. Use Authenticator recovery for exact-login preview and support-reference readback. No provider credential or OpenBao login is requested from the operator. See evidence/2026-09-13-p04-recovery.md for the implemented boundary and acceptance. + +RPF-WP-0040 supplies the dedicated factor-read credential and renewal contract. +P05 verified its operating recovery; P06 deployed scoped policy and guarded +onboarding. The historical resolver-credential incident remains NK-WP-0033, +not a gate on the completed platform journeys. diff --git a/tests/journey-coverage.json b/tests/journey-coverage.json index a227782..a3bfad7 100644 --- a/tests/journey-coverage.json +++ b/tests/journey-coverage.json @@ -273,11 +273,15 @@ { "id": "P06", "role": "platform_admin", - "implementation": "external-blocked", + "implementation": "implemented", "tests": [ - "test_account_clarity.AccountClarityTests.test_provider_handoff_rejects_unsafe_configuration" + "test_authentication_policy.AuthenticationPolicyJourney.test_aal1_account_access_does_not_grant_administration", + "test_authentication_policy.AuthenticationPolicyJourney.test_role_csrf_and_stale_mfa_deny_before_policy_service", + "test_authentication_policy.AuthenticationPolicyJourney.test_review_explains_lockout_scope_rollback_and_receipts", + "test_authentication_policy.AuthenticationPolicyJourney.test_outage_is_redacted_and_current_status_is_retryable", + "test_authentication_policy.AuthenticationPolicyJourney.test_policy_stepup_binds_only_supported_return_path" ], - "remaining": "Boundary test only. KeyCape policy suite covers optional/required/step-up; live policy management and provider rollout remain KEY-WP-0035." + "remaining": "" }, { "id": "P07", diff --git a/workplans/USER-WP-0030-platform-admin-journeys.md b/workplans/USER-WP-0030-platform-admin-journeys.md index 3c11939..25b9641 100644 --- a/workplans/USER-WP-0030-platform-admin-journeys.md +++ b/workplans/USER-WP-0030-platform-admin-journeys.md @@ -4,11 +4,11 @@ type: workplan title: "Platform administration and operational recovery" domain: communication repo: user-engine -status: active +status: finished owner: codex topic_slug: communication created: "2026-09-13" -updated: "2026-09-13" +updated: "2026-09-14" state_hub_workstream_id: "ab18c962-4ac4-5cf4-a8d9-edb773afbe8c" --- @@ -41,7 +41,7 @@ P02/P04/P07: preserve first-admin setup on retry without duplication, explicit t ```task id: USER-WP-0030-T03 -status: progress +status: done priority: high state_hub_task_id: "07510026-caee-54d7-998c-a8d2b2f17773" ``` @@ -113,3 +113,10 @@ serialization, provider evidence and clear assisted recovery. All source/image CI and deployment/non-sending live checks passed. RPF-WP-0040 expiry/recovery acceptance is finished. Evidence: docs/evidence/2026-09-13-p05-service-operations.md. T03 now remains in progress for P06 only; notification/credential P05 is closed. + +2026-09-14: P06 completed with CI-published portal and issuer images, provider +guard and scoped client migration. Both services are ready; persistent policy +readback confirms the two optional-after-enrollment clients. Evidence: +`docs/evidence/2026-09-13-p06-authentication-policy.md`. Residual customer sign-in, +user onboarding and workload propagation remain live in USER-WP-0028, +KEY-WP-0034 and VERGABE-WP-0019; this does not close those workplans. diff --git a/workplans/USER-WP-0033-authentication-policy.md b/workplans/USER-WP-0033-authentication-policy.md index 1b96555..13694f4 100644 --- a/workplans/USER-WP-0033-authentication-policy.md +++ b/workplans/USER-WP-0033-authentication-policy.md @@ -4,11 +4,12 @@ type: workplan title: "P06 scoped authentication policy and safe optional onboarding" domain: communication repo: user-engine -status: active +status: finished owner: codex topic_slug: communication created: "2026-09-13" -updated: "2026-09-13" +updated: "2026-09-14" +state_hub_workstream_id: "d49778a4-faca-5712-8db3-1d15a7989aee" --- Implements P06 under USER-WP-0030-T03 and KEY-WP-0035. Authorized by the @@ -21,6 +22,7 @@ for unrelated clients remains in its current configuration. id: USER-WP-0033-T01 status: done priority: high +state_hub_task_id: "193b737c-f9a0-5d30-8e70-efe7550df258" ``` AAL1 users can reach account/onboarding and authenticator setup. Administrative @@ -33,6 +35,7 @@ recent MFA. Explain step-up, unavailable setup and identity-switch recovery. id: USER-WP-0033-T02 status: done priority: high +state_hub_task_id: "4e635786-6a68-54a1-b1a1-b73c14fd417f" ``` Issuer-owned persistent policy state for the two reviewed browser clients only. @@ -46,8 +49,9 @@ credential. Changes affect subsequent authorization, not already issued tokens. ```task id: USER-WP-0033-T03 -status: progress +status: done priority: high +state_hub_task_id: "006b3246-4f54-5989-9126-5ac0b95e226d" ``` Test no-factor/password-only, pending/cancel/confirmed enrollment, enrolled OTP, @@ -62,3 +66,10 @@ regressions and rollout tests pass. Installed-provider acceptance Job `provider-p06-browser-46713f7e` passed native optional/old-session OTP plus five browser checks. Fixed test endpoint and asynchronous TOTP/detail readiness. Release and live readback remain in progress. See P06 evidence. + +2026-09-14: P06 completed with CI-published portal and issuer images, provider +guard and scoped client migration. Both services are ready; persistent policy +readback confirms the two optional-after-enrollment clients. Evidence: +`docs/evidence/2026-09-13-p06-authentication-policy.md`. Residual customer sign-in, +user onboarding and workload propagation remain live in USER-WP-0028, +KEY-WP-0034 and VERGABE-WP-0019; this does not close those workplans.