This example demonstrates how to implement classic Unix-style permission bits using MPE's annotation system. Just like Unix files have rwxrwxrwx permissions for owner, group, and other, this PolicyDomain models read/write access for three permission classes.
<SectionHeader icon="version" level={2}>Overview
In Unix filesystems, every file has:
We'll model this using:
resource.owner — The principal who owns the resourceresource.annotations.group — The MRN of the owning groupresource.annotations.mode — Permission bits encoded as an objectprincipal.mgroups — Groups the principal belongs to<SectionHeader icon="settings" level={2}>Design
Each resource has a mode annotation containing permission bits:
{
"owner": { "read": true, "write": true },
"group": { "read": true, "write": false },
"other": { "read": true, "write": false }
}
This is equivalent to Unix mode 0644 (owner: rw, group: r, other: r).
flowchart TD
A[Request arrives] --> B{Principal == Owner?}
B -->|Yes| C[Use owner permissions]
B -->|No| D{Principal in group?}
D -->|Yes| E[Use group permissions]
D -->|No| F[Use other permissions]
C --> G{Has required permission?}
E --> G
F --> G
G -->|Yes| H[GRANT]
G -->|No| I[DENY]
<SectionHeader icon="security" level={2}>Complete PolicyDomain
apiVersion: iamlite.manetu.io/v1beta1
kind: PolicyDomain
metadata:
name: unix-filesystem
spec:
# ============================================================
# Policy Libraries - Reusable helper functions
# ============================================================
policy-libraries:
- mrn: &lib-utils "mrn:iam:library:utils"
name: utils
description: "Common utility functions"
rego: |
package utils
import rego.v1
# Check if request has a valid principal (authenticated)
has_principal if {
input.principal != {}
input.principal.sub != ""
}
- mrn: &lib-unix-perms "mrn:iam:library:unix-perms"
name: unix-perms
description: "Unix permission checking utilities"
rego: |
package unix_perms
import rego.v1
# Determine which permission class applies to this principal
# Returns: "owner", "group", or "other"
permission_class(principal, resource) := "owner" if {
principal.sub == resource.owner
}
permission_class(principal, resource) := "group" if {
principal.sub != resource.owner
resource.annotations.group in principal.mgroups
}
permission_class(principal, resource) := "other" if {
principal.sub != resource.owner
not resource.annotations.group in principal.mgroups
}
# Check if the permission class has the required permission
has_permission(mode, class, permission) if {
mode[class][permission] == true
}
# Map operations to required permissions
required_permission(operation) := "read" if {
some suffix in {":read", ":list", ":get"}
endswith(operation, suffix)
}
required_permission(operation) := "write" if {
some suffix in {":write", ":create", ":update", ":delete"}
endswith(operation, suffix)
}
# ============================================================
# Policies
# ============================================================
policies:
# Operation phase - require authentication for all file operations
- mrn: &policy-require-auth "mrn:iam:policy:require-auth"
name: require-auth
description: "Require authentication for file operations"
dependencies:
- *lib-utils
rego: |
package authz
import rego.v1
import data.utils
# Tri-level: negative=DENY, 0=GRANT, positive=GRANT Override
# Default deny - only grant if authenticated
default allow = -1
# Grant authenticated requests
allow = 0 if utils.has_principal
# Identity phase - any authenticated user can attempt file operations
# The actual permission check happens in the resource phase
- mrn: &policy-authenticated-user "mrn:iam:policy:authenticated-user"
name: authenticated-user
description: "Allow any authenticated user to proceed to resource phase"
dependencies:
- *lib-utils
rego: |
package authz
import rego.v1
import data.utils
default allow = false
# Grant if authenticated
allow if utils.has_principal
# Resource phase - Unix permission bit checking
- mrn: &policy-unix-permissions "mrn:iam:policy:unix-permissions"
name: unix-permissions
description: "Check Unix-style permission bits"
dependencies:
- *lib-unix-perms
rego: |
package authz
import rego.v1
import data.unix_perms
default allow = false
# Main permission check
allow if {
# Get the permission mode from the resource
mode := input.resource.annotations.mode
# Determine which class this principal falls into
class := unix_perms.permission_class(input.principal, input.resource)
# Get the required permission for this operation
required := unix_perms.required_permission(input.operation)
# Check if the permission is granted
unix_perms.has_permission(mode, class, required)
}
# Superuser bypass - if principal has superuser role, always allow
allow if {
"mrn:iam:role:superuser" in input.principal.mroles
}
# ============================================================
# Roles
# ============================================================
roles:
# Regular user - relies on file permissions
- mrn: &role-user "mrn:iam:role:user"
name: user
description: "Regular filesystem user"
policy: *policy-authenticated-user
# Superuser - bypasses permission checks (like root)
- mrn: &role-superuser "mrn:iam:role:superuser"
name: superuser
description: "Superuser with full access (like root)"
policy: *policy-authenticated-user
# ============================================================
# Groups - User groups for file permissions
# ============================================================
groups:
- mrn: "mrn:iam:group:developers"
name: developers
description: "Development team"
roles:
- *role-user
- mrn: "mrn:iam:group:admins"
name: admins
description: "System administrators"
roles:
- *role-superuser
- mrn: "mrn:iam:group:finance"
name: finance
description: "Finance department"
roles:
- *role-user
- mrn: "mrn:iam:group:hr"
name: hr
description: "Human resources"
roles:
- *role-user
# ============================================================
# Resource Groups
# ============================================================
resource-groups:
- mrn: &rg-files "mrn:iam:resource-group:files"
name: files
description: "All filesystem resources"
default: true
policy: *policy-unix-permissions
# ============================================================
# Operations
# ============================================================
operations:
- name: file-operations
selector:
- "file:.*"
policy: *policy-require-auth
<SectionHeader icon="test" level={2}>Test Cases
The owner can read their own file with owner read permission:
{
"principal": {
"sub": "alice",
"mroles": ["mrn:iam:role:user"],
"mgroups": ["mrn:iam:group:developers"]
},
"operation": "file:document:read",
"resource": {
"id": "mrn:fs:home:alice:document.txt",
"owner": "alice",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:developers",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": false },
"other": { "read": false, "write": false }
}
}
}
}
Expected: (alice is owner, owner has read permission)
mpe test decision -b policydomain.yml -i input.json | jq .decision
# "GRANT"
The owner can write to their own file:
{
"principal": {
"sub": "alice",
"mroles": ["mrn:iam:role:user"],
"mgroups": ["mrn:iam:group:developers"]
},
"operation": "file:document:write",
"resource": {
"id": "mrn:fs:home:alice:document.txt",
"owner": "alice",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:developers",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": false },
"other": { "read": false, "write": false }
}
}
}
}
Expected: (alice is owner, owner has write permission)
A group member can read a file with group read permission:
{
"principal": {
"sub": "bob",
"mroles": ["mrn:iam:role:user"],
"mgroups": ["mrn:iam:group:developers"]
},
"operation": "file:document:read",
"resource": {
"id": "mrn:fs:home:alice:shared.txt",
"owner": "alice",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:developers",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": false },
"other": { "read": false, "write": false }
}
}
}
}
Expected: (bob is in developers group, group has read permission)
A group member cannot write to a file without group write permission:
{
"principal": {
"sub": "bob",
"mroles": ["mrn:iam:role:user"],
"mgroups": ["mrn:iam:group:developers"]
},
"operation": "file:document:write",
"resource": {
"id": "mrn:fs:home:alice:shared.txt",
"owner": "alice",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:developers",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": false },
"other": { "read": false, "write": false }
}
}
}
}
Expected: (bob is in group, but group lacks write permission)
A user not in the file's group falls back to "other" permissions:
{
"principal": {
"sub": "charlie",
"mroles": ["mrn:iam:role:user"],
"mgroups": ["mrn:iam:group:finance"]
},
"operation": "file:document:read",
"resource": {
"id": "mrn:fs:home:alice:private.txt",
"owner": "alice",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:developers",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": false },
"other": { "read": false, "write": false }
}
}
}
}
Expected: (charlie is not owner or in developers group, other has no read)
Any authenticated user can read a world-readable file:
{
"principal": {
"sub": "charlie",
"mroles": ["mrn:iam:role:user"],
"mgroups": ["mrn:iam:group:finance"]
},
"operation": "file:document:read",
"resource": {
"id": "mrn:fs:public:readme.txt",
"owner": "system",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:admins",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": true },
"other": { "read": true, "write": false }
}
}
}
}
Expected: (other has read permission)
A superuser can access any file regardless of permissions:
{
"principal": {
"sub": "root",
"mroles": ["mrn:iam:role:superuser"],
"mgroups": ["mrn:iam:group:admins"]
},
"operation": "file:document:write",
"resource": {
"id": "mrn:fs:home:alice:locked.txt",
"owner": "alice",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:developers",
"mode": {
"owner": { "read": true, "write": false },
"group": { "read": false, "write": false },
"other": { "read": false, "write": false }
}
}
}
}
Expected: (superuser bypasses all permission checks)
An unauthenticated request is denied at the operation phase:
{
"principal": {},
"operation": "file:document:read",
"resource": {
"id": "mrn:fs:public:readme.txt",
"owner": "system",
"group": "mrn:iam:resource-group:files",
"annotations": {
"group": "mrn:iam:group:admins",
"mode": {
"owner": { "read": true, "write": true },
"group": { "read": true, "write": true },
"other": { "read": true, "write": false }
}
}
}
}
Expected: (no authentication)
<SectionHeader icon="version" level={2}>Key Concepts Demonstrated
The unix-perms library contains helper functions that can be imported by any policy. This keeps the main policy clean and makes the logic testable in isolation.
Instead of hardcoding permissions in policies, we store them as resource annotations. This allows:
The permission_class function implements the Unix model:
The superuser role bypasses all permission checks, similar to Unix root. This is implemented cleanly in the resource policy with an additional allow rule.
The required_permission function maps operations like :read, :write, :delete to the corresponding permission bits. This makes it easy to add new operations that map to existing permissions.
<SectionHeader icon="build" level={2}>Extending This Example
To support execute permission (for running scripts):
required_permission(operation) := "execute" if {
some suffix in {":execute", ":run"}
endswith(operation, suffix)
}
Update the mode annotations to include execute:
{
"mode": {
"owner": { "read": true, "write": true, "execute": true },
"group": { "read": true, "write": false, "execute": true },
"other": { "read": true, "write": false, "execute": false }
}
}
For special bits, add additional annotations:
{
"sticky": true,
"setuid": false,
"setgid": true
}
Then check these in the policy before allowing certain operations.
For Access Control Lists beyond owner/group/other:
{
"acl": [
{ "principal": "bob", "read": true, "write": true },
{ "principal": "mrn:iam:group:auditors", "read": true, "write": false }
]
}
Add a check in the policy that evaluates ACL entries before falling back to standard permissions.
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 |