Authorization Gem for Ruby on Rails.
Go to file
2011-01-05 23:12:59 -08:00
lib Use Mongoid::Matchers#matches? instead of a database query in MongoidAdapter#matches_conditions_hash? 2011-01-05 23:12:59 -08:00
spec Use Mongoid::Matchers#matches? instead of a database query in MongoidAdapter#matches_conditions_hash? 2011-01-05 23:12:59 -08: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
.rspec adding Gemfile, to get specs running just bundle and rake - closes #163 2010-10-05 10:09:37 -07:00
.rvmrc adding .rvmrc file to switch to Ruby 1.8.7 for development 2010-12-29 13:04:22 -08:00
cancan.gemspec move mongoid/dm gems into Gemfile and load dynamically based on MODEL_ADAPTER env variable 2010-12-29 13:22:14 -08:00
CHANGELOG.rdoc releasing version 1.4.1 2010-11-12 10:51:29 -08:00
Gemfile improving DataMapper adapter and specs 2011-01-05 13:22:06 -08: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 updating readme and documentation 2010-10-05 16:18:35 -07:00

= CanCan

Wiki[http://wiki.github.com/ryanb/cancan] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-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.


== Installation

In <b>Rails 3</b>, add this to your Gemfile.

  gem "cancan"

In <b>Rails 2</b>, add this to your environment.rb file.

  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. If you have not already, set up some authentication (such as Authlogic[http://github.com/binarylogic/authlogic] or Devise[http://github.com/plataformatec/devise]). See {Changing Defaults}[http://wiki.github.com/ryanb/cancan/changing-defaults] if you need different behavior.

Next create a class called +Ability+ in "models/ability.rb" or anywhere else in the load path. It should look similar to this.

  class Ability
    include CanCan::Ability

    def initialize(user)
      if user.admin?
        can :manage, :all
      else
        can :read, :all
      end
    end
  end

The +current_user+ is passed in to this method which is where the abilities are defined. See the "Defining Abilities" section below for more information.

The current user's permissions can be accessed using the "can?" and "cannot?" methods in the view and controller.

  <% if can? :update, @article %>
    <%= link_to "Edit", edit_article_path(@article) %>
  <% end %>

See {Checking Abilities}[http://wiki.github.com/ryanb/cancan/checking-abilities] for more information

The "authorize!" 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
  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 each action.

  class ArticlesController < ApplicationController
    load_and_authorize_resource

    def show
      # @article is already loaded and authorized
    end
  end

See {Authorizing Controller Actions}[http://wiki.github.com/ryanb/cancan/authorizing-controller-actions] for more information

If the user authorization fails, a <tt>CanCan::AccessDenied</tt> exception will be raised. You can catch this and modify its behavior in the +ApplicationController+.

  class ApplicationController < ActionController::Base
    rescue_from CanCan::AccessDenied do |exception|
      flash[:alert] = exception.message
      redirect_to root_url
    end
  end

See {Exception Handling}[http://wiki.github.com/ryanb/cancan/exception-handling] for more information.


== Defining Abilities

As shown above, the +Ability+ class is where all user permissions are defined. The current user model is passed into the initialize method so the permissions can be modified based on any user attributes. CanCan makes no assumption about how roles are handled in your application. See {Role Based Authorization}[http://wiki.github.com/ryanb/cancan/role-based-authorization] for an example.

The +can+ method is used to define permissions and requires 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

You can pass an array for either of these parameters to match any one. In this case the user will have the ability to update or destroy both articles and comments.

  can [:update, :destroy], [Article, Comment]

Use :+manage+ to represent any action and :+all+ to represent any class. Here are some examples.

  can :manage, Article   # has permissions to do anything to articles
  can :read, :all        # has permission to read any model
  can :manage, :all      # has permission to do anything to any model

You can pass a hash of conditions as the third argument to further define what the user is able to access. Here the user will only have permission to read active projects which he owns.

  can :read, Project, :active => true, :user_id => user.id

See {Defining Abilities with Hashes}[http://wiki.github.com/ryanb/cancan/defining-abilities-with-hashes] for more information.

Blocks can also be used if you need more control.

  can :update, Project do |project|
    project.groups.include?(user.group)
  end

If the block returns true then the user has that ability for that project, otherwise he will be denied access. See {Defining Abilities with Blocks}[http://wiki.github.com/ryanb/cancan/defining-abilities-with-blocks] for more information.


== Aliasing Actions

You will usually be working with four actions when defining and checking permissions: :+read+, :+create+, :+update+, :+destroy+. These aren't the same as the 7 RESTful actions in Rails. CanCan automatically adds some default aliases for mapping those actions.

  alias_action :index, :show, :to => :read
  alias_action :new, :to => :create
  alias_action :edit, :to => :update

Notice the +edit+ action is aliased to +update+. This means if the user is able to update a record he also has permission to edit it. You can define your own aliases in the +Ability+ class.

  alias_action :update, :destroy, :to => :modify
  can :modify, Comment
  can? :update, Comment # => true

The +alias_action+ method is an instance method and usually called in +initialize+. See {Custom Actions}[http://wiki.github.com/ryanb/cancan/custom-actions] for information on adding other actions.


== Fetching Records

It is possible to fetch records which the user has permission to read using the +accessible_by+ scope in Active Record.

  @articles = Article.accessible_by(current_ability)

Since version 1.4 this is done automatically when loading resources in the index action, so one rarely needs to do it manually.

This will only work when abilities are defined using hash conditions, not blocks. See {Fetching Records}[http://wiki.github.com/ryanb/cancan/fetching-records] for more information.


== Additional Docs

* {Upgrading to 1.4}[http://github.com/ryanb/cancan/wiki/Upgrading-to-1.4]
* {Nested Resources}[http://wiki.github.com/ryanb/cancan/nested-resources]
* {Testing Abilities}[http://wiki.github.com/ryanb/cancan/testing-abilities]
* {Accessing Request Data}[http://wiki.github.com/ryanb/cancan/accessing-request-data]
* {Admin Namespace}[http://wiki.github.com/ryanb/cancan/admin-namespace]
* {See more}[http://wiki.github.com/ryanb/cancan/]


== Questions or Problems?

If you have any issues with CanCan which you cannot find the solution to in the documentation, please add an {issue on GitHub}[http://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+. Specs currently do not work in Ruby 1.9 due to the RR mocking framework.


== Special Thanks

CanCan was inspired by declarative_authorization[http://github.com/stffn/declarative_authorization/] and aegis[http://github.com/makandra/aegis]. Also many thanks to the CanCan contributors[http://github.com/ryanb/cancan/contributors]. See the CHANGELOG[http://github.com/ryanb/cancan/blob/master/CHANGELOG.rdoc] for the full list.