Role-based access control
Kessel’s authorization system is built on role-based access control (RBAC) — users get permissions through roles, and roles are assigned to users via role bindings. This document explains the core RBAC concepts: permissions, roles, role bindings, groups, and how authorization decisions are evaluated.
The RBAC Model
Section titled “The RBAC Model”At its core, a role binding answers the question: “Who has what permissions on which resource?”
Subject (who) + Role (what permissions) + Resource (where) = Role Binding- Subject — A group or individual principal (user/service account)
- Role — A collection of permissions
- Resource — The target object (workspace, tenant, or platform)
- Role Binding — The link that grants a role’s permissions to a subject on a specific resource
Permissions
Section titled “Permissions”A permission is an action that can be performed on a specific type of resource. Permissions use the format:
application:resource_type:operationExamples:
inventory:hosts:read— Read hosts in the inventoryadvisor:recommendation_results:write— Write recommendation resultsrbac:workspaces:role_binding_grant— Grant role bindings on workspaces
Wildcards are supported:
inventory:*:*— All inventory permissionsinventory:hosts:*— All operations on inventory hostsinventory:*:read— Read all inventory resource types
Wildcards make it easier to grant broad permissions without listing every individual permission.
Permission Scope
Section titled “Permission Scope”Permissions fall into two categories based on their scope:
Data-level permissions — Apply to resources within workspaces, respecting the workspace hierarchy:
inventory:hosts:readadvisor:recommendation_results:*
These permissions are evaluated using the resource’s workspace assignment. If you have inventory:hosts:read on the “Engineering” workspace, you can read hosts assigned to Engineering or any of its child workspaces.
Org-level permissions — Apply organization-wide, regardless of workspace:
notifications:notifications:readrbac:workspaces:create
These permissions are bound to the tenant or platform resource rather than workspaces.
A role is a named collection of permissions. Instead of granting individual permissions to users, you grant roles that bundle related permissions together.
Example role:
{ "name": "Inventory Viewer", "permissions": [ "inventory:hosts:read", "inventory:groups:read", "inventory:staleness_counts:read" ]}Role Types
Section titled “Role Types”Kessel has three types of roles:
| Type | Created By | Modifiable | Use Case |
|---|---|---|---|
| CUSTOM | Organization admins | Yes | Org-specific roles tailored to business needs |
| SEEDED | System (pre-defined) | No | Common roles like “Org Admin”, “Workspace Admin” |
| PLATFORM | System (internal) | No | Aggregate roles used for default bindings |
Custom roles are created by organization administrators to match their specific access control needs. They can be updated and deleted.
Seeded roles are provided by the system with predefined permissions. They cannot be modified but can be assigned to users. Examples include “Organization Admin” and “Workspace Viewer”.
Platform roles are internal roles that aggregate multiple seeded roles. They are used for default role bindings and are not directly visible in the API.
How Roles Work
Section titled “How Roles Work”Custom roles store each permission as a relationship in the authorization graph. When a user receives a role binding to that role, they inherit all the role’s permissions.
Seeded roles have permissions defined directly in the authorization schema rather than stored individually. This makes system-provided roles like “Organization Admin” and “Workspace Viewer” more efficient since they’re used by many users.
Role Bindings
Section titled “Role Bindings”A role binding connects a role to a subject (group or principal) on a specific resource (workspace or tenant). This is the mechanism that actually grants permissions.
Role binding structure:
role— The role being granted (UUID reference)subject— The group or principal receiving the roleresource— The workspace or tenant where the role appliesresource_type— Either"workspace"or"tenant"resource_id— The UUID of the workspace or tenant
Example:
{ "role": "inventory-viewer-role-uuid", "subject": { "id": "engineering-group-uuid", "type": "group" }, "resource": { "id": "engineering-workspace-uuid", "type": "workspace" }}This grants the “Inventory Viewer” role to the “Engineering” group on the “Engineering” workspace.
Subject Types
Section titled “Subject Types”Role bindings can target two types of subjects:
Groups (recommended) — Role bindings typically target groups rather than individual users. This makes it easier to manage access for teams:
- Add/remove users from the group to grant/revoke access
- One role binding serves all group members
- Easier to audit and maintain
Principals (direct user bindings) — Role bindings can also target individual users or service accounts directly, though this is less common:
- Use for exceptional cases where a specific user needs unique access
- Harder to audit and maintain at scale
How Role Bindings Create Relationships
Section titled “How Role Bindings Create Relationships”A role binding creates three types of relationships in the authorization graph:
- Binding → Role: Links the binding to the role it grants
- Resource → Binding: Links the workspace or tenant to the binding
- Binding → Subject: Links the binding to the users or groups who receive it
These relationships form a graph that Kessel traverses to evaluate permission checks.
Visualizing the Relationship Graph
Section titled “Visualizing the Relationship Graph”Here’s how the components connect when evaluating a permission check:
graph TD
User["👤 alice@redhat"]
Group["👥 Engineering Group"]
Binding["🔗 Role Binding"]
Role["📋 Inventory Viewer Role"]
Workspace["📁 Engineering Workspace"]
Permission["✓ inventory:hosts:read"]
User -->|member of| Group
Group -->|subject of| Binding
Binding -->|grants| Role
Binding -->|applies to| Workspace
Role -->|includes| Permission
classDef userNode fill:#e1f5ff,stroke:#0066cc,stroke-width:2px
classDef groupNode fill:#fff4e1,stroke:#cc9900,stroke-width:2px
classDef bindingNode fill:#ffe1f5,stroke:#cc0099,stroke-width:2px
classDef roleNode fill:#e1ffe1,stroke:#009900,stroke-width:2px
classDef workspaceNode fill:#f5e1ff,stroke:#9900cc,stroke-width:2px
classDef permNode fill:#ffe1e1,stroke:#cc0000,stroke-width:2px
class User userNode
class Group groupNode
class Binding bindingNode
class Role roleNode
class Workspace workspaceNode
class Permission permNode
When a permission check asks “Can Alice read hosts in Engineering?”, Kessel:
- Finds Alice’s group memberships → Engineering Group
- Finds role bindings where Engineering Group is the subject → Role Binding
- Checks if the binding’s role grants
inventory:hosts:read→ Yes - Confirms the binding applies to the Engineering workspace → Yes
- Returns: Allowed
Groups
Section titled “Groups”A group is a collection of principals. Groups make it easier to manage access for teams or organizational units.
Group membership:
- A principal can belong to multiple groups
- A group can contain multiple principals
- Membership is many-to-many
Special groups:
- Platform default group — All users in the organization automatically belong to this group
- Admin default group — Organization admins automatically belong to this group
When a role binding targets a group, all members of that group receive the role’s permissions. If a user is added to the group later, they automatically gain access. If they’re removed, access is revoked.
The authorization graph tracks group memberships as relationships between groups and principals. When checking permissions, the system follows these membership relationships to determine if a user is a subject of a group-targeted role binding.
Permission Evaluation
Section titled “Permission Evaluation”When a client calls Check to verify if a user can perform an action on a resource, here’s how Kessel evaluates the request:
Step 1: Find the resource’s workspace
If the resource is an inventory host, Kessel finds which workspace it belongs to by looking up its workspace assignment.
Step 2: Walk up the workspace hierarchy
Starting from the resource’s workspace, the authorization system checks:
- Are there any role bindings on this workspace?
- For each binding, is the user a subject (directly or via group membership)?
- Does the binding’s role grant the requested permission?
If no match is found, the system moves to the parent workspace and repeats. This continues up to the ROOT workspace.
Step 3: Check tenant/platform bindings
If the permission is org-level (not workspace-scoped), the system checks role bindings on the tenant or platform resource instead.
Result:
Permission is granted if both conditions are true:
- The user is a subject of a role binding (directly or via group)
- The binding’s role includes the requested permission
Permission Inheritance
Section titled “Permission Inheritance”Role bindings on parent workspaces automatically grant permissions on child workspaces. This is a powerful feature of the hierarchy:
Example:
If Alice has the “Workspace Admin” role on the “Engineering” workspace, she automatically has those permissions on:
- All child workspaces (“Frontend Team”, “Backend Team”)
- All resources assigned to those workspaces
Why this matters:
You can grant broad access at a high level (ROOT workspace) or narrow access at lower levels (specific team workspaces). Users higher in the hierarchy have more permissions.
Combining direct and inherited bindings:
A user can have multiple role bindings on different workspaces in the hierarchy. The authorization system evaluates all of them and grants access if any binding provides the required permission.
Consistency and Replication
Section titled “Consistency and Replication”Role bindings are stored in two places:
- RBAC service database — The source of truth for role binding metadata
- Authorization graph — The authorization engine that evaluates permission checks
When you create or modify a role binding via the RBAC API, the change is replicated to the authorization backend using an outbox pattern:
- The RBAC service writes the binding to its database and publishes an outbox event
- The event stream captures the event and routes it to Kessel Relations
- Relations writes the corresponding tuples to the authorization graph
- Permission checks reflect the new binding once the CDC pipeline completes
This process typically completes in 100-500ms. See the Consistency Model documentation for details on consistency guarantees.