User roles in Rails applications

27 Jun 2007

This morning I decided to add roles to CatchTheBest to start adding a few simple permissions to the application. Sometimes people’s eyes glaze over when they hear role-based access control, but it really doesn’t have to be complicated. There are some plugins out there that are more or less plug and play for implementing RBAC, but here’s a quick and simple example you can use in your project to easily add user roles.

First, the migration:

class CreateRoles < ActiveRecord::Migration
    def self.up
      create_table :roles do |t|
        t.column :name, :string
      end

      create_table :user_roles do |t|
        t.column :user_id, :integer
        t.column :role_id, :integer
        t.column :created_at, :datetime
      end

      add_index :roles, :name
      Role.create(:name => ‘Admin’)

    end

    def self.down
      drop_table :roles
      drop_table :user_roles
    end
  end

This adds the roles and user_roles tables and creates the first role (Admin). I have added an index to the name field of the roles table because we’ll be querying on that field quite a bit.

Here are the new models:

class Role < ActiveRecord::Base
  end
 
  class UserRole < ActiveRecord::Base
    belongs_to :user
    belongs_to :role
  end

There’s nothing terribly exciting there. :) Notice that I don’t have any relationship declarations in Role, as I currently don’t care about finding users by role — I only care about finding roles by user.

Here are the changes to the User model:

class User < ActiveRecord::Base
    has_many :user_roles
    has_many :roles, :through => :user_roles
 
    def has_role?(role)
      self.roles.count(:conditions => [‘name = ?’, role]) > 0
    end

    def add_role(role)
      return if self.has_role?(role)
      self.roles < < Role.find_by_name(role)
    end
  end

And that’s it. Easy, eh?


Actions

Informations

7 responses to “User roles in Rails applications”

Curtis Miller (11:58:46) :

Hi Ben, how are you using the RBAC functionality?

If you’re using has_role? to show/hide things in the view, do you also need the same type of check in your controller actions? Maybe you created a before_filter to do the check on certain actions?

Does it make sense to add a privileges table that contains controller/action pairs that could be associated with a role? Then you can see if a user is in a role that has the privilege of accessing that controller action.

Ben (12:37:15) :

Since I’m using acts_as_authenticated, I have current_user available, so I use has_role? in views or helpers like this: current_user.has_role?(‘Admin’)

I do a similar check to that in filters in the controllers.

It certainly can make sense to add a controller/action -> role permission mapping table, but it would be overkill for my particular case.

scott (18:58:15) :

I just recently added role authorization to a site too and am doing almost the same thing you are here (I’m also piggy-backing on acts_as_authenticated).

Seeing as I ended up asking role questions in so many places, I decided to eager load my roles with the user associations. With the a role being such a small thing (just a name property) and the count of roles per user being fairly small, I felt it was worth the eager load when measured up against the number of places the has_role?(role) is/can be called. To take advantage of the eager loaded roles, I then changed my has_role? method to do something like this:
def has_role?(role)
self.roles.any?{|r| r.name == role}
end

Louis (05:33:29) :

hi Ben,how can you make so the application to make roles with rights,and can you tell me how doyou change the Views..can you write step by step all changes..
thanks…

links for 2007-07-15 (Hej Varlden) (23:40:04) :

[...] BenCurtis.com » User roles in Rails applications (tags: RoR) [...]

Devastator (13:18:50) :

Not bad at all..

Although I prefer to use STI for roles..eliminates the need for that awkward join table and speeds things up a bit.

Michael (16:57:51) :

Hrm,

Why no “has_many :users” for roles?