Authorization Gem for Ruby on Rails.
Go to file
2012-09-27 23:18:45 -05:00
lib load hooks return ActiveRecord::Model in Rails 4, use Concern 2012-09-27 23:18:45 -05:00
spec fix namespace split, so we can use / for namespace 2012-06-29 18:29:08 +07:00
.gitignore checks if active record responds to 'joins', so this can work with internuity's quick_scopes gem; added .swp files to git ignore 2010-11-13 02:21:56 +08:00
.rbenv-version bringing up to date with master branch 2012-06-26 17:10:01 -07:00
.rspec getting all specs passing again 2011-03-23 19:47:34 -07:00
.travis.yml fixing Ruby versions running on travis.yml 2012-05-11 08:43:49 -07:00
cancan.gemspec tests passing with Rails 3.2.6 2012-06-26 15:13:35 -07:00
CHANGELOG.rdoc releasing 1.6.8 2012-06-25 16:17:32 -07:00
Gemfile getting data_mapper and mongoid specs passwing with latest versions 2012-04-22 17:01:10 -07:00
init.rb turning into a funtioning Rails plugin 2009-11-16 19:24:04 -08:00
LICENSE improving DataMapper adapter and specs 2011-01-05 13:22:06 -08:00
Rakefile adding spec_all task for running specs for all model adapters 2010-12-30 15:50:40 -08:00
README.rdoc bringing up to date with master branch 2012-06-26 17:10:01 -07:00

= CanCan {<img src="https://secure.travis-ci.org/ryanb/cancan.png" />}[http://travis-ci.org/ryanb/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]

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].


== Setup

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.

To install CanCan, add it to your Gemfile and run the `bundle` command.

  gem "cancan", :git => "git://github.com/ryanb/cancan.git", :branch => "2.0"

Next generate an Ability class, this is where your permissions will be defined.

  rails g cancan:ability

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.


== Defining Abilities

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 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 %>

Here the link will only show up the user can create comments.


== 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

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 ProjectsController < ApplicationController
    load_and_authorize_resource
    def edit
      # @project already loaded here and authorized
    end
  end

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.

  def index
    @projects = @projects.where(:hidden => false)
  end

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.


== Resource Attributes

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+.

  can :update, :projects, [:name, :priority]

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.

  can :update, :products
  cannot :update, :products, :price, :discontinued => true

You can check permissions on specific attributes to determine what to show in the form.

  <%= f.text_field :name if can? :update, @project, :name %>