-
Notifications
You must be signed in to change notification settings - Fork 45
CanCan vs cantango
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!
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.
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!?
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:
- Access rule requirements are somewhat complex
- Access must be controlled on a more fine grained level
- 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.
- 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
- 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)
- 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!