Use bounded Railiance time for protected validity checks
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
parent
5841d8e88a
commit
f4b4dd6b17
8 changed files with 514 additions and 14 deletions
53
tests/test_application_time.py
Normal file
53
tests/test_application_time.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from datetime import datetime, timezone, timedelta
|
||||
from types import SimpleNamespace
|
||||
import pytest
|
||||
from railiance_clock.core import Window, unix_ns
|
||||
from secrets_engine.application_time import read_window, validity_bounds, recheck_binding
|
||||
from secrets_engine.approval_claim import validate_approval_claim
|
||||
from secrets_engine.authorization import validate_decision_envelope
|
||||
from secrets_engine.approval_consume import ConsumeBinding
|
||||
from secrets_engine.errors import DecisionError
|
||||
from tests.test_approval_claim import _claim, _binding, APPROVAL_ID, claim_binding_digest
|
||||
from tests.test_decision_replay import pair
|
||||
|
||||
|
||||
def window(lower, upper):
|
||||
return Window(unix_ns(lower),unix_ns(upper),'test-authority','epoch','test-policy','sample')
|
||||
|
||||
|
||||
def test_claim_uses_interval_not_workstation_time():
|
||||
claim=_claim();start=datetime(2030,1,1,tzinfo=timezone.utc)
|
||||
claim['validity']={'not_before':start.isoformat(),'expires_at':(start+timedelta(seconds=30)).isoformat()}
|
||||
claim['freshness']['not_after']=(start+timedelta(seconds=10)).isoformat()
|
||||
kw=dict(approval_id=APPROVAL_ID,expected_binding_digest=claim_binding_digest(**_binding()))
|
||||
validate_approval_claim(claim,time_window=window(start+timedelta(seconds=1),start+timedelta(seconds=2)),**kw)
|
||||
with pytest.raises(DecisionError):validate_approval_claim(claim,time_window=window(start-timedelta(seconds=1),start+timedelta(seconds=1)),**kw)
|
||||
with pytest.raises(DecisionError):validate_approval_claim(claim,time_window=window(start+timedelta(seconds=9),start+timedelta(seconds=10)),**kw)
|
||||
|
||||
|
||||
def test_producer_decision_interval_boundary():
|
||||
request,decision=pair('rotate')
|
||||
start=datetime.fromisoformat(decision['lifetime']['not_before'].replace('Z','+00:00'))
|
||||
end=datetime.fromisoformat(decision['lifetime']['expires_at'].replace('Z','+00:00'))
|
||||
kw=dict(accepted_policy_packages={'secrets-engine.catalog-lane.lifecycle'},accepted_policy_versions={'v2'})
|
||||
validate_decision_envelope(decision,request,time_window=window(start,start+timedelta(seconds=1)),**kw)
|
||||
with pytest.raises(DecisionError):validate_decision_envelope(decision,request,time_window=window(start-timedelta(microseconds=1),start+timedelta(seconds=1)),**kw)
|
||||
with pytest.raises(DecisionError):validate_decision_envelope(decision,request,time_window=window(end-timedelta(seconds=1),end),**kw)
|
||||
|
||||
|
||||
def test_configured_missing_trust_never_falls_back(tmp_path):
|
||||
with pytest.raises(DecisionError):read_window(SimpleNamespace(clock_trust_file=tmp_path/'missing.json'))
|
||||
assert read_window(SimpleNamespace(clock_trust_file=None)) is None
|
||||
|
||||
|
||||
def test_no_mixing_shifted_now_and_interval():
|
||||
now=datetime.now(timezone.utc)
|
||||
with pytest.raises(DecisionError):validity_bounds(now,window(now,now))
|
||||
|
||||
|
||||
def test_consume_rechecks_expiry(monkeypatch):
|
||||
now=datetime.now(timezone.utc)
|
||||
cfg=SimpleNamespace(clock_trust_file='configured')
|
||||
binding=ConsumeBinding('approval','sha256:'+'a'*64,decision_expires_at=now.isoformat())
|
||||
monkeypatch.setattr('secrets_engine.application_time.read_window',lambda cfg:window(now,now+timedelta(seconds=1)))
|
||||
with pytest.raises(DecisionError):recheck_binding(cfg,binding)
|
||||
Loading…
Add table
Add a link
Reference in a new issue