Implement scoped P06 authentication policy and guarded optional onboarding
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Build and Publish Container Image / build-and-push (push) Successful in 51s
Account journey acceptance / journeys (push) Successful in 7s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
tegwick 2026-09-14 00:00:07 +02:00
parent 0801ec55ac
commit 3bd1827a7f
10 changed files with 298 additions and 8 deletions

View file

@ -73,6 +73,27 @@ try{
await evaluate(`document.querySelector('input[name="identity_verified"]').checked=true;Array.from(document.forms).find(f=>f.querySelector('input[name="confirmation"]')).querySelector('button').click()`);
await waitFor('document.body.innerText.includes("Authenticator recovery recorded")');
await check(`document.body.innerText.includes("case-1") && document.body.innerText.includes("Enroll a replacement") && !document.body.innerText.includes("server-only-token")`,'P04 receipt and replacement onboarding without credentials');
await navigate('/platform/authentication-policy');
await check(`document.body.innerText.includes("Application step-up always wins") && document.body.innerText.includes("Current policy")`,'P06 effective policy and step-up boundary');
await evaluate(`document.querySelector('input[name="reference"]').value='p06-case';document.querySelector('select[name="mode"]').value='mandatory';document.querySelector('button[value="preview"]').click()`);
await waitFor('document.body.innerText.includes("Review policy change")');
await check(`document.body.innerText.includes("unable to complete sign-in") && !!document.querySelector('input[name="acknowledged"][required]')`,'P06 lockout impact requires acknowledgement');
await evaluate(`Array.from(document.links).find(a=>a.textContent==='Cancel without changes').click()`);
await waitFor('document.body.innerText.includes("Current policy") && !document.querySelector("input[name=confirmation]")');
await check(`document.body.innerText.includes("Optional until an authenticator is activated")`,'P06 cancel preserves current policy');
await evaluate(`document.querySelector('input[name="reference"]').value='p06-case';document.querySelector('select[name="mode"]').value='mandatory';document.querySelector('button[value="preview"]').click()`);
await waitFor('!!document.querySelector("input[name=acknowledged]")');
await evaluate(`document.querySelector('input[name="acknowledged"]').checked=true;document.querySelector('input[name="confirmation"]').form.querySelector('button').click()`);
await waitFor('document.body.innerText.includes("Policy change recorded")');
await check(`document.body.innerText.includes("p06-case") && !document.body.innerText.includes("server-only-token")`,'P06 recorded change has safe receipt');
await evaluate(`document.querySelector('input[name="reference"]').value='rollback';document.querySelector('button[value="rollback"]').click()`);
await waitFor('document.body.innerText.includes("Review policy change")');
await check(`!!document.querySelector('input[name="acknowledged"][required]')`,'P06 rollback requires a new review');
await identity('operator-aal1');await navigate('/platform');
await check(`document.body.innerText.includes("Verify with MFA to administer") && !!document.querySelector('a[href="/security"]')`,'P06 AAL1 operator cannot administer');
await navigate('/onboarding');
await check(`document.body.innerText.includes("My account") && !document.body.innerText.includes("Verify with MFA to administer")`,'P06 AAL1 account setup remains available');
await identity('operator');
await navigate('/logout');
await check(`document.body.innerText.includes("Log out of this portal?")`,'U11 logout requires confirmation');
await evaluate(`document.querySelector('form[action="/logout"] button').click()`);

View file

@ -32,6 +32,11 @@ class Delivery:
def mail_delivery_status(self, event_id):
return {"state":"failed" if self.attempts == 1 else "provider_accepted" if self.attempts else "not_found"}
fixture.app.outbox_delivery = Delivery()
from test_authentication_policy import PolicyFixture
from dataclasses import replace
fixture.app.authentication_policy = PolicyFixture()
session = fixture.oidc.sessions['operator']
fixture.oidc.sessions['operator-aal1'] = replace(session, claims=dict(session.claims, assurance=dict(level='aal1', mfa=False)), csrf_token='operator-aal1-csrf')
class Quiet(WSGIRequestHandler):
def log_message(self,*args):pass
server=make_server('127.0.0.1',0,fixture.app,handler_class=Quiet)