updating some documentation for CanCan 2.0
This commit is contained in:
parent
e5b76210e4
commit
c6f9abb6ab
139
README.rdoc
139
README.rdoc
|
@ -1,111 +1,108 @@
|
|||
= CanCan
|
||||
|
||||
Wiki[https://github.com/ryanb/cancan/wiki] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan]
|
||||
This is the branch for CanCan 2.0 which is in very early development. For a stable release please check out the {master branch}[https://github.com/ryanb/cancan]
|
||||
|
||||
CanCan is an authorization library for Ruby on Rails which restricts what resources a given user is allowed to access. All permissions are defined in a single location (the +Ability+ class) and not duplicated across controllers, views, and database queries.
|
||||
Here are some instructions for setting up CanCan 2.0. Try this out and provide feedback in the {issue tracker}[https://github.com/ryanb/cancan/issues].
|
||||
|
||||
|
||||
== Installation
|
||||
== Setup
|
||||
|
||||
In <b>Rails 3</b>, add this to your Gemfile and run the +bundle+ command.
|
||||
CanCan expects your controllers to have a +current_user+ method. Add some authentication for this (such as Devise[https://github.com/plataformatec/devise], Authlogic[https://github.com/binarylogic/authlogic] or {nifty:authentication}[https://github.com/ryanb/nifty-generators]). See {Changing Defaults}[https://github.com/ryanb/cancan/wiki/Changing-Defaults] to customize this behavior.
|
||||
|
||||
gem "cancan"
|
||||
To install CanCan, add it to your Gemfile and run the `bundle` command.
|
||||
|
||||
In <b>Rails 2</b>, add this to your environment.rb file.
|
||||
gem "cancan", :git => "git://github.com/ryanb/cancan.git", :branch => "2.0"
|
||||
|
||||
config.gem "cancan"
|
||||
|
||||
Alternatively, you can install it as a plugin.
|
||||
|
||||
rails plugin install git://github.com/ryanb/cancan.git
|
||||
|
||||
|
||||
== Getting Started
|
||||
|
||||
CanCan expects a +current_user+ method to exist in the controller. First, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]). See {Changing Defaults}[https://github.com/ryanb/cancan/wiki/changing-defaults] if you need different behavior.
|
||||
|
||||
|
||||
=== 1. Define Abilities
|
||||
|
||||
User permissions are defined in an +Ability+ class. CanCan 1.5 includes a Rails 3 generator for creating this class.
|
||||
Next generate an Ability class, this is where your permissions will be defined.
|
||||
|
||||
rails g cancan:ability
|
||||
|
||||
See {Defining Abilities}[https://github.com/ryanb/cancan/wiki/defining-abilities] for details.
|
||||
Add authorization by calling {enable_authorization}[https://github.com/ryanb/cancan/wiki/enable_authorization] in your ApplicationController.
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
enable_authorization
|
||||
end
|
||||
|
||||
This will add an authorization check locking down every controller action. If you try visiting a page, a <tt>CanCan::Unauthorized</tt> exception will be raised since you have not granted the user ability to access it.
|
||||
|
||||
|
||||
=== 2. Check Abilities & Authorization
|
||||
== Defining Abilities
|
||||
|
||||
The current user's permissions can then be checked using the <tt>can?</tt> and <tt>cannot?</tt> methods in the view and controller.
|
||||
You grant access to controller actions through the +Ability+ class which was generated above. The +current_user+ is passed in allowing you to define permissions based on user attributes. For example:
|
||||
|
||||
<% if can? :update, @article %>
|
||||
<%= link_to "Edit", edit_article_path(@article) %>
|
||||
if user
|
||||
can :access, :all
|
||||
else
|
||||
can :access, :home
|
||||
can :create, [:users, :sessions]
|
||||
end
|
||||
|
||||
Here if there is a user he will be able to perform any action on any controller. If someone is not logged in he can only access the home, users, and sessions controllers.
|
||||
|
||||
The first argument to +can+ is the action the user can perform. The second argument is the controller name they can perform that action on. You can pass <tt>:access</tt> and <tt>:all</tt> to represent any action and controller respectively.
|
||||
|
||||
As shown above, pass an array to either of these will grant permission on each item in the array. It isn't necessary to pass the +new+ action here because CanCan includes some default aliases. See the {Aliases}[https://github.com/ryanb/cancan/wiki/Aliases] page for details.
|
||||
|
||||
You can check permissions in any controller or view using the <tt>can?</tt> method.
|
||||
|
||||
<% if can? :create, :comments %>
|
||||
<%= link_to "New Comment", new_comment_path %>
|
||||
<% end %>
|
||||
|
||||
See {Checking Abilities}[https://github.com/ryanb/cancan/wiki/checking-abilities] for more information
|
||||
Here the link will only show up the user can create comments.
|
||||
|
||||
The <tt>authorize!</tt> method in the controller will raise an exception if the user is not able to perform the given action.
|
||||
|
||||
def show
|
||||
@article = Article.find(params[:id])
|
||||
authorize! :read, @article
|
||||
== Resource Conditions
|
||||
|
||||
What if you need to change authorization based on a model's attributes? You can do so by passing a hash of conditions as the last argument to +can+. For example, if you want to only allow one to access projects which he owns you can set the <tt>:user_id</tt>.
|
||||
|
||||
can :access, :projects, :user_id => user.id
|
||||
|
||||
A block can also be used for complex condition checks just like in CanCan 1, but here it is not necessary.
|
||||
|
||||
If you try visiting any of the project pages at this point you will see a <tt>CanCan::InsufficientAuthorizationCheck</tt> exception is raised. This is because the default authorization has no way to check permissions on the <tt>@project</tt> instance. You can check permissions on an object manually using the <tt>authorize!</tt> method.
|
||||
|
||||
def edit
|
||||
@project = Project.find(params[:id])
|
||||
authorize! :edit, @project
|
||||
end
|
||||
|
||||
Setting this for every action can be tedious, therefore the +load_and_authorize_resource+ method is provided to automatically authorize all actions in a RESTful style resource controller. It will use a before filter to load the resource into an instance variable and authorize it for every action.
|
||||
However this can get tedious. Instead CanCan provides a +load_and_authorize_resource+ method to load the <tt>@project</tt> instance in every controller action and authorize it.
|
||||
|
||||
class ArticlesController < ApplicationController
|
||||
class ProjectsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def show
|
||||
# @article is already loaded and authorized
|
||||
def edit
|
||||
# @project already loaded here and authorized
|
||||
end
|
||||
end
|
||||
|
||||
See {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/authorizing-controller-actions] for more information.
|
||||
The +index+ (and other collection actions) will load the <tt>@projects</tt> instance which automatically limits the projects the user is allowed to access. This is a scope so you can make further calls to +where+ to limit what is returned from the database.
|
||||
|
||||
|
||||
=== 3. Handle Unauthorized Access
|
||||
|
||||
If the user authorization fails, a <tt>CanCan::Unauthorized</tt> exception will be raised. You can catch this and modify its behavior in the +ApplicationController+.
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
rescue_from CanCan::Unauthorized do |exception|
|
||||
redirect_to root_url, :alert => exception.message
|
||||
end
|
||||
def index
|
||||
@projects = @projects.where(:hidden => false)
|
||||
end
|
||||
|
||||
See {Exception Handling}[https://github.com/ryanb/cancan/wiki/exception-handling] for more information.
|
||||
You can check permissions on instances using the <tt>can?</tt> method.
|
||||
|
||||
<%= link_to "Edit Project", edit_project_path if can? :update, @project %>
|
||||
|
||||
Here it will only show the edit link if the +user_id+ matches.
|
||||
|
||||
|
||||
=== 4. Lock It Down
|
||||
== Resource Attributes
|
||||
|
||||
If you want to ensure authorization happens on every action in your application, add +check_authorization+ to your ApplicationController.
|
||||
In CanCan 2.0 it is possible to define permissions on specific resource attributes. For example, if you want to allow a user to only update the name and priority of a project, pass that as the third argument to +can+.
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
check_authorization
|
||||
end
|
||||
can :update, :projects, [:name, :priority]
|
||||
|
||||
This will raise an exception if authorization is not performed in an action. If you want to skip this add +skip_authorization_check+ to a controller subclass. See {Ensure Authorization}[https://github.com/ryanb/cancan/wiki/Ensure-Authorization] for more information.
|
||||
If you use this in combination with +load_and_authorize_resource+ it will ensure that only those two attributes exist in <tt>params[:project]</tt> when updating the project. If you do this everywhere it will not be necessary to use +attr_accessible+ in your models.
|
||||
|
||||
You can combine this with a hash of conditions. For example, here the user can update all attributes except the price when the product is discontinued.
|
||||
|
||||
== Wiki Docs
|
||||
can :update, :products
|
||||
cannot :update, :products, :price, :discontinued => true
|
||||
|
||||
* {Upgrading to 1.6}[https://github.com/ryanb/cancan/wiki/Upgrading-to-1.6]
|
||||
* {Defining Abilities}[https://github.com/ryanb/cancan/wiki/Defining-Abilities]
|
||||
* {Checking Abilities}[https://github.com/ryanb/cancan/wiki/Checking-Abilities]
|
||||
* {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/Authorizing-Controller-Actions]
|
||||
* {Exception Handling}[https://github.com/ryanb/cancan/wiki/Exception-Handling]
|
||||
* {Changing Defaults}[https://github.com/ryanb/cancan/wiki/Changing-Defaults]
|
||||
* {See more}[https://github.com/ryanb/cancan/wiki]
|
||||
You can check permissions on specific attributes to determine what to show in the form.
|
||||
|
||||
<%= f.text_field :name if can? :update, @project, :name %>
|
||||
|
||||
== Questions or Problems?
|
||||
|
||||
If you have any issues with CanCan which you cannot find the solution to in the documentation[https://github.com/ryanb/cancan/wiki], please add an {issue on GitHub}[https://github.com/ryanb/cancan/issues] or fork the project and send a pull request.
|
||||
|
||||
To get the specs running you should call +bundle+ and then +rake+. See the {spec/README}[https://github.com/ryanb/cancan/blob/master/spec/README.rdoc] for more information.
|
||||
|
||||
|
||||
== Special Thanks
|
||||
|
||||
CanCan was inspired by declarative_authorization[https://github.com/stffn/declarative_authorization/] and aegis[https://github.com/makandra/aegis]. Also many thanks to the CanCan contributors[https://github.com/ryanb/cancan/contributors]. See the CHANGELOG[https://github.com/ryanb/cancan/blob/master/CHANGELOG.rdoc] for the full list.
|
||||
|
|
|
@ -22,21 +22,12 @@ module CanCan
|
|||
#
|
||||
# You can also pass the class instead of an instance (if you don't have one handy).
|
||||
#
|
||||
# can? :create, Project
|
||||
# can? :create, :projects
|
||||
#
|
||||
# Nested resources can be passed through a hash, this way conditions which are
|
||||
# dependent upon the association will work when using a class.
|
||||
#
|
||||
# can? :create, @category => Project
|
||||
#
|
||||
# Any additional arguments will be passed into the "can" block definition. This
|
||||
# can be used to pass more information about the user's request for example.
|
||||
#
|
||||
# can? :create, Project, request.remote_ip
|
||||
#
|
||||
# can :create Project do |project, remote_ip|
|
||||
# # ...
|
||||
# end
|
||||
# can? :create, @category => :projects
|
||||
#
|
||||
# Not only can you use the can? method in the controller and view (see ControllerAdditions),
|
||||
# but you can also call it directly on an ability instance.
|
||||
|
@ -71,22 +62,22 @@ module CanCan
|
|||
# Defines which abilities are allowed using two arguments. The first one is the action
|
||||
# you're setting the permission for, the second one is the class of object you're setting it on.
|
||||
#
|
||||
# can :update, Article
|
||||
# can :update, :articles
|
||||
#
|
||||
# You can pass an array for either of these parameters to match any one.
|
||||
# Here the user has the ability to update or destroy both articles and comments.
|
||||
#
|
||||
# can [:update, :destroy], [Article, Comment]
|
||||
# can [:update, :destroy], [:articles, :comments]
|
||||
#
|
||||
# You can pass :all to match any object and :access to match any action. Here are some examples.
|
||||
#
|
||||
# can :access, :all
|
||||
# can :update, :all
|
||||
# can :access, Project
|
||||
# can :access, :projects
|
||||
#
|
||||
# You can pass a hash of conditions as the third argument. Here the user can only see active projects which he owns.
|
||||
#
|
||||
# can :read, Project, :active => true, :user_id => user.id
|
||||
# can :read, :projects, :active => true, :user_id => user.id
|
||||
#
|
||||
# See ActiveRecordAdditions#accessible_by for how to use this in database queries. These conditions
|
||||
# are also used for initial attributes when building a record in ControllerAdditions#load_resource.
|
||||
|
@ -94,7 +85,7 @@ module CanCan
|
|||
# If the conditions hash does not give you enough control over defining abilities, you can use a block
|
||||
# along with any Ruby code you want.
|
||||
#
|
||||
# can :update, Project do |project|
|
||||
# can :update, :projects do |project|
|
||||
# project.groups.include?(user.group)
|
||||
# end
|
||||
#
|
||||
|
@ -102,22 +93,16 @@ module CanCan
|
|||
# will be denied access. The downside to using a block is that it cannot be used to generate
|
||||
# conditions for database queries.
|
||||
#
|
||||
# You can pass custom objects into this "can" method, this is usually done with a symbol
|
||||
# and is useful if a class isn't available to define permissions on.
|
||||
# IMPORTANT: Neither a hash of conditions or a block will be used when checking permission on a symbol.
|
||||
#
|
||||
# can :read, :stats
|
||||
# can? :read, :stats # => true
|
||||
#
|
||||
# IMPORTANT: Neither a hash of conditions or a block will be used when checking permission on a class.
|
||||
#
|
||||
# can :update, Project, :priority => 3
|
||||
# can? :update, Project # => true
|
||||
# can :update, :projects, :priority => 3
|
||||
# can? :update, :projects # => true
|
||||
#
|
||||
# If you pass no arguments to +can+, the action, class, and object will be passed to the block and the
|
||||
# block will always be executed. This allows you to override the full behavior if the permissions are
|
||||
# defined in an external source such as the database.
|
||||
#
|
||||
# can do |action, object_class, object|
|
||||
# can do |action, subject, object|
|
||||
# # check the database and return true/false
|
||||
# end
|
||||
#
|
||||
|
@ -133,7 +118,7 @@ module CanCan
|
|||
# A block can be passed just like "can", however if the logic is complex it is recommended
|
||||
# to use the "can" method.
|
||||
#
|
||||
# cannot :read, Product do |product|
|
||||
# cannot :read, :projects do |product|
|
||||
# product.invisible?
|
||||
# end
|
||||
#
|
||||
|
@ -144,19 +129,19 @@ module CanCan
|
|||
# Alias one or more actions into another one.
|
||||
#
|
||||
# alias_action :update, :destroy, :to => :modify
|
||||
# can :modify, Comment
|
||||
# can :modify, :comments
|
||||
#
|
||||
# Then :modify permission will apply to both :update and :destroy requests.
|
||||
#
|
||||
# can? :update, Comment # => true
|
||||
# can? :destroy, Comment # => true
|
||||
# can? :update, :comments # => true
|
||||
# can? :destroy, :comments # => true
|
||||
#
|
||||
# This only works in one direction. Passing the aliased action into the "can?" call
|
||||
# will not work because aliases are meant to generate more generic actions.
|
||||
#
|
||||
# alias_action :update, :destroy, :to => :modify
|
||||
# can :update, Comment
|
||||
# can? :modify, Comment # => false
|
||||
# can :update, :comments
|
||||
# can? :modify, :comments # => false
|
||||
#
|
||||
# The following aliases are added by default for conveniently mapping common controller actions.
|
||||
#
|
||||
|
|
|
@ -2,26 +2,22 @@ class Ability
|
|||
include CanCan::Ability
|
||||
|
||||
def initialize(user)
|
||||
# Define abilities for the passed in user here. For example:
|
||||
# Define abilities for the passed in (current) user. For example:
|
||||
#
|
||||
# user ||= User.new # guest user (not logged in)
|
||||
# if user.admin?
|
||||
# can :manage, :all
|
||||
# if user
|
||||
# can :access, :all
|
||||
# else
|
||||
# can :read, :all
|
||||
# can :access, :home
|
||||
# can :create, [:users, :sessions]
|
||||
# end
|
||||
#
|
||||
# The first argument to `can` is the action you are giving the user permission to do.
|
||||
# If you pass :manage it will apply to every action. Other common actions here are
|
||||
# :read, :create, :update and :destroy.
|
||||
# Here if there is a user he will be able to perform any action on any controller.
|
||||
# If someone is not logged in he can only access the home, users, and sessions controllers.
|
||||
#
|
||||
# The second argument is the resource the user can perform the action on. If you pass
|
||||
# :all it will apply to every resource. Otherwise pass a Ruby class of the resource.
|
||||
#
|
||||
# The third argument is an optional hash of conditions to further filter the objects.
|
||||
# For example, here the user can only update published articles.
|
||||
#
|
||||
# can :update, Article, :published => true
|
||||
# The first argument to `can` is the action the user can perform. The second argument
|
||||
# is the controller name they can perform that action on. You can pass :access and :all
|
||||
# to represent any action and controller respectively. Passing an array to either of
|
||||
# these will grant permission on each item in the array.
|
||||
#
|
||||
# See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user