Is CanCanCan Dead?

Ryan Bates originally released CanCan in 2009. The gem was written to handle Authorization, the problem of deciding which resources a given user is allowed to access. In 2013 all serious development on CanCan stopped when Ryan took a break from Rails development. In an effort to keep the gem going, the community forked it and created CanCanCan.

CanCanCan is still under active development today but it follows much of the same principles and design as Ryan’s original CanCan project. The question is, should you use CanCanCan today, in 2017?

To answer this question I’ll first look at CanCanCan and then compare it to some of the alternatives. I’ll be comparing CanCanCan to Pundit, Authority & Declarative Authorization.

CanCanCan

CanCanCan assumes that you have current_user defined in your controllers so you’ll probably want to use something like Devise for authentication.

Adding authorization rules with CanCanCan is a two step process: defining the ability and then checking for the ability.

Defining abilities

The gem creates an ability file for you to define permissions. Here is a simple ability file:

class Ability
  include CanCan::Ability

  def initialize(user) 
    if user.admin?
      can :manage, :all
    else
      can :read, :all
    end
  end
end

The can method is used to define permissions. The method accepts two arguments: the action that the user can perform and the resource that the user can perform it on:

can :manage, Article  # user can perform any action on the article
can :read, :all       # user can read any object
can :manage, :all     # user can perform any action on any object
Checking abilities

CanCanCan provides the can? and cannot? methods for checking the current_users permissions in views and controllers:

<% if can? :update, @article %>
  <%= link_to "Edit", edit_article_path(@article) %>
<% end %>

<% if cannot? :update, @article %>
  <h1>You don't have access</h1>
<% end %>

Additionally, CanCanCan provides the authorize! method to be used in controllers. The method will raise an exception if the user is not able to perform the given action:

def show
  @article = Article.find(params[:id])
  authorize! :read, @article
end

If the authorize! method is successful then it will load the resource(s) into an instance variable. You can also use load_and_authorize_resource as a shorthand so that you don’t have to use authorize! in every controller action.

Benefits of CanCanCan

The benefits of this gem are, I believe, for small projects. CanCanCan is very easy to setup and the ability file allows developers to see all of the abilities in one place.

CanCan (and by extension CanCanCan) is also one of the oldest authorization gems so you’ll find that it is used on many Rails projects. For that reason I think that it’s definitely worth knowing, even if it’s not your favourite gem from this list.

Drawbacks of CanCanCan

Having all abilities in one place is great for small projects but for large projects this file can become large and unwieldy. I’ve worked on projects that have 1000+ line ability files with many conditional statements. It becomes very difficult to make changes to the file and it’s easy to break things that aren’t covered by tests.

I also consider CanCanCan’s scopes to be a drawback. Because the entire ability file is evaluated every time the application makes a call to check the users ability, a slow query in the ability file can bring the whole site down.

Pundit

This gem allows you to set up authorization using regular Ruby classes and object oriented design patterns.

Defining abilities

The basis of Pundit is the notion of policy classes:

class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? or not post.published?
  end
end

A policy class is initialized with the current_user and a model object (whose authorization you want to check). The policy class implements some query methods which usually map to controller actions. These query methods decide whether the user has permission to perform the given action, for the resource.

Checking abilities

You can check whether a user is authorized, in a controller, using the authorize method:

def update
  @post = Post.find(params[:id])
  authorize @post
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end

authorize @post will raise an exception unless the current user is allowed to perform the action.

You can also check a user’s permissions in a view template:

<% if policy(@post).update? %>
  <%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
Scopes

If you want to restrict which records a user has access to, you can use scopes. With Pundit, scopes work within a Pundit policy class:

class PostPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      if user.admin?
        scope.all
      else
        scope.where(published: true)
      end
    end
  end

  def update?
    user.admin? or not post.published?
  end
end

Now, rather than getting all posts in your controller you can just get the ones that the current user has permission to see:

def index
  @posts = policy_scope(Post)
end
Benefits of Pundit

The benefits of Pundit, over something like CanCanCan, is that Pundit is much more Object Oriented. This gives you a number of benefits. With CanCanCan you have a large ability.rb file to maintain. With Pundit you have small Policy classes that sit alongside your ActiveRecord classes. This also has a big benefit on performance because when checking abilities Pundit only needs to check a single class whereas CanCanCan has to run through the entire ability file. Lastly, I think scopes are a lot easier to use in Pundit than CanCanCan because you can write a plain ruby method whereas CanCanCan requires you to work with Ruby blocks.

Drawbacks of Pundit

The drawbacks of Pundit are also a result of its object oriented nature. Having multiple policy classes makes it more difficult for beginner Ruby developers to understand. Multiple policy classes also mean that there is no central place to see all of your permission rules. However, I would argue that the benefit of being able to see all of your rules in one place quickly diminishes as your app grows.

Authority

This gem is ORM-neutral, has a simple syntax and allows you to group models under one plain ruby Authorizer class.

Defining abilities

First, you need to tell Authority which model represents the users in your system:

class User
  include Authority::UserAbilities
  ...
end

Then you need to tell your models which authorizer class to use:

class Article
  # Adds `creatable_by?(user)`, etc
  include Authority::Abilities

  # Without this, 'ArticleAuthorizer' is assumed;
  # if that doesn't exist, 'ApplicationAuthorizer'
  self.authorizer_name = 'AdminAuthorizer'
  ...
end

The authorizer class is how you configure authorization logic in Authority. You can write methods like readable_by? to configure who can read the resource. An authorizer looks something like this:

# app/authorizers/schedule_authorizer.rb
class ScheduleAuthorizer < ApplicationAuthorizer
  def self.readable_by?(user)
    true
  end

  def self.creatable_by?(user)
    user.manager?
  end
end

In this case, anyone can read a Schedule and any user, who is a manger, can create a Schedule record. Authorizer methods don’t map to controller actions like they do in Pundit. Instead they map to CRUD actions.

Checking abilities

Once you’ve configured your authorizer class, you can check abilities in your controller:

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
    authorize_action_for(@articles)
  end
end

You can also check abilities in view templates:

link_to 'Edit Widget', edit_widget_path(@widget) if current_user.can_update?(@widget)
Benefits of Authority

Authority is similar to Pundit in that it separates authorization logic into classes for each of your models. It is more configurable than Pundit. For example, you can easily create your own abilities.

It has many of the same benefits as Pundit compared with CanCanCan.

Drawbacks of Authority

The extra configurability does come with a price, I would say that Authority is more complicated to set up than Pundit and there is more boilerplate.

Declarative Authorization

Declarative Authorization is another popular authorization gem. It is inspired by Role Based Access Control. I haven’t reviewed Declarative Authorization in this post because it doesn’t support Rails 5 and hasn’t been updated for a few years.

Conclusion

If I was starting a new Rails project in 2017 I would choose Pundit for authorization. Pundit is easy to maintain as the app grows and it helps the app to stay performant.

Authority is a close second. However, Pundit was easier to setup and has less configuration to maintain.