Access Control
Lumenize uses a flavor of relationship-based access control (ReBAC), also known as graph-based access control (GBAC), to provide flexible, scalable permissions that model real-world organizational structures.
Understanding ReBAC
While ReBAC may be unfamiliar, it’s ideal for modern enterprise software. Traditional role-based access control (RBAC) struggles with complex organizational relationships. ReBAC excels at modeling:
- Teams that work across multiple departments
- Temporary projects borrowing resources from various units
- Coaches or consultants who need visibility across organizational boundaries
- Matrix organizations with multiple reporting structures
ReBAC goes beyond traditional Role-Based Access Control (RBAC) by considering relationships between users, resources, and organizational units.
The Directed Acyclic Graph (DAG)
The OrgTree uses a DAG structure where each node can have multiple parents, unlike traditional single-parent hierarchies.
Real-World Example
Consider a security team that:
- Officially reports to the IT department
- Does most of its work for the Legal department
- Provides services to the entire organization
In a DAG, this team can have multiple parents:
- IT Department (official reporting)
- Legal Department (primary work relationship)
- Enterprise Services (organization-wide visibility)
Each relationship can grant different permission levels based on the context.
Permission Inheritance
Permissions flow down the graph:
- Grant
read
permission on IT Department → Security team inheritsread
- Grant
write
permission on Legal Department → Security team inheritswrite
- Grant
admin
permission on Enterprise Services → Security team inheritsadmin
The user gets the highest permission level from any path in the graph.
Permission Levels
Read Permission
- View node values and attached entities
- Navigate the organizational structure
- Access custom fields added to schemas
Write Permission
- All
read
capabilities, plus: - Modify node and entity values
- Create new entities attached to the node or descendants
- Soft delete/restore entities
- Add additional parent relationships (with
write
on new parent)
Admin Permission
- All
write
capabilities, plus: - Update permissions for the node and descendants
- Manage organizational structure
Visibility Rules
Public information (visible to everyone):
- OrgTree structure itself
- Base schema fields (id, slug, name)
Protected information (requires permissions):
- Custom schema fields
- Attached entities
- Business data
This allows navigation without exposing sensitive information.
Key Concepts
- Subjects: Users who perform actions
- Objects: Resources being accessed (entities, fields, operations)
- Relationships: Connections between subjects and objects
- Permissions: What actions are allowed based on relationships
Defining Permissions
Schema-Level Permissions
Define permissions directly in your entity schemas:
{ "name": "Document", "fields": { "title": {"type": "string"}, "content": {"type": "text"}, "authorId": {"type": "reference", "entity": "User"}, "teamId": {"type": "reference", "entity": "Team"} }, "permissions": { "create": ["authenticated"], "read": ["owner", "team_member", "public"], "update": ["owner", "team_admin"], "delete": ["owner", "admin"] }}
Field-Level Permissions
Control access to specific fields:
{ "name": "Employee", "fields": { "name": {"type": "string"}, "email": {"type": "email"}, "salary": {"type": "decimal", "precision": 2}, "performance": {"type": "json"} }, "permissions": { "read": ["authenticated"], "update": ["owner", "hr"] }, "fieldPermissions": { "salary": { "read": ["owner", "hr", "manager"], "update": ["hr"] }, "performance": { "read": ["owner", "manager", "hr"], "update": ["manager", "hr"] } }}
Operation-Level Permissions
Define permissions for custom operations:
{ "name": "Project", "customOperations": { "archive": { "permissions": ["owner", "admin"] }, "export": { "permissions": ["member", "admin"] }, "clone": { "permissions": ["authenticated"] } }}
Permission Types
Built-in Permission Roles
Public Access
{ "permissions": { "read": ["public"] // Anyone can read }}
Authenticated Users
{ "permissions": { "create": ["authenticated"], // Any logged-in user "read": ["authenticated"] }}
Owner Permissions
{ "permissions": { "read": ["owner"], // User who created the record "update": ["owner"], "delete": ["owner"] }}
Relationship-Based Permissions
{ "permissions": { "read": ["team_member"], // Members of associated team "update": ["team_admin"], // Admins of associated team "delete": ["organization_admin"] // Admins of associated organization }}
Custom Permission Roles
Define custom roles for specific use cases:
{ "name": "BlogPost", "permissions": { "read": ["public"], "create": ["author", "editor"], "update": ["post_author", "editor", "admin"], "delete": ["post_author", "admin"], "publish": ["editor", "admin"], "feature": ["admin"] }, "customRoles": { "post_author": { "condition": "authorId == currentUser.id" }, "author": { "condition": "currentUser.role == 'author'" }, "editor": { "condition": "currentUser.role in ['editor', 'admin']" } }}
Organizational Hierarchy
OrgTree Structure
Model your organization as a directed acyclic graph:
// Create organization structureconst org = await client.organizations.create({ name: "Acme Corp", type: "company"});
const engineering = await client.organizations.create({ name: "Engineering", type: "department", parent: org.id});
const frontend = await client.organizations.create({ name: "Frontend Team", type: "team", parent: engineering.id});
// Users inherit permissions from their position in the treeawait client.users.update("user_123", { organizationId: frontend.id, role: "member"});
Multiple Parent Relationships
Handle complex organizational structures:
// A user can belong to multiple teamsconst securityTeam = await client.organizations.create({ name: "Security Team", type: "team", parent: engineering.id});
// Add user to multiple teamsawait client.organizationMemberships.create({ userId: "user_123", organizationId: securityTeam.id, role: "consultant"});
// Now user has permissions from both frontend and security teams
Permission Inheritance
Permissions flow down the organizational hierarchy:
// Grant permission at department levelawait client.permissions.grant({ subjectId: "user_123", objectType: "Project", objectId: engineering.id, permission: "read", scope: "descendants" // Includes all teams under engineering});
// User can now read all projects owned by engineering teams
Dynamic Permissions
Attribute-Based Permissions
Use entity attributes to determine access:
{ "name": "Document", "permissions": { "read": ["owner", "confidentiality_cleared"], "update": ["owner"] }, "customRoles": { "confidentiality_cleared": { "condition": "this.confidentialityLevel <= currentUser.clearanceLevel" } }}
Time-Based Permissions
Implement temporary or scheduled access:
{ "name": "Event", "permissions": { "register": ["before_deadline", "organizer"], "attend": ["registered", "organizer"] }, "customRoles": { "before_deadline": { "condition": "now() < this.registrationDeadline" }, "registered": { "condition": "currentUser.id in this.attendeeIds" } }}
Context-Based Permissions
Consider request context in permission decisions:
{ "name": "FinancialReport", "permissions": { "read": ["business_hours_only", "manager"] }, "customRoles": { "business_hours_only": { "condition": "isBusinessHours(context.requestTime) && currentUser.role == 'analyst'" } }}
Permission Checking
Client-Side Checks
Check permissions before showing UI elements:
// Check if user can perform actionconst canEdit = await client.permissions.check({ action: 'update', resource: 'Document', resourceId: 'doc_123'});
// Show/hide edit button based on permissionsif (canEdit) { showEditButton();}
// Check multiple permissions at onceconst permissions = await client.permissions.checkMany([ { action: 'update', resource: 'Document', resourceId: 'doc_123' }, { action: 'delete', resource: 'Document', resourceId: 'doc_123' }, { action: 'share', resource: 'Document', resourceId: 'doc_123' }]);
console.log(permissions); // { update: true, delete: false, share: true }
Server-Side Enforcement
Permissions are automatically enforced on the server:
try { // This will throw an error if user lacks permission const doc = await client.documents.update('doc_123', { title: 'New Title' });} catch (error) { if (error.code === 'PERMISSION_DENIED') { console.log('You do not have permission to update this document'); }}
Bulk Permission Checks
Efficiently check permissions for multiple resources:
// Check read permissions for all documentsconst documents = await client.documents.findMany({ where: { teamId: 'team_123' }, includePermissions: true});
documents.forEach(doc => { console.log(`Can edit ${doc.title}:`, doc._permissions.update); console.log(`Can delete ${doc.title}:`, doc._permissions.delete);});
Permission Management
Granting Permissions
// Grant specific permissionawait client.permissions.grant({ subjectType: 'User', subjectId: 'user_123', objectType: 'Document', objectId: 'doc_456', permission: 'update'});
// Grant role-based permissionawait client.permissions.grantRole({ userId: 'user_123', organizationId: 'org_789', role: 'admin'});
// Grant temporary permissionawait client.permissions.grant({ subjectId: 'user_123', objectType: 'Project', objectId: 'proj_456', permission: 'read', expiresAt: new Date('2024-12-31')});
Revoking Permissions
// Revoke specific permissionawait client.permissions.revoke({ subjectId: 'user_123', objectType: 'Document', objectId: 'doc_456', permission: 'update'});
// Revoke all permissions for a user on a resourceawait client.permissions.revokeAll({ subjectId: 'user_123', objectType: 'Document', objectId: 'doc_456'});
Permission Delegation
Allow users to delegate their permissions:
// User delegates their edit permission to another userawait client.permissions.delegate({ fromUserId: 'user_123', toUserId: 'user_456', objectType: 'Document', objectId: 'doc_789', permission: 'update', expiresAt: new Date('2024-06-30')});
Advanced Patterns
Permission Templates
Create reusable permission sets:
{ "permissionTemplates": { "documentCollaborator": { "permissions": ["read", "comment"], "fieldPermissions": { "content": ["read", "suggest"] } }, "projectManager": { "permissions": ["read", "update", "invite"], "customOperations": ["archive", "export"] } }}
Apply templates to users:
await client.permissions.applyTemplate({ userId: 'user_123', template: 'documentCollaborator', resourceType: 'Document', resourceId: 'doc_456'});
Conditional Permissions
Implement complex business logic:
{ "name": "ExpenseReport", "permissions": { "approve": ["manager_above_amount", "finance_any_amount"] }, "customRoles": { "manager_above_amount": { "condition": "currentUser.role == 'manager' && this.amount > currentUser.approvalLimit" }, "finance_any_amount": { "condition": "currentUser.department == 'finance'" } }}
Audit Trail
Track permission changes:
// Enable permission auditingawait client.permissions.enableAuditing({ entity: 'Document', trackChanges: true, retentionDays: 365});
// View permission historyconst auditLog = await client.permissions.getAuditLog({ resourceType: 'Document', resourceId: 'doc_123', fromDate: new Date('2024-01-01')});
console.log(auditLog); // Array of permission changes
Testing Permissions
Permission Testing Framework
// Test suite for document permissionsdescribe('Document Permissions', () => { test('owner can update document', async () => { const doc = await createTestDocument({ authorId: 'user_123' });
const canUpdate = await client.permissions.check({ userId: 'user_123', action: 'update', resource: 'Document', resourceId: doc.id });
expect(canUpdate).toBe(true); });
test('non-owner cannot delete document', async () => { const doc = await createTestDocument({ authorId: 'user_123' });
const canDelete = await client.permissions.check({ userId: 'user_456', action: 'delete', resource: 'Document', resourceId: doc.id });
expect(canDelete).toBe(false); });});
Permission Simulation
Test permissions without affecting real data:
// Simulate user contextconst permissions = await client.permissions.simulate({ userId: 'user_123', context: { role: 'manager', department: 'engineering', clearanceLevel: 3 }, checks: [ { action: 'read', resource: 'Document', resourceId: 'doc_123' }, { action: 'approve', resource: 'ExpenseReport', resourceId: 'exp_456' } ]});
console.log(permissions); // Results of permission checks
Performance Optimization
Permission Caching
const client = new LumenizeClient({ projectId: 'proj_abc123', apiKey: 'lum_live_xyz789', permissions: { cache: { enabled: true, ttl: 300, // 5 minutes maxSize: 1000 } }});
// Permissions are cached automaticallyconst canEdit = await client.permissions.check({ action: 'update', resource: 'Document', resourceId: 'doc_123'});
Batch Permission Checks
// Check permissions for multiple resources efficientlyconst documentIds = ['doc_1', 'doc_2', 'doc_3'];const permissions = await client.permissions.checkBatch({ action: 'read', resource: 'Document', resourceIds: documentIds});
// Returns: { doc_1: true, doc_2: false, doc_3: true }
Advanced Use Cases
Rollup Reporting
Create temporary organizational groupings for analysis:
- Create node “High Performers”
- Add top teams as additional children
- Generate comparative reports
- Remove grouping when analysis complete
Peer Group Benchmarking
Enable competitive analysis without exposing raw data:
- Create challenge group with participating teams
- Teams see their percentile ranking within the group
- Individual team metrics remain private
- Temporary structure dissolves after challenge
Cross-Functional Projects
Model complex project relationships:
- Create project node
- Add team members from various departments as children
- Project manager gets
admin
on project node - Stakeholders get appropriate
read
/write
permissions
Implementation Benefits
- Flexibility: Model complex real-world relationships
- Security: Granular, inherited permissions
- Scalability: Efficient permission checking
- Auditability: Clear permission paths
- Maintainability: Change structure without rebuilding permissions
Next Steps
- Real-time Features - Add live permission updates
- Temporal Data - Track permission changes over time
- Examples - See access control in real applications