Permission metadata defines access control rules for objects, fields, actions, and views using Role-Based Access Control (RBAC) and field-level security.
ObjectQL's permission system provides a pure RBAC model with:
- Role-based access control: Define roles and assign permissions to roles
- Object-level permissions: Control CRUD operations on entire objects
- Field-level security: Hide/protect sensitive fields from unauthorized users
- Record-level rules: Dynamic filtering based on ownership, sharing, or custom rules
- Action permissions: Control who can execute specific actions
- View permissions: Restrict access to specific UI views
File Naming Convention: <object_name>.permission.yml
The filename (without the .permission.yml extension) automatically identifies which object these permission rules apply to.
Examples:
project.permission.yml→ Applies to object:projectcustomer_order.permission.yml→ Applies to object:customer_order
# File: project.permission.yml
# Object is inferred from filename!
description: Permission rules for project object
# Role Definitions
roles:
- admin
- manager
- developer
- viewer
# Object-Level Permissions
object_permissions:
create: [admin, manager]
read: [admin, manager, developer, viewer]
update: [admin, manager]
delete: [admin]
# Field-Level Security
field_permissions:
budget:
read: [admin, manager]
update: [admin]
internal_notes:
read: [admin, manager]
update: [admin, manager]
# Record-Level Rules
record_rules:
- name: owner_full_access
description: Project owner has full access
condition:
field: owner_id
operator: "="
value: $current_user.id
permissions:
read: true
update: true
delete: true
- name: team_member_access
description: Team members can view and edit
condition:
field: team_members
operator: contains
value: $current_user.id
permissions:
read: true
update: true
delete: false
# Action Permissions
action_permissions:
approve_project:
execute: [admin, manager]
archive_project:
execute: [admin]Control basic CRUD operations on objects.
| Permission | Description |
|---|---|
create |
Create new records |
read |
View existing records |
update |
Modify existing records |
delete |
Delete records |
view_all |
See all records (bypass ownership rules) |
modify_all |
Edit all records (bypass ownership rules) |
object_permissions:
# Standard users
create: [user]
read: [user]
update: [user] # Limited by record rules
delete: [] # Nobody can delete
# Admins have full access
view_all: [admin]
modify_all: [admin]Restrict access to specific fields based on roles.
field_permissions:
# Sensitive field - admins only
social_security_number:
read: [admin, hr_manager]
update: [admin]
# Financial data - finance team only
salary:
read: [admin, finance_manager]
update: [admin, finance_manager]
# System fields - read-only for most users
created_by:
read: [admin, manager, user]
update: [] # System managed
# Public field - everyone can read
name:
read: [admin, manager, user, guest]
update: [admin, manager, user]Fields inherit object-level permissions unless explicitly overridden.
# If object read permission is [user], all fields are readable by [user]
# unless field_permissions specifies otherwise
object_permissions:
read: [user]
field_permissions:
# This field is MORE restrictive than object level
salary:
read: [admin]Dynamic access control based on record data and user context.
record_rules:
# Rule 1: Owner Access
- name: owner_access
priority: 100
description: Record owner has full access
condition:
type: simple
field: owner_id
operator: "="
value: $current_user.id
permissions:
read: true
update: true
delete: true
# Rule 2: Department Access
- name: department_access
priority: 50
description: Same department can view
condition:
type: simple
field: department_id
operator: "="
value: $current_user.department_id
permissions:
read: true
update: false
delete: false
# Rule 3: Complex Condition
- name: approved_public_access
priority: 10
description: Approved records are public
condition:
type: complex
expression:
- field: status
operator: "="
value: approved
- and
- field: is_public
operator: "="
value: true
permissions:
read: true
update: false
delete: falseWhen multiple rules match, the highest priority rule takes precedence. Default priority is 0.
| Type | Description |
|---|---|
simple |
Single field comparison |
complex |
Multiple conditions with AND/OR logic |
formula |
Custom JavaScript/formula expression |
lookup |
Check related record data |
Available in permission conditions:
| Variable | Description |
|---|---|
$current_user.id |
Current user's ID |
$current_user.role |
Current user's role |
$current_user.department_id |
User's department |
$current_user.team_id |
User's team |
$current_date |
Current date |
$current_timestamp |
Current timestamp |
Extend access beyond owner and role-based rules.
sharing_rules:
# Manual Sharing
- name: manual_share
type: manual
description: Users can manually share records
enabled: true
permissions:
read: true
update: false
delete: false
# Criteria-based Sharing
- name: public_projects
type: criteria
description: Public projects visible to all
condition:
field: visibility
operator: "="
value: public
shared_with:
type: role
roles: [user, guest]
permissions:
read: true
update: false
# Team-based Sharing
- name: team_sharing
type: team
description: Share with team members
team_field: assigned_team_id
permissions:
read: true
update: trueControl execution of custom actions.
action_permissions:
# Record Action
approve_order:
execute: [manager, admin]
conditions:
# Can only approve if order is pending
- field: status
operator: "="
value: pending
# Can only approve orders in their department
- field: department_id
operator: "="
value: $current_user.department_id
# Global Action
import_data:
execute: [admin]
rate_limit:
requests_per_hour: 10Control access to specific views.
view_permissions:
# List View
all_projects:
access: [admin, manager, developer]
# Kanban View
project_kanban:
access: [admin, manager]
# Report View
financial_dashboard:
access: [admin, finance_manager]
field_restrictions:
profit_margin:
visible_to: [admin]Automatically filter queries based on permissions.
row_level_security:
enabled: true
default_rule:
# Users only see their own records by default
field: owner_id
operator: "="
value: $current_user.id
exceptions:
# Admins bypass RLS
- role: admin
bypass: true
# Managers see their department
- role: manager
condition:
field: department_id
operator: "="
value: $current_user.department_idMask sensitive data for unauthorized users.
field_masking:
# Credit Card Numbers
credit_card:
mask_format: "****-****-****-{last4}"
visible_to: [admin, finance]
# Email Addresses
email:
mask_format: "{first}***@{domain}"
visible_to: [admin, user_owner]
# Phone Numbers
phone:
mask_format: "***-***-{last4}"
visible_to: [admin, hr]Track permission changes and access.
audit:
enabled: true
# Log these events
events:
- permission_grant
- permission_revoke
- access_denied
- sensitive_field_access
# Retention period
retention_days: 365
# Alert on suspicious activity
alerts:
- event: access_denied
threshold: 5
window_minutes: 10
notify: [security_team]// Check if user can perform operation
const canUpdate = await objectql.checkPermission({
user: currentUser,
object: 'projects',
operation: 'update',
recordId: 'proj_123'
});
// Check field access
const canViewSalary = await objectql.checkFieldPermission({
user: currentUser,
object: 'employees',
field: 'salary',
operation: 'read',
recordId: 'emp_456'
});Hooks and actions receive permission context:
// In a hook
beforeUpdate: async ({ user, permissions }) => {
if (!permissions.canUpdate('budget')) {
throw new Error('No permission to update budget');
}
}- Principle of Least Privilege: Grant minimum necessary permissions
- Define Clear Roles: Create well-defined roles that match organizational structure
- Test Permissions: Validate permission rules with different user roles
- Document Rules: Add clear descriptions to all permission rules
- Regular Audits: Review permissions regularly for compliance
- Separation of Duties: Prevent conflicts of interest with permission design
- Default Deny: Deny access unless explicitly granted
- SQL Injection: All permission filters use parameterized queries
- Privilege Escalation: Validate permission changes require admin access
- Session Management: Permissions cached per session, invalidated on role change
- API Security: Rate limiting on permission checks to prevent DoS
- Audit Logging: All permission denials logged for security analysis
- Objects & Fields - Data model definition
- Actions - Custom operations
- Views - UI presentation
- Hooks - Business logic triggers