We recently needed to implement a fairly standard user authentication / permission system on a Rails 3 project, with the ability to define these permissions using roles so that it could easily be centralized and expanded on (ie with new roles and permissions down the line). While rolling your own custom code is always an option, there is already a robust collection of gems to do exactly this. This blog post quickly presents how to combine three of these to implement this system.

Devise

Devise is a modular user authentication system that allows you to pick and choose the components you need for your application, covering the majority of common functionalities you find in login systems (registration, password recovery, email validation, etc…)

Setting it up is pretty straightforward:

  • Add the gem to your gemfile and then run bundle install:
  • Run the generator, which will create a number of associated views under app/views/devise along with an initializer:
  • Now you can add Devise to one of your models, typically User:
  • Edit your user.rb and select which Devise modules you’d like to use. Using registerable initially is strongly recommended so that you can actually sign up and log in.

Now pick an existing controller and require authentication for it:

The documentation is very thorough and there’s no point in rehashing all of it here, so I strongly suggest just gradually investigating the components you need for your app and configuring them as you need. I did run into a mild issue with some of the generated sign in views returning errors, and it was simply a case of replacing the Submit buttons there with:

CanCan

This is the gem we’ll use to actually define permissions, since just verifying that a user is logged in can only go so far and we need the ability to control access to specific actions. Again, installation comes down to:

  • Add the gem to your gemfile and then run bundle install:
  • Generate the Ability class which will contain your user permissions:
  • Open up this file and add the user line so that you can tie your abilities to the currently logged-in user:

Your application controller will need to know what to do if a CanCan exception is thrown:

Now you can decide which actions require CanCan authorization:

Adding this manually to every action in every controller is obviously a dumb idea, so you can just use load_and_authorize_resource at the top of your controller to authorize all actions in a RESTful fashion.

At this point you’ll want to actually make this more granular and allow abilities to be based on roles, which brings us to Role Model.

Role Model

The CanCan documentation provides a very nice explanation of the basic concept of using a bitmask to represent many roles per user, and Role Model is a ready-to-go solution if this approach fits your needs.

  • Add the role_model gem to your gemfile and run bundle install.
  • Edit your user.rb to make it aware of Role Model. With the existing Devise stuff, it should look more or less similar to this:

We effectively defined 3 roles now, now we need to update our ability.rb model to tie our permissions to them.

This is a really simple demonstration of what can be done fairly quickly with this stack, and in our application it was extended to leverage user-level custom permissions for specific assets and very fine-tuned abilities with conditional displays of specific view segments.

  • http://www.facebook.com/sean.doig Sean Doig

    I would have loved to read this but you need to sort out the HTML that’s growing in your code blocks.

    • Danielle Miller

      Hey Sean!

      We went back in and fixed the code. For some reason there was some issues when we migrated this post over from our old blog site. Sorry for the confusion. Enjoy the article!

  • http://www.gearcrm.com/ Brad

    Do you have any tips on how one could integrate omniauth into the stack for off-site login and registration?

  • baburaj

    we can have 2 admin account for the site?If possible how to implement this using rails_admin gem..

  • Jenny @ Abundant.Life

    Thanks for this! Looks like just what I need. :)

  • Jon Denly

    Thanks for posting this, it helped get me up and running quickly.

  • Honza Vosmik

    Thanks for the article. Just one quick question, how can you let the new user to select his role/s. I am not sure how to implement it in a view(form). Thank you!