83 lines
3.8 KiB
Markdown
83 lines
3.8 KiB
Markdown
---
|
|
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"})}) }
|
|
```
|