Bind typed execution context across authorization requests
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
This commit is contained in:
tegwick 2026-10-11 16:28:54 +02:00
parent e24437607b
commit d8edd9e516
6 changed files with 475 additions and 4 deletions

View file

@ -19,6 +19,7 @@ from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
from secrets_engine.approval_auth import approval_auth_configured, approval_token, credential_urlopen
from secrets_engine.execution_contract import ExecutionContract, require_execution_deadline
from secrets_engine.approval_claim import observe_pdp_approval_claim, validate_approval_claim
from secrets_engine.catalog import human_control_required
from secrets_engine.decision_check import check_decision
@ -148,6 +149,7 @@ def _expected_request(
fields: tuple[str, ...] = (),
policy_targets: tuple[str, ...] = (),
auth_targets: tuple[str, ...] = (),
execution_contract: ExecutionContract | None = None,
) -> dict[str, Any]:
"""Build the exact CheckRequest both steps must agree on.
@ -175,6 +177,7 @@ def _expected_request(
fields=fields,
policy_targets=policy_targets,
auth_targets=auth_targets,
execution_contract=execution_contract,
)
@ -230,6 +233,7 @@ def resolve_approval_observation(
fields: tuple[str, ...] = (),
policy_targets: tuple[str, ...] = (),
auth_targets: tuple[str, ...] = (),
execution_contract: ExecutionContract | None = None,
opener: Callable[..., Any] | None = None,
) -> ApprovalObservation | None:
"""Observe a fresh fact and prepare the claim-bearing request (step 1).
@ -238,20 +242,30 @@ def resolve_approval_observation(
human control raises instead, so no stage/demo fallback can discharge it.
Correspondence and a consume binding require authorize_action's PDP step.
"""
execution_request = None
if action == "execute-ccr" or execution_contract is not None:
execution_request = _expected_request(
cfg, entry, action, fields=fields, policy_targets=policy_targets,
auth_targets=auth_targets, execution_contract=execution_contract,
)
# The caller's descriptor is not authenticated principal/owner evidence.
# This requirement cannot disappear on an unserved/demo lane.
base_url = str(getattr(cfg, "approval_url", "") or "")
auth_configured = approval_auth_configured(cfg)
authorization_id = _authorization_id(entry, decision)
required_human_control = human_control_required(entry)
required_human_control = action == "execute-ccr" or human_control_required(entry)
if not base_url or not auth_configured or not authorization_id:
if required_human_control:
raise DecisionError("human_control requires a served approval claim and exact action binding")
return None
expected_request = _expected_request(
expected_request = execution_request or _expected_request(
cfg, entry, action,
fields=fields, policy_targets=policy_targets, auth_targets=auth_targets,
execution_contract=execution_contract,
)
require_execution_deadline(expected_request, time_window=read_window(cfg))
claim = fetch_approval_claim(
base_url=base_url,
token_provider=lambda: approval_token(cfg, scope="approval:read"),
@ -260,6 +274,8 @@ def resolve_approval_observation(
)
observe_pdp_approval_claim(claim, approval_id=authorization_id,
require_human_control=required_human_control, time_window=read_window(cfg))
require_execution_deadline(expected_request, time_window=read_window(cfg),
bounds=(claim["validity"]["expires_at"],))
expected_request["context"]["approval"] = claim
return ApprovalObservation(authorization_id, expected_request, claim)
@ -273,6 +289,7 @@ def authorize_action(
fields: tuple[str, ...] = (),
policy_targets: tuple[str, ...] = (),
auth_targets: tuple[str, ...] = (),
execution_contract: ExecutionContract | None = None,
opener: Callable[..., Any] | None = None,
pdp_opener: Callable[..., Any] | None = None,
) -> AuthorizedAction | None:
@ -288,6 +305,7 @@ def authorize_action(
fields=fields,
policy_targets=policy_targets,
auth_targets=auth_targets,
execution_contract=execution_contract,
opener=opener,
)
if observation is None:
@ -334,6 +352,8 @@ def authorize_action(
require_human_control=expected_request["context"].get("human_control") is True,
time_window=window,
)
require_execution_deadline(expected_request, time_window=window,
bounds=(observation.claim["validity"]["expires_at"], validated.expires_at))
if validated.action != action:
raise DecisionError("access-engine decision does not bind this action")
return AuthorizedAction(