Role Hierarchy
The MATIH Platform implements a hierarchical role model where roles can inherit permissions from parent roles. This creates a flexible structure where complex permission sets can be composed from simpler building blocks. This page documents the built-in role hierarchy, how to create custom roles, and the inheritance resolution algorithm.
Built-in Role Hierarchy
The platform provides five standard roles arranged in a conceptual hierarchy of increasing privilege:
super_admin (wildcard: *)
|
+-- tenant_admin (user management, settings, reports, audit)
| |
| +-- operator (data read/write, pipelines, reports read)
| |
| +-- analyst (data read, queries, reports read/write)
| |
| +-- viewer (data read, reports read)Role Details
super_admin
The super admin role has the wildcard permission *, which grants access to all resources and actions across the entire platform. This role is typically assigned to platform operators who manage the MATIH infrastructure.
registerRole("super_admin", Set.of("*"), Set.of());| Attribute | Value |
|---|---|
| Permissions | * (all) |
| Parent roles | None |
| Typical users | Platform operators, SRE team |
| Scope | Platform-wide |
tenant_admin
The tenant admin role provides full administrative control within a single tenant. It does not include data manipulation or query execution permissions, focusing instead on user and tenant management.
registerRole("tenant_admin", Set.of(
"users:read", "users:write", "users:delete",
"settings:read", "settings:write",
"reports:read", "reports:write",
"audit:read"
), Set.of());| Attribute | Value |
|---|---|
| Permissions | 8 permissions covering users, settings, reports, audit |
| Parent roles | None (but conceptually a subset of super_admin) |
| Typical users | Tenant administrators, team leads |
| Scope | Single tenant |
operator
The operator role is designed for data engineers and pipeline operators who build and manage data workflows.
registerRole("operator", Set.of(
"data:read", "data:write",
"pipelines:read", "pipelines:write", "pipelines:execute",
"reports:read"
), Set.of());| Attribute | Value |
|---|---|
| Permissions | 6 permissions covering data, pipelines, reports |
| Parent roles | None |
| Typical users | Data engineers, pipeline operators |
| Scope | Single tenant |
analyst
The analyst role is designed for business analysts and data scientists who query data and create reports but do not manage data or pipelines.
registerRole("analyst", Set.of(
"data:read",
"queries:read", "queries:write", "queries:execute",
"reports:read", "reports:write"
), Set.of());| Attribute | Value |
|---|---|
| Permissions | 6 permissions covering data read, queries, reports |
| Parent roles | None |
| Typical users | Business analysts, data scientists |
| Scope | Single tenant |
viewer
The viewer role provides minimum read-only access. It is suitable for stakeholders who need to view dashboards and reports but not create or modify them.
registerRole("viewer", Set.of(
"data:read",
"reports:read"
), Set.of());| Attribute | Value |
|---|---|
| Permissions | 2 permissions (read-only) |
| Parent roles | None |
| Typical users | Executives, external stakeholders |
| Scope | Single tenant |
Permission Comparison Matrix
| Permission | super_admin | tenant_admin | operator | analyst | viewer |
|---|---|---|---|---|---|
* (all) | Yes | - | - | - | - |
users:read | Yes | Yes | - | - | - |
users:write | Yes | Yes | - | - | - |
users:delete | Yes | Yes | - | - | - |
settings:read | Yes | Yes | - | - | - |
settings:write | Yes | Yes | - | - | - |
data:read | Yes | - | Yes | Yes | Yes |
data:write | Yes | - | Yes | - | - |
queries:read | Yes | - | - | Yes | - |
queries:write | Yes | - | - | Yes | - |
queries:execute | Yes | - | - | Yes | - |
pipelines:read | Yes | - | Yes | - | - |
pipelines:write | Yes | - | Yes | - | - |
pipelines:execute | Yes | - | Yes | - | - |
reports:read | Yes | Yes | Yes | Yes | Yes |
reports:write | Yes | Yes | - | Yes | - |
audit:read | Yes | Yes | - | - | - |
Custom Roles
Tenant administrators can create custom roles tailored to their organization's needs. Custom roles support full inheritance from both built-in and other custom roles.
Creating a Custom Role
Custom roles are registered using the same API as built-in roles:
// A custom "data_steward" role that inherits from analyst and adds audit access
rbacService.registerRole("data_steward",
Set.of(
"data:write",
"audit:read",
"data_quality:read", "data_quality:write"
),
Set.of("analyst") // Inherits all analyst permissions
);This data_steward role would have the following effective permissions:
data:write(own permission)audit:read(own permission)data_quality:read(own permission)data_quality:write(own permission)data:read(inherited from analyst)queries:read(inherited from analyst)queries:write(inherited from analyst)queries:execute(inherited from analyst)reports:read(inherited from analyst)reports:write(inherited from analyst)
Inheritance Resolution
The permission collection algorithm recursively walks the parent role chain:
// From RbacService.java
private void collectPermissions(String roleName, Set<String> permissions, Set<String> visited) {
if (visited.contains(roleName)) {
return; // Prevent infinite loops
}
visited.add(roleName);
Role role = roles.get(roleName);
if (role != null) {
permissions.addAll(role.permissions());
for (String parent : role.parentRoles()) {
collectPermissions(parent, permissions, visited);
}
}
}The visited set prevents infinite loops from circular inheritance (e.g., if role A inherits from role B which inherits from role A).
Multi-level Inheritance
Roles can form deep inheritance chains:
// Base role
rbacService.registerRole("data_reader", Set.of("data:read"), Set.of());
// Intermediate role inheriting from data_reader
rbacService.registerRole("data_analyst",
Set.of("queries:read", "queries:execute"),
Set.of("data_reader")
);
// Advanced role inheriting from data_analyst
rbacService.registerRole("senior_analyst",
Set.of("reports:write", "queries:write"),
Set.of("data_analyst")
);The senior_analyst effective permissions would be:
reports:write,queries:write(own)queries:read,queries:execute(from data_analyst)data:read(from data_reader, via data_analyst)
Multiple Inheritance
A role can inherit from multiple parent roles:
rbacService.registerRole("team_lead",
Set.of("users:read"),
Set.of("operator", "analyst") // Inherits from both
);When a role inherits from multiple parents, all permissions from all parents are combined (union).
Role Assignment
Assigning Roles to Users
// Assign a set of roles
rbacService.assignRoles("user-123", Set.of("analyst", "data_steward"));
// Add a single role
rbacService.addRole("user-123", "operator");
// Remove a single role
rbacService.removeRole("user-123", "operator");Querying User Roles
// Get assigned roles
Set<String> roles = rbacService.getUserRoles("user-123");
// Returns: {"analyst", "data_steward"}
// Check role assignment
boolean isAdmin = rbacService.hasRole("user-123", "tenant_admin");
// Returns: false
// Check any role
boolean canManage = rbacService.hasAnyRole("user-123", "tenant_admin", "super_admin");
// Returns: falseRole Management Best Practices
Principle of Least Privilege
Always assign the minimum set of roles needed for a user to perform their job. Prefer specific custom roles over broad built-in roles.
Role Naming Convention
Custom roles should follow a consistent naming pattern:
| Pattern | Example | Description |
|---|---|---|
{function}_{level} | data_admin | Function with access level |
{team}_{function} | engineering_operator | Team-scoped role |
{department}_{access} | finance_viewer | Department-scoped role |
Avoid Deep Hierarchies
Keep inheritance chains to a maximum of 3 levels. Deep hierarchies are harder to audit and can lead to unexpected permission accumulation.
Regular Audits
Use the audit log to review permission usage patterns. Roles with many unused permissions should be split into more granular roles.
Related Pages
- RBAC Model -- Overview of the RBAC system
- Permission Types -- Resource, action, and scope permissions
- OPA Policies -- OPA integration for dynamic policies