Deploy user-engine authorization policy
This commit is contained in:
parent
5217109103
commit
ccf4b509be
5 changed files with 141 additions and 5 deletions
13
examples/user-engine/README.md
Normal file
13
examples/user-engine/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# user-engine policy service
|
||||
|
||||
Dynamic human subjects arrive as verified KeyCape claims, so the registry
|
||||
defines the protected system while the policy evaluates tenant, roles, and
|
||||
self context from each request. Platform operators may cross tenants;
|
||||
tenant administrators and self-service users may not.
|
||||
|
||||
Validate with:
|
||||
|
||||
```bash
|
||||
flex-auth test-policy -file examples/user-engine/policy_package.md
|
||||
flex-auth load-registry -file examples/user-engine/registry_snapshot.json
|
||||
```
|
||||
8
examples/user-engine/policy_fixtures.yaml
Normal file
8
examples/user-engine/policy_fixtures.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{"id":"self-allow","request":{"id":"c1","tenant":"tenant:friendly:binky","subject":{"id":"u1","type":"human","tenant":"tenant:friendly:binky","attributes":{"roles":[]}},"action":"me.read","resource":{"id":"u1","type":"user-engine:me","system":"user-engine","tenant":"tenant:friendly:binky"},"context":{"self":true}},"expect":{"effect":"allow","reason":"self_service"}},
|
||||
{"id":"tenant-admin-allow","request":{"id":"c2","tenant":"tenant:friendly:binky","subject":{"id":"admin","type":"human","tenant":"tenant:friendly:binky","attributes":{"roles":["tenant-admin"]}},"action":"family_member.invite","resource":{"id":"i1","type":"user-engine:family-invitation","system":"user-engine","tenant":"tenant:friendly:binky"},"context":{}},"expect":{"effect":"allow","reason":"tenant_admin"}},
|
||||
{"id":"platform-allow","request":{"id":"c3","tenant":"tenant:friendly:binky","subject":{"id":"operator","type":"human","tenant":"platform:root","attributes":{"roles":["platform-operator"]}},"action":"tenant.diagnostics.read","resource":{"id":"tenant:friendly:binky","type":"user-engine:tenant","system":"user-engine","tenant":"tenant:friendly:binky"},"context":{}},"expect":{"effect":"allow","reason":"platform_operator"}},
|
||||
{"id":"cross-tenant-deny","request":{"id":"c4","tenant":"tenant:friendly:binky","subject":{"id":"admin","type":"human","tenant":"tenant:family:other","attributes":{"roles":["tenant-admin"]}},"action":"membership.write","resource":{"id":"m1","type":"user-engine:membership","system":"user-engine","tenant":"tenant:friendly:binky"},"context":{}},"expect":{"effect":"deny","reason":"cross_tenant"}},
|
||||
{"id":"missing-role-deny","request":{"id":"c5","tenant":"tenant:friendly:binky","subject":{"id":"u1","type":"human","tenant":"tenant:friendly:binky","attributes":{"roles":[]}},"action":"membership.write","resource":{"id":"m1","type":"user-engine:membership","system":"user-engine","tenant":"tenant:friendly:binky"},"context":{}},"expect":{"effect":"deny","reason":"no_matching_role_or_context"}},
|
||||
{"id":"wrong-system-deny","request":{"id":"c6","tenant":"tenant:friendly:binky","subject":{"id":"operator","type":"human","tenant":"platform:root","attributes":{"roles":["platform-operator"]}},"action":"outbox.replay","resource":{"id":"e1","type":"user-engine:outbox-event","system":"other","tenant":"tenant:friendly:binky"},"context":{}},"expect":{"effect":"deny","reason":"wrong_system"}}
|
||||
]
|
||||
83
examples/user-engine/policy_package.md
Normal file
83
examples/user-engine/policy_package.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
id: user-engine.portal.authorize
|
||||
name: user-engine portal authorization
|
||||
namespace: user-engine:portal
|
||||
version: v1
|
||||
status: ready
|
||||
package: flexauth.user_engine.portal
|
||||
actions:
|
||||
- "*"
|
||||
owner: team:platform-security
|
||||
fixtures:
|
||||
- policy_fixtures.yaml
|
||||
caring:
|
||||
profile: caring-0.4.0-rc2
|
||||
enforce: false
|
||||
canonical_roles: [Operator, Administrator, User]
|
||||
organization_relations: [ServiceProvider, Customer]
|
||||
scopes:
|
||||
- {level: Platform, id: platform:user-engine, tenant: "platform:root"}
|
||||
- {level: Tenant, id: tenant:dynamic}
|
||||
planes: [Identity, Policy, Audit]
|
||||
capabilities: [Read, Create, Update, Delete, Grant, Audit]
|
||||
exposure_modes: [Metadata]
|
||||
conditions: [Logged]
|
||||
restrictions: [PrivilegeEscalationBlocked, TenantBoundary]
|
||||
activation:
|
||||
mode: local
|
||||
metadata:
|
||||
source: examples/user-engine/policy_package.md
|
||||
flex_auth_contract: protected-system-v0
|
||||
---
|
||||
|
||||
# user-engine portal authorization
|
||||
|
||||
The portal supplies verified identity claims. This policy enforces platform,
|
||||
tenant, and self boundaries and denies unknown role/context combinations.
|
||||
|
||||
```rego
|
||||
import future.keywords.if
|
||||
import future.keywords.in
|
||||
|
||||
roles := object.get(object.get(input.subject, "attributes", {}), "roles", [])
|
||||
subject_tenant := object.get(input.subject, "tenant", "")
|
||||
resource_tenant := object.get(input.resource, "tenant", input.tenant)
|
||||
self_request := object.get(input.context, "self", false)
|
||||
|
||||
decision := {"effect": "allow", "reason": "platform_operator"} if {
|
||||
valid_system
|
||||
"platform-operator" in roles
|
||||
} else := {"effect": "allow", "reason": "tenant_admin"} if {
|
||||
valid_system
|
||||
same_tenant
|
||||
"tenant-admin" in roles
|
||||
} else := {"effect": "allow", "reason": "self_service"} if {
|
||||
valid_system
|
||||
same_tenant
|
||||
self_request == true
|
||||
} else := {"effect": "deny", "reason": first_denial} if { true }
|
||||
|
||||
valid_system if { input.resource.system == "user-engine" }
|
||||
same_tenant if { subject_tenant != ""; subject_tenant == input.tenant; resource_tenant == input.tenant }
|
||||
|
||||
default first_denial := "no_matching_role_or_context"
|
||||
first_denial := "wrong_system" if { not valid_system }
|
||||
else := "cross_tenant" if { subject_tenant != ""; subject_tenant != input.tenant }
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```rego test
|
||||
package flexauth.user_engine.portal_test
|
||||
import future.keywords.if
|
||||
import data.flexauth.user_engine.portal
|
||||
|
||||
base := {"tenant": "tenant:friendly:binky", "subject": {"id": "u1", "type": "human", "tenant": "tenant:friendly:binky", "attributes": {"roles": []}}, "action": "me.read", "resource": {"id": "u1", "type": "user-engine:me", "system": "user-engine", "tenant": "tenant:friendly:binky"}, "context": {"self": true}}
|
||||
|
||||
test_self_allowed if { portal.decision.effect == "allow" with input as base }
|
||||
test_tenant_admin_allowed if { portal.decision.effect == "allow" with input as object.union(base, {"subject": object.union(base.subject, {"attributes": {"roles": ["tenant-admin"]}}), "context": {}}) }
|
||||
test_platform_operator_cross_tenant_allowed if { portal.decision.effect == "allow" with input as object.union(base, {"subject": object.union(base.subject, {"tenant": "platform:root", "attributes": {"roles": ["platform-operator"]}}), "context": {}}) }
|
||||
test_cross_tenant_denied if { portal.decision.reason == "cross_tenant" with input as object.union(base, {"subject": object.union(base.subject, {"tenant": "tenant:family:other"})}) }
|
||||
test_missing_role_denied if { portal.decision.effect == "deny" with input as {"tenant": "tenant:friendly:binky", "subject": {"id": "u1", "type": "human", "tenant": "tenant:friendly:binky", "attributes": {"roles": []}}, "action": "membership.write", "resource": {"id": "m1", "type": "user-engine:membership", "system": "user-engine", "tenant": "tenant:friendly:binky"}, "context": {}} }
|
||||
test_wrong_system_denied if { portal.decision.reason == "wrong_system" with input as object.union(base, {"resource": object.union(base.resource, {"system": "other"})}) }
|
||||
```
|
||||
16
examples/user-engine/registry_snapshot.json
Normal file
16
examples/user-engine/registry_snapshot.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"systems": [{
|
||||
"id": "user-engine",
|
||||
"name": "User Engine",
|
||||
"resource_types": [
|
||||
{"name":"user-engine:user","scope_level":"Resource","planes":["Identity","Audit"]},
|
||||
{"name":"user-engine:membership","scope_level":"Resource","planes":["Identity","Policy","Audit"]},
|
||||
{"name":"user-engine:family-invitation","scope_level":"Resource","planes":["Identity","Audit"]},
|
||||
{"name":"user-engine:tenant","scope_level":"Tenant","planes":["Identity","Audit"]},
|
||||
{"name":"user-engine:outbox","scope_level":"Platform","planes":["Audit"]}
|
||||
],
|
||||
"actions": [],
|
||||
"metadata": {"flex_auth_contract":"protected-system-v0","dynamic_actions":true}
|
||||
}],
|
||||
"resource_manifests": [], "tenants": [], "subjects": [], "groups": [], "relationships": []
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Provide production authorization for user-engine"
|
||||
domain: infotech
|
||||
repo: flex-auth
|
||||
status: ready
|
||||
status: active
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-08"
|
||||
|
|
@ -24,7 +24,7 @@ net-kingdom/docs/user-engine-platform-expansion-contract.md.
|
|||
|
||||
```task
|
||||
id: FLEX-WP-0009-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e940c5a3-ecb4-43d3-9554-2bfb422ec56d"
|
||||
```
|
||||
|
|
@ -38,11 +38,15 @@ deny.
|
|||
Done when all manifests validate and the vocabulary matches the action and
|
||||
resource strings emitted by user-engine.
|
||||
|
||||
Done 2026-08-09: `examples/user-engine` defines the dynamic protected-system
|
||||
registry and verified-claim request vocabulary for platform, tenant-admin,
|
||||
self-service, cross-tenant, missing-role, and wrong-system cases.
|
||||
|
||||
## T02 - Implement and verify the policy package
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0009-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6e0fe708-d7ff-411f-a64d-84a1692a6e11"
|
||||
```
|
||||
|
|
@ -55,11 +59,14 @@ mismatch, cross-tenant, malformed-context, and stale-policy fixtures.
|
|||
Done when fixture evaluation is deterministic, default deny is proven, and
|
||||
decision envelopes contain stable decision IDs and policy provenance.
|
||||
|
||||
Done 2026-08-09: all six embedded Rego tests and all six request fixtures pass;
|
||||
the package validates under CARING 0.4.0-rc2 and the registry loads cleanly.
|
||||
|
||||
## T03 - Deploy the cluster-local service
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0009-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "f8293230-136d-4f2d-8d3f-bb9840ea7e63"
|
||||
```
|
||||
|
|
@ -72,11 +79,15 @@ NetworkPolicy, and ingress restricted to approved protected systems.
|
|||
Done when user-engine can reach POST /v1/check, an unrelated namespace
|
||||
cannot, and restart/rollback procedures are documented.
|
||||
|
||||
Done 2026-08-09: immutable digest `sha256:a31961c45215aa6baf3bc748c6741ab703c2c8325e61aa7983a355026195e51b`
|
||||
is deployed as `flex-auth-user-engine.flex-auth.svc.cluster.local:8080`, Ready
|
||||
behind ingress restricted to the user-engine workload and with no egress.
|
||||
|
||||
## T04 - Hand back production evidence
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0009-T04
|
||||
status: todo
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "97b931e9-ac5b-46c7-a462-e23c0c18c4f4"
|
||||
```
|
||||
|
|
@ -87,3 +98,8 @@ policy version, and correlation IDs. Send completion evidence to NK-WP-0024.
|
|||
|
||||
Done when user-engine can replace its local authorization bridge without an
|
||||
availability bypass and the deployed failure matrix remains fail closed.
|
||||
|
||||
2026-08-09 live evidence: from the user-engine pod, `live-self` returned allow
|
||||
with decision `decision:4bf95ebb989ac628`; the cross-tenant variant returned
|
||||
deny/cross_tenant with decision `decision:bab072ce3ee72d98`. Runtime activation
|
||||
remains gated on the separately owned event and mail receivers.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue