Question

I have looked at declarative_authorization, CanCan, and CanTango. They all are good in adding authorization to the application but I was wondering how does one add authorization to specific instance of a model i.e. a person can have a manage access in one project and only limited (read less than manage: limited update, etc) in another.

Could you please a better way? Apologies if my question sounds too trivial. It could be because I am new to RoR.

thanks, John

Was it helpful?

Solution

As I know CanCan and declarative_authorization, and I implemented role-based authorizations with both, I recommend CanCan. Just my two cents.

Example (untested, unfortunately I cannot test here and I have no access to my code)

So let's say we have a structure like this:

class User < ActiveRecord::Base
  belongs_to :role
end

class Role < ActiveRecord::Base
  has_many :users

  # attributes: project_read, project_create, project_update
end

Then, CanCan could look like this:

class Ability
  include CanCan::Ability

  def initialize(user)
    @user = user
    @role = user.role

    # user can see a project if he has project_read => true in his role
    can :read, Project if role.project_read? 

    # same, but with create
    can :create, Project if role.project_create?

    # can do everything with projects if he is an admin
    can :manage, Project if user.admin?
  end

end

You can find all information you need in the CanCan wiki on github. Personal recommendation to read:

Basically you just need to extend the example above to include your roles through your relations. To keep it simple, you can also create additional helper methods in ability.rb.

The main mean caveat you may fall for (at least I do): Make sure your user can do something with a model before you define what the user can't. Otherwise you'll sit there frustrated and think "but why? I never wrote the user can't.". Yeah. But you also never explicitly wrote that he can...

OTHER TIPS

class User < ActiveRecord::Base

  belongs_to :role
  delegate :permissions, :to => :role
  
  def method_missing(method_id, *args)
    if match = matches_dynamic_role_check?(method_id)
      tokenize_roles(match.captures.first).each do |check|
        return true if role.name.downcase == check
      end
      return false
    elsif match = matches_dynamic_perm_check?(method_id)
      return true if permissions.find_by_name(match.captures.first)
    else
      super
    end
  end
  
  
  private

  def matches_dynamic_perm_check?(method_id)
    /^can_([a-zA-Z]\w*)\?$/.match(method_id.to_s)
  end
  
  def matches_dynamic_role_check?(method_id)
    /^is_an?_([a-zA-Z]\w*)\?$/.match(method_id.to_s)
  end
 
  def tokenize_roles(string_to_split)
    string_to_split.split(/_or_/)
  end
  
end

Usage:

user.is_an? admin

user.can_delete?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top