CanCanCan Documentation

repository·develop·Indexed 26 days ago

https://github.com/cancancommunity/cancancan

An authorization library for Ruby and Ruby on Rails that centralizes permission logic. It provides an Ability class to define access rules, helpers like can? and cannot? for views and controllers, and the load_and_authorize_resource method to automate resource loading and authorization in RESTful controllers.

Tokens
18.9K
Snippets
72
Records
104
Agent score
90%

What's inside CanCanCan

  1. Get started with CanCanCan

    develop
    CanCanCan is an authorization library for Ruby. To begin using it, you should follow the developer guide which covers installation, defining abilities, checking permissions, and using controller helpers. For a quick start, you can skip the introduction and proceed directly to the installation guide.
  2. Define permissions for specific attributes

    develop

    You can restrict a user's ability to a specific subset of an object's attributes by passing an array of attribute names as the third argument to the can method in your Ability class.

    For example, to allow a user to only read the :first_name and :last_name of a User instance:

    can :read, User, [:first_name, :last_name]
  3. Configure dependencies for custom adapters using Appraisal

    develop

    CanCanCan uses Appraisals to test adapters against different dependency versions. To add your own dependencies for a custom adapter, create an entry in your Appraisal file.

    Example configuration:

    appraise 'cancancan_custom_adapter' do
      gem 'activerecord', '~> 5.0.2', require: 'active_record'
    
      gemfile.platforms :jruby do
        gem 'jdbc-postgres'
      end
    
      gemfile.platforms :ruby, :mswin, :mingw do
        gem 'pg', '~> 0.21'
      end
    end

    After defining your dependencies, install them using:

    bundle exec appraisal install
  4. Combine multiple abilities for the same resource

    develop

    You can define multiple can rules for the same resource. When multiple can rules are defined, they are logically OR'ed together. This means a user will be granted access if they satisfy ANY of the defined rules.

    For example, if you want a user to be able to read projects that are either released OR preview, you define two separate can rules.

    can :read, Project, released: true
    can :read, Project, preview: true
  5. Remove permissions using the `cannot` method

    develop

    While CanCanCan assumes no access by default, you can explicitly remove specific permissions using the cannot method. This is typically used after a more generic can definition to create exceptions. The cannot method accepts the same arguments as the can method (action and subject).

    can :manage, Project
    cannot :destroy, Project
  6. Check abilities for users other than current_user

    develop

    While can? in views/controllers automatically uses current_user, you can manually check the abilities of any user by instantiating the Ability class directly.

    Alternatively, you can delegate the can? method to an ability method within your User model to make the syntax cleaner.

    # Manual check for a specific user
    Ability.new(some_user).can? :update, @article
    
    # Recommended: Delegate in app/models/user.rb
    class User
      delegate :can?, :cannot?, to: :ability
    
      def ability
        @ability ||= Ability.new(self)
      end
    end
    
    # Now you can call it directly on the user instance
    some_user.can? :update, @article
  7. Load and authorize through `has_many :through` associations

    develop

    Example setup:

    # Models
    class User < ActiveRecord::Base
      has_many :groups_users
      has_many :groups, through: :groups_users
    end
    
    class GroupsUsers < ActiveRecord::Base
      belongs_to :group, inverse_of: :groups_users
      belongs_to :user, inverse_of: :groups_users
    end
    
    # Controller
    class UsersController < ApplicationController
      load_and_authorize_resource :group
      load_and_authorize_resource through: :group
    end
    
    # Ability.rb
    # IMPORTANT: Define through the join model (groups_users), not the target (groups)
    can :create, User, groups_users: { group: { CONDITION_ON_GROUP } }
  8. Configure Cucumber to handle AccessDenied exceptions

    develop

    By default, Cucumber ignores rescue_from calls in ApplicationController and reports CanCan::AccessDenied exceptions directly. To allow Rails to handle these exceptions (for full integration testing), you have two options:

    1. Global configuration: Set ActionController::Base.allow_rescue = true in your features/support/env.rb file.
    2. Scenario tagging: Tag specific scenarios with @allow-rescue to enable the rescue_from block only for those cases.
    # in features/support/env.rb
    ActionController::Base.allow_rescue = true
    @allow-rescue
    Scenario: Update Article
  9. Handle singleton resources with `has_one` associations

    develop

    If a parent has a has_one association with a child, use the :singleton option. This instructs CanCanCan to use @parent.child and @parent.build_child instead of collection methods.

    class TasksController < ApplicationController
      load_and_authorize_resource :project
      load_and_authorize_resource :task, through: :project, singleton: true
    end
  10. Handle CanCan::AccessDenied exceptions with Devise

    develop

    To provide a smooth user experience when authorization fails, you can rescue from CanCan::AccessDenied. A common pattern is to redirect unauthenticated users to the login page (while saving their intended destination in the session) and redirect authenticated users back to the root URL with an alert message.

    rescue_from CanCan::AccessDenied do |exception|
      if current_user.nil?
        session[:next] = request.fullpath
        redirect_to login_url, alert: 'You have to log in to continue.'
      else
        respond_to do |format|
          format.json { render nothing: true, status: :not_found }
          format.html { redirect_to main_app.root_url, alert: exception.message }
          format.js   { render nothing: true, status: :not_found }
        end
      end
    end
  11. Verify if a user can perform an action on all records of a class

    develop

    Because can? :action, ClassName ignores condition hashes and returns true if the user has permission for that class, you cannot use it to determine if a user has permission to access every record in a table.

    To check if the user's permissions allow them to see all records of a specific class, compare the count of records accessible via accessible_by against the total count of the class.

    Article.accessible_by(current_ability).count == Article.count
  12. Define abilities using a Hash of conditions

    develop

    The can method accepts a third argument: a hash of conditions used to restrict which records a permission applies to. Keys in this hash must be either a database column name or an association name of the model.

    Common patterns include:

    • Direct column matching: Use the database column name (e.g., user_id: user.id).
    • Association matching: Use the association name defined in your model (e.g., owner: user).
    • Range/Array matching: Use a range or array to match multiple values (e.g., priority: 1..3).
    • Negative matching: Pass nil to match records where a value is absent (e.g., members: { id: nil }).