[Dive into Gems - 2] Cancancan

Authentication and authorization are two fundamental parts of almost every website. While authentication answers the question "who are you?", authorization determines "what can you do?". This sounds pretty straightforward but putting things right is not always a piece of cake. Fortunately, in Rails, there are great libaries can help us out with that. In this blog, I will discuss the authorization problem and what Cancancan actually does to save us from headache.

In a non-trival website, authorization usually consists of a bunch of different rules. This set of rule sometimes can be very complicated. For example, on Facebook we have rules such as:

  • a user can only see public profile of another user if they are not friends
  • a user cannot see a post of his friend if he is on restricted group or the post is hidden to him
  • ...

To implement these rules, the most naive way we can think of is checking in every action. However, this way will quickly polute our code base, make it extremely hard to maintain and often a huge source of bugs. To deal with this problem, the author of Cancancan had a great idea that collect every rules and put them into a separate file. He named that file ability.rb.

This is an example of ability.rb:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)

    if user.admin?
      can :manage, :all
      can :read, :all

As we can see in the example above, authorization rules are defined very clearly. Cancan::Ability is a module which provides handy methods to define authorization rule.

def can(action = nil, subject = nil, conditions = nil, &block)
  add_rule(Rule.new(true, action, subject, conditions, block))

def cannot(action = nil, subject = nil, conditions = nil, &block)
  add_rule(Rule.new(false, action, subject, conditions, block))

def add_rule(rule)
  rules << rule
  add_rule_to_index(rule, rules.size - 1)

def add_rule_to_index(rule, position)
  @rules_index ||= Hash.new { |h, k| h[k] = [] }

  subjects = rule.subjects.compact
  subjects << :all if subjects.empty?

  subjects.each do |subject|
    @rules_index[subject] << position

As we can see, all the rules are put into an array, in which, each element is an instance of class Rule. To increase the performance, they also build an index. This module's responsibility is containing all the rules and exposing methods that check if a user is allowed to perform a specific action or not.

def authorize!(action, subject, *args)
  message = nil
  if args.last.kind_of?(Hash) && args.last.has_key?(:message)
    message = args.pop[:message]
  if cannot?(action, subject, *args)
    message ||= unauthorized_message(action, subject)
    raise AccessDenied.new(message, action, subject)

def cannot?(*args)

def can?(action, subject, *extra_args)
  subject = extract_subjects(subject)

  match = subject.map do |subject|
    relevant_rules_for_match(action, subject).detect do |rule|
      rule.matches_conditions?(action, subject, extra_args)

  match ? match.base_behavior : false

With this module, we have the first half of the solution - defining all the rules. We still need to enforce them. It's done via a module named Cancan::ControllerAdditions which is automatically included into all controllers.

module CanCan
  module ControllerAdditions
    module ClassMethods
      def load_and_authorize_resource(*args)
        cancan_resource_class.add_before_filter(self, :load_and_authorize_resource, *args)

      def load_resource(*args)
        cancan_resource_class.add_before_filter(self, :load_resource, *args)

      def authorize_resource(*args)
        cancan_resource_class.add_before_filter(self, :authorize_resource, *args)

The most used method probably is load_and_authorize_resource. In most case, we only need to call it at the begining of the controller and everything is magically done. This method actually added a before filter in the controller, which perform every checks needed to make sure no authorization rule is violated.

Behind this module, there is another module which really contains the logic

module CanCan
  class ControllerResource
    def self.add_before_filter(controller_class, method, *args)
      options = args.extract_options!
      resource_name = args.first
      before_filter_method = options.delete(:prepend) ? :prepend_before_filter : :before_filter
      controller_class.send(before_filter_method, options.slice(:only, :except, :if, :unless)) do |controller|
        controller.class.cancan_resource_class.new(controller, resource_name, options.except(:only, :except, :if, :unless)).send(method)

    def load_and_authorize_resource

    def load_resource
      unless skip?(:load)
        if load_instance?
          self.resource_instance ||= load_resource_instance
        elsif load_collection?
          self.collection_instance ||= load_collection

    def authorize_resource
      unless skip?(:authorize)
        @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)

    def authorize!(*args)
      @_authorized = true
  • add_before_filter makes sure the filter is added before other filters of the controller.
  • load_and_authorize_resource turns out to consist of 2 steps: load_resource and authorize_resource.
  • load_resource loads the object of the model class.
  • authorize_resource invokes the rules storage Cancan::Ability to determined the current user is allowed to perform the current action on this object or not.

With that, the solution is completed. Very handy and clearly. Let take a look at the whole gem again.

Screen Shot 2016-05-16 at 1.11.38 AM.png

We've covered the most fundamental part of the gem. In summary, the main logic of Cancancan reside in 5 files:

  • rule.rb: the rule class
  • ability.rb: stores all the rules
  • controller_resource.rb and inherited_resource.rb (a special kind of controller resource): authorizing process
  • controller_addition.rb: wrapper of controller_resourse

There are also other parts which provides test helpers, model helpers, generator and data adapters. For more details, you can look at the actual code. This is a very well documented gem and pretty easy to read so I highly recommend you take a look at it to better understand a very important part of Rails apps.