Skip to main content

RBAC: The Three-Tier Hierarchy

RBAC stands for Role-Based Access Control. It is the system that decides what an authenticated user is allowed to do. The Authentication section answered "who are you?", RBAC answers "what can you do?"

Dashify's RBAC is built on a clear three tier hierarchy that maps directly to how SaaS platforms are operated in real life. This page walks through every tier, every check, and the 19-point shipping checklist that every new feature has to pass.

The three tiers

SuperAdmin, operates the platform

The SuperAdmin is the person running Dashify itself. They:

  • Provision and configure tenants.
  • Manage subscription packages (which menus / features each package includes).
  • View global health and metrics.
  • Investigate cross tenant audit logs.

There is typically one SuperAdmin (you, if you adopt Dashify). The role exists outside any specific tenant, it is the operator's view of the whole platform.

Org Admin, runs one tenant

Each tenant has one or more Org Admins. They:

  • Manage users within their tenant.
  • Configure tenant-specific settings (SSO, AI, notification policies).
  • Set up integrations (SCIM, API tokens).
  • View their tenant's audit log.

An Org Admin cannot see other tenants and cannot modify packages, that is SuperAdmin territory.

User, does the work

Regular users are the largest group. They use the features their package and their role grant them: project boards, knowledge base, chat, files, AI assistant. They cannot manage other users or change tenant settings.

Within "User" there can be multiple roles, Standard User, Project Manager, Read-Only, depending on the tenant's setup. The Org Admin defines which roles exist and what they include.

The three checks before any action

Before a single privileged endpoint runs, three checks must pass in order:

Check 1, Authentication. The session is verified, the user is loaded. We covered this in the Auth section.

Check 2, Package gating. Every feature belongs to a menu in the menu registry. Every package declares which menus are included. If the user's tenant is on a package that does not include this menu, the request is denied with a 403 and the client redirects to /upgrade?reason=<menuKey>. The upgrade page shows what they would unlock by switching plans.

Check 3, Role permission. The route handler declares the permission it requires (e.g. pm.workitem.delete). The middleware checks whether the user's role includes that permission. If not, 403.

All three checks pass before the handler runs. None of them can be bypassed.

What a permission looks like

Permissions are simple strings, organised by module:

  • pm.project.create, pm.project.delete
  • pm.workitem.create, pm.workitem.edit, pm.workitem.delete
  • users.invite, users.deactivate, users.assign_role
  • organization.manage_sso, organization.manage_api_tokens, organization.view_audit_log
  • kb.create, kb.edit, kb.publish
  • ai.use, ai.configure

A role is a list of permissions. The default roles (User, Manager, Org Admin) have predefined permission lists; tenants can also create custom roles with custom lists.

Permission elevation defence

There is one important defence to call out. When an Org Admin edits a user's role, they can only assign permissions they themselves have. If the admin does not have organization.view_audit_log, they cannot create a role that grants it. The role-edit endpoint rejects the request server side; the matrix UI also disables ungrantable rows on the client.

This stops a confused or malicious sub-admin from creating a role that elevates a user above the admin's own permissions.

The 19-point shipping checklist

Before any feature ships in Dashify, it has to pass a manual three tier audit. Nineteen specific checkpoints, grouped:

Tenant isolation, does the feature respect the automatic tenantId filter? Are there any cross tenant joins? Does it use the escape hatch correctly?

Package gating, does the feature have a menu key? Does the menu key live in the right packages? Does the upgrade page handle the relevant ?reason=?

Role / permission gating, is the new permission defined in the right module? Is it in the right default roles? Does the matrix UI render it? Does the route check it?

Org Admin management surface, can the Org Admin actually configure this feature for their tenant? Is the surface gated by an admin permission like organization.manage_X?

End-user flow, does a normal user see the feature when they should? Does the feature gracefully refuse when they should not?

Audit logging, is every state-changing endpoint audit-logged with a sensible event name?

Notifications, should the user be notified when this happens? If yes, is it implemented?

Real-time, should other connected sessions see this update live? If yes, is the Socket.IO event wired?

Tests, are there integration tests covering the happy path and at least one denial case?

The checklist is annoying. It is also why bugs in this area are rare. Every feature that ships passes it, and the platform's three tier story stays coherent.

Custom roles per tenant

By default, three roles exist in every tenant: Org Admin, Manager, User. The Org Admin can create additional custom roles from the role-management screen, picking permissions from a matrix.

The matrix groups permissions by module so admins are not staring at one giant flat list. Each row has a checkbox per role; toggling the checkbox grants/revokes the permission immediately. Rows the current admin cannot grant are disabled.

What changes when a permission is granted or revoked

Permissions are not re-checked from the database on every request. The role's permissions are cached briefly. When a role is changed, a capability version is bumped and every cached copy is invalidated transparently. The next request from a user with that role re-reads the permissions and sees the change.

This caching is what makes RBAC fast, a permission check is a memory lookup, not a database query.

Key takeaways

  • SuperAdmin to Org Admin to User is the single hierarchy that gates every privileged action.
  • Three checks before any handler: authenticated, package includes feature, role permits action.
  • A 19-point checklist must be passed before any feature ships.
  • Org Admins can create custom roles with custom permission lists, but cannot grant permissions they do not have.
  • A capability-version cache makes permission checks fast without losing freshness.