"""Contained provider fixtures: exact quota, conditional publication and retention.""" from datetime import datetime,timedelta,timezone import hashlib import importlib.util import io import json from pathlib import Path import urllib.error import pytest ROOT=Path(__file__).resolve().parents[1] def load(name): spec=importlib.util.spec_from_file_location(name,ROOT/f'tools/bootstrap-recovery/{name}.py') m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m);return m o=load('identity-offsite');r=load('identity-retention');setup=load('identity-credential-setup');runtime=load('identity-runtime') TOKEN='fixturefixturefixture' class Response(io.BytesIO): def __init__(self,url,data=b'',status=200,version='"generation"'): super().__init__(data);self.url=url;self.status=status self.headers={'ETag':version,'Content-Length':str(len(data))} class Storage: def __init__(self):self.objects={};self.calls=[];self.free=10*1024**3;self.tamper=False def open(self,req,timeout): self.calls.append((req.get_method(),req.full_url,dict(req.headers))) name=req.full_url.removeprefix(o.BASE) if req.get_method()=='PROPFIND': return Response(req.full_url,f'{self.free}'.encode(),207) if req.get_method()=='PUT': assert req.get_header('If-none-match')=='*' if name in self.objects:raise urllib.error.HTTPError(req.full_url,412,'exists',{},None) value=req.data if isinstance(req.data,bytes) else req.data.read() self.objects[name]=value return Response(req.full_url,status=201) if name not in self.objects:raise urllib.error.HTTPError(req.full_url,404,'missing',{},None) body=self.objects[name] if req.get_method()=='GET' and self.tamper:body=b'X'*len(body) if req.get_method()=='DELETE': assert req.get_header('If-match')=='"generation"' del self.objects[name];return Response(req.full_url,status=204) return Response(req.full_url,body) @pytest.fixture def source(tmp_path): name='net-kingdom-identity-20261016T000000Z-0123456789ab.tar.age';path=tmp_path/name path.write_bytes(b'age-encryption.org/v1\n'+b'ciphertext-fixture'*10);path.chmod(0o600) record={'status':'encrypted_bundle_created','helper_cleanup_verified':True,'temporary_plaintext_cleanup_verified':True, 'ciphertext_sha256':o.digest(path),'ciphertext_bytes':path.stat().st_size,'finished_at':'2026-10-16T00:00:30+00:00'} return path,record def test_conditional_upload_exact_readback_and_index_commit(source,tmp_path): path,capture=source;server=Storage();target=tmp_path/'fetched.age' receipt=o.publish(server,TOKEN,path,capture,target) assert receipt['status']=='readback_verified' and target.read_bytes()==path.read_bytes() assert receipt['verified_index_published'] assert len(server.objects)==2 assert not any(method=='DELETE' for method,_,_ in server.calls) assert TOKEN not in json.dumps(receipt) assert all(url.startswith(o.BASE) for _,url,_ in server.calls) def test_quota_shortfall_refuses_before_any_provider_write(source,tmp_path): path,capture=source;server=Storage();server.free=o.HEADROOM+path.stat().st_size-1 with pytest.raises(o.Refused):o.publish(server,TOKEN,path,capture,tmp_path/'fetched.age') assert not server.objects and not any(method=='PUT' for method,_,_ in server.calls) def test_existing_archive_never_overwritten(source,tmp_path): path,capture=source;server=Storage();server.objects[path.name]=b'existing-protected-object' with pytest.raises(o.Refused):o.publish(server,TOKEN,path,capture,tmp_path/'fetched.age') assert server.objects[path.name]==b'existing-protected-object' def test_corrupted_readback_never_publishes_success_index(source,tmp_path): path,capture=source;server=Storage();server.tamper=True with pytest.raises(o.Refused):o.publish(server,TOKEN,path,capture,tmp_path/'fetched.age') assert list(server.objects)==[path.name] @pytest.mark.parametrize('key',['helper_cleanup_verified','temporary_plaintext_cleanup_verified']) def test_unverified_capture_cleanup_prevents_provider_access(source,tmp_path,key): path,capture=source;capture[key]=False;server=Storage() with pytest.raises(o.Refused):o.publish(server,TOKEN,path,capture,tmp_path/'fetched.age') assert not server.calls def points(count=30): now=datetime(2026,10,15,tzinfo=timezone.utc) return [{'artifact':f'point-{i}','stamp':now-timedelta(days=i)} for i in range(count)] def test_retention_preserves_recovered_point_newest_daily_and_weekly(): rows=points();delete,keep=r.select(rows,'point-29') assert 'point-29' in keep and 'point-0' in keep assert all(f'point-{i}' in keep for i in range(7)) assert len(keep)>=8 and not (keep&{p['artifact'] for p in delete}) assert {p['artifact'] for p in delete}|keep=={p['artifact'] for p in rows} def test_missing_independently_recovered_point_refuses_all_expiration(): with pytest.raises(r.offsite.Refused):r.select(points(),'lost-point') @pytest.mark.parametrize('action,number',[('publisher',48),('retention',49)]) def test_proposed_ccr_cannot_create_provider_share_or_reader(monkeypatch,action,number): path=setup.PLATFORM/f'credential-change-requests/CCR-2026-{number:04d}-net-kingdom-backup-{action}.yaml' from types import SimpleNamespace import yaml proposed=yaml.safe_load(path.read_text());proposed['status']='proposed';proposed['review']['comments']=[] # The negative fixture must stay proposed after the real owner approves a CCR. original_load=setup.load monkeypatch.setattr(setup,'load',lambda name,p:SimpleNamespace(validate_ccr=lambda _: (proposed,[],[])) if name=='credential_change' else original_load(name,p)) monkeypatch.setattr(setup.inventory,'caller_credentials',lambda:pytest.fail('unapproved request reached credential read')) with pytest.raises(setup.Refused):setup.run(action,hashlib.sha256(path.read_bytes()).hexdigest(),{}) def test_runtime_refuses_gate_without_actual_independent_restore(tmp_path): path=tmp_path/'accepted.json';path.write_text(json.dumps({'schema':'netkingdom.identity-recovery.v1','status':'prepared'}));path.chmod(0o600) with pytest.raises(runtime.offsite.Refused):runtime.accepted(path) def test_isolation_preserves_dns_peers_and_tests_immutable_target(monkeypatch): from copy import deepcopy iso=load('identity-isolation');iso.capture.KUBE=['kubectl','--kubeconfig','/reviewed/kubeconfig'] original={'podSelector':{},'policyTypes':['Egress'],'egress':[{'ports':[{'protocol':'UDP','port':53}]}]} def get(ns,kind,name): if name=='kube-system':return {'metadata':{'uid':iso.capture.prepare.CLUSTER_UID}} return {'metadata':{'name':name,'uid':ns+'-uid','resourceVersion':'7'},'spec':deepcopy(original)} def command(args,payload=None): if 'get' in args:return json.dumps({'items':[{'metadata':{'labels':{'app':'service'}}}]}).encode() patch=json.loads(args[args.index('-p')+1]);assert patch[2]['value']==original wanted=deepcopy(original);wanted['podSelector']=iso.SELECTOR return json.dumps({'metadata':{'uid':args[1]+'-uid'},'spec':wanted}).encode() monkeypatch.setattr(iso.capture,'get',get);monkeypatch.setattr(iso.capture,'command',command) packet=iso.plan();receipt={'attempted':[],'verified':[]};iso.apply(packet,receipt,True) assert receipt['status']=='server_dry_run_passed' and len(receipt['verified'])==2 packet['policies'][0]['uid']='foreign' with pytest.raises(iso.capture.Refused):iso.apply(packet,{'attempted':[],'verified':[]}) def test_bootstrap_dispatcher_handoff_help_and_missing_checkout(tmp_path): import os,subprocess bootstrap=ROOT.parent/'railiance-bootstrap' if not bootstrap.is_dir():pytest.skip('bootstrap integration checkout absent') help_run=subprocess.run(['bash',str(bootstrap/'bin/railiance'),'identity-recovery','--help'],capture_output=True) assert help_run.returncode==0 and b'isolated archive verifier' not in help_run.stderr assert b'--transfer-receipt' in help_run.stdout env=dict(os.environ,NETKINGDOM_RECOVERY_ROOT=str(tmp_path/'missing')) failure=subprocess.run(['bash',str(bootstrap/'bin/railiance'),'identity-recovery','--transfer-receipt','missing','--receipt','new'],env=env,capture_output=True) assert failure.returncode==1 and b'Missing reviewed NetKingdom' in failure.stderr @pytest.mark.parametrize('parent_status,outside_status,accepted',[(400,401,True),(400,200,False),(500,401,False)]) def test_provider_parent_400_requires_independent_outside_folder_denial(monkeypatch,parent_status,outside_status,accepted): store={};owner_auth=('owner','fixture');token=TOKEN def request(client,url,auth,method='GET',body=None,extra=None): if '%2e%2e' in url:return parent_status,b'' if url.endswith('/r01-net-kingdom-pg/'):return outside_status,b'' name=url.rsplit('/',1)[-1] if method=='PUT': if name in store:return 403,b'' store[name]=body;return 201,b'' if method=='DELETE':return 403,b'' return 200,store[name] def remove(client,url,auth,expected): name=url.rsplit('/',1)[-1];assert auth==owner_auth and store[name]==expected;del store[name] monkeypatch.setattr(setup,'request',request);monkeypatch.setattr(setup,'remove_fixture',remove) receipt={} if accepted: setup.boundary(None,owner_auth,token,'publisher',receipt) assert receipt['provider_boundary_verified'] and receipt['other_account_folder_status']==401 else: with pytest.raises(setup.Refused):setup.boundary(None,owner_auth,token,'publisher',receipt) assert not receipt.get('provider_boundary_verified') assert not store def test_eso_native_default_fields_preserve_exact_source_scope(monkeypatch): import base64 from copy import deepcopy es={'metadata':{'uid':'fixture-eso'},'spec':{'target':{'name':'net-kingdom-backup-publisher'},'secretStoreRef':{'kind':'SecretStore','name':'net-kingdom-backup-publisher'},'data':[{'secretKey':'token','remoteRef':{'key':'workloads/net-kingdom/identity-backup/publisher','property':'NC_WEBDAV_TOKEN','conversionStrategy':'Default','decodingStrategy':'None','metadataPolicy':'None'}}]},'status':{'conditions':[{'type':'Ready','status':'True'}]}} secret={'metadata':{'ownerReferences':[{'uid':'fixture-eso'}]},'data':{'token':base64.b64encode(TOKEN.encode()).decode()}} monkeypatch.setattr(runtime.capture,'get',lambda ns,kind,name:es if kind=='externalsecret' else secret) assert runtime.token('publisher')==TOKEN original=deepcopy(es) for key,value in [('conversionStrategy','Unicode'),('property','OTHER'),('version','old')]: es.clear();es.update(deepcopy(original));es['spec']['data'][0]['remoteRef'][key]=value with pytest.raises(runtime.offsite.Refused):runtime.token('publisher') es.clear();es.update(deepcopy(original));es['spec']['dataFrom']=[{'extract':{'key':'foreign'}}] with pytest.raises(runtime.offsite.Refused):runtime.token('publisher')