Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
70 lines
5 KiB
Python
70 lines
5 KiB
Python
import time
|
|
from dataclasses import replace
|
|
from urllib.parse import parse_qs, urlsplit
|
|
from test_journey_roles import JourneyFixture
|
|
from test_web import invoke
|
|
|
|
class PolicyFixture:
|
|
def __init__(self): self.calls=[]; self.unavailable=False
|
|
def call(self, token, body):
|
|
self.calls.append((token,body))
|
|
if self.unavailable: raise RuntimeError('private-provider-error')
|
|
result=dict(success=True,status='current',revision=1,clients=[dict(id='vergabe-demo-company',name='Demo Company',mode='optional_after_enrollment')],history=[])
|
|
if body['action'] in {'preview','rollback'}:
|
|
result.update(status='preview',client='vergabe-demo-company',before='optional_after_enrollment',after='mandatory',reference='p06-case',confirmation='fixture-confirmation')
|
|
if body['action']=='apply':
|
|
result.update(status='recorded',receipt=dict(client='vergabe-demo-company',after='mandatory',reference='p06-case'))
|
|
return result
|
|
|
|
class AuthenticationPolicyJourney(JourneyFixture):
|
|
def setUp(self):
|
|
super().setUp(); self.provider=PolicyFixture(); self.app.authentication_policy=self.provider
|
|
session=self.oidc.sessions['operator']
|
|
self.oidc.sessions['operator']=replace(session,id_token='server-only-id-token',claims=dict(session.claims,assurance=dict(level='aal2',mfa=True,at=time.time())))
|
|
def test_aal1_account_access_does_not_grant_administration(self):
|
|
for who in ['member','admin','operator']:
|
|
self.oidc.sessions[who].claims['assurance']=dict(level='aal1',mfa=False)
|
|
for path in ['/','/onboarding','/security']:
|
|
response,_=invoke(self.app,path,cookie='ue_session='+who)
|
|
self.assertEqual('200 OK',response['status'])
|
|
for path in ['/platform','/admin/tenant:trial:demo-company','/platform/authentication-policy']:
|
|
_,body=invoke(self.app,path,cookie='ue_session='+who)
|
|
self.assertIn(b'Verify with MFA to administer',body)
|
|
response,_=self.post('/api/v1/platform/tenants',who=who)
|
|
self.assertEqual('403 Forbidden',response['status'])
|
|
self.assertEqual([],self.provider.calls)
|
|
self.assertEqual([],self.app.provisioning.actions)
|
|
def test_role_csrf_and_stale_mfa_deny_before_policy_service(self):
|
|
for who in ['member','admin']:
|
|
response,_=self.post('/platform/authentication-policy',who=who,action='preview')
|
|
self.assertEqual('403 Forbidden',response['status'])
|
|
response,_=self.post('/platform/authentication-policy',who='operator',csrf_token='wrong',action='preview')
|
|
self.assertEqual('403 Forbidden',response['status'])
|
|
self.oidc.sessions['operator'].claims['assurance']['at']=time.time()-301
|
|
_,body=invoke(self.app,'/platform/authentication-policy',cookie='ue_session=operator')
|
|
self.assertIn(b'fresh MFA sign-in',body);self.assertEqual([],self.provider.calls)
|
|
def test_review_explains_lockout_scope_rollback_and_receipts(self):
|
|
_,body=self.post('/platform/authentication-policy',who='operator',action='preview',client='vergabe-demo-company',mode='mandatory',reference='p06-case')
|
|
self.assertIn(b'Review policy change',body);self.assertIn(b'unable to complete sign-in',body)
|
|
self.assertIn(b'does not revoke already issued tokens',body);self.assertIn(b'Cancel without changes',body)
|
|
self.assertNotIn(b'server-only-id-token',body)
|
|
self.assertEqual('preview',self.provider.calls[-1][1]['action'])
|
|
_,body=self.post('/platform/authentication-policy',who='operator',action='apply',confirmation='fixture-confirmation',acknowledged='yes')
|
|
self.assertIn(b'Policy change recorded',body);self.assertIn(b'p06-case',body)
|
|
self.assertTrue(self.provider.calls[-1][1]['acknowledged'])
|
|
_,body=self.post('/platform/authentication-policy',who='operator',action='rollback',client='vergabe-demo-company',reference='rollback')
|
|
self.assertIn(b'Review policy change',body)
|
|
def test_outage_is_redacted_and_current_status_is_retryable(self):
|
|
self.provider.unavailable=True
|
|
_,body=invoke(self.app,'/platform/authentication-policy',cookie='ue_session=operator')
|
|
self.assertIn(b'Existing policy remains in force',body);self.assertNotIn(b'private-provider-error',body)
|
|
self.provider.unavailable=False
|
|
_,body=invoke(self.app,'/platform/authentication-policy',cookie='ue_session=operator')
|
|
self.assertIn(b'Current policy',body)
|
|
def test_policy_stepup_binds_only_supported_return_path(self):
|
|
response,_=invoke(self.app,'/login',query='recovery=1&return_path=/platform/authentication-policy')
|
|
query=parse_qs(urlsplit(dict(response['headers'])['Location']).query)
|
|
self.assertEqual(['aal2'],query['acr_values']);self.assertEqual(['login'],query['prompt'])
|
|
self.assertEqual('/platform/authentication-policy',self.oidc.pending[query['state'][0]].return_path)
|
|
response,_=invoke(self.app,'/login',query='recovery=1&return_path=https://evil.example')
|
|
self.assertEqual('400 Bad Request',response['status'])
|