Adopt canonical flex-auth credential checks
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02e56-e4ad-71a2-b3e2-b6193e0d8093
This commit is contained in:
parent
8ae77ca006
commit
c9d02147d3
8 changed files with 338 additions and 50 deletions
|
|
@ -31,6 +31,11 @@ UNSAFE_VERBOSE_VALUES = {"debug", "trace"}
|
|||
DEFAULT_ACTOR_TYPE = "approved-agent"
|
||||
DEFAULT_ACTOR = f"codex:{os.environ.get('USER', 'unknown')}"
|
||||
DEFAULT_SUBJECT = "agent:codex/railiance-platform"
|
||||
FLEX_AUTH_SUBJECT_TYPES = {
|
||||
"human-operator": "Human",
|
||||
"approved-agent": "Agent",
|
||||
"ci-runner": "Automation",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -207,7 +212,7 @@ def post_json(
|
|||
return data
|
||||
|
||||
|
||||
def request_metadata(
|
||||
def flex_auth_check_request(
|
||||
*,
|
||||
grant: dict[str, Any],
|
||||
ttl: str,
|
||||
|
|
@ -217,17 +222,26 @@ def request_metadata(
|
|||
actor_type: str,
|
||||
subject: str,
|
||||
) -> dict[str, Any]:
|
||||
subject_type = FLEX_AUTH_SUBJECT_TYPES.get(actor_type)
|
||||
if subject_type is None:
|
||||
fail(f"actor type {actor_type!r} has no flex-auth subject type mapping")
|
||||
return {
|
||||
"grant_id": grant["id"],
|
||||
"actor": actor,
|
||||
"actor_type": actor_type,
|
||||
"subject": subject,
|
||||
"purpose": purpose,
|
||||
"requested_ttl": ttl,
|
||||
"delivery_mode": delivery,
|
||||
"audience": grant.get("audience"),
|
||||
"issuer": grant.get("issuer"),
|
||||
"credential_type": grant.get("credential_type"),
|
||||
"tenant": "tenant:platform",
|
||||
"subject": {"id": subject, "type": subject_type},
|
||||
"action": "issue",
|
||||
"resource": {
|
||||
"id": f"credential-grant:{grant['id']}",
|
||||
"type": "credential-grant",
|
||||
"system": "railiance-platform",
|
||||
},
|
||||
"context": {
|
||||
"actor": actor,
|
||||
"actor_type": actor_type,
|
||||
"bound_subject": subject,
|
||||
"purpose": purpose,
|
||||
"delivery_mode": delivery,
|
||||
"requested_ttl_seconds": ttl_seconds(ttl),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -264,7 +278,7 @@ def authorize_request(
|
|||
)
|
||||
|
||||
endpoint = join_url(args.flex_auth_url, args.flex_auth_path)
|
||||
payload = request_metadata(
|
||||
payload = flex_auth_check_request(
|
||||
grant=grant,
|
||||
ttl=ttl,
|
||||
purpose=purpose,
|
||||
|
|
@ -288,30 +302,36 @@ def authorize_request(
|
|||
"flex-auth unavailable; local preauthorization used",
|
||||
)
|
||||
|
||||
allowed_value = response.get("allowed")
|
||||
decision_value = str(
|
||||
response.get("decision") or response.get("status") or ""
|
||||
).lower()
|
||||
allowed = allowed_value is True or decision_value in {
|
||||
"allow",
|
||||
"allowed",
|
||||
"approved",
|
||||
"pass",
|
||||
}
|
||||
denied = allowed_value is False or decision_value in {
|
||||
"deny",
|
||||
"denied",
|
||||
"rejected",
|
||||
"fail",
|
||||
}
|
||||
decision_id = response.get("decision_id") or response.get("id")
|
||||
reason = response.get("reason") or response.get("message")
|
||||
if denied or not allowed:
|
||||
effect = response.get("effect")
|
||||
decision_id = response.get("id")
|
||||
reason = response.get("reason")
|
||||
if effect != "allow":
|
||||
fail(f"flex-auth denied credential request: {reason or 'no reason supplied'}")
|
||||
if not isinstance(decision_id, str) or not decision_id:
|
||||
fail("flex-auth returned an allow effect without a decision id")
|
||||
response_subject = response.get("subject")
|
||||
if (
|
||||
not isinstance(response_subject, dict)
|
||||
or response_subject.get("id") != payload["subject"]["id"]
|
||||
):
|
||||
fail("flex-auth allow decision subject does not match the request")
|
||||
response_resource = response.get("resource")
|
||||
if not isinstance(response_resource, dict) or any(
|
||||
response_resource.get(field) != payload["resource"][field]
|
||||
for field in ("id", "type", "system")
|
||||
):
|
||||
fail("flex-auth allow decision resource does not match the request")
|
||||
provenance = response.get("provenance")
|
||||
if (
|
||||
not isinstance(provenance, dict)
|
||||
or not provenance.get("evaluator")
|
||||
or not provenance.get("mode")
|
||||
):
|
||||
fail("flex-auth allow decision has no evaluator provenance")
|
||||
return AuthorizationResult(
|
||||
True,
|
||||
"flex-auth",
|
||||
str(decision_id) if decision_id else None,
|
||||
decision_id,
|
||||
str(reason) if reason else None,
|
||||
)
|
||||
|
||||
|
|
@ -1096,7 +1116,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
parser.add_argument("--flex-auth-url", default=os.environ.get("FLEX_AUTH_URL"))
|
||||
parser.add_argument(
|
||||
"--flex-auth-path",
|
||||
default=os.environ.get("FLEX_AUTH_PATH", "/credential-grants/authorize"),
|
||||
default=os.environ.get("FLEX_AUTH_PATH", "/v1/check"),
|
||||
)
|
||||
parser.add_argument("--require-flex-auth", action="store_true")
|
||||
parser.add_argument(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue