Ability works for single object, but not collection #820

graysonwright opened this Issue Feb 9, 2013 · 6 comments


None yet
5 participants


def initialize(user)
  can :read, User, id: user.id
  if user.admin?
    can :read, User

rails console

user = User.find(2)    # User 2 is an admin
ability = Ability.new(user)
ability.can? :read, user
 => true
ability.can? :read, [user]
 => false

I'm getting a not authorized exception every time I try to authorize this read ability with a collection. This is a problem for my index action, where I have:

authorize! :read, User.all

Is anyone else having this problem? It's not happening in my other controllers.

Note: I am using Devise, but I don't think it's part of the problem. This is a problem in the model layer, not the controller layer.

rdlugosz commented Feb 9, 2013

I don't recall seeing in the docs that you can pass a collection to Ability#can? like that.

Am I correct in assuming that what you are attempting to do is to only allow a user to modify themselves, but let an Admin view the entire list of users? If so, I think you'd have more luck with something like this:

#...in your Ability class

if user.admin?
  can :manage, User
  can :manage, User, id: user.id
  can :read, User   # only if you want the user to be able to see other users but not modify them

Then, in your controller you could test for the :manage ability on the User class in the index action (the Class, not the collection of User objects).

I have another project where I'm doing the same thing (passing an array of users into the authorize! method), and it works fine. I thought this was the recommended way to authorize index actions, but you're right -- I can't find any documentation either.

For this specific instance, I can use authorize! :read, User, but there are instances where you'd need to authorize based on a list of items -- like when you're restricting the objects based on the state of an attribute.

The behavior I'm getting in my other application is that when I authorize a list, all of the items that are unauthorized get stripped out of the list. That way, if a user pulls up an index page, they get a list of the items that they're authorized to see, while other users may get a list with different contents.

rdlugosz commented Feb 9, 2013

You could pretty easily achieve that via Array#select, checking the authorization inside the block.

wafcio commented Mar 9, 2013

@rdlugosz: It is definitely bug for mongoid. In other project I use ActiveRecord and authorize! :read, @users works. Beside that Array#select look awful in this case.

Same problem here, almost got me crazy because it was happening in an ajax request that was getting redirected (with a DELETE) to parent.....

Had to change from:

def delete_multiple

      @items = Item.find(params[:item_ids])

      authorize! :destroy, @items

      @items.each do {|item| item.destroy } 



def delete_multiple

      @items = Item.find(params[:item_ids])

      @items.each do |item|
        authorize! :destroy, item

after a painful debugging session...

xhoy commented Apr 10, 2014

Dear submitter, Since cancan/raynB hasn't been active for more than 6 months and no body else then ryam himself has commit permissions the cancan project is on a stand still.
Since cancan has several issues including missing support for rails 4 cancan is moving forward to cancancan. More details on: #994

If your feel that your pull request or bug is still applicable (and hasn't been merged in to cancan) it would be really appreciated if you would resubmit it to cancancan (https://github.com/cancancommunity/cancancan)

We hope to see you on the other side!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment