-
Notifications
You must be signed in to change notification settings - Fork 783
Authorization for Namespaced Controllers
The default operation for CanCan is to authorize based only on user and the object identified in load_resource
. So if you have a WidgetsController
and also an Admin::WidgetsController
, it looks to me like CanCan will allow the same authorizations for each. This probably defeats the whole purpose of creating a separate Admin controller in the first place.
Just like in the example given for Accessing Request Data, you can also create differing authorization rules that depend on the controller namespace.
In this case, just override the current_ability
method in ApplicationController
to include the controller namespace, and create an Ability
class that knows what to do with it.
class ApplicationController < ActionController::Base
#...
private
def current_ability
# I am sure there is a slicker way to capture the controller namespace
controller_name_segments = params[:controller].split('/')
controller_name_segments.pop
controller_namespace = controller_name_segments.join('/').camelize
Ability.new(current_user, controller_namespace)
end
end
class Ability
include CanCan::Ability
def initialize(user, controller_namespace)
case controller_namespace
when 'Admin'
can :manage, :all if user.has_role? 'admin'
else
# rules for non-admin controllers here
end
end
end
I used the following code on my ApplicationController:
# ...
private
def namespace
# 2012.3.13 didn't work on Rails 3.0.7, cancan 1.6.7; looks promising, but needs some figuring out.
#cns = @controller.class.to_s.split('::')
#cns.size == 2 ? cns.shift.downcase : ""
# I am sure there is a slicker way to capture the controller namespace
# 2012.3.13 But it works!
controller_name_segments = params[:controller].split('/')
controller_name_segments.pop
controller_namespace = controller_name_segments.join('/').camelize
end
def current_ability
# Ability.new(current_user, namespace)
@current_ability ||= Ability.new(current_user, namespace)
end
I have not tested it extensively yet.
##Limitations of this approach
While this approach handles the permissions of the controller actions themselves ok, it can be problematic in the views. Let's say we have a namespaced admin section that shows page statistics. If a user is logged in and has the relevant permissions then we want to show them a link to the stats section:
# static_pages#show
if can? :stats, @page
link_to "page stats", stats_admin_page
end
class Ability
include CanCan::Ability
def initialize(user, controller_namespace)
case controller_namespace
when 'Admin'
can :manage, :all if user.has_role? 'admin'
else
# rules for non-admin controllers here
end
end
end
In theory this should work however the permissions are evaluated in the context of the static_pages
controller which is not in the admin namespace. The permission will therefore always evaluate to false since it takes no context argument.
This project is abandoned, see its successor: CanCanCan