CanCanCan Documentation
repository·develop·Indexed 26 days ago
https://github.com/cancancommunity/cancancanAn 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.
What's inside CanCanCan
- 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.
Define permissions for specific attributes
developYou 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
canmethod in yourAbilityclass.For example, to allow a user to only read the
:first_nameand:last_nameof aUserinstance:can :read, User, [:first_name, :last_name]Configure dependencies for custom adapters using Appraisal
developCanCanCan uses Appraisals to test adapters against different dependency versions. To add your own dependencies for a custom adapter, create an entry in your
Appraisalfile.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 endAfter defining your dependencies, install them using:
bundle exec appraisal installCombine multiple abilities for the same resource
developYou can define multiple
canrules for the same resource. When multiplecanrules 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
releasedORpreview, you define two separatecanrules.can :read, Project, released: true can :read, Project, preview: trueRemove permissions using the `cannot` method
developWhile CanCanCan assumes no access by default, you can explicitly remove specific permissions using the
cannotmethod. This is typically used after a more genericcandefinition to create exceptions. Thecannotmethod accepts the same arguments as thecanmethod (action and subject).can :manage, Project cannot :destroy, ProjectCheck abilities for users other than current_user
developWhile
can?in views/controllers automatically usescurrent_user, you can manually check the abilities of any user by instantiating theAbilityclass directly.Alternatively, you can delegate the
can?method to anabilitymethod within yourUsermodel 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, @articleLoad and authorize through `has_many :through` associations
developExample 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 } }Configure Cucumber to handle AccessDenied exceptions
developBy default, Cucumber ignores
rescue_fromcalls inApplicationControllerand reportsCanCan::AccessDeniedexceptions directly. To allow Rails to handle these exceptions (for full integration testing), you have two options:- Global configuration: Set
ActionController::Base.allow_rescue = truein yourfeatures/support/env.rbfile. - Scenario tagging: Tag specific scenarios with
@allow-rescueto enable therescue_fromblock only for those cases.
# in features/support/env.rb ActionController::Base.allow_rescue = true@allow-rescue Scenario: Update Article- Global configuration: Set
Handle singleton resources with `has_one` associations
developIf a parent has a
has_oneassociation with a child, use the:singletonoption. This instructs CanCanCan to use@parent.childand@parent.build_childinstead of collection methods.class TasksController < ApplicationController load_and_authorize_resource :project load_and_authorize_resource :task, through: :project, singleton: true endHandle CanCan::AccessDenied exceptions with Devise
developTo 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 endVerify if a user can perform an action on all records of a class
developBecause
can? :action, ClassNameignores condition hashes and returnstrueif 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_byagainst the total count of the class.Article.accessible_by(current_ability).count == Article.countDefine abilities using a Hash of conditions
developThe
canmethod 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
nilto match records where a value is absent (e.g.,members: { id: nil }).
- Direct column matching: Use the database column name (e.g.,