There are a couple of things that I appreciate about the Lockdown project which I feel CanCan lacks.
While it's possible to mimic this behavior with Ensure Authorization and use CanCan in Non RESTful Controllers. I would like this behavior to be simpler and intuitive.
What if there is a controller class method for enabling authorization. This will usually be done in the ApplicationController so everything is restricted.
class ApplicationController < ActionController::Base
This would add a before_filter to every action which checks the permission on Ability. The default behavior would simply pass in the controller action name as the verb and the controller name as the noun.
can :create, :projects
This makes Non RESTful controllers just as easy to limit. For example, if there's a HomeController#index action we could do.
can :index, :home
The action aliases are still set up so one can use :read or :manage instead of :index there.
The problem is how will this fit in with handling conditions based on model attributes. Also, because this changes the focus to controller actions it makes setting permissions on individual attributes (see #154) less obvious.
Just throwing this idea out there, let me know what you think!
I will be moving forward with what's mentioned in this issue for the 2.0 release.
I will also be changing :manage to :access because it's more generic and fits other non restful controllers.
can :access, :home # can access any action on home controller
Checking on model instances will match based on the pluralized class name.
can :read, :projects, :discontinued => false
can? :read, Project.new(:discontinued => false) # will return true
can? :read, :projects # will return true like normal
Using a class directly will be deprecated. For cases where the subject name can't be inferred there will be support for subject aliases which work much like action aliases. There will be the ability to pass a class name and alias it to a subject name.
alias_subject User, :to => :authors
can :read, :authors
can? :read, User.new # returns true
Since authorization will be enabled by default in all controllers. The load_and_authorize_resource call will be deprecated and only load_resource will be needed. One can call skip_authorization on any controller where they don't want authorization to take place. This works for restful and non restful controllers.
Setting permissions for individual attributes will also be supported as mentioned in issue #154.
can :update, :projects, [:name, :priority]
can? :update, @project, :name # returns true
This removes the need for attr_accessible in models since each attribute will be checked automatically with authorization if you're using load_resource.
This also makes it easier to add conditions for parent resources when nesting is involved. The association can be treated as an attribute:
can :access, :projects, :categories
These are all some ideas on where I see CanCan 2.0 going. What do you think?
is it posible to add a method that prints the resulting permissions for a given resource? it would be so helpful during development.
Good idea. I hope to improve debugging in 2.0 including a way to enable logging which will state all the permissions it goes through, etc.
How is the attributes default to be handled?
can :update, :projects # allow to all attributes
can :update, :projects, [:name, :priority] # allow only these two attributes
I think :only and :except semantics are a good candidate here
can :read, :user, :only => [:email]
Also "authorization will be enabled by default in all controllers" should allow opt-out.
@clyfe, thanks for the suggestion. I haven't thought through all the possibilities of the 3rd parameter yet. I will need to do some experimenting. The problem with allowing an only/except hash of options is that it conflicts with the use of a hash of conditions.
I think we can make more use of the cannot call. If you want to do all attributes except one or two, do this.
can :update, :products # allow all attributes
cannot :update, :products, :price
I will also consider adding an option for the 3rd parameter to always be required. This way one needs to always be explicit on the attributes and use :all to represent all.
can :update, :products, :all
If one is using this in place of attr_accessible I think this is a good solution to ensure they don't forget it anywhere.
Regarding your second suggestion, the enable_authorization method will accept :if and :unless options to disable it dynamically. I thought about adding a skip_authorization method one can call, but separating authorization from loading gets complicated in load_and_authorize_resource. CanCan 1 attempts to do this but I don't think it does a very good job.
If you want to "skip" authorization in a controller, it's just as easy to allow access to it in the Ability class, so I'm not certain it's entirely necessary. This is something I will have to wait until I get some implementation to test it.
The majority of what I mentioned here is now implemented in the 2.0 branch. Closing this. Open up a separate issue if you find specific problems.