feat(policy): add credential grant authorization package
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 37s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02e47-6aac-7ee1-914d-0584c75d3c81
This commit is contained in:
tegwick 2026-08-23 13:59:03 +02:00
parent df54c1b8f5
commit acbaa4a7c9
12 changed files with 476 additions and 3 deletions

View file

@ -0,0 +1,16 @@
# Railiance Platform credential-grant example
This package provides the flex-auth side of `FLEX-WP-0012`: registered grant
metadata, representative requester subjects, a default-deny `issue` policy,
and fixtures for TTL, actor type, purpose, delivery mode, and unknown grants.
The wire translation is intentionally outside the policy. Both candidate
integration shapes must emit `requested_ttl_seconds` as a positive number;
duration strings such as `15m` are rejected rather than guessed.
```bash
go run ./cmd/flex-auth load-registry --file examples/railiance-platform/registry_snapshot.json
go run ./cmd/flex-auth test-policy --file examples/railiance-platform/policy_package.md
go run ./cmd/flex-auth check --registry examples/railiance-platform/registry_snapshot.json --policy examples/railiance-platform/policy_package.md --request examples/railiance-platform/check_request_allow.json
go run ./cmd/flex-auth check --registry examples/railiance-platform/registry_snapshot.json --policy examples/railiance-platform/policy_package.md --request examples/railiance-platform/check_request_deny_unknown.json
```

View file

@ -0,0 +1,21 @@
{
"id": "check:credential-grant-allow-http-shape",
"tenant": "tenant:platform",
"subject": {
"id": "agent:codex/railiance-platform",
"type": "Agent"
},
"action": "issue",
"resource": {
"id": "credential-grant:rapp-postgres/audit-core-runtime",
"type": "credential-grant",
"system": "railiance-platform"
},
"context": {
"actor_type": "approved-agent",
"bound_subject": "agent:codex/railiance-platform",
"purpose": "audit-core-runtime",
"delivery_mode": "exec-env",
"requested_ttl_seconds": 900
}
}

View file

@ -0,0 +1,21 @@
{
"id": "check:credential-grant-unknown-http-shape",
"tenant": "tenant:platform",
"subject": {
"id": "agent:codex/railiance-platform",
"type": "Agent"
},
"action": "issue",
"resource": {
"id": "credential-grant:unknown/grant",
"type": "credential-grant",
"system": "railiance-platform"
},
"context": {
"actor_type": "approved-agent",
"bound_subject": "agent:codex/railiance-platform",
"purpose": "unknown",
"delivery_mode": "exec-env",
"requested_ttl_seconds": 900
}
}

View file

@ -0,0 +1,59 @@
- id: fixture:credential-grant-allow
request: &allow_request
id: check:credential-grant-allow
tenant: tenant:platform
subject: {id: agent:codex/railiance-platform, type: Agent}
action: issue
resource:
id: credential-grant:rapp-postgres/audit-core-runtime
type: credential-grant
system: railiance-platform
attributes:
grant_id: rapp-postgres/audit-core-runtime
credential_type: openbao-database-credential
issuer: openbao
audience: audit-core
max_ttl_seconds: 3600
allowed_actor_types: [human-operator, approved-agent, ci-runner]
allowed_purposes: [audit-core-runtime]
allowed_delivery_modes: [exec-env]
context: &allow_context
actor_type: approved-agent
bound_subject: agent:codex/railiance-platform
purpose: audit-core-runtime
delivery_mode: exec-env
requested_ttl_seconds: 900
expect: {effect: allow, reason: credential_grant_allowed}
- id: fixture:credential-grant-ttl-deny
request:
<<: *allow_request
id: check:credential-grant-ttl-deny
context: {<<: *allow_context, requested_ttl_seconds: 5400}
expect: {effect: deny, reason: ttl_out_of_bounds}
- id: fixture:credential-grant-actor-type-deny
request:
<<: *allow_request
id: check:credential-grant-actor-deny
context: {<<: *allow_context, actor_type: unapproved-agent}
expect: {effect: deny, reason: actor_type_not_allowed}
- id: fixture:credential-grant-purpose-deny
request:
<<: *allow_request
id: check:credential-grant-purpose-deny
context: {<<: *allow_context, purpose: unrelated-purpose}
expect: {effect: deny, reason: purpose_not_allowed}
- id: fixture:credential-grant-delivery-deny
request:
<<: *allow_request
id: check:credential-grant-delivery-deny
context: {<<: *allow_context, delivery_mode: chat}
expect: {effect: deny, reason: delivery_mode_not_allowed}
- id: fixture:credential-grant-unknown-deny
request:
<<: *allow_request
id: check:credential-grant-unknown-deny
resource:
id: credential-grant:unknown/grant
type: credential-grant
system: railiance-platform
expect: {effect: deny, reason: unknown_grant}

View file

@ -0,0 +1,158 @@
---
id: railiance-platform.credential-grant.issue
name: Railiance Platform credential-grant issuance
namespace: railiance-platform:credential-grant
version: v1
status: ready
package: flexauth.railiance_platform.credential_grant
actions: [issue]
owner: team:platform-security
fixtures: [policy_fixtures.yaml]
caring:
profile: caring-0.4.0-rc2
enforce: false
canonical_roles: [Operator]
organization_relations: [ServiceProvider]
scopes:
- level: Platform
id: platform:credential-broker
tenant: tenant:platform
planes: [Identity, Secret, Audit]
capabilities: [Use, Operate, Audit]
exposure_modes: [Metadata]
conditions: [PurposeBound, TimeLimited, Logged]
restrictions: [PrivilegeEscalationBlocked, SecretAccessBlocked]
activation: {mode: local}
metadata:
source: examples/railiance-platform/policy_package.md
ttl_unit: seconds
---
# Railiance Platform Credential-grant Issuance
The policy evaluates non-secret grant metadata only. Duration strings such as
`15m` must be parsed by the eventual wire translator into the numeric
`context.requested_ttl_seconds`; Rego never infers duration units.
## Rules
```rego
import future.keywords.if
import future.keywords.in
decision := {"effect": "allow", "reason": "credential_grant_allowed"} if {
allowed
} else := {"effect": "deny", "reason": first_denial} if {
true
}
allowed if {
input.action == "issue"
input.resource.system == "railiance-platform"
input.resource.type == "credential-grant"
input.tenant == "tenant:platform"
known_grant
actor_type_allowed
purpose_allowed
delivery_mode_allowed
ttl_allowed
has_bound_subject
}
default first_denial := "no_matching_rule"
first_denial := "wrong_action" if {
input.action != "issue"
} else := "wrong_system" if {
input.resource.system != "railiance-platform"
} else := "wrong_resource_type" if {
input.resource.type != "credential-grant"
} else := "wrong_tenant" if {
input.tenant != "tenant:platform"
} else := "unknown_grant" if {
not known_grant
} else := "actor_type_not_allowed" if {
not actor_type_allowed
} else := "purpose_not_allowed" if {
not purpose_allowed
} else := "delivery_mode_not_allowed" if {
not delivery_mode_allowed
} else := "ttl_out_of_bounds" if {
not ttl_allowed
} else := "missing_subject_binding" if {
not has_bound_subject
}
known_grant if {
is_string(input.resource.attributes.grant_id)
input.resource.id == sprintf("credential-grant:%s", [input.resource.attributes.grant_id])
is_number(input.resource.attributes.max_ttl_seconds)
}
actor_type_allowed if {
is_string(input.context.actor_type)
input.context.actor_type in input.resource.attributes.allowed_actor_types
}
purpose_allowed if {
is_string(input.context.purpose)
input.context.purpose != ""
input.context.purpose in input.resource.attributes.allowed_purposes
}
delivery_mode_allowed if {
is_string(input.context.delivery_mode)
input.context.delivery_mode in input.resource.attributes.allowed_delivery_modes
}
ttl_allowed if {
is_number(input.context.requested_ttl_seconds)
input.context.requested_ttl_seconds > 0
input.context.requested_ttl_seconds <= input.resource.attributes.max_ttl_seconds
}
has_bound_subject if {
is_string(input.context.bound_subject)
input.context.bound_subject != ""
}
```
## Tests
```rego test
package flexauth.railiance_platform.credential_grant_test
import future.keywords.if
import data.flexauth.railiance_platform.credential_grant
request := {
"tenant": "tenant:platform",
"subject": {"id": "agent:codex/railiance-platform", "type": "Agent"},
"action": "issue",
"resource": {
"id": "credential-grant:rapp-postgres/audit-core-runtime",
"type": "credential-grant",
"system": "railiance-platform",
"attributes": {
"grant_id": "rapp-postgres/audit-core-runtime",
"max_ttl_seconds": 3600,
"allowed_actor_types": ["human-operator", "approved-agent", "ci-runner"],
"allowed_purposes": ["audit-core-runtime"],
"allowed_delivery_modes": ["exec-env"]
}
},
"context": {"actor_type": "approved-agent", "bound_subject": "agent:codex/railiance-platform", "purpose": "audit-core-runtime", "delivery_mode": "exec-env", "requested_ttl_seconds": 900}
}
test_allow if {
credential_grant.decision.effect == "allow" with input as request
}
test_ttl_string_denied if {
credential_grant.decision.reason == "ttl_out_of_bounds" with input as object.union(request, {"context": object.union(request.context, {"requested_ttl_seconds": "15m"})})
}
test_unknown_grant_denied if {
credential_grant.decision.reason == "unknown_grant" with input as object.union(request, {"resource": {"id": "credential-grant:missing", "type": "credential-grant", "system": "railiance-platform", "attributes": {}}})
}
```

View file

@ -0,0 +1,19 @@
id: railiance-platform
name: Railiance Platform Credential Broker
resource_types:
- name: credential-grant
scope_level: Resource
planes: [Identity, Secret, Audit]
metadata:
description: Non-secret authorization metadata for a bounded credential lease.
actions:
- name: issue
capabilities: [Use, Operate, Audit]
planes: [Identity, Secret, Audit]
exposure_modes: [Metadata]
metadata:
required_context: [actor_type, bound_subject, purpose, delivery_mode, requested_ttl_seconds]
caring_profiles: [caring-0.4.0-rc2]
metadata:
flex_auth_contract: protected-system-v0
tenant: tenant:platform

View file

@ -0,0 +1,45 @@
{
"systems": [
{
"id": "railiance-platform",
"name": "Railiance Platform Credential Broker",
"resource_types": [
{
"name": "credential-grant",
"scope_level": "Resource",
"planes": ["Identity", "Secret", "Audit"]
}
],
"actions": [
{
"name": "issue",
"capabilities": ["Use", "Operate", "Audit"],
"planes": ["Identity", "Secret", "Audit"],
"exposure_modes": ["Metadata"]
}
],
"caring_profiles": ["caring-0.4.0-rc2"],
"metadata": {"tenant": "tenant:platform"}
}
],
"resource_manifests": [
{
"id": "railiance-platform-credential-grants",
"system": "railiance-platform",
"resources": [
{"id": "credential-grant:rapp-postgres/audit-core-runtime", "type": "credential-grant", "owner": "team:platform-security", "attributes": {"grant_id": "rapp-postgres/audit-core-runtime", "credential_type": "openbao-database-credential", "issuer": "openbao", "audience": "audit-core", "max_ttl_seconds": 3600, "allowed_actor_types": ["human-operator", "approved-agent", "ci-runner"], "allowed_purposes": ["audit-core-runtime"], "allowed_delivery_modes": ["exec-env"]}},
{"id": "credential-grant:rapp-postgres/audit-core-migration", "type": "credential-grant", "owner": "team:platform-security", "attributes": {"grant_id": "rapp-postgres/audit-core-migration", "credential_type": "openbao-database-credential", "issuer": "openbao", "audience": "audit-core", "max_ttl_seconds": 1800, "allowed_actor_types": ["human-operator", "approved-agent", "ci-runner"], "allowed_purposes": ["audit-core-migration"], "allowed_delivery_modes": ["exec-env"]}},
{"id": "credential-grant:ops-warden/warden-sign", "type": "credential-grant", "owner": "team:platform-security", "attributes": {"grant_id": "ops-warden/warden-sign", "credential_type": "openbao-token", "issuer": "openbao", "audience": "ops-warden", "max_ttl_seconds": 3600, "allowed_actor_types": ["human-operator", "approved-agent", "ci-runner"], "allowed_purposes": ["flex-auth-openbao-smoke", "ops-warden-production-sign-smoke"], "allowed_delivery_modes": ["exec-env", "response-wrap", "local-token-file", "kubernetes-auth"]}}
],
"actions": ["issue"],
"caring_profile": "caring-0.4.0-rc2",
"metadata": {"tenant": "tenant:platform"}
}
],
"tenants": [{"id": "tenant:platform", "name": "Platform Tenant"}],
"subjects": [
{"id": "operator:platform", "type": "Human", "roles": ["Operator"], "tenant": "tenant:platform"},
{"id": "agent:codex/railiance-platform", "type": "Agent", "roles": ["Operator"], "tenant": "tenant:platform"},
{"id": "ci:railiance-platform", "type": "Automation", "roles": ["Operator"], "tenant": "tenant:platform"}
]
}

View file

@ -0,0 +1,44 @@
id: railiance-platform-credential-grants
system: railiance-platform
resources:
- id: credential-grant:rapp-postgres/audit-core-runtime
type: credential-grant
owner: team:platform-security
attributes:
grant_id: rapp-postgres/audit-core-runtime
credential_type: openbao-database-credential
issuer: openbao
audience: audit-core
max_ttl_seconds: 3600
allowed_actor_types: [human-operator, approved-agent, ci-runner]
allowed_purposes: [audit-core-runtime]
allowed_delivery_modes: [exec-env]
- id: credential-grant:rapp-postgres/audit-core-migration
type: credential-grant
owner: team:platform-security
attributes:
grant_id: rapp-postgres/audit-core-migration
credential_type: openbao-database-credential
issuer: openbao
audience: audit-core
max_ttl_seconds: 1800
allowed_actor_types: [human-operator, approved-agent, ci-runner]
allowed_purposes: [audit-core-migration]
allowed_delivery_modes: [exec-env]
- id: credential-grant:ops-warden/warden-sign
type: credential-grant
owner: team:platform-security
attributes:
grant_id: ops-warden/warden-sign
credential_type: openbao-token
issuer: openbao
audience: ops-warden
max_ttl_seconds: 3600
allowed_actor_types: [human-operator, approved-agent, ci-runner]
allowed_purposes: [flex-auth-openbao-smoke, ops-warden-production-sign-smoke]
allowed_delivery_modes: [exec-env, response-wrap, local-token-file, kubernetes-auth]
actions: [issue]
caring_profile: caring-0.4.0-rc2
metadata:
flex_auth_contract: resource-registration-v0
tenant: tenant:platform

View file

@ -0,0 +1,23 @@
id: subjects:railiance-platform
tenants:
- id: tenant:platform
name: Platform Tenant
subjects:
- id: operator:platform
type: Human
display_name: Platform Operator
organization_relation: ServiceProvider
roles: [Operator]
tenant: tenant:platform
- id: agent:codex/railiance-platform
type: Agent
display_name: Railiance Platform Agent
organization_relation: ServiceProvider
roles: [Operator]
tenant: tenant:platform
- id: ci:railiance-platform
type: Automation
display_name: Railiance Platform CI
organization_relation: ServiceProvider
roles: [Operator]
tenant: tenant:platform