MATIH Platform is in active MVP development. Documentation reflects current implementation status.
3. Security & Multi-Tenancy
rbac
Role Hierarchy

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());
AttributeValue
Permissions* (all)
Parent rolesNone
Typical usersPlatform operators, SRE team
ScopePlatform-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());
AttributeValue
Permissions8 permissions covering users, settings, reports, audit
Parent rolesNone (but conceptually a subset of super_admin)
Typical usersTenant administrators, team leads
ScopeSingle 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());
AttributeValue
Permissions6 permissions covering data, pipelines, reports
Parent rolesNone
Typical usersData engineers, pipeline operators
ScopeSingle 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());
AttributeValue
Permissions6 permissions covering data read, queries, reports
Parent rolesNone
Typical usersBusiness analysts, data scientists
ScopeSingle 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());
AttributeValue
Permissions2 permissions (read-only)
Parent rolesNone
Typical usersExecutives, external stakeholders
ScopeSingle tenant

Permission Comparison Matrix

Permissionsuper_admintenant_adminoperatoranalystviewer
* (all)Yes----
users:readYesYes---
users:writeYesYes---
users:deleteYesYes---
settings:readYesYes---
settings:writeYesYes---
data:readYes-YesYesYes
data:writeYes-Yes--
queries:readYes--Yes-
queries:writeYes--Yes-
queries:executeYes--Yes-
pipelines:readYes-Yes--
pipelines:writeYes-Yes--
pipelines:executeYes-Yes--
reports:readYesYesYesYesYes
reports:writeYesYes-Yes-
audit:readYesYes---

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: false

Role 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:

PatternExampleDescription
{function}_{level}data_adminFunction with access level
{team}_{function}engineering_operatorTeam-scoped role
{department}_{access}finance_viewerDepartment-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