Skip to content
ravinggenius edited this page Feb 14, 2013 · 1 revision

CanCan vs CanTango

Page History

Choosing the right Access Control system is an important decision in any project. The following is an attempt at an objective guide to help you make the right decision.

It is important to stress, that it is never recommended to choose an overly complex solution to handle simple requirements. In many simple access control scenarios, a simple access control system will do just fine and even allow for greater flexibility!

When to use CanCan

CanCan is useful to use on its own when:

  • Access rule requirements are simple
  • There are few roles (and/or role groups)
  • A few can? statements in critical views and controllers
  • Access is mostly controlled on the controller REST method level
  • Guest user logic is simple
  • You have a User model class and a #current_user method that works for all logged in users.

If this fits with your requirements, by all means start off with just CanCan. It should be pretty easy to upgrade to CanTango if you need to in the future.

Let's dive in and see how this really plays out...

In CanCan you would define your ability something like this:

class CanCan::Ability
  def initialize user, options = {}
    if !user
      can :read, :all
    end
    if user
      admin_rules if user.roles.include? :admin
      editor_rules if user.roles.include? :editor
      default_rules
    end
  end

  def admin_rules
    can :manage, :all
  end

  def editor_rules
    can :manage, [Article, Post]
  end
end

Not very elegant for a first attempt, but not too bad. Let's try to make this more generic!

class CanCan::Ability
  def initialize user, options = {}
    user ? user_rules : guest_user_rules
  end

  def user_rules
    user.roles.each do |role|
      exec_role_rules(role) if user.roles.include? role
    end
    default_rules
  end

  def exec_role_rules role
    meth = :"#{role}_rules"
    send(meth) if respond_to? meth
  end
  
  # various rules methods for each role
  def admin_rules
    can :manage, :all
  end

  def editor_rules
    can :manage, [Article, Post]
  end
  ...

  def default_rules
    can :read, :all
  end
end

Ok, we are getting somewhere, but still, in order to manage this we always have to dive into the Ability class and find the right method for a given role. It is hard to keep track of which rules method has been defined for which roles and the Ability class will begin to grow rapidly in size as more complex requirements are added. Why not "split out" and have separate rules classes for each role and role group? This is the approach taken by CanTango, which even lets you define your own permit types, fx for a memberships attribute or whatever your requirements are.

Using CanTango

Now let's see how we could implement the above scenario using CanTango. We employ RolePermits for candidates (users) with specific roles.

class AdminRolePermit < CanTango::RolePermit
  def permit_rules
    can :manage, :all
  end
end

class EditorRolePermit < CanTango::RolePermit
  def permit_rules
    can :manage, [Article, Post]
  end
end

We use the special permit AnyPermit to specify the default rules that apply for any candidate.

class AnyPermit < CanTango::Permit
  def permit_rules
    can :read, :all
  end
end

Isn't this a much prettier, intuitive and more elegant way to specify these concerns!?

When to use CanTango

If you find that your requirements go beyond what CanCan can satisfy out of the box without too much tweaking on your own part, CanTango just might be the solution you are looking for. Use CanTango when you have/need:

Complexity and fine-grained control

  • Access rule requirements are somewhat complex
  • Access must be controlled on a more fine grained level

Users

  • You have multiple types of users, e.g. User, Guest and Admin
  • There are more than a few roles (and/or role groups) or other User attributes that affect permissions.

Performance

  • Access control checks should be fast, you need caching of permits for each user
  • Users not logged in should be granted a Guest user instance

User accounts

  • Your app has multiple User accounts, one for each sub-app
  • Access control logic can differ for each account
  • A user can be logged into one or more accounts simultaneously
  • Some users are allowed to masquerade as other users
  • Some users are allowed to masquerade as if logged into a different account (rare)

Administration

  • Access control (permissions) should be maintained in logical containers, on a per-role or role group basis
  • Access control should be maintained and administrated in a permission store, e.g. a Yaml file
  • You want to administrate Permissions in a web UI, accessible by administrators from within your app!

If more than a few items on this list reflect your requirements, give CanTango a chance!

If your requirements go beyond this, create your own extension or even better help enhance CanTango directly!

Have FUN! Let's Tango!