- CCR-2026-0050: record secrets-engine-owner approval (two receipt conditions) and platform runbook review; link founder decision 18731e84. - Attended creation runbook with preconditions, rollback and negatives. - Warden routing receipt: openbao-platform-admin-login / oidc_login now selected. - Native adapter: owner-review digest excludes expires_at so reviews can precede the 120 s window (close-down checklist row 27). Nothing generated, written or applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 809600@bnt-lap001 Assistant-Session: 40ba228f-c8a0-4ec9-bb29-3129951afdc4
346 lines
22 KiB
Python
346 lines
22 KiB
Python
"""Actual disposable KV effects through native production state machine.
|
|
|
|
Kubernetes, attended identity and owner dispositions are explicitly synthetic
|
|
fixture inputs; no production authority/identity/custody claim follows.
|
|
"""
|
|
from copy import deepcopy
|
|
from datetime import datetime,timedelta,timezone
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import sys
|
|
import yaml
|
|
import pytest
|
|
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'scripts'))
|
|
from test_task_execution_native_fixture import bao_dev
|
|
import flex_auth_signer_native as m
|
|
from task_execution_journal import Refused
|
|
|
|
|
|
@pytest.fixture
|
|
def native(bao_dev,tmp_path,monkeypatch):
|
|
origin,token,call,process=bao_dev
|
|
assert call('POST','sys/mounts/platform',{'type':'kv','options':{'version':'2'}})[0] in {200,204}
|
|
source=tmp_path/'source';source.mkdir()
|
|
for name in [*m.SOURCE_FILES,m.CCR,m.PLAN,'openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl']:
|
|
p=source/name;p.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(m.ROOT/name,p)
|
|
ccr=yaml.safe_load((source/m.CCR).read_text());ccr['status']='approved';ccr['openbao']['auth']['bound_claims_confirmed']=True
|
|
(source/m.CCR).write_text(yaml.safe_dump(ccr,sort_keys=False))
|
|
plan=json.loads((source/m.PLAN).read_text());plan['bootstrap']['kid']='synthetic-native-kid';plan['receipts']['directory']=str(tmp_path/'private');(source/m.PLAN).write_text(json.dumps(plan))
|
|
monkeypatch.setattr(m,'ROOT',source)
|
|
directory=tmp_path/'private';directory.mkdir(mode=0o700)
|
|
image='forgejo.coulomb.social/coulomb/flex-auth@sha256:'+'1'*64
|
|
dep={'metadata':{'uid':'dep','generation':1},'spec':{'replicas':0,'template':{'spec':{'serviceAccountName':m.NAME,
|
|
'containers':[{'image':image,'args':['serve','--caller-auth-mode','enforce','--caller-audience','flex-auth','--caller-binding','secrets-engine=system:serviceaccount:secrets-engine:secrets-engine','--envelope-signing-key-file','/var/run/secrets/decision-envelope-signing/seed','--envelope-signing-kid','synthetic-native-kid'],
|
|
'volumeMounts':[{'name':'flex-auth-envelope-signing','mountPath':'/var/run/secrets/decision-envelope-signing','readOnly':True}]}],
|
|
'volumes':[{'name':'flex-auth-envelope-signing','secret':{'secretName':m.SECRET,'defaultMode':288,'items':[{'key':'seed','path':'seed'}]}}]}}},'status':{'observedGeneration':1,'replicas':0}}
|
|
class IO:
|
|
def __init__(self):
|
|
self.calls=[];self.dep=deepcopy(dep);self.pods=[];self.hpas=[];self.rs=[];self.cluster=m.CLUSTER
|
|
self.entity='11111111-1111-1111-1111-111111111111';self.backend=m.BAO_CLUSTER;self.root=False;self.role_drift=False;self.lost=False;self.denied=False;self.pod_on_recheck=False;self.fences=0
|
|
def bao(self,args,payload=None,allowed=(0,)):
|
|
self.calls.append(('bao',tuple(args)))
|
|
if args[0]=='status':return 0,{'cluster_id':self.backend,'sealed':False}
|
|
if args[:2]==['token','lookup']:return 0,{'data':{'policies':['root'] if self.root else ['platform-admin'],'path':'auth/netkingdom/login/x','entity_id':self.entity,'id':token}}
|
|
if args[0]=='audit':return 0,{'file/':{}}
|
|
if args[-1].startswith('sys/policies/acl/'):
|
|
return 0,{'data':{'policy':(source/'openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl').read_text()}}
|
|
if args[-1].startswith('auth/kubernetes/role/'):
|
|
return 0,{'data':{'bound_service_account_names':['foreign'] if self.role_drift else ['flex-auth-envelope-signing-eso'],'bound_service_account_namespaces':['flex-auth'],'bound_service_account_namespace_selector':'','audience':'openbao','token_policies':['workload-kv-read-flex-auth-envelope-signing'],'token_ttl':900,'token_max_ttl':900,'token_explicit_max_ttl':900,'token_no_default_policy':True,'token_period':0,'token_num_uses':0,'token_bound_cidrs':[]}}
|
|
raise AssertionError('unexpected fixed API')
|
|
def kube(self,resource,name=None):
|
|
self.calls.append(('kube',resource,name))
|
|
if resource=='namespace':return {'metadata':{'uid':self.cluster}}
|
|
if resource=='deployment':
|
|
self.fences+=1
|
|
if self.pod_on_recheck and self.fences>=3:self.pods=[{'spec':{'serviceAccountName':m.NAME}}]
|
|
return deepcopy(self.dep)
|
|
return {'items':deepcopy({'pods':self.pods,'hpa':self.hpas,'replicasets':self.rs}[resource])}
|
|
def http(self,method,path,body=None):
|
|
self.calls.append(('http',method,path))
|
|
if self.denied and path in {m.PATH,m.PATH+'?version=1'} and method=='GET':return 403,{'errors':['secret backend detail']}
|
|
result=call(method,path,body)
|
|
if self.lost and method=='POST' and path==m.PATH:raise OSError('secret response lost')
|
|
return result
|
|
io=IO()
|
|
c={'schema':'railiance-platform.signer-native-admission.v1','action':'bootstrap','operation_id':'synthetic-native-operation','kid':'synthetic-native-kid',
|
|
'expires_at':(datetime.now(timezone.utc)+timedelta(seconds=100)).isoformat(),'founder_entity_id':io.entity,
|
|
'ccr_sha256':m.digest((source/m.CCR).read_bytes()),'source_bundle':{n:m.digest((source/n).read_bytes()) for n in m.SOURCE_FILES},
|
|
'bindings':{k:'sha256:'+'2'*64 for k in m.BINDINGS},'producer_image':image,'caller_binding':'secrets-engine=system:serviceaccount:secrets-engine:secrets-engine',
|
|
'deployment_projection_sha256':m.digest(m.encoded(m.deployment_projection(dep))),
|
|
'authority':'founder-attended-exact-source-scope; no standing grant',
|
|
'routing':{'lane_id':'openbao-platform-admin-login','verdict':'founder_required','founder_act':'oidc_login','receipt_sha256':'sha256:'+'3'*64},
|
|
'owner_reviews':{},'exclusive_window_sha256':'sha256:'+'4'*64,'consumer_trust_sha256':'sha256:'+'5'*64,'original_receipt_sha256':None,'original_execution':None}
|
|
c['bindings'].update(proposal_sha256=m.digest((source/m.PLAN).read_bytes()),policy_sha256=m.LIFECYCLE,image_sha256='sha256:'+'1'*64,trust_sha256=c['consumer_trust_sha256'],deployment_sha256=c['deployment_projection_sha256'],executed_revision_sha256=m.digest(m.encoded(c['source_bundle'])))
|
|
def approve(value):
|
|
if value['action']=='recover' and value['original_execution'] is None:
|
|
old=json.loads((directory/(c['operation_id']+'.json')).read_bytes())
|
|
value['original_execution']={key:deepcopy(old[key]) for key in ['bindings','writer_sha256','source_ccr_sha256']}
|
|
scope=m.review_scope_digest(value)
|
|
value['owner_reviews']={role:{'approved_admission_sha256':scope,'approval_ref':'synthetic-fixture-'+role} for role in m.REVIEWS}
|
|
return value
|
|
approve(c)
|
|
return c,io,directory,call,approve,source
|
|
|
|
|
|
def test_actual_native_bootstrap_exact_readback_and_no_retry(native):
|
|
c,io,directory,call,approve,source=native
|
|
result=m.execute(c,directory,io=io)
|
|
assert result['status']=='custody-verified-pending-independent-public-admission' and result['exact_readback']
|
|
assert call('GET',m.META)[1]['data']['current_version']==1
|
|
assert [(x[1],x[2]) for x in io.calls if x[0]=='http' and x[1]=='POST']==[('POST',m.META),('POST',m.PATH)]
|
|
private=call('GET',m.PATH)[1]['data']['data']['ED25519_PRIVATE_KEY']
|
|
raw=(directory/(c['operation_id']+'.json')).read_bytes();assert private.encode() not in raw
|
|
before=len(io.calls)
|
|
with pytest.raises(FileExistsError):m.execute(c,directory,io=io)
|
|
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls[before:])
|
|
|
|
|
|
def test_actual_process_loss_unknown_fresh_recovery_read_only(native):
|
|
c,io,directory,call,approve,source=native
|
|
child=os.fork()
|
|
if child==0:
|
|
original_http=io.http
|
|
def die_after_native_write(method,path,body=None):
|
|
result=original_http(method,path,body)
|
|
if method=='POST' and path==m.PATH:os._exit(73)
|
|
return result
|
|
io.http=die_after_native_write
|
|
try:m.execute(c,directory,io=io)
|
|
except BaseException:os._exit(74)
|
|
os._exit(75)
|
|
_,status=os.waitpid(child,0)
|
|
assert os.waitstatus_to_exitcode(status)==73
|
|
old=directory/(c['operation_id']+'.json');original=old.read_bytes();assert json.loads(original)['status']=='write-intent-unknown'
|
|
recovery=deepcopy(c);recovery.update(action='recover',operation_id='synthetic-independent-recovery',original_receipt_sha256=m.digest(original));approve(recovery)
|
|
io.lost=False;io.calls.clear()
|
|
result=m.execute(recovery,directory,io=io,original_receipt=old)
|
|
assert result['status']=='custody-reconciled-pending-independent-public-admission'
|
|
assert result['no_write_or_remint'] and old.read_bytes()==original
|
|
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
|
|
assert call('GET',m.META)[1]['data']['current_version']==1
|
|
assert (directory/(recovery['operation_id']+'.json')).stat().st_mode&0o777==0o600
|
|
io.calls.clear()
|
|
with pytest.raises(FileExistsError):m.execute(recovery,directory,io=io,original_receipt=old)
|
|
assert not any(x[0]=='http' for x in io.calls)
|
|
|
|
|
|
@pytest.mark.parametrize('case',['missing-image','missing-trust','mismatched-source','bad-review','bad-route','expired','wrong-entity','wrong-backend','wrong-cluster','root','pod','terminating-pod','hpa','replicas','image-drift','role-drift','pod-after-metadata'])
|
|
def test_refusals_no_seed_effect(native,case):
|
|
c,io,directory,call,approve,source=native
|
|
if case=='missing-image':c['producer_image']=''
|
|
elif case=='missing-trust':c['consumer_trust_sha256']=''
|
|
elif case=='mismatched-source':(source/'scripts/flex_auth_signer_custody.py').write_text('changed source')
|
|
elif case=='bad-review':c['owner_reviews']['founder']['approved_admission_sha256']='sha256:'+'f'*64
|
|
elif case=='bad-route':c['routing']['lane_id']='openbao-structure-survey';approve(c)
|
|
elif case=='expired':c['expires_at']=(datetime.now(timezone.utc)-timedelta(seconds=1)).isoformat();approve(c)
|
|
elif case=='wrong-entity':io.entity='other'
|
|
elif case=='wrong-backend':io.backend='other'
|
|
elif case=='wrong-cluster':io.cluster='other'
|
|
elif case=='root':io.root=True
|
|
elif case=='pod':io.pods=[{'spec':{'serviceAccountName':m.NAME}}]
|
|
elif case=='terminating-pod':io.pods=[{'metadata':{'deletionTimestamp':'now'},'spec':{'volumes':[{'secret':{'secretName':m.SECRET}}]}}]
|
|
elif case=='hpa':io.hpas=[{'spec':{'scaleTargetRef':{'name':m.NAME}}}]
|
|
elif case=='replicas':io.dep['spec']['replicas']=1
|
|
elif case=='image-drift':io.dep['spec']['template']['spec']['containers'][0]['image']='unreviewed'
|
|
elif case=='role-drift':io.role_drift=True
|
|
elif case=='pod-after-metadata':io.pod_on_recheck=True
|
|
with pytest.raises(Refused):m.execute(c,directory,io=io)
|
|
assert not any(x[0]=='http' and x[1]=='POST' and x[2]==m.PATH for x in io.calls)
|
|
assert call('GET',m.PATH)[0]==404
|
|
|
|
|
|
@pytest.mark.parametrize('case',['damaged-receipt','typed-damaged-receipt','changed-version','read-denied','no-fresh-review'])
|
|
def test_recovery_refuses_without_new_effect(native,case):
|
|
c,io,directory,call,approve,source=native;io.lost=True
|
|
with pytest.raises(Refused):m.execute(c,directory,io=io)
|
|
old=directory/(c['operation_id']+'.json');recovery=deepcopy(c);recovery.update(action='recover',operation_id='recovery',original_receipt_sha256=m.digest(old.read_bytes()));approve(recovery)
|
|
if case=='damaged-receipt':old.write_text('{}')
|
|
elif case=='typed-damaged-receipt':
|
|
damaged=json.loads(old.read_text());damaged['expected_version']=False;old.write_text(json.dumps(damaged));recovery['original_receipt_sha256']=m.digest(old.read_bytes());approve(recovery)
|
|
elif case=='changed-version':
|
|
doc=call('GET',m.PATH)[1]['data']['data'];assert call('POST',m.PATH,{'options':{'cas':1},'data':doc})[0]==200
|
|
elif case=='read-denied':io.denied=True
|
|
elif case=='no-fresh-review':recovery['owner_reviews']['founder']['approved_admission_sha256']='sha256:'+'f'*64
|
|
io.calls.clear();io.lost=False
|
|
with pytest.raises(Refused):m.execute(recovery,directory,io=io,original_receipt=old)
|
|
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
|
|
|
|
|
|
@pytest.mark.parametrize('bad_args',[False,True])
|
|
def test_main_missing_approval_silent_and_no_io(tmp_path,capsys,bad_args):
|
|
if bad_args:
|
|
assert m.main(['--unrecognized'])==1
|
|
output=capsys.readouterr();assert output.out==output.err==''
|
|
return
|
|
assert m.main(['--admission',str(tmp_path/'missing'),'--admission-sha256','sha256:'+'0'*64,'--receipt-directory',str(tmp_path)])==1
|
|
output=capsys.readouterr();assert output.out==output.err==''
|
|
|
|
|
|
def test_native_command_byte_and_elapsed_bounds_no_output(capsys):
|
|
io=m.NativeIO()
|
|
with pytest.raises(Refused):io.captured([sys.executable,'-c',"import sys;sys.stdout.write('x'*1048577)"])
|
|
import time
|
|
started=time.monotonic()
|
|
with pytest.raises(Refused):io.captured([sys.executable,'-c',"import time;time.sleep(10)"])
|
|
assert time.monotonic()-started<3
|
|
output=capsys.readouterr();assert output.out==output.err==''
|
|
|
|
|
|
def test_exclusive_native_window_refuses_second_writer(native):
|
|
import fcntl
|
|
c,io,directory,call,approve,source=native
|
|
fd=os.open(directory/'.signer-window.lock',os.O_RDWR|os.O_CREAT,0o600)
|
|
try:
|
|
fcntl.flock(fd,fcntl.LOCK_EX|fcntl.LOCK_NB)
|
|
with pytest.raises(BlockingIOError):m.execute(c,directory,io=io)
|
|
assert io.calls==[]
|
|
finally:os.close(fd)
|
|
|
|
|
|
def test_native_fixed_http_transport_against_disposable_backend(bao_dev,monkeypatch,capsys):
|
|
origin,token,call,process=bao_dev
|
|
assert call('POST','sys/mounts/platform',{'type':'kv','options':{'version':'2'}})[0] in {200,204}
|
|
factory=m.http.client.HTTPConnection;port=int(origin.rsplit(':',1)[1]);destinations=[]
|
|
def fixture_socket(host,requested_port,*args,**kwargs):
|
|
destinations.append((host,requested_port))
|
|
return factory(host,port if requested_port==18200 else requested_port,*args,**kwargs)
|
|
monkeypatch.setattr(m.http.client,'HTTPConnection',fixture_socket)
|
|
io=m.NativeIO();io._token=token
|
|
assert io.http('GET',m.META)==(404,{'errors':[]})
|
|
assert io.http('POST',m.META,{'cas_required':True,'max_versions':2,'delete_version_after':'0s'})[0] in {200,204}
|
|
m.exact_metadata(*io.http('GET',m.META),created=False)
|
|
assert all(host=='127.0.0.1' and selected_port==18200 for host,selected_port in destinations)
|
|
with pytest.raises(Refused):io.http('GET','other/path')
|
|
output=capsys.readouterr();assert output.out==output.err==''
|
|
|
|
|
|
def metadata_phase_crash(native,*,after):
|
|
c,io,directory,call,approve,source=native
|
|
child=os.fork()
|
|
if child==0:
|
|
original=io.http
|
|
def die(method,path,body=None):
|
|
if method=='POST' and path==m.META:
|
|
if after:original(method,path,body)
|
|
os._exit(73)
|
|
return original(method,path,body)
|
|
io.http=die
|
|
try:m.execute(c,directory,io=io)
|
|
except BaseException:os._exit(74)
|
|
os._exit(75)
|
|
_,status=os.waitpid(child,0);assert os.waitstatus_to_exitcode(status)==73
|
|
old=directory/(c['operation_id']+'.json');raw=old.read_bytes();receipt=json.loads(raw)
|
|
assert receipt['phase']=='metadata-setup' and receipt['status']=='write-intent-unknown'
|
|
assert receipt['public_key'] is None and receipt['kv_version'] is None
|
|
recovery=deepcopy(c);recovery.update(action='recover',operation_id='metadata-recovery',original_receipt_sha256=m.digest(raw));approve(recovery)
|
|
return old,raw,recovery
|
|
|
|
|
|
@pytest.mark.parametrize('after',[False,True])
|
|
def test_real_metadata_process_loss_read_only_phase_recovery(native,after):
|
|
c,io,directory,call,approve,source=native
|
|
old,raw,recovery=metadata_phase_crash(native,after=after)
|
|
io.calls.clear();result=m.execute(recovery,directory,io=io,original_receipt=old)
|
|
assert result['status']==('metadata-only-confirmed' if after else 'current-custody-absent')
|
|
assert result['public_key'] is result['public_fingerprint'] is result['kv_version'] is None
|
|
assert result['key_custody_verified'] is False and result['historical_effect']=='unresolved'
|
|
assert 'fresh independently reviewed exact action required' in result['next_action']
|
|
assert old.read_bytes()==raw and result['original_unknown_preserved']
|
|
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
|
|
assert call('GET',m.PATH+'?version=1')==(404,{'errors':[]})
|
|
|
|
|
|
@pytest.mark.parametrize('case',['metadata-denied','metadata-misleading404','metadata-misleading200','metadata-malformed','metadata-timeout','data-denied','unexpected-version'])
|
|
def test_metadata_recovery_ambiguous_or_denied_never_classifies_or_writes(native,case):
|
|
c,io,directory,call,approve,source=native
|
|
old,raw,recovery=metadata_phase_crash(native,after=True)
|
|
if case=='unexpected-version':
|
|
assert call('POST',m.PATH,{'options':{'cas':0},'data':{'ED25519_PRIVATE_KEY':'synthetic-unadmitted','ED25519_PUBLIC_KEY':'synthetic-unadmitted','kid':c['kid']}})[0]==200
|
|
original=io.http
|
|
def faulty(method,path,body=None):
|
|
if path==m.META:
|
|
if case=='metadata-denied':return 403,{'errors':['denied']}
|
|
if case=='metadata-misleading404':return 404,{'errors':['permission denied']}
|
|
if case=='metadata-misleading200':
|
|
result=original(method,path,body);result[1]['errors']=['denied'];return result
|
|
if case=='metadata-malformed':return 200,{'data':{'current_version':False}}
|
|
if case=='metadata-timeout':raise OSError('synthetic private backend detail')
|
|
if path==m.PATH+'?version=1' and case=='data-denied':return 404,{'errors':['denied']}
|
|
return original(method,path,body)
|
|
io.http=faulty;io.calls.clear()
|
|
with pytest.raises((Refused,OSError)):m.execute(recovery,directory,io=io,original_receipt=old)
|
|
evidence=json.loads((directory/(recovery['operation_id']+'.json')).read_bytes())
|
|
assert evidence['status']=='recovery-read-intent-unconfirmed' and evidence['key_custody_verified'] is False
|
|
assert old.read_bytes()==raw
|
|
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
|
|
if case=='unexpected-version':assert not any(x[0]=='http' and x[2]==m.PATH+'?version=1' for x in io.calls)
|
|
|
|
|
|
def test_stale_current_primitive_lineage_refuses_before_native_io(native):
|
|
c,io,directory,call,approve,source=native
|
|
plan=json.loads((source/m.PLAN).read_bytes());plan['bootstrap']['source_primitive_sha256']='sha256:'+'8'*64
|
|
(source/m.PLAN).write_text(json.dumps(plan));c['bindings']['proposal_sha256']=m.digest((source/m.PLAN).read_bytes());approve(c)
|
|
with pytest.raises(Refused,match='primitive lineage'):m.execute(c,directory,io=io)
|
|
assert io.calls==[]
|
|
|
|
|
|
def test_recovery_explicitly_binds_historical_execution_and_current_source(native):
|
|
c,io,directory,call,approve,source=native
|
|
old,raw,recovery=metadata_phase_crash(native,after=True)
|
|
# Model a separately retained older executed bundle/proposal. It must be
|
|
# explicitly selected in fresh recovery review, never treated as current.
|
|
original=json.loads(raw);original['bindings']['executed_revision_sha256']='sha256:'+'6'*64
|
|
original['bindings']['proposal_sha256']='sha256:'+'7'*64
|
|
old.write_text(json.dumps(original));recovery['original_receipt_sha256']=m.digest(old.read_bytes())
|
|
recovery['original_execution']={key:deepcopy(original[key]) for key in ['bindings','writer_sha256','source_ccr_sha256']};approve(recovery)
|
|
result=m.execute(recovery,directory,io=io,original_receipt=old)
|
|
assert result['status']=='metadata-only-confirmed'
|
|
assert result['original_execution']['bindings']['executed_revision_sha256']!=recovery['bindings']['executed_revision_sha256']
|
|
assert json.loads(old.read_text())==original
|
|
|
|
|
|
def test_generated_but_unstored_seed_phase_recovers_without_public_admission(native):
|
|
c,io,directory,call,approve,source=native
|
|
io.pod_on_recheck=True
|
|
with pytest.raises(Refused):m.execute(c,directory,io=io)
|
|
old=directory/(c['operation_id']+'.json');raw=old.read_bytes();receipt=json.loads(raw)
|
|
assert receipt['phase']=='seed-cas' and receipt['public_key'] is not None
|
|
assert call('GET',m.PATH+'?version=1')==(404,{'errors':[]})
|
|
io.pod_on_recheck=False;io.pods=[];io.calls.clear()
|
|
recovery=deepcopy(c);recovery.update(action='recover',operation_id='unstored-seed-recovery',original_receipt_sha256=m.digest(raw));approve(recovery)
|
|
result=m.execute(recovery,directory,io=io,original_receipt=old)
|
|
assert result['status']=='metadata-only-confirmed' and result['key_custody_verified'] is False
|
|
assert result['public_key'] is None and result['public_fingerprint'] is None
|
|
assert old.read_bytes()==raw
|
|
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
|
|
|
|
|
|
def test_exact_runtime_storage_owner_slot_replaces_conflated_role(native):
|
|
c,io,directory,call,approve,source=native
|
|
assert m.REVIEWS=={'founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-runtime-storage-owner'}
|
|
assert m.validate_admission(c)>5
|
|
old=deepcopy(c)
|
|
old['owner_reviews']['cluster-gitops-owner']=old['owner_reviews'].pop('cluster-runtime-storage-owner')
|
|
with pytest.raises(Refused,match='distinct owner reviews'):
|
|
m.validate_admission(old)
|
|
missing=deepcopy(c);missing['owner_reviews'].pop('cluster-runtime-storage-owner')
|
|
with pytest.raises(Refused,match='distinct owner reviews'):
|
|
m.validate_admission(missing)
|
|
extra=deepcopy(c);extra['owner_reviews']['railiance-enablement-owner']=next(iter(extra['owner_reviews'].values()))
|
|
with pytest.raises(Refused,match='distinct owner reviews'):
|
|
m.validate_admission(extra)
|
|
assert io.calls==[]
|
|
|
|
|
|
def test_owner_reviews_survive_fresh_expiry_but_not_scope_change(native):
|
|
c,io,directory,call,approve,source=native
|
|
reviewed=m.review_scope_digest(c)
|
|
c['expires_at']=(datetime.now(timezone.utc)+timedelta(seconds=90)).isoformat()
|
|
assert m.review_scope_digest(c)==reviewed
|
|
m.validate_admission(c)
|
|
changed=deepcopy(c);changed['kid']='other-native-kid'
|
|
assert m.review_scope_digest(changed)!=reviewed
|
|
with pytest.raises(m.Refused):m.validate_admission(changed)
|