Empower Your Rails Application with CanCanCan Gem

railsMarch 05, 2024Dotby Alkesh Ghorpade

Introduction

In the vast realm of Ruby on Rails development, managing user permissions and authorization can be challenging. However, with the CanCanCan gem, developers can streamline this process and ensure that only authorized users access specific resources within their application. CanCanCan simplifies role-based access control (RBAC) implementation and makes it easier to define and manage user permissions.

In this blog post, we'll explore the CanCanCan gem in detail, discussing its features, installation process, usage, and best practices for integrating it into your Rails applications.

What is CanCanCan?

CanCanCan is an authorization library for Ruby on Rails applications. It is the successor of the original CanCan gem created by Ryan Bates. CanCanCan is a community-driven effort that provides an elegant and easy-to-use solution for managing user permissions.

Key Features

  • Role-based Access Control (RBAC): CanCanCan allows developers to define roles and permissions for users within their Rails application. This role-based approach simplifies the process of managing access control.

  • Ability Class: CanCanCan introduces the concept of an Ability class, which serves as a centralized location for defining permissions. Developers can specify a user's actions on specific resources based on their role.

  • Simple Syntax: CanCanCan utilizes a straightforward syntax for defining abilities, making it easy for developers to understand and maintain authorization rules.

  • Flexibility: CanCanCan offers flexibility in defining authorization rules. Developers can use conditions, blocks, or custom methods to determine whether a user has permission to perform a specific action.

Installation

To install CanCanCan in your Rails application, add it to your Gemfile:

gem 'cancancan'

Then, run bundle install to install the gem:

bundle install

Execute the provided command to generate a boilerplate abilities file.

rails generate cancan:ability

The above command generates the following file. All the permissions will be defined in this file.

# /app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
  end
end

Usage

You need to define the checks in the ability.rb file. There are two basic methods in CanCanCan that you will use. The following code will define which set or roles users can perform the actions. The below code defines the check.

can actions, subjects, conditions

To verify if the user is authorized to perform a particular action, you need to call the can? method.

can? action, subject

Let's take an example to understand the code more clearly. Consider a Rails application where you have a Post model that belongs to a User. A post can only be updated by a user.

class Post < ApplicationRecord
  belongs_to :user
end

To ensure only the post's author can edit the post, you need to define the following permissions in the ability.rb file.

class Ability
  include CanCan::Ability

  def initialize(user)
    can :update, Post, user: user
  end
end

You can check if the logged-in user can update the post by adding the can? method in the update action of the PostController.

class PostController < ApplicationController
  def update
    @post = Post.find(params[:id])
    can? :update, @post
  end
end

CanCanCan utilizes the current_user method (assumed to be defined) when checking permissions via can? in controllers or views.

Note: By default, CanCanCan assumes no one can do any action on any object. The code can :update, Post, user: user specifies the user can update the post if it is its author.

A complete check on Post actions can be defined as below:

class Ability
  include CanCan::Ability

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

    if user.admin?
      can :manage, :all
    else
      can :read, Post
      can :create, Post
      can :update, Post do |post|
        post.user == user
      end
      can :destroy, Post do |post|
        post.user == user
      end
    end
  end
end

Actions

CanCanCan defines four aliases (:read, :create, :update, :destroy) for frequently used actions, distinct from the complete set of seven RESTful actions in Rails. These aliases are automatically mapped to corresponding controller actions by CanCanCan for efficient permission management.

read: [:index, :show]
create: [:new, :create]
update: [:edit, :update]
destroy: [:destroy]

This not only gives you the ability to define can :read, Post, but you can also verify:

can? :show, @post

can? :edit, @post

CanCanCan also provides a :manage action. It grants users all CRUD (Create, Read, Update, Destroy) permissions for a specific resource. You need to define the method as below:

can :manage, Post

Verify user abilities

While current_user is convenient, CanCanCan's true power lies in permission checks beyond the active user. Imagine needing to determine if a specific user has the ability to update a post.

Ability.new(some_user).can? :update, @post

can? and authorize!

As mentioned before, checking user permissions requires a current_user method. You can then use can? :update @post to verify if the current user can update the post.

class PostController < ApplicationController
  def update
    @post = Post.find(params[:id])

    if can? :update, @post
      redirect_to :show
    else
      head :forbidden
    end
  end
end

CanCanCan provides us with authorize! helper that allows us to simplify the code above:

class PostController < ApplicationController
  def update
    @post = Post.find(params[:id])
    authorize! :update, @post
    redirect_to :show
  end
end

The authorize! method will raise a CanCan::AccessDenied if the user is not permitted to perform the action.

Split the huge ability file

With increasing complexity and numerous abilities, splitting your ability file becomes beneficial. This approach promotes organization; here's an example of structuring them by model.

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

  def initialize(user)
    can :edit, User, id: user.id
    can :read, Post, published: true
    can :manage, Comment, user: user
    can :manage, Followers, user: user
  end
end

The CanCanCan gem suggests adding an app/abilities folder and creating a separate file for each model.

# app/abilities/user_ability.rb
class UserAbility
  include CanCan::Ability

  def initialize(user)
    can :edit, User, id: user.id
  end
end

# app/abilities/comment_ability.rb
class CommentAbility
  include CanCan::Ability

  def initialize(user)
    can :manage, Comment, user: user
  end
end

# app/abilities/book_ability.rb
class FollowerAbility
  include CanCan::Ability

  def initialize(user)
    can :read, Follower, published: true
    can :edit, Follower, user: user
  end
end

By splitting your CanCanCan ability file, you maintain a clean and scalable approach to user permission management in your Rails application.

CanCanCan vs Pundit

Both CanCanCan and Pundit are popular authorization gems for Ruby on Rails applications, but they have strengths and weaknesses. For more details on Pundit gem, please refer to our previous blog post.

Here's a breakdown to help you decide which one might be a better fit for your project:

CanCanCan

  • More straightforward approach: Offers a more concise way to define permissions, often using a single ability file for all models.

  • Easier to learn: Generally considered easier to grasp for beginners due to its straightforward syntax.

  • Limited organization: A single ability file can become cumbersome with complex applications and numerous abilities.

Pundit

  • Object-oriented: Permissions are defined within policy classes specific to each model, promoting better organization and maintainability.

  • More granular control: Provides finer control over permissions by allowing conditions and checks within policies.

  • Steeper learning curve: The object-oriented approach requires a steeper learning curve than CanCanCan.

Choosing Between CanCanCan and Pundit

  • For smaller projects: CanCanCan's simplicity might be ideal, especially if you're new to authorization gems.

  • For complex applications: Pundit's organization and granular control become more valuable as your application grows and requires more intricate permission checks.

  • For existing CanCanCan projects: Switching to Pundit might only be necessary if you encounter limitations with an organization or need more advanced features.

Conclusion

CanCanCan is a powerful gem that simplifies the implementation of user permissions and authorization in Ruby on Rails applications. Empower your Rails application with CanCanCan and take control of user authorization with ease.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right