Simple RBAC implementation with Rails

Authentication and Authorization are 2 security mechanisms to manage user access to a system. They are sometimes used interchangeable but they actually have different functions:

  • Authentication is the process of verifying who a user is.
  • Authorization is the process of verifying what a user has access to.

There are many techniques/strategies for authorization, such as:

  • Access control list (ACL)
  • Role-based access control (RBAC)
  • Attribute-based access control (ABAC)

This post will introduce about RBAC, a popular authroization technique for common web apps, and how to implement a simple RBAC system in Rails with the help from Pundit gem.

What is RBAC?

Role-based access control is "an approach to restricting system access to authorized users" (Wikipedia). It is about user management and role assignments. What a user could access is defined by their defined roles. A role is a collection of permissions that define actions (an operation on a resource) that a role can do.

Core actors of a RBAC system:

  • Users
  • Roles
  • Permissions
  • Operations
  • Resources

RBAC has advantage of simple to implement and execute but easy to be flooded with role explosions where admins keep adding roles for specific purpose. RBAC is also has difficulties with complex access rules like time-based rules, per asset access.

Implement a simple RBAC in Rails with Pundit

We're going to setup User, Role and Permission models with these assumptions:

  • A user may have many roles
  • A role may be attached to many users
  • A role may have my permissions
  • A permission may be attached to many roles
  • A permission name is formatted with {resource}.{action}, e.g. read.employees

User model

class User < ApplicationRecord
  has_and_belongs_to_many :roles

  def has_permission?(action, resource)

  def permissions
    @permissions ||= roles.flat_map(&:permissions).map(&:name).uniq

Role model

class Role < ApplicationRecord
  has_and_belongs_to_many :permissions
  has_and_belongs_to_many :users

Permission model

class Permission < ApplicationRecord
  has_and_belongs_to_many :roles

Pundit is a popular gem for authorzation on Rails. I won't explain how to use it here but go straight into the code for ApplicationPolicy which will be used as based for other policies.

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record

  def index?
    user.has_permission?(:read, resource)

  def show?

  def create?
    user.has_permission?(:create, resource)

  def new?

  def update?
    user.has_permission?(:update, resource)

  def edit?

  def destroy?
    user.has_permission?(:delete, resource)


  def resource
    raise NotImplementedError

Then for a particular policy we only need to specify resource name

class EmployeePolicy < ApplicationPolicy

  def resource



What if a user could only access a portion of a resouce instead of all records of the resource? For example a manager should only be able to manage their own department employees.

There are 2 options:

  1. Implement a more complex RBAC system
  2. Use Pundit scope

There are no obvious choices. Deciding which option to implement depends on the requirements of the app and other factors.

Why just not use Pundit and roles only

Using RBAC we could dynamically add roles, change role permissions on UI without the need to change the code.