feat: complete and prove the authorization chain end to end
Implements step 2 (access-engine POST /v1/check) and wires the whole GH-DEC-2026-003 sequence together, then proves it against a live throwaway OpenBao rather than only unit-level fakes. - decision_check.check_decision performs the PDP call; an unreachable or non-200 PDP raises, since silence is never permission. - approval_consume.authorize_action coordinates steps 1 and 2 and returns an AuthorizedAction. Both steps build the same CheckRequest via a shared _expected_request, since two descriptions of the action cannot produce corresponding digests. - apply_unreachable_engine_stance takes authorized=. The published map defines fail_closed as no side effect WITHOUT a durable decision record, so holding a validated one means the residue does not apply. Not a bypass: both steps must have succeeded and CAS consume still precedes OpenBao. Unconfigured still returns None and fails closed. The end-to-end test caught one more instance of the cross-vocabulary bug: a leftover comparison of the claim's binding.action against ours. The claim says secrets.kv.destroy where we say destroy, so it would have failed against every real claim. Removed; the tie is pdp_digest. Integration coverage asserts PIP-then-PDP ordering, that consume is the last step before the backend, and that an unreachable PDP, denied decision, invalid claim, missing pdp_digest, consume conflict and action mismatch each stop before OpenBao. 284 tests pass; production still fails closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD Assistant: claude-code Assistant-Model: opus Assistant-Process: 393550@bnt-lap001 Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
This commit is contained in:
parent
e8144f315c
commit
f62d3fe789
9 changed files with 675 additions and 44 deletions
|
|
@ -35,6 +35,7 @@ from types import SimpleNamespace
|
|||
from secrets_engine import __version__
|
||||
from secrets_engine.apply import apply_plan
|
||||
from secrets_engine.approval_consume import (
|
||||
authorize_action,
|
||||
require_production_consume,
|
||||
resolve_consume_binding,
|
||||
)
|
||||
|
|
@ -122,9 +123,19 @@ def _require_lane_approval(
|
|||
consume path. Build/test ``fail_open`` still requires the existing
|
||||
lane-approval check — a tracked gap until SECRETS-WP-0008-T02.
|
||||
"""
|
||||
stance = apply_unreachable_engine_stance(cfg, entry, action or "unknown")
|
||||
# Steps 1 and 2 first: a validated claim and decision are what make the
|
||||
# unreachable-engine residue inapplicable. Absent configuration returns
|
||||
# None and production stays closed exactly as before.
|
||||
authorization = authorize_action(
|
||||
cfg, entry, action or "unknown", None, fields=fields
|
||||
)
|
||||
stance = apply_unreachable_engine_stance(
|
||||
cfg, entry, action or "unknown", authorized=authorization is not None
|
||||
)
|
||||
if evidence is not None:
|
||||
evidence.mark_stance(stance)
|
||||
if authorization is not None:
|
||||
evidence.detail.update(authorization.as_evidence())
|
||||
decision = None
|
||||
if entry.approval_required():
|
||||
decision = resolve_decision(
|
||||
|
|
@ -138,9 +149,7 @@ def _require_lane_approval(
|
|||
require_production_consume(
|
||||
cfg,
|
||||
entry,
|
||||
binding=resolve_consume_binding(
|
||||
cfg, entry, action or "unknown", decision, fields=fields
|
||||
),
|
||||
binding=authorization.binding if authorization is not None else None,
|
||||
evidence=evidence,
|
||||
)
|
||||
return decision
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue