How to Use Cancancan for Authorization in Ruby on Rails
Use
cancancan by defining user abilities in an Ability class and checking permissions in controllers or views with authorize! or can?. Install the gem, generate the ability file, then specify what users can do based on roles or conditions.Syntax
The core of Cancancan is the Ability class where you define user permissions using can and cannot methods. You specify the action (like :read, :create), the resource (model class), and optional conditions.
In controllers or views, use authorize! :action, @resource to enforce permissions or can? :action, @resource to check if a user can perform an action.
ruby
class Ability include CanCan::Ability def initialize(user) user ||= User.new # guest user if user.admin? can :manage, :all else can :read, Article can :create, Comment cannot :destroy, Article end end end
Example
This example shows how to set up Cancancan in a Rails app with a simple Ability class and how to use authorize! in a controller to restrict access.
ruby
# Gemfile # Add cancancan gem gem 'cancancan', '~> 3.3' # Run bundle install # Generate Ability class rails g cancan:ability # app/models/ability.rb class Ability include CanCan::Ability def initialize(user) user ||= User.new if user.admin? can :manage, :all else can :read, Article can :create, Comment cannot :destroy, Article end end end # app/controllers/articles_controller.rb class ArticlesController < ApplicationController before_action :set_article, only: [:show, :destroy] def show authorize! :read, @article # show article end def destroy authorize! :destroy, @article @article.destroy redirect_to articles_path end private def set_article @article = Article.find(params[:id]) end end
Output
If a non-admin user tries to destroy an article, Cancancan raises CanCan::AccessDenied and prevents the action.
Common Pitfalls
- Not handling
CanCan::AccessDeniedexceptions, which causes app crashes instead of friendly error messages. - Forgetting to define abilities for guest users (
user ||= User.newis important). - Using
authorize!without loading the resource first can cause errors. - Mixing authorization logic in controllers instead of centralizing in
Abilityclass.
ruby
# Wrong: No guest user handling class Ability include CanCan::Ability def initialize(user) if user&.admin? can :manage, :all end end end # Right: Handle guest user class Ability include CanCan::Ability def initialize(user) user ||= User.new if user.admin? can :manage, :all else can :read, Article end end end
Quick Reference
| Method | Purpose | Example |
|---|---|---|
| can | Define allowed action on resource | can :read, Article |
| cannot | Define disallowed action | cannot :destroy, Article |
| authorize! | Check permission and raise error if denied | authorize! :update, @article |
| can? | Check permission, returns true/false | can? :create, Comment |
| load_and_authorize_resource | Controller helper to load and authorize | before_action :load_and_authorize_resource |
Key Takeaways
Define all user permissions centrally in the Ability class using can and cannot.
Use authorize! in controllers to enforce permissions and handle AccessDenied exceptions gracefully.
Always handle guest users by initializing user with User.new in Ability.
Use load_and_authorize_resource to simplify controller authorization.
Test authorization rules to avoid unexpected access or errors.