Very simple permissions in Rails

Timi Ajiboye
chunks of code*
Published in
2 min readJan 27, 2017

I was working on a very simple Rails API, using devise_token_auth and I need to authorize controller actions based on user roles.

(Although, this should work fine for regular ol’ devise)

I started out using cancancan and I even tried out pundit but they weren’t simple enough for my needs.

Requirements

  • I needed a simple hash to define which role can access which controller actions.
  • I needed my controllers to check this hash to determine if a user can access the action he’s trying to access.

Solution

I created a concern controllers/concerns/check_permission.rb

module CheckPermission
extend ActiveSupport::Concern


included do
before_action :authenticate_user!, unless: :devise_controller?
before_action :check_permission, unless: :devise_controller?
end

def
permissions
{
tickets: {
registered: [:index, :create, :reply, :close, :reopen],
support: [:index, :reply, :close, :reopen],
admin: []
},

users: {
admin: [:create, :update, :index, :show, :destroy]
}
}
end


def
check_permission
controller_permissions = permissions[controller_name.to_sym]
role = current_user.role.to_sym
if controller_permissions.keys.include?(role)
if !controller_permissions[role].include?(action_name.to_sym)
head 403
end
else
head 403
end
end
end

Then I included this concern in my ApplicationController

class ApplicationController < ActionController::API
include DeviseTokenAuth::Concerns::SetUserByToken
include CheckPermission

end

The method permissions returns a hash with the controllers, actions and roles in following format.

{
controller_name: {
role: [:action, :another_action],
another_role: [:action...],
},

another_controller_name: {
role: [:action, :another_action],
}

...
}

Also, whatcheck_permissions does is pretty simple. It checks if the current user’s role array includes the action that is currently being attempted. If not, it returns a 403.

P.S

This obviously works only in situations where a User has only one role. But it is easy to adjust check_permission to work for a many-to-many relationship between User & Role.

def check_permission
controller_permissions = permissions[controller_name.to_sym]
roles = current_user.roles.pluck(:name).map &:to_sym
if !(controller_permissions.keys & roles).empty?
actions = []
(controller_permissions.keys & roles).each { |x| actions | controller_permissions[x] }
head 403 if !actions.include?(action_name.to_sym)
else
head 403
end
end

This will first check if any of the user’s roles is present in the permitted roles of that controller.

Then it will create a union array of all the permitted actions of all the user’s roles and check if the current action_name exists in that array.

Simple, right?

--

--

Timi Ajiboye
chunks of code*

I make stuff, mostly things that work on computers. Building Gandalf.