Top 5 RBAC Examples In the Real World
RBAC, is one of the most widely used authorization models in software systems like finance, HR, CRMs, etc. Instead of attaching permissions directly to users, RBAC assigns permissions to roles, and users assume those roles. This indirection simplifies access management and keeps authorization consistent as applications scale.
Common role examples include:
For example, in a project management tool like Jira or Asana, you might define a project_manager
role with permissions to create tasks, assign work, and close projects. Team members take the contributor
role, which only allows creating and updating tasks. A client user might be assigned a viewer
role, restricted to reading project status but not editing it. By modeling access this way, you don’t manage permissions for every individual user; you manage roles and apply them across the system.
The importance of RBAC becomes clear in multi-tenant and collaborative systems like project management tools, customer support platforms, or SaaS CRMs. In a customer support application, one user could be a support_manager
with permissions to assign tickets and update workflows, while another is a support_agent
limited to responding to tickets. An account_admin
might configure organization-wide settings, and a billing_viewer
could access invoices without the ability to change subscription details.
The problem is that RBAC often becomes hard to maintain over time. Teams usually begin with two roles, admin and user, but soon add exceptions like read_only_admin or limited_user, leading to what is known as role explosion. Permissions end up scattered across the codebase, and developers can no longer confidently answer, Who has access to what?
In this post, we will examine RBAC examples, hierarchical roles, resource-scoped roles, action-constrained roles, and hybrid models that address these challenges and keep role systems manageable.
RBAC (Role-Based Access Control) is best understood not through theory but by looking at how it works in real-world scenarios. Instead of abstract policies or technical rules, it is the practical use cases that show how roles and permissions are assigned to manage access effectively.
Before moving forward, let’s understand two important terms often used in RBAC implementations: Polar and Rego.
Polar is the declarative policy language used by Oso, an open-source authorization framework. It allows developers to define access control logic in a structured and readable way. With Polar, you can express who can perform what action on which resource using simple rules. It integrates directly into application code, making authorization decisions consistent and easy to maintain.
Rego, on the other hand, is the policy language used by Open Policy Agent (OPA). It’s a more general-purpose language that can enforce policies across systems, like APIs, CI/CD pipelines, Kubernetes clusters, or microservices. Rego policies define rules for evaluating requests and returning authorization decisions, making it suitable for large-scale, distributed environments.
Looking at such examples helps clarify how permissions, roles, and responsibilities translate into practice. So let’s start one-by-one:
Healthcare applications need a clear separation of responsibilities between medical, financial, and administrative staff. Doctors update patient records, nurses maintain vitals and medication details, receptionists handle appointments, and administrators manage system settings.
In the healthcare sector, organizations like Tamr have used Oso's authorization platform to secure regulated, high-sensitivity data in the cloud. By implementing fine-grained access controls, Tamr enables users to access only the data pertinent to their roles, thereby reducing the risk of unauthorized access and ensuring compliance with healthcare regulations.
Similarly, Kaleidoscope, a biotech R&D platform, utilizes Oso to manage access to sensitive research data. Their platform allows lab and computational teams to plan and track scientific work, integrating data and streamlining collaboration, all while maintaining strict access controls to protect intellectual property and comply with industry standards.
These examples demonstrate how RBAC, when implemented effectively, can protect sensitive healthcare data, ensure compliance with regulations, and streamline workflows by providing appropriate access to users based on their roles.
A common implementation is role–permission mapping in the database. When a user calls an API endpoint, the system validates whether the role tied to that user includes the requested permission.
With Oso Polar policies, we can define these roles and their permissions directly in code. Instead of relying on SQL queries to validate access, the policy itself enforces which actions each role can perform.
actor User {}
resource HealthcareSystem {
roles = ["doctor", "nurse", "receptionist", "administrator"];
permissions = [
"view_patient_record", "update_patient_record", "prescribe_medication",
"update_vitals", "administer_treatment",
"schedule_appointment", "manage_patient_intake",
"manage_users", "audit_logs", "system_settings"
];
# doctors
"view_patient_record" if "doctor";
"update_patient_record" if "doctor";
"prescribe_medication" if "doctor";
# nurses
"update_vitals" if "nurse";
"administer_treatment" if "nurse";
# receptionists
"schedule_appointment" if "receptionist";
"manage_patient_intake" if "receptionist";
# admins
"manage_users" if "administrator";
"audit_logs" if "administrator";
"system_settings" if "administrator";
}
When an API call is made, Oso evaluates whether the requesting user has the right role to perform that action. For instance, a receptionist cannot update patient records because the "update_patient_record"
permission is never linked to their role. This ensures least-privilege access without manually writing SQL checks.
Banking applications require strict separation of duties to prevent fraud and ensure compliance with financial regulations. Customers, tellers, managers, and auditors operate in the same system but must have carefully bounded access.
For example, customers should never query or update another user’s account, while tellers can process transactions but not authorize unusually large ones. Managers handle escalations and approvals, and auditors must review immutable records without the ability to alter them.
Here’s an example of what roles and permissions might look like in a typical banking application. This table illustrates how different roles are defined to control access to sensitive financial operations and ensure secure handling of customer data:
We’ll implement this RBAC in Polar using Oso to define these roles and their permissions directly in code instead of relying on row-level database security. Consider this policy:
actor User {}
resource BankSystem {
roles = ["customer", "teller", "manager", "auditor"];
permissions = [
"view_balance", "transfer_funds", "download_statements",
"process_transaction", "verify_identity",
"approve_large_transaction", "manage_tellers",
"view_audit_logs"
];
# customers
"view_balance" if "customer";
"transfer_funds" if "customer";
"download_statements" if "customer";
# tellers
"process_transaction" if "teller";
"verify_identity" if "teller";
# managers
"approve_large_transaction" if "manager";
"manage_tellers" if "manager";
# auditors
"view_audit_logs" if "auditor";
}
In this setup, customers can view their balance, transfer funds, and download account statements within their own accounts. Tellers handle deposits, withdrawals, and customer verification but cannot approve high-value transactions. Managers have the authority to approve those transactions and supervise teller activities, adding a layer of oversight. Auditors maintain read-only access to financial logs for compliance checks without the ability to modify records.
Instead of scattering access rules across SQL queries, Oso centralizes these permissions in the policy. For example, when a teller attempts to approve a large transaction, Oso will deny the request because "approve_large_transaction"
is only mapped to managers. This makes access decisions clear, auditable, and consistent across the entire banking system.
Cloud environments demand strict access segmentation because misconfigured permissions are one of the most common causes of breaches. In AWS, Identity and Access Management (IAM) enforces access through roles and policies.
Similarly, with Oso Polar, we can represent cloud personas such as developers, DevOps engineers, auditors, and administrators with scoped permissions that mirror least-privilege IAM setups.
actor User {}
resource CloudAccount {
roles = ["developer", "devops", "auditor", "administrator"];
permissions = ["read", "write", "manage_lambda", "view_logs", "manage_ec2", "manage_ecs", "manage_eks", "manage_cloudformation", "manage_iam", "manage_billing", "full_access"];
# role-permission mapping
"manage_lambda" if "developer";
"view_logs" if "developer";
"manage_ec2" if "devops";
"manage_ecs" if "devops";
"manage_eks" if "devops";
"manage_cloudformation" if "devops";
"read" if "auditor"; # auditors get read permission
"manage_iam" if "administrator";
"manage_billing" if "administrator";
"full_access" if "administrator";
}
Developers can deploy and debug workloads by managing Lambda functions and viewing logs but have no access to IAM or billing. DevOps engineers manage infrastructure components such as EC2, ECS, EKS, and CloudFormation stacks without account-level governance rights. Auditors operate with read-only access to review system activity, while administrators retain full privileges, including IAM and billing, forming the highest trust boundary.
So, by centralizing access rules in Oso, permissions become explicit, consistent, and auditable across the entire cloud environment. For example, a developer attempting to modify IAM policies will be denied because "manage_iam"
is tied exclusively to the administrator role. This prevents privilege creep and enforces least-privilege access in the same spirit as AWS IAM’s JSON policies, but expressed at the application layer for clarity and maintainability.
In SaaS environments, RBAC must not only manage user actions but also enforce strict tenant isolation. A contributor from one company should never gain access to another company’s data, even if their role name matches. Roles such as tenant admin, team lead, contributor, and viewer must always be scoped to the tenant ID at both the application and authorization layers.
With Oso Polar policies, we can model this tenant-aware RBAC directly. Each role is defined relative to a tenant resource, ensuring that permissions cannot leak across organizational boundaries.
Have a look at the example policy of a Multitenant SaaS environment in Polar:
actor User {}
resource Tenant {
roles = ["tenant_admin", "team_lead", "contributor", "viewer"];
permissions = [
"manage_users", "manage_roles", "manage_billing", "manage_settings",
"create_project", "manage_project", "assign_contributors",
"update_task", "add_comment", "upload_file",
"view_project", "view_task"
];
# tenant admin
"manage_users" if "tenant_admin";
"manage_roles" if "tenant_admin";
"manage_billing" if "tenant_admin";
"manage_settings" if "tenant_admin";
# team lead
"create_project" if "team_lead";
"manage_project" if "team_lead";
"assign_contributors" if "team_lead";
# contributor
"update_task" if "contributor";
"add_comment" if "contributor";
"upload_file" if "contributor";
# viewer
"view_project" if "viewer";
"view_task" if "viewer";
}
Tenant scoping is enforced at runtime by combining user identity and tenant claims, often carried in JWT tokens. Middleware verifies the token, extracts the tenant ID, and ensures that every Oso allow
check is executed in the context of the correct tenant resource.
For example, a contributor from tenant=acme_corp
can update tasks in their own projects, but the same role tied to another tenant, such as tenant=globex_inc
, has no access to Acme’s data. This guarantees tenant isolation while still supporting flexible, role-based permissions inside each organization.
Kubernetes includes Role-Based Access Control (RBAC) as a first-class feature of the API server, allowing administrators to define which users or service accounts can perform specific actions on resources. This prevents accidental disruptions, such as a developer deleting workloads or modifying cluster-wide configurations.
Common roles include the cluster admin, who has unrestricted access across all namespaces and resources; the namespace admin, who manages deployments, services, and configurations within a single namespace; the developer, who is restricted to basic workload creation in their assigned namespace; and the auditor, who is limited to read-only visibility of resources, logs, and events.
This RBAC structure can be expressed in Oso Polar, modeling clusters and namespaces as resources, with roles scoped to each resource. Cluster-level roles like cluster_admin and auditor provide broad privileges, while namespace-specific roles like namespace_admin and developer allow finer-grained access.
Roles can also inherit permissions from higher-level resources, ensuring that a cluster admin automatically gains management permissions in all namespaces. This approach mirrors Kubernetes’ native RBAC system while using Oso’s declarative, testable authorization model.
actor User {}
resource Cluster {
roles = ["cluster_admin", "auditor"];
permissions = ["full_access", "read_only"];
"full_access" if "cluster_admin";
"read_only" if "auditor";
}
resource Namespace {
roles = ["namespace_admin", "developer", "auditor"];
permissions = ["manage_workloads", "create_pods", "view_resources", "list_pods"];
relations = { cluster: Cluster };
"manage_workloads" if "cluster_admin" on "cluster";
"view_resources" if "auditor" on "cluster";
"manage_workloads" if "namespace_admin";
"create_pods" if "developer";
"list_pods" if "developer";
"view_resources" if "auditor";
"auditor" if "developer";
"developer" if "namespace_admin";
}
# Example Test: Simulate users performing actions
test "developer can create and list pods in assigned namespace" {
setup {
has_role(User{"maria"}, "developer", Namespace{"dev"});
has_relation(Namespace{"dev"}, "cluster", Cluster{"prod"});
}
# Developer actions
assert allow(User{"maria"}, "create_pods", Namespace{"dev"});
assert allow(User{"maria"}, "list_pods", Namespace{"dev"});
assert_not allow(User{"maria"}, "manage_workloads", Namespace{"dev"}); # Not allowed
}
test "auditor can view resources but not manage workloads" {
setup {
has_role(User{"stephan"}, "auditor", Namespace{"dev"});
has_relation(Namespace{"dev"}, "cluster", Cluster{"prod"});
}
assert allow(User{"stephan"}, "view_resources", Namespace{"dev"});
assert_not allow(User{"stephan"}, "create_pods", Namespace{"dev"});
assert_not allow(User{"stephan"}, "manage_workloads", Namespace{"dev"});
}
In this Polar example, maria
is a developer who can create and list pods in her assigned namespace but cannot manage workloads, while stephan
is an auditor who can view resources but cannot modify or create workloads. Relations link namespaces to clusters, enabling inheritance of cluster-level permissions and ensuring consistent enforcement of least privilege. This model mirrors Kubernetes RBAC behavior in a declarative, testable, and maintainable way.
While Role-Based Access Control provides structure and scalability for authorization, implementing it in real-world applications is rarely straightforward. Even experienced developers encounter hurdles when defining, maintaining, and enforcing roles and permissions across complex systems.
Writing RBAC policies directly in Rego can be error-prone. Small mistakes in indentation, variable naming, or logical operators can result in policies that fail silently or incorrectly deny access. Developers often spend hours debugging why a seemingly correct rule does not grant or restrict access as intended. As the number of roles, resources, and conditional rules grows, the policy code becomes harder to read, test, and maintain.
Example:
A missing array index or a typo in a role name might allow a user to escalate privileges unintentionally. Debugging these subtle errors often requires inspecting the full evaluation trace of the Rego engine, which can be time-consuming.
# Intended policy: only 'admin' can delete repos
allow {
input.action == "delete_repo"
input.role == roles[_] # roles = ["admin"]
}
# Bug: 'admins' typo instead of 'admin'
roles = ["admins"]
In this case, the policy silently fails because "admins"
does not match the actual role "admin"
. Instead of denying access cleanly, the rule never evaluates as expected, which can create confusion during testing.
RBAC roles are static and don’t capture conditions like time, location, or environment. Without attributes layered on top, roles become either too permissive or too restrictive, making the access model misaligned with real-world requirements.
Example:
A developer might have different roles for dozens of repositories, each with slightly different permissions. Without careful abstraction, adding a new repository or changing a permission could require updating multiple Rego rules, introducing the risk of inconsistencies.
A customer reports that a "project_admin"
cannot create new projects. The developer inspects logs but finds no error, Rego simply evaluates the policy as false
. To diagnose, they must run opa eval
with full input JSON and inspect the trace output. This requires reconstructing the exact request context (user role, resource, action, tenant ID, time-based constraints), which is error-prone and slow under production pressure.
Tools like Oso are designed to simplify these challenges by providing built-in support for roles, hierarchies, and relationships. Instead of hand-coding every policy in a textual language, Oso uses Polar and provides a drag-and-drop, input-driven approach to define roles, permissions, and resource constraints.
Developers can configure roles and access rules using structured input values rather than manually writing full policy code. These definitions can then be applied directly in the application, reducing the risk of syntax errors and simplifying the process of scaling RBAC examples across multiple resources and environments.
Oso is an authorization framework designed to make access control easier to implement, maintain, and scale in applications today. Unlike hand-coded policies in Rego or custom checks scattered throughout application logic, Oso provides a centralized, declarative approach using its own policy language, Polar.
Oso allows you to define roles, permissions, and resource constraints in a way that is reusable, auditable, and directly enforceable in your app. Rather than manually writing long conditional statements, you describe what access looks like, and Oso evaluates these rules at runtime.
Let’s understand how to create an RBAC example in Oso:
In the Rules Editor (Workbench mode), you start by selecting the type of resource (e.g., Item
) and the actor type (User
). Then, you add roles (like viewer
or owner
) and assign permissions to each role using a form-based interface with drag-and-drop menus.
This policy ensures that users with a given role automatically receive the correct permissions on the resource.
After defining roles and permissions, you assign roles to individual users. Using a form-based interface with drag-and-drop menus, you add facts like:
has_role(User{"alice"}, "viewer", Item{"foo"});
has_role(User{"bob"}, "owner", Item{"foo"});
This visually maps users to roles on specific resources. Oso automatically integrates these assignments into the Polar policy.
Once roles and permissions are configured, you can check if a user can perform a specific action on a resource. For instance, check if alice
has the view
permission on Item: foo
.
In the interface:
alice
view
foo
Oso evaluates the request against the policy and the assigned roles. The output will indicate whether the action is ALLOWED or DENIED, along with an explanation.
The platform allows you to create and run tests to validate your RBAC setup. For example, you can assert:
assert allow(User{"alice"}, "view", Item{"foo"});
assert_not allow(User{"alice"}, "edit", Item{"foo"});
After running the test, Oso shows results directly in the interface, confirming that the roles and permissions are correctly enforced.
Let’s see the dashboard of Oso here:
In this screenshot, the Oso Cloud Rule Editor is being used to model and test Role-Based Access Control (RBAC). A resource called Item
is defined with two roles, viewer
and owner
, and two permissions, view
and edit
. The policy specifies that a viewer
can only view an item, while an owner
can both edit and view it (since owners inherit viewer privileges).
Users are then assigned roles on a specific resource: Alice is assigned the viewer
role, and Bob is assigned the owner
role on the same Item("example")
. To validate these assignments, a test is created that checks different access scenarios. The assertions confirm that Alice can view but cannot edit, while Bob can both view and edit.
Since all assertions pass, the test shows that the RBAC rules are correctly enforced, preventing unauthorized actions and allowing legitimate ones. Essentially, Oso here is being used to write authorization policies, link roles to permissions, assign users to those roles, and verify that the system enforces the intended access boundaries.
Role-Based Access Control is a reliable framework for managing permissions by assigning them to roles rather than individual users. This approach simplifies access management, ensures consistency, and scales well across complex systems.
Real-world examples, from project management tools to code hosting platforms, show how users can hold different roles across resources, allowing precise control without administrative overhead.
RBAC can become complex as roles multiply, but hierarchical, resource-scoped, and hybrid RBAC-ABAC models help maintain clarity and security. Tools like Oso further simplify implementation by providing a centralized, declarative interface for defining roles, permissions, and constraints, along with built-in testing to verify access rules. Using structured RBAC models with such tools ensures predictable, secure, and maintainable access control in modern applications.
RBAC (Role-Based Access Control) is an authorization model that assigns permissions to roles rather than individual users. Users assume these roles, which simplifies access management, ensures consistency, and reduces the risk of unauthorized access, especially in large or multi-tenant systems.
A common RBAC role is Project Manager in a project management system. A Project Manager can create tasks, assign work, and close projects. Other roles might include Contributor (can create and update tasks) and Viewer (can only read project status).
IAM roles grant temporary, as-needed permissions in cloud environments, for example, a Lambda function assuming a role to perform a task. RBAC is a broader model where users are assigned roles with associated permissions, usually persistent, though ephemeral roles are possible. Essentially, IAM roles implement RBAC concepts but focus on temporary access.
Over time, RBAC can lead to role explosion, where too many specialized roles are created. This makes it difficult to track who has access to what and can complicate permission management if not carefully maintained.