Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using accessible_by with overriding roles #47

Closed
lesliev opened this issue Apr 16, 2014 · 4 comments
Closed

Using accessible_by with overriding roles #47

lesliev opened this issue Apr 16, 2014 · 4 comments

Comments

@lesliev
Copy link

lesliev commented Apr 16, 2014

I am having trouble using cancancan to select records based on abilities.

My ability.rb file looks like this:

    can :read, Business if user.admin?

    can :read, Business, Business.user_related(user) do
      Business.user_related(user).any?
    end

My controller tries this:

@businesses = Business.accessible_by(current_ability, :read)

If I comment out the top rule, everything seems to work. That is, I can load businesses produced by the Business.user_related scope. But if I include the top rule I get the error: "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read Business ability."

If the user is an admin, cancancan doesn't need to consult the database at all - they have access to all businesses. How can I express this in ability.rb?

@bryanrite
Copy link
Member

Looks like the problem is the block section of the second ability is supposed to be run on instances of the business and the first scope is run on collections. ie:

can [:ability], Model, Model.scope_to_select_on_index_action do |model_instance|
  model_instance.condition_to_evaluate_for_new_create_edit_update_destroy
end

You should be able to do:

can :read, Business, Business.user_related(user) do |business|
   business.related_to?(user) # true/false
end

Then, when an admin tries to read a Business it will do (can read all businesses) or (can read user_related businesses)... which will always return all.

Generally I find it useful to separate out rules by roles:

case user.role
when 'admin'
  can :read, Business
  # Other admin rules
when 'user'
  # ...
else
  # guest/not logged in rules
end

@bryanrite
Copy link
Member

@lesliev
Copy link
Author

lesliev commented Apr 16, 2014

Excellent, thanks a lot! Your "can [:ability]" example should be in the docs.

This still doesn't solve the problem (I still get the same error) - but when I separate out the roles like you suggest, it works. I suppose it does make sense to not run the second block at all if we have an admin.

For others, here's the working code:

    if user.admin?
      can :read, Business 
    else
      can :read, Business, Business.user_related(user) do |business|
        business.related_to?(user)
      end
    end

@robertokl
Copy link

+1 on the "example should be in the docs".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants