Skip to content

CanCanCan — Authorization and Abilities in Rails

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about CanCanCan. We cover key concepts, practical examples, and best practices to help you master this topic.

CanCanCan provides role-based authorization with an Ability class defining user permissions, supporting block conditions, hash conditions, resource loading, and accessible scopes.

What You'll Learn

By the end of this tutorial, you'll define abilities, load and authorize resources, implement role-based access, use hash and block conditions, and test authorization.

Why Authorization Matters

Authentication identifies users. Authorization controls what they can do. CanCanCan separates authorization rules from controllers for clean, testable permission management.

Real-World Use

A content management system defines abilities for editor, author, and admin roles. Editors can publish any content. Authors can edit only their own. Admins have full access.

Authorization Path

flowchart LR
  A[Authentication] --> B[Authorization]
  B --> C[Ability Class]
  B --> D[Roles]
  B --> E[Conditions]
  B --> F[Loading]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Ability Class

Define permissions in the Ability class.

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new

    if user.admin?
      can :manage, :all
    elsif user.editor?
      can :manage, Post
      can :manage, Category
      can :read, :all
      cannot :destroy, User
    elsif user.author?
      can :read, Post
      can :create, Post
      can :update, Post, user_id: user.id
      can :destroy, Post, user_id: user.id
      can :read, Comment
      can :create, Comment
    else
      can :read, Post, published: true
      can :create, User
    end
  end
end

Loading Resources

Automatically load and authorize resources.

class PostsController < ApplicationController
  load_and_authorize_resource

  def index
    # @posts is already loaded and authorized
  end

  def show
    # @post is loaded and authorized
  end

  def create
    # @post is initialized and authorized
    if @post.save
      redirect_to @post, notice: "Post created."
    else
      render :new
    end
  end
end

# Custom loading
class PostsController < ApplicationController
  load_resource only: [:show, :edit, :update, :destroy]
  authorize_resource only: [:new, :create]
end

Hash Conditions

Define abilities with hash conditions.

class Ability
  def initialize(user)
    can :read, Post, published: true, deleted_at: nil
    can :update, Post, user_id: user.id
    can :manage, Post, organization_id: user.organization_id

    # Nested hash conditions
    can :read, Comment, post: { published: true, organization_id: user.organization_id }

    # Array conditions
    can :manage, [Post, Comment, Category], organization_id: user.organization_id
  end
end

Block Conditions

Use blocks for complex authorization logic.

class Ability
  def initialize(user)
    can :manage, Subscription do |subscription|
      subscription.user_id == user.id ||
        user.admin? ||
        (user.manager? && subscription.organization_id == user.organization_id)
    end

    can :export, Report do |report|
      report.completed? && report.user_id == user.id
    end

    can :publish, Post do |post|
      post.draft? && post.user_id == user.id && user.editor?
    end
  end
end

Common Mistakes

1. Defining Permissions in Controllers

Authorization rules belong in the Ability class, not scattered across controllers.

2. Not Handling Unauthorized Access

CanCanCan raises CanCan::AccessDenied. Rescue and respond with 403.

3. Overusing Block Conditions

Block conditions cannot be used with accessible_by scopes. Prefer hash conditions.

4. Forgetting to Register Abilities for New Actions

Custom controller actions need ability definitions for each action.

5. Not Testing Abilities

Authorization is critical security logic. Test ability definitions in ability specs.

Practice Questions

1. What is the Ability class?

The central class where all user permissions are defined.

2. What does load_and_authorize_resource do?

Automatically loads the resource and checks authorization before the action.

3. What is the difference between hash and block conditions?

Hash conditions can be used with accessible_by. Block conditions run in Ruby.

4. How do you define admin access to everything?

can :manage, :all in the admin role.

5. Challenge: Define abilities for a multi-tenant app.

class Ability
  def initialize(user)
    can :manage, Project, organization_id: user.organization_id
    can :read, Task, project: { organization_id: user.organization_id }
    can :manage, Task, user_id: user.id
    can :manage, Organization, id: user.organization_id if user.admin?
  end
end

FAQ

What is the difference between can and cannot?

can grants permission. cannot denies it even if a broader can matches.

How do I check authorization manually?

Use authorize! :action, @resource in controllers.

What is accessible_by?

A scope that filters records the user can access: Post.accessible_by(current_ability).

Can I use CanCanCan with API-only apps?

Yes. Use authorize! in API controllers and rescue AccessDenied.

What happens when authorization fails?

CanCanCan raises CanCan::AccessDenied. Rescue in ApplicationController.

Mini Project: Role-Based Blog Authorization

Implement authorization for a blog with roles.

class Ability
  def initialize(user)
    user ||= User.new
    if user.admin?
      can :manage, :all
    elsif user.editor?
      can :manage, [Post, Category]
      can :read, Comment
      can :destroy, Comment, post: { user_id: user.id }
    elsif user.author?
      can [:create, :read], Post
      can [:update, :destroy], Post, user_id: user.id
      can :create, Comment
    else
      can :read, Post, published: true
    end
  end
end

What's Next

Rails API Mode Rails Serializers Rails Testing RSpec

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro