PORC Expressions
A PORC Expression is the normalized input format for policy evaluation. PORC stands for:
Overview
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:
- Write policies that work across different enforcement points
- Reuse policy logic regardless of the original request format
- Create consistent audit trails
- Decouple policy logic from application-specific request formats
PORC Structure
{
"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"
}
}
Principal
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 |
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.
Principal in Policies
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"
}
Operation
The Operation identifies what action is being performed. Operations follow a consistent naming convention:
<subsystem>:<resource-class>:<verb>
Examples
| 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.
Operation in 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"
}
Resource
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 |
MRN String
{
"resource": "mrn:app:myservice:document:12345"
}
Fully-Qualified Descriptor
{
"resource": {
"id": "mrn:app:myservice:document:12345",
"owner": "user@example.com",
"group": "mrn:iam:resource-group:documents",
"classification": "MODERATE",
"annotations": {"department": "engineering"}
}
}
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.
Resource in Policies
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
}
Context
The Context contains additional information about the request environment. This is free-form and depends on the enforcement point.
Common Context Fields
{
"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"
}
}
Context in Policies
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
}
How PORC Connects to Policy Phases
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 |
Complete PORC Example
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"
}
}
Related Concepts
- Policy Conjunction: How PORC drives multi-phase evaluation
- MRN: The identifier format used in PORC
- Operations: How the operation field routes to policies
- Roles: Identity policies selected by
mroles - Groups: Role bundles referenced by
mgroups - Resources: The resource field and its metadata
- Resource Groups: Resource policy selection
- Scopes: Access-method constraints via
scopes
Integration
For implementation details on building PORC expressions in your application:
- Integration Overview: PDP/PEP architecture
- Resource Resolution: When to use MRN strings vs fully-qualified descriptors
- Go Library: Building PORC in Go applications
- HTTP API: Sending PORC via HTTP