A PORC Expression is the normalized input format for policy evaluation. PORC stands for:
PORC is the universal language for expressing authorization requests in the PolicyEngine. Every policy evaluation receives a PORC expression as input, regardless of the origin of the request. This standardized format makes it possible to:
{
"principal": {
"sub": "user@example.com",
"mroles": ["mrn:iam:role:admin"],
"mgroups": ["mrn:iam:group:developers"],
"scopes": ["mrn:iam:scope:full-access"],
"mclearance": "HIGH",
"mannotations": {
"department": "engineering"
}
},
"operation": "api:users:create",
"resource": "mrn:app:users:12345",
"context": {
"timestamp": "2024-01-15T10:30:00Z",
"source_ip": "192.168.1.100"
}
}
The Principal represents who is making the request. This typically comes from JWT claims or other authentication tokens.
| Field | Description |
|---|---|
sub | Subject identifier (user ID, service account) |
mroles | Array of role MRNs assigned to the principal |
mgroups | Array of group MRNs the principal belongs to |
scopes | Array of scope MRNs from the access method |
mclearance | Security clearance level (LOW, MODERATE, HIGH, MAXIMUM) |
mannotations | Key-value metadata about the principal |
:::note Audit Consideration
The sub field should always be provided. While omitting it may not affect policy evaluation, principal.sub has first-class representation in the AccessRecord and its absence will impact audit data quality.
:::
Access principal fields in Rego via input.principal:
package authz
default allow = false
# Check if principal has admin role
allow {
"mrn:iam:role:admin" in input.principal.mroles
}
# Check security clearance
allow {
input.principal.mclearance == "HIGH"
}
# Check department annotation
allow {
input.principal.mannotations.department == "engineering"
}
The Operation identifies what action is being performed. Operations follow a consistent naming convention:
<subsystem>:<resource-class>:<verb>
| Operation | Description |
|---|---|
iam:identity:create | Create an identity |
vault:attributes:read | Read vault attributes |
api:users:list | List users via API |
admin:settings:update | Update admin settings |
See Operations for details on how operations route requests to policies.
package authz
default allow = false
# Allow specific operations
allow {
input.operation in {"api:users:read", "api:users:list"}
}
# Pattern matching with glob
allow {
glob.match("*:*:read", [], input.operation)
}
# Parse operation components
allow {
parts := split(input.operation, ":")
parts[0] == "api"
parts[2] == "read"
}
The Resource identifies the entity being accessed. Resources have metadata—group membership, classification, ownership, and annotations—that policies use to make decisions. The two PORC formats differ in who provides that metadata:
| Format | Metadata Provider |
|---|---|
| MRN String | PolicyEngine resolves via selectors or external resolvers |
| Fully-Qualified Descriptor | PEP provides directly in the request |
{
"resource": "mrn:app:myservice:document:12345"
}
{
"resource": {
"id": "mrn:app:myservice:document:12345",
"owner": "user@example.com",
"group": "mrn:iam:resource-group:documents",
"classification": "MODERATE",
"annotations": {"department": "engineering"}
}
}
:::note Audit Consideration
The resource.id field has first-class representation in the AccessRecord. When using the MRN string format, the PolicyEngine automatically populates resource.id from the provided string. When using the Fully-Qualified Descriptor format, the id field should be provided to ensure complete audit data.
:::
See Resource Resolution for guidance on choosing between these formats, and Resources for details on resource metadata.
package authz
default allow = false
# Check if principal owns the resource
allow {
input.principal.sub == input.resource.owner
}
# Check resource classification against clearance
allow {
clearance_levels := {"LOW": 1, "MODERATE": 2, "HIGH": 3, "MAXIMUM": 4}
clearance_levels[input.principal.mclearance] >= clearance_levels[input.resource.classification]
}
# Check resource annotations
allow {
input.resource.annotations.department == input.principal.mannotations.department
}
The Context contains additional information about the request environment. This is free-form and depends on the enforcement point.
{
"context": {
"timestamp": "2024-01-15T10:30:00Z",
"source_ip": "10.0.0.1",
"user_agent": "Mozilla/5.0...",
"request_id": "abc-123",
"method": "POST",
"path": "/api/users"
}
}
package authz
default allow = false
# Allow if all conditions pass
allow {
trusted_network(input.context.source_ip)
not weekend_restricted
}
# Helper: Check trusted network
trusted_network(ip) {
net.cidr_contains("10.0.0.0/8", ip)
}
# Helper: Weekend access restriction
weekend_restricted {
time.weekday(time.now_ns()) == "Saturday"
not "mrn:iam:role:weekend-access" in input.principal.mroles
}
The PORC expression drives the Policy Conjunction evaluation:
| PORC Component | Used By |
|---|---|
operation | Phase 1 - Routes to operation policy |
principal.mroles | Phase 2 - Selects identity policies via roles |
principal.mgroups | Phase 2 - Expands to additional roles |
resource.group | Phase 3 - Selects resource policy |
principal.scopes | Phase 4 - Selects scope policies |
Here's a fully-populated PORC expression showing all components:
{
"principal": {
"sub": "alice@acme.com",
"mrealm": "acme.com",
"mroles": [
"mrn:iam:role:editor",
"mrn:iam:role:viewer"
],
"mgroups": [
"mrn:iam:group:content-team"
],
"scopes": [
"mrn:iam:scope:full-access"
],
"mclearance": "MODERATE",
"mannotations": {
"department": "marketing",
"employee_id": "12345"
}
},
"operation": "documents:article:update",
"resource": {
"id": "mrn:content:acme.com:article:summer-campaign",
"owner": "bob@acme.com",
"group": "mrn:iam:resource-group:marketing-content",
"classification": "LOW",
"annotations": {
"campaign": "summer-2024",
"status": "draft"
}
},
"context": {
"timestamp": "2024-06-15T14:30:00Z",
"source_ip": "10.0.1.50",
"user_agent": "ContentEditor/2.0",
"request_id": "req-abc-123",
"correlation_id": "corr-xyz-789"
}
}
mrolesmgroupsscopesFor implementation details on building PORC expressions in your application:
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |