CanCan and Datasets

I tried this after a while, and I finally got to the point that CanCan does not allow you to allow collection of records. For instance:

ads_controller.rb

def index @ads = Ad.where("ads.published_at >= ?", 30.days.ago).order("ads.published_at DESC") authorize! :read, @ads end 

ability.rb

 def initialize(user) user ||= User.new # Guest user if user if user.role? :admin # Logged in as admin can :manage, :all else # Logged in as general user can :read, Ad can :read_own, Ad, :user_id => user.id can :create, Ad end else # Not logged in (Guest) can :read, Ad end end 

This results in an unauthorized access message when trying to access the index action.

 You are not authorized to access this page. 

However, if you change the authorization call in the index action to check the Ad class and not the collection, for example

 def index @ads = Ad.where("ads.published_at >= ?", 30.days.ago) authorize! :read, Ad end 

... it works great.

Any help in explaining this would be greatly appreciated.

Thanks in advance.

ps. Initially, I was getting redirect cycles when trying to solve this problem. It turned out that with the recommended rescue_from, you got into the application controller to give you good error messages. If your root_path is installed in the same place where you log in! the call is incorrect (or does not work), you will get a redirect cycle. Comment on rescue_from Found out that this is the hard way.

+4
source share
2 answers

CanCan is not intended to be used in this way. You can check whether the user has rights to a model class (e.g. Ad ) or a single instance (e.g. @ad ).

I suggest you use accessible_by to filter your collection:

 @ads = Ad.where("ads.published_at >= ?", 30.days.ago).accessible_by(current_ability) # @ads will be empty if none are accessible by current user raise CanCan::AccessDenied if @ads.empty? # handle however you like 

Another approach would be to define a custom permission based on the conditions that you use to retrieve the collection:

 # ability.rb can :read_ads_from_past_month, Ad, ["ads.published_at >= ?", 30.days.ago] # in your controller def index authorize! :read_ads_from_past_month, Ad @ads = Ad.where("ads.published_at >= ?", 30.days.ago) end 
+2
source

I solved this problem. In this code example, I'm trying to allow users to build TimeOffRequests . They must be authorized if the User is an administrator, manager or request a time to exit.

 # time_off_requests_controller.rb authorize! :read, *@time_off_requests # Ability.rb can :manage, TimeOffRequest do |*time_off_requests| membership.has_any_role?(:admin, :manager) || time_off_requests.all? { |tor| membership.id == tor.employee_id } end 

I wrote about this in detail here if you are interested: http://zacstewart.com/2012/01/08/defining-abilities-for-collections-of-records-using-cancan.html

+2
source

All Articles