fix(auth): authenticate producer-signed decisions before authorization
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
parent
6bd7129eac
commit
c80a944105
30 changed files with 2059 additions and 32 deletions
|
|
@ -12,7 +12,7 @@ from secrets_engine.catalog import validate_entry
|
|||
from secrets_engine.errors import DecisionError
|
||||
from secrets_engine.execution_contract import ExecutionContract, catalog_target_digest
|
||||
from tests.test_catalog import VALID
|
||||
from tests.test_consume_binding_join import _Cfg, _token, _opener, AUTH_ID
|
||||
from tests.test_consume_binding_join import _Cfg, _token, _opener, APPROVAL_ID
|
||||
from tests.authorization_stub import AuthorizationStub
|
||||
|
||||
H = 'sha256:' + 'a' * 64
|
||||
|
|
@ -21,7 +21,7 @@ H = 'sha256:' + 'a' * 64
|
|||
def entry():
|
||||
raw = copy.deepcopy(VALID)
|
||||
raw['stage'] = 'prod'
|
||||
raw['approval'] = {'model': 'bootstrap-only', 'authorization_id': AUTH_ID, 'purpose': 'metadata-test'}
|
||||
raw['approval'] = {'model': 'bootstrap-only', 'authorization_id': APPROVAL_ID, 'purpose': 'metadata-test'}
|
||||
return validate_entry(raw)
|
||||
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ def test_four_builders_preserve_context_and_binding(tmp_path):
|
|||
expected = request(e, contract)
|
||||
assert expected['context']['human_control'] is True
|
||||
assert _expected_request(cfg,e,'execute-ccr',execution_contract=contract) == expected
|
||||
pdp = AuthorizationStub(approval_id=AUTH_ID, package=cfg.authorization_policy_package, version=cfg.authorization_policy_version)
|
||||
pdp = AuthorizationStub(approval_id=APPROVAL_ID, package=cfg.authorization_policy_package, version=cfg.authorization_policy_version)
|
||||
pdp.bind_request(expected); claim = served(pdp)
|
||||
observed = resolve_approval_observation(cfg,e,'execute-ccr',None,execution_contract=contract,opener=_opener(claim))
|
||||
assert observed.request['context']['execution_contract'] == raw
|
||||
|
|
@ -76,7 +76,7 @@ def test_four_builders_preserve_context_and_binding(tmp_path):
|
|||
consume_bodies = []
|
||||
def consume(req, timeout=None):
|
||||
value = json.loads(req.data); consume_bodies.append(value)
|
||||
return _Response(200, {'status':'consumed', 'approval_id':AUTH_ID,
|
||||
return _Response(200, {'status':'consumed', 'approval_id':APPROVAL_ID,
|
||||
'request_digest':value['request_digest'], 'decision_id':value['decision_id'],
|
||||
'consumed_at':datetime.now(timezone.utc).isoformat(), 'idempotent':False})
|
||||
confirmed = consume_approval(base_url=cfg.approval_url, token_file=cfg.approval_token_file,
|
||||
|
|
@ -92,7 +92,7 @@ def test_four_builders_preserve_context_and_binding(tmp_path):
|
|||
@pytest.mark.parametrize('change', ['operation_id','ccr','grant','source','body','method','path','budget','deadline','state','cluster'])
|
||||
def test_changed_binding_cannot_use_original_claim(tmp_path,change):
|
||||
e=entry(); original=descriptor(e); cfg=_Cfg(_token(tmp_path),authorization_subject_id='secrets-engine',authorization_subject_type='service')
|
||||
pdp=AuthorizationStub(approval_id=AUTH_ID,package=cfg.authorization_policy_package,version=cfg.authorization_policy_version)
|
||||
pdp=AuthorizationStub(approval_id=APPROVAL_ID,package=cfg.authorization_policy_package,version=cfg.authorization_policy_version)
|
||||
pdp.bind_request(request(e,ExecutionContract.parse(original))); claim=served(pdp)
|
||||
changed=copy.deepcopy(original)
|
||||
if change=='operation_id': changed[change]='775ab8f4-4731-47c8-bd98-342af22885dd'
|
||||
|
|
@ -164,7 +164,7 @@ def test_expired_contract_refuses_before_claim(tmp_path):
|
|||
def test_request_correspondence_changes_refuse_before_authorization(tmp_path, changed):
|
||||
e = entry(); contract = ExecutionContract.parse(descriptor(e))
|
||||
cfg = _Cfg(_token(tmp_path),authorization_subject_id='secrets-engine',authorization_subject_type='service')
|
||||
pdp = AuthorizationStub(approval_id=AUTH_ID,package=cfg.authorization_policy_package,version=cfg.authorization_policy_version)
|
||||
pdp = AuthorizationStub(approval_id=APPROVAL_ID,package=cfg.authorization_policy_package,version=cfg.authorization_policy_version)
|
||||
pdp.bind_request(request(e,contract)); claim = served(pdp)
|
||||
if changed == 'subject': cfg.authorization_subject_id = 'foreign-service'
|
||||
elif changed == 'purpose': e = replace(e,approval={**e.approval, 'purpose':'different-purpose'})
|
||||
|
|
@ -190,8 +190,13 @@ def test_raw_dictionary_and_direct_invalid_type_cannot_bypass_validation():
|
|||
def test_deadline_beyond_claim_refuses_before_pdp(tmp_path):
|
||||
e=entry(); raw=descriptor(e); raw['operation_deadline']=(datetime.now(timezone.utc)+timedelta(hours=2)).isoformat()
|
||||
contract=ExecutionContract.parse(raw); cfg=_Cfg(_token(tmp_path),authorization_subject_id='secrets-engine',authorization_subject_type='service')
|
||||
pdp=AuthorizationStub(approval_id=AUTH_ID,package=cfg.authorization_policy_package,version=cfg.authorization_policy_version)
|
||||
pdp=AuthorizationStub(approval_id=APPROVAL_ID,package=cfg.authorization_policy_package,version=cfg.authorization_policy_version)
|
||||
pdp.bind_request(request(e,contract)); calls=[]
|
||||
with pytest.raises(DecisionError,match='exceeds served authority'):
|
||||
authorize_action(cfg,e,'execute-ccr',execution_contract=contract,opener=_opener(served(pdp)),pdp_opener=lambda *a,**k:calls.append('check'))
|
||||
assert not calls
|
||||
|
||||
# Reuse admitted native producer signing, never bypass responder verification.
|
||||
from tests.test_consume_binding_join import admitted_native_signer
|
||||
|
||||
from tests.signature_support import signature_tools
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue