Role Hierarchy
Understanding and implementing role hierarchies in Fire Shield.
What is Role Hierarchy?
Role hierarchy is a level-based system where higher-level roles can perform actions on behalf of lower-level roles. This creates a natural organizational structure that mirrors real-world authority relationships.
Key Concepts
Level-Based System:
- Each role is assigned a numeric level
- Higher numbers = more privileged
- Level determines if a role can "act as" another role
Use Cases:
- Organizational hierarchies (CEO > Manager > Employee)
- Permission delegation (Admin can act as any lower role)
- Resource access control (only higher roles can manage lower-role resources)
- Approval workflows (requires someone of higher level)
Setting Up Hierarchy
Basic Setup
typescript
import { RBAC } from '@fire-shield/core';
const rbac = new RBAC();
// Create roles
rbac.createRole('employee', ['document:read', 'document:create']);
rbac.createRole('manager', ['document:*', 'team:view']);
rbac.createRole('admin', ['*']);
// Set hierarchy levels
const hierarchy = rbac.getRoleHierarchy();
hierarchy.setRoleLevel('employee', 1);
hierarchy.setRoleLevel('manager', 5);
hierarchy.setRoleLevel('admin', 10);With RBAC Builder
typescript
import { RBACBuilder } from '@fire-shield/core';
const rbac = new RBACBuilder()
.addRole('employee', ['document:read'], { level: 1 })
.addRole('manager', ['document:*'], { level: 5 })
.addRole('admin', ['*'], { level: 10 })
.build();Checking Hierarchy
canActAs
Check if a role can act as another role:
typescript
// Can admin act as manager?
rbac.canActAsRole('admin', 'manager'); // true (10 >= 5)
// Can manager act as admin?
rbac.canActAsRole('manager', 'admin'); // false (5 < 10)
// Can manager act as employee?
rbac.canActAsRole('manager', 'employee'); // true (5 >= 1)Direct Hierarchy Access
typescript
const hierarchy = rbac.getRoleHierarchy();
// Check if role1 can act as role2
hierarchy.canActAs('admin', 'manager'); // true
// Check if role1 has higher level than role2
hierarchy.hasHigherLevel('admin', 'employee'); // true
// Get role level
const adminLevel = hierarchy.getRoleLevel('admin'); // 10Real-World Examples
Corporate Hierarchy
typescript
const rbac = new RBACBuilder()
// C-Level
.addRole('ceo', ['*'], {
level: 100,
description: 'Chief Executive Officer'
})
.addRole('cto', ['technology:*', 'team:*'], {
level: 90,
description: 'Chief Technology Officer'
})
// Management
.addRole('director', ['department:*', 'team:manage'], {
level: 50,
description: 'Department Director'
})
.addRole('manager', ['team:*', 'project:*'], {
level: 30,
description: 'Team Manager'
})
// Individual Contributors
.addRole('senior-engineer', ['project:*', 'code:*'], {
level: 20,
description: 'Senior Engineer'
})
.addRole('engineer', ['code:read', 'code:write'], {
level: 10,
description: 'Engineer'
})
.addRole('intern', ['code:read'], {
level: 1,
description: 'Intern'
})
.build();
// CEO can act as anyone
rbac.canActAsRole('ceo', 'intern'); // true
// Manager can act as engineers
rbac.canActAsRole('manager', 'engineer'); // true
// Engineer cannot act as manager
rbac.canActAsRole('engineer', 'manager'); // falsePermission Delegation
typescript
function canDelegatePermission(
delegator: RBACUser,
delegatee: RBACUser,
permission: string
): boolean {
// Check if delegator has the permission
if (!rbac.hasPermission(delegator, permission)) {
return false;
}
// Check if delegator can act as delegatee (higher level)
const delegatorRoles = delegator.roles;
const delegateeRoles = delegatee.roles;
for (const delegatorRole of delegatorRoles) {
for (const delegateeRole of delegateeRoles) {
if (rbac.canActAsRole(delegatorRole, delegateeRole)) {
return true;
}
}
}
return false;
}
// Usage
const admin = { id: 'admin-1', roles: ['admin'] };
const manager = { id: 'mgr-1', roles: ['manager'] };
canDelegatePermission(admin, manager, 'team:manage'); // trueResource Access Control
typescript
interface Document {
id: string;
ownerId: string;
ownerRole: string;
content: string;
}
function canAccessDocument(
user: RBACUser,
document: Document
): boolean {
// Owner can always access
if (user.id === document.ownerId) {
return true;
}
// Check if user's role is higher than document owner's role
for (const userRole of user.roles) {
const userLevel = rbac.getRoleHierarchy().getRoleLevel(userRole);
const ownerLevel = rbac.getRoleHierarchy().getRoleLevel(document.ownerRole);
if (userLevel > ownerLevel) {
return true;
}
}
return false;
}
// Usage
const manager = { id: 'mgr-1', roles: ['manager'] };
const employeeDoc = {
id: 'doc-1',
ownerId: 'emp-1',
ownerRole: 'employee',
content: 'Employee document'
};
canAccessDocument(manager, employeeDoc); // true (manager > employee)Approval Workflows
typescript
interface ApprovalRequest {
id: string;
requesterId: string;
requesterRole: string;
action: string;
status: 'pending' | 'approved' | 'rejected';
}
function canApproveRequest(
approver: RBACUser,
request: ApprovalRequest
): boolean {
// Must have approval permission
if (!rbac.hasPermission(approver, 'approval:grant')) {
return false;
}
// Approver must be higher level than requester
const hierarchy = rbac.getRoleHierarchy();
for (const approverRole of approver.roles) {
const approverLevel = hierarchy.getRoleLevel(approverRole);
const requesterLevel = hierarchy.getRoleLevel(request.requesterRole);
if (approverLevel > requesterLevel) {
return true;
}
}
return false;
}
// Usage
const manager = { id: 'mgr-1', roles: ['manager'] };
const employeeRequest: ApprovalRequest = {
id: 'req-1',
requesterId: 'emp-1',
requesterRole: 'employee',
action: 'vacation',
status: 'pending'
};
canApproveRequest(manager, employeeRequest); // trueAdvanced Patterns
Multi-Role Users
typescript
const user = {
id: 'user-1',
roles: ['engineer', 'team-lead'] // Multiple roles
};
// Check if user can act as any role
function canActAsAnyRole(user: RBACUser, targetRole: string): boolean {
return user.roles.some(role => rbac.canActAsRole(role, targetRole));
}
canActAsAnyRole(user, 'intern'); // true if either engineer or team-lead canCross-Department Hierarchy
typescript
const rbac = new RBACBuilder()
// Engineering
.addRole('engineering-director', ['engineering:*'], { level: 50 })
.addRole('engineering-manager', ['engineering:team:*'], { level: 30 })
.addRole('engineer', ['engineering:code:*'], { level: 10 })
// Sales
.addRole('sales-director', ['sales:*'], { level: 50 })
.addRole('sales-manager', ['sales:team:*'], { level: 30 })
.addRole('sales-rep', ['sales:deal:*'], { level: 10 })
// Executive (above all departments)
.addRole('ceo', ['*'], { level: 100 })
.build();
// CEO can act as any role
rbac.canActAsRole('ceo', 'engineering-director'); // true
rbac.canActAsRole('ceo', 'sales-director'); // true
// Directors cannot cross departments
rbac.canActAsRole('engineering-director', 'sales-director'); // false (same level)Dynamic Level Assignment
typescript
function assignRoleLevel(role: string, departmentSize: number): number {
const baseLevel = {
'ceo': 100,
'director': 50,
'manager': 30,
'lead': 20,
'member': 10
};
// Adjust level based on department size
const sizeMultiplier = Math.log10(departmentSize);
return Math.floor(baseLevel[role] * (1 + sizeMultiplier * 0.1));
}
// Large department managers get slightly higher level
const largeTeamManager = assignRoleLevel('manager', 100); // ~33
const smallTeamManager = assignRoleLevel('manager', 10); // ~30Best Practices
1. Use Meaningful Levels
typescript
// ✅ Good: Clear hierarchy
.addRole('ceo', ['*'], { level: 100 })
.addRole('director', ['department:*'], { level: 50 })
.addRole('manager', ['team:*'], { level: 30 })
.addRole('employee', ['task:*'], { level: 10 })
// ❌ Avoid: Random numbers
.addRole('admin', ['*'], { level: 42 })
.addRole('user', ['read'], { level: 7 })2. Leave Room for Growth
typescript
// ✅ Good: Spacing allows insertions
.addRole('ceo', ['*'], { level: 100 })
.addRole('vp', ['division:*'], { level: 80 })
.addRole('director', ['department:*'], { level: 60 })
.addRole('manager', ['team:*'], { level: 40 })
// Now you can add 'senior-manager' at level 503. Document Hierarchy
typescript
// ✅ Good: Clear descriptions
.addRole('admin', ['*'], {
level: 100,
description: 'System administrator - can manage all resources',
metadata: {
canDelegate: true,
maxDelegationLevel: 90
}
})4. Validate Hierarchy
typescript
// Validate hierarchy on startup
function validateHierarchy(rbac: RBAC) {
const hierarchy = rbac.getRoleHierarchy();
const roles = hierarchy.getAllRoles();
for (const role of roles) {
const level = hierarchy.getRoleLevel(role);
console.log(`${role}: level ${level}`);
if (level === 0) {
console.warn(`⚠️ Role ${role} has no level set`);
}
}
}Common Pitfalls
1. Circular Dependencies
typescript
// ❌ Avoid: Same levels can cause issues
rbac.getRoleHierarchy().setRoleLevel('admin', 10);
rbac.getRoleHierarchy().setRoleLevel('manager', 10);
// Who can act as who?
rbac.canActAsRole('admin', 'manager'); // false (10 >= 10 is false)2. Forgetting to Set Levels
typescript
// ❌ Avoid: Roles without levels
rbac.createRole('admin', ['*']); // No level set!
// ✅ Good: Always set levels
rbac.createRole('admin', ['*']);
rbac.getRoleHierarchy().setRoleLevel('admin', 10);Next Steps
- Learn about Permissions
- Explore Roles
- Check out Examples
