Cannot authorize access to nested controller (with different name than model) #381

Open
gamov opened this Issue May 20, 2011 · 17 comments

Projects

None yet

5 participants

@gamov
gamov commented May 20, 2011
#abilities
can :access, :customers, :business_site => {:business_id  => user.business_site.business_id}
can :access, :sales, :business_site => {:business_id  => user.business_site.business_id}
#routes
resources :customers do
    resources :sales, :only => :index, :controller => 'CustomerSales'
end
#controllers
class CustomerSalesController < ApplicationController

#  authorize_resource #removed
#  load_and_authorize_resource :customer
#  load_and_authorize_resource :customer, :class => Customer, :except => :index
#  load_and_authorize_resource :sale, :through => :customer, :class => Sale #@cancan_report: @sales not accessible

 def index
    @customer = Customer.find(params[:customer_id])
     authorize! :access, @customer, 'my cancan error message'

    @sales = @customer.sales.order('created_at DESC').includes(:sold_items)
    authorize! :access, @sales
  end
end

I tried to no avail to access the nested sales via this controller through /customers/6/sales but I always hit CanCan::Unauthorized, no matter what I try... even though I don't load_and_authorize, the request never access my controller and seems to be stopped by Cancan into the before_filter even without using the load_and_authorize.

Even tried:

can :access, :customer_sales  #trying to authorize access to the controller, doesn't work

I'm really out of ideas, I also tried to play around with the l&a options as seen in the commented code. I must add that I have a normal SalesController that works perfectly.

Using CanCan 2.0.

NB: Customer class is a STI subclass of Company but I don't think it is the problem here.

@mabounassif

I think this is similar to issue #204. I've submitted a tentative fix.

@gamov
gamov commented May 20, 2011

I have no SQL requests at all from CanCan, the only SQL activity I see is Rails loading the current user then CanCan directly throws the UnUnauthorized exception.

@mabounassif

Do you at least have an (1=1) SQL statement? If so that's the only Script generated by Cancan

@gamov
gamov commented May 21, 2011

Nope, nothing. No SQL related to CanCan. Usually, if you don't authorize manually or l&a, CanCan will issue a InsufficientCheck exception, not a Unauthorized one.
Actually, according to the doc,

can :access, :customer_sales  #trying to authorize access to the controller, doesn't work

should work because we are instructing CanCan to let user access all actions of CustomerSalesController...

@ryanb
Owner
ryanb commented May 21, 2011

I think this may be related to your issue #368 but I need to do some more investigating. I haven't done much testing yet with CanCan 2.0 and nested resources so there are likely problems such as the one you describe here. Thanks for reporting.

@gamov
gamov commented Jun 9, 2011

I created a test application to compare 1.6.5 and 2.0 to be sure
http://dl.dropbox.com/u/102608/testcancan.zip
It is now configured to use cancan 2.0 and with a differently named controller for the nested resource: user_projects. If you remove the controller parameter in the routes for the nested projects, it is displayed properly.

The only problem seems to be the name of the controller (you can easily switch back to 1.6.5 to check (see tags #cc). I confirm it is not related to issue #368

I'm thinking for going back to 1.6.5 because I have those nested controllers all over my app (via inherited resources).

PS: POIs with tag @comment

@ryanb
Owner
ryanb commented Jun 13, 2011

Thanks for the extensive reporting on this issue. I don't know when I will get around to addressing this so it would be best to go back to 1.6.5 in the meantime.

@julian7
julian7 commented Jun 30, 2011

I had the first issue with devise. I had to add

can :access, :"devise/sessions"

to Ability. However, you have to have two rights granted in Ability:

can :access, [:"customers/sales", :customer_sales]

The first is for your controller, the second for load_and_authorize_resource. Maybe not. Nevertheless this helped me.

@ryanb
Owner
ryanb commented Jun 30, 2011

For Devise I recommend doing this in the application controller.

enable_authorization :unless => :devise_controller?

That should disable authorization for devise specific controllers. Does that work for you?

@gamov
gamov commented Jul 1, 2011

Julian7, this looks like a nice workaround. However, I can't seem to make it work. What did you put in your nested controller?:
I tried this without success:

CustomerSalesController:

load_and_authorize_resource

#AND

  load_and_authorize_resource :customer
  load_and_authorize_resource :sale, :through => :customer, :class => Sale
@ryanb
Owner
ryanb commented Jul 1, 2011

@gamov I'm a little surprised if @julian7's solution works as well since this is something that I had planned to add but didn't think it worked yet. Still I want to get something similar to this in 2.0 before release.

@gamov
gamov commented Jul 1, 2011

@ryanb, that's how good you are... You plan something and the implementation is left to an unconscious part of your brain. Awesome :op

Still doesn't work for me though...

@julian7
julian7 commented Jul 25, 2011

@gamov I downloaded your test app, and I was able to see user projects using this ability.rb:

class Ability
  include CanCan::Ability
  def initialize(user)
    if user
      can :access, :users
      can :access, :projects, :user_id => user.id
      can :access, :"UserProjects", :user_id => user.id
    end
  end
end
@gamov
gamov commented Jul 25, 2011

Excellent @julian7, thank you so much! I should have tried this...

This will allow a user to load another user projects page (/users/2/projects) but will not list any (because none belong to that user). Is it possible to declare in Ability that access to that link is not allowed. I tried to add this after your list line:

     cannot :access, :"UserProjects", :user_id.ne => user.id

but it doesn't seem to work. Of course I can do it manually in the controller but I'm wondering if there is a 'better' way to do it in Ability.rb

Cheers!

@ryanb
Owner
ryanb commented Jul 25, 2011

@gamov you can use a block for doing more complex checking (not equals), but I don't think that will work here because it only gets evaluated when there's an instance to work with. You're visiting the index action so there is no single project to check against. Unfortunately there's not a very good way to handle this yet.

In CanCan 2.1 I plan to focus a lot on this nested resource problem. See the 2.1 issues for more discussion on it.

@gamov
gamov commented Jul 26, 2011

@ryanb, yes, I'm aware of this concept, just wanted to confirm. It's not a problem, it's only one line in the index method of the controller.
With julian7 workaround, I can go ahead. I let you close or keep the issue alive.

Thanks again for your support,
Gam

@xhoy
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