Overview
The policy configuration file (policy.yml) defines role-based access control (RBAC) including role definitions, inheritance hierarchies, reusable templates, and collection-specific permissions with field-level masking.
Configuration Structure
version: "1.0"
roles:
# Role definitions with inheritance
templates:
# Reusable policy templates
policies:
# Collection-specific policies
defaults:
# Default policy settings
Role Definitions
Define roles with descriptions and inheritance relationships.
roles.<role_name>.description
Human-readable description of the role’s purpose.
roles.<role_name>.inherits
List of role names this role inherits permissions from. Supports multiple inheritance.
Example
roles:
admin:
description: "Full system access"
manager:
description: "Can manage users and documents within their tenant"
inherits: ["user"]
user:
description: "Regular user with limited access"
inherits: ["viewer"]
viewer:
description: "Read-only access"
Role Inheritance
Roles can inherit permissions from multiple parent roles:
roles:
super_manager:
description: "Manager with admin privileges"
inherits: ["manager", "admin"]
Inheritance is resolved recursively, preventing circular dependencies.
Policy Templates
Define reusable permission templates that can be applied to multiple collections.
templates.<template_name>.<role_name>
Policy definition for a specific role within the template.
Common Templates
Tenant Isolation
templates:
tenant_isolation:
admin:
actions: [create, read, update, delete]
manager:
actions: [create, read, update, delete]
when: "resource.tenant_id == user.tenant_id"
user:
actions: [create, read, update]
when: "resource.tenant_id == user.tenant_id"
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id"
Owner Access
templates:
owner_access:
admin:
actions: [create, read, update, delete]
manager:
actions: [create, read, update, delete]
when: "resource.tenant_id == user.tenant_id"
user:
actions: [create, read, update, delete]
when: "resource.owner_id == user._id || resource.tenant_id == user.tenant_id"
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id"
Collection Policies
Define permissions for specific collections. Policies can use templates or define custom rules.
policies.<collection>.<role>.template
Name of the template to apply. Template settings can be overridden by other fields.
policies.<collection>.<role>.actions
List of allowed actions for this role on this collection.Valid actions:
create - Create new documents
read - Read existing documents
update - Modify existing documents
delete - Soft-delete documents
restore - Restore soft-deleted documents
aggregate - Run aggregation queries
policies.<collection>.<role>.when
Expression that must evaluate to true for the permission to apply. Uses MongoDB query syntax with access to user and resource context variables.
policies.<collection>.<role>.fields
Field-level access control configuration.
Example
policies:
users:
admin:
actions: [create, read, update, delete]
manager:
actions: [read, update]
when: "resource.tenant_id == user.tenant_id && resource.role != 'admin'"
fields:
deny_write: ["role", "tenant_id"]
user:
actions: [read, update]
when: "resource._id == user._id"
fields:
deny_write: ["role", "tenant_id", "email"]
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id"
fields:
deny: ["email"]
mask:
name: partial
Field-Level Permissions
Control access to specific fields within documents.
Whitelist of fields the role can access. If specified, all other fields are denied.
Blacklist of fields the role cannot read. These fields are removed from query results.
Fields the role cannot modify. Read access is still allowed.
Field masking rules. Keys are field names, values are mask types.Mask types:
email - Mask email addresses (e.g., j***@example.com)
phone - Mask phone numbers (e.g., ***-***-1234)
partial - Show first and last characters only (e.g., J***n)
Examples
Allow-list fields
fields:
allow: ["name", "email", "role"]
Deny sensitive fields
fields:
deny: ["ssn", "salary", "bank_account"]
Read-only fields
fields:
deny_write: ["created_at", "created_by", "tenant_id"]
Field masking
fields:
mask:
email: email
phone: phone
ssn: partial
Permission Expressions
The when field supports MongoDB query expressions with context variables:
Context Variables
user - Current user from JWT token (contains _id, role, tenant_id, etc.)
resource - Document being accessed
Expression Examples
Tenant isolation
when: "resource.tenant_id == user.tenant_id"
Owner-based access
when: "resource.owner_id == user._id"
Complex conditions
when: |
(resource.owner_id == user._id ||
resource.tenant_id == user.tenant_id) &&
resource.status != 'archived'
Role-based restrictions
when: "resource.role != 'admin' && resource.tenant_id == user.tenant_id"
Using Templates
Apply a template and override specific settings:
policies:
documents:
manager:
template: owner_access
# Override template's when clause
when: "resource.tenant_id == user.tenant_id && resource.status != 'archived'"
# Add field restrictions
fields:
deny_write: ["created_by", "created_at"]
Default Settings
Default deny all access unless explicitly granted. Set to false for permissive mode.
Enable audit logging for all policy evaluations.
Example
defaults:
deny_all: true
audit_log: true
Complete Example
version: "1.0"
# Role definitions with inheritance
roles:
admin:
description: "Full system access"
manager:
description: "Can manage users and documents within their tenant"
inherits: ["user"]
user:
description: "Regular user with limited access"
inherits: ["viewer"]
viewer:
description: "Read-only access"
# Reusable policy templates
templates:
tenant_isolation:
admin:
actions: [create, read, update, delete]
manager:
actions: [create, read, update, delete]
when: "resource.tenant_id == user.tenant_id"
user:
actions: [create, read, update]
when: "resource.tenant_id == user.tenant_id"
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id"
owner_access:
admin:
actions: [create, read, update, delete]
manager:
actions: [create, read, update, delete]
when: "resource.tenant_id == user.tenant_id"
user:
actions: [create, read, update, delete]
when: "resource.owner_id == user._id || resource.tenant_id == user.tenant_id"
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id"
# Collection-specific policies
policies:
users:
admin:
actions: [create, read, update, delete]
manager:
actions: [read, update]
when: "resource.tenant_id == user.tenant_id && resource.role != 'admin'"
fields:
deny_write: ["role", "tenant_id"]
user:
actions: [read, update]
when: "resource._id == user._id"
fields:
deny_write: ["role", "tenant_id", "email"]
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id"
fields:
deny: ["email"]
mask:
name: partial
documents:
admin:
actions: [create, read, update, delete, restore]
manager:
actions: [create, read, update, delete, restore]
when: "resource.tenant_id == user.tenant_id"
user:
actions: [create, read, update, delete]
when: "resource.owner_id == user._id"
viewer:
actions: [read]
when: "resource.tenant_id == user.tenant_id && resource.status == 'published'"
# Default settings
defaults:
deny_all: true
audit_log: true
Environment Variables
Policy configuration supports environment variable substitution:
policies:
users:
admin:
actions: [create, read, update, delete]
when: "resource.tenant_id == '${ENV.ADMIN_TENANT_ID}'"
Loading Policy Configuration
Load the policy configuration at server startup:
permission-mongo --policy=/path/to/policy.yml
Or use the PM_POLICY environment variable:
export PM_POLICY=/path/to/policy.yml
permission-mongo
Validation
The policy configuration is validated on load:
- All roles referenced in policies must be defined
- All templates referenced must exist
- All actions must be valid
- Circular role inheritance is detected and rejected
- Field mask types must be valid