Alright, so I am tacking a Role Based Authentication and Multi-tenancy problem using Ruby on Rails. Up to now, I have been doing this manually as I add new functionality with items in controllers like this:
Role based authentication
[code]
class SomeController < ApplicationController
before filter :check_authorized
authorized_roles = ["platinum", "gold"]
def check_authorized
redirect_to some_path, :notice => "Come on…you know you can’t do that unless you upgrade!" unless authorized_roles.include? current_user.role
end
#other methods
end
[/code]
Multi-tenancy
[code]
class SomeController < ApplicationController
def show
@something = Somthing.find(params[:id])
if current_user.id != @something.user_id then
@something = nil
redirect_to some_path, :notice => "Stop trying to see other people’s stuff!!!!"
end
end
[/code]
This was fine and dandy for the first 10 months of my app because I don’t add a ton of models/controller actions and most of the functionality has been on our mining server. However, it’s getting kind of tedious, not to mention that I could easily misplace a filter and user would have access to anothers data, which would be not good. Also, it would be kind of nice to be able to see what everyone can do in one spot.
CanCan encourages users to have an Ability.rb class and to use a RESTful architecture. Lets assume that you have a silver, gold, platinum architecture. We will create RBAC and Multi-tenancy for Platinum users to edit a model Content that belongs to them.
For the user, we will keep this super simple and just add a column to the database that represents their role. So a call to User.role will return that users role. In our content model, their will be a user_id column.
User
has_many :contents
Content
belongs_to :user
Role Based Authentication
We will allow the platinum user to manage every action in the Content model. This means that they will be able to perform ANY action that is in the contents controller, keep this in mind as you add functionality if you stray from REST
[code]
Ability.rb
class Ability
include CanCan::Ability
def initialize(user)
if user.role == :admin
can :manage, Content
end
end
end
[/code]
Code Enforced Multi-tenancy
For this we really just need to make one addition to the ability declaration since CanCan will take in a hash of conditions and we have given the Content a column that has the user_id of the owning user.
[code]
can :manage, Content
turns into
can :manage, Content, :user_id => current_user.id
[/code]
and Ta-da you now have role based abilities and the most primative form of Multi-tenancy available.