How to implement the dynamic authorization with CanCan ?


I have used CanCan plugin to manage authorization on my rails app, but it’s still static authorization. I just create code like this:

def initialize(user)
  if user.admin?
    can :manage, :all
  else
    can :read, :all
  end
end

My app is getting bigger. There are users with many user types. Every user type has different role / authorization. I’d like to create a feature that user admin can manage authorization for all user types. Admin can assign or remove the permission to each user types. So, what should I do to implement that feature?

To create dynamic authorization with CanCan, first we must prepare the database to store authorization data, said that “permissions” table. The structure of permissions table can look like this:

permissions:
• id
• object_type
• action_name
• description

Permission model should have some validations:

validates_presence_of "object_type", :action_name
validates_uniqueness_of "object_type", :scope => [:action_name]

We should also have users, user_types, and user_type_permissions table. User belongs to user_type. UserType has many users. UserType has many permissions through user_type_permissions. Permission has many user_type through user_type_permissions.
And then on the ability model, we should put code like this :

class Ability
  include CanCan::Ability

  def initialize(user)
    if user.admin?
      can :manage, :all
    else
      user.user_type.permissions.each do |permission|
        can permission.action_name.to_sym, permission.object_type.constantize
      end
    end
  end
end

Dynamic ability is done, the ability model read the authorization for all user types from database.

For the manage permission feature for admin, just create CRUD function as usual. However, don’t give the admin an authorization to modify data on the permissions table because it’s can make the application error if the data in the permissions table can’t be defined on source code. Just give the admin an authorization to modify user_type_permissions table. The structure of the table can look like this:

user_type_permissions:
• id
• user_type_id
• permission_id

Then, let the program to create permission data automatically. We must do some modification on CanCan plugin for it. Please modify the authorize method on the /vendor/plugins/cancan/lib/cancan/ability.rb to be like this :

 # See ControllerAdditions#authorize! for documentation.
    def authorize!(action, subject, *args)
      case action.to_s
        when "show", "index" then action_name = "read"
        when "create", "new" then action_name = "create"
        when "update", "edit" then action_name = "update"
        when "delete", "destroy" then action_name = "destroy"
        else action_name = action.to_s
      end

      # save data to permissions table
      Permission.create(:object_type => subject.to_s, :action_name => action_name)

      message = nil
      if args.last.kind_of?(Hash) && args.last.has_key?(:message)
        message = args.pop[:message]
      end
      if cannot?(action, subject, *args)
        message ||= unauthorized_message(action, subject)
        raise AccessDenied.new(message, action, subject)
      end
    end

By this code, the authorize data can automatically save to the permissions table everytime we access any method in the controller. For example, we have users controller with method index, show, create, and new. So everytime we access method index/show, authorize method will check index/show method and save it to permissions table as: object_type = User; action_name = read. Everytime we access method create/new, authorize method will check create/new method and save it to permissions table as: object_type = User; action_name = create. However, in order this function works, don’t forget to put “authorize_resource” in the controller :

class Admin::UsersController < Admin::ApplicationController
  authorize_resource

  ........
end

or call “authorize!” in the method :

  def show
    authorize! :read_users, User

    .........
  end

Let’s practice! :D

About these ads

,

  1. #1 by Ganesh Kunwar on March 14, 2013 - 1:09 pm

    Reblogged this on Ganesh Kunwar's and commented:
    How to implement the dynamic authorization with CanCan ?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: