Authorization in Rails 3.1 : CanCan, CanTango, declarative_authorization?
Asked Answered
D

2

7

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

Delano answered 18/1, 2012 at 6:45 Comment(2)
In CanCan you can define abilities via blocks with conditions - github.com/ryanb/cancan/wiki/Defining-Abilities-with-Blocks .Melone
1. TeamMembers has many Users, Roles 2. Teams has many TeamMembers 3. Project has one Team... With Blocks, the above setup doesn't give a means to filter for specific instance of project. If you think it can be done... Could you please provide me an e.g. Thanks in advance.Delano
L
4

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...

Lucier answered 18/1, 2012 at 7:10 Comment(3)
I have implemented cancan.. but can't figure out how to define/restrict user for a specific instance (of say project). Any e.g. would be really helpful. There is indirect connection between User and Project... 1. TeamMembers has many Users 2. Teams has many TeamMembers 3. Project has one TeamDelano
do I need to install cancan AND create a role model? I don't see in the cancan documentation that it is needed. Can you please explain this? thanksResolute
No, you don't need to, this is just an example. There are many more, see e.g github.com/ryanb/cancan/wiki/Abilities-in-DatabaseLucier
I
0
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?

Innerdirected answered 18/1, 2012 at 21:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.