Role Hierarchy
CORTEX supports hierarchical roles where child roles inherit permissions from their parent roles.
How Inheritance Works
TENANT_ADMIN
├── users:create ✓
├── users:read ✓
├── users:update ✓
├── users:delete ✓
└── inherits from: ORG_ADMIN
├── organizations:read ✓
├── organizations:update ✓
└── inherits from: MEMBER
├── profile:read ✓
├── profile:update ✓
└── inherits from: VIEWER
└── dashboard:read ✓
A user with TENANT_ADMIN role has all permissions from TENANT_ADMIN, ORG_ADMIN, MEMBER, and VIEWER.
Get Role Hierarchy
Endpoint
GET /roles/:id/hierarchy
Response (200 OK)
{
"role": {
"id": "tenant-admin-id",
"name": "TENANT_ADMIN",
"scopeLevel": "TENANT"
},
"depth": 0,
"children": [
{
"role": {
"id": "org-admin-id",
"name": "ORG_ADMIN",
"scopeLevel": "ORGANIZATION"
},
"depth": 1,
"children": [
{
"role": {
"id": "member-id",
"name": "MEMBER",
"scopeLevel": "ORGANIZATION"
},
"depth": 2,
"children": [
{
"role": {
"id": "viewer-id",
"name": "VIEWER",
"scopeLevel": "ORGANIZATION"
},
"depth": 3,
"children": []
}
]
}
]
}
]
}
Setting Up Inheritance
When creating a role, specify the parentId to inherit from:
Request
{
"name": "SENIOR_DEVELOPER",
"description": "Senior developer with additional permissions",
"scopeLevel": "ORGANIZATION",
"parentId": "member-role-id"
}
The new role inherits all permissions from MEMBER and its ancestors.
Effective Permissions
To get all permissions a user has (including inherited):
Endpoint
GET /users/:id/effective-permissions
Response (200 OK)
{
"permissions": [
{ "resource": "users", "action": "create", "source": "TENANT_ADMIN" },
{ "resource": "users", "action": "read", "source": "TENANT_ADMIN" },
{ "resource": "organizations", "action": "read", "source": "ORG_ADMIN" },
{ "resource": "profile", "action": "read", "source": "MEMBER" },
{ "resource": "dashboard", "action": "read", "source": "VIEWER" }
]
}
The source field indicates which role granted each permission.
Hierarchy Rules
1. No Circular References
A role cannot be its own ancestor:
ROLE_A → ROLE_B → ROLE_A ❌ Not allowed
2. Maximum Depth
Hierarchy is limited to 5 levels by default:
Level 0: TENANT_ADMIN
Level 1: ORG_ADMIN
Level 2: MANAGER
Level 3: MEMBER
Level 4: VIEWER ← Maximum depth
3. Scope Level Consistency
Child roles must have the same or narrower scope:
TENANT scope → ORGANIZATION scope ✓ Valid
ORGANIZATION scope → TENANT scope ❌ Invalid
Modifying Hierarchy
Change Parent Role
PATCH /roles/:id
{
"parentId": "new-parent-role-id"
}
caution
Changing a parent role affects all users with that role. Their effective permissions will change immediately.
Remove Inheritance
Set parentId to null:
{
"parentId": null
}
Visualization
Building a Role Tree
interface RoleNode {
role: {
id: string;
name: string;
scopeLevel: string;
};
depth: number;
children: RoleNode[];
}
function renderRoleTree(node: RoleNode, indent = 0): string {
const prefix = ' '.repeat(indent) + (indent > 0 ? '└── ' : '');
let output = `${prefix}${node.role.name}\n`;
node.children.forEach(child => {
output += renderRoleTree(child, indent + 1);
});
return output;
}
// Output:
// TENANT_ADMIN
// └── ORG_ADMIN
// └── MEMBER
// └── VIEWER
Code Examples
TypeScript
interface RoleHierarchy {
role: {
id: string;
name: string;
scopeLevel: string;
};
depth: number;
children: RoleHierarchy[];
}
async function getRoleHierarchy(
accessToken: string,
roleId: string
): Promise<RoleHierarchy> {
const response = await fetch(
`http://localhost:8091/roles/${roleId}/hierarchy`,
{
headers: { 'Authorization': `Bearer ${accessToken}` },
}
);
return response.json();
}
// Flatten hierarchy to list all descendants
function flattenHierarchy(node: RoleHierarchy): string[] {
const roles = [node.role.name];
for (const child of node.children) {
roles.push(...flattenHierarchy(child));
}
return roles;
}
cURL
# Get role hierarchy
curl http://localhost:8091/roles/tenant-admin-id/hierarchy \
-H "Authorization: Bearer <access-token>"
# Create role with parent
curl -X POST http://localhost:8091/roles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"name": "SENIOR_DEV",
"description": "Senior developer",
"scopeLevel": "ORGANIZATION",
"parentId": "member-role-id"
}'