Require declared human control in factory credential delivery
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-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-10 20:16:55 +02:00
parent d1c13b5dd6
commit 2b0d04e8e1
11 changed files with 451 additions and 27 deletions

View file

@ -20,6 +20,7 @@ from urllib.request import Request, urlopen
from secrets_engine.approval_auth import approval_auth_configured, approval_token, credential_urlopen
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
from secrets_engine.authorization import (
build_action_request,
@ -40,6 +41,7 @@ class ConsumeBinding:
approval_id: str
request_digest: str
decision_id: str = ""
human_control: bool = False
@dataclass(frozen=True)
@ -68,6 +70,7 @@ class AuthorizedAction:
"authorization_decision_id": self.decision_id,
"authorization_expires_at": self.expires_at,
"request_digest": self.binding.request_digest,
"approval_human_control": self.binding.human_control,
}
@ -222,13 +225,17 @@ def resolve_approval_observation(
) -> ApprovalObservation | None:
"""Observe a fresh fact and prepare the claim-bearing request (step 1).
Missing serving coordinates return None, preserving the production refusal.
Missing serving coordinates return None for undeclared lanes. A declared
human control raises instead, so no stage/demo fallback can discharge it.
Correspondence and a consume binding require authorize_action's PDP step.
"""
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)
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(
@ -242,7 +249,8 @@ def resolve_approval_observation(
authorization_id=authorization_id,
opener=opener or credential_urlopen,
)
observe_pdp_approval_claim(claim, approval_id=authorization_id)
observe_pdp_approval_claim(claim, approval_id=authorization_id,
require_human_control=required_human_control)
expected_request["context"]["approval"] = claim
return ApprovalObservation(authorization_id, expected_request, claim)
@ -310,6 +318,7 @@ def authorize_action(
validate_approval_claim(
observation.claim, approval_id=observation.approval_id,
expected_pdp_digest=envelope["binding"]["approval_binding_digest"],
require_human_control=expected_request["context"].get("human_control") is True,
)
if validated.action != action:
raise DecisionError("access-engine decision does not bind this action")
@ -318,6 +327,7 @@ def authorize_action(
approval_id=observation.approval_id,
request_digest=validated.request_digest,
decision_id=validated.decision_id,
human_control=observation.claim["binding"].get("human_control") is True,
),
decision_id=validated.decision_id,
expires_at=validated.expires_at,
@ -413,16 +423,21 @@ def require_production_consume(
evidence: Any = None,
opener: Callable[..., Any] | None = None,
) -> ConsumedApproval | None:
"""CAS-consume before a production OpenBao call. No-op off the prod path.
"""CAS-consume before production or an explicitly human-controlled action.
Build/test remain fail-open relative to approval-engine. The three-factor
unsafe-demo exception is not a consume path. Missing binding, URL, or
credential fail closed so a stance bypass cannot reach OpenBao.
Undeclared build/test and the unsafe-demo exception retain their existing
behavior. Declared human controls require a verified binding and consume
regardless of stage/demo; missing inputs cannot fall back to a lane review.
"""
if getattr(entry, "stage", "") != "prod":
return None
if demo_exception_enabled(cfg):
return None
required_human_control = human_control_required(entry)
if required_human_control:
if binding is None or binding.human_control is not True:
raise DecisionError("human_control requires an observed declared-human consume binding")
else:
if getattr(entry, "stage", "") != "prod":
return None
if demo_exception_enabled(cfg):
return None
if binding is None:
raise DecisionError(
"production OpenBao call requires CAS consume of an approval "