Rails 4 user roles and permissions
Asked Answered
R

3

18

I am writing a rails application for an organization. Every user may have 1 or more roles and can only access certain controller actions depending on those roles.

For example, only admins can create, destroy and update certain fields of Users. Also, there are Teams which each have a team leader, and only the team leader can update certain information about the Team (like the member list, for example). However, Admins are the one who assign the team leader in the first place.

The specific details of my scenario are not important, I merely hope I described the situation where there are many different roles and permissions.

My question is: what gem to use? My first thought was CanCan, but the last commit was almost a year ago and there is no mention of Rails 4 compatibility. Is there a currently maintained alternative?

Reverberate answered 12/8, 2014 at 19:51 Comment(3)
CanCanCan is the continuation of CanCan. Last commit 4 days ago.Sacaton
The Ruby toolbox is a great place to search for gems. It shows if the gem is alive or not.Prow
Oh! I should have known the community would fork it!Reverberate
L
28

Your first guess was right, use cancancan and you'll be good with it.

EDIT Jul 24, 2015

I've been using cancancan for a long time now and it was always working great. I've recently started working on a project where Pundit is used for authorization.

It is awesome. It prompts you to define the policy for each resource and it feels more natural than one bloated Ability class.

For bigger projects, I would definitely recommend Pundit.

Loon answered 12/8, 2014 at 19:54 Comment(1)
Interesting.. Thank you.. I will surely give Pundit a try in my future projects.Descry
L
5

To control access to actions I'd recommend Action Access, it boils down to this:

class UsersController < ApplicationController
  let :admin, :all
  let :user, [:index, :show]

  # ...
end

This will automatically lock the controller, allowing admins to access every action, users only to show or index users and anyone else will be rejected and redirected with an alert.

If you need more control, you can use not_authorized! inside actions to check and reject access.

It's completely independent of the authentication system and it can work without User models or predefined roles. All you need is to set the clearance level for the current request:

class ApplicationController < ActionController::Base
  def current_clearance_level
    session[:role] || :guest
  end
end

You can return whatever you app needs here, like current_user.role for example.

Although it isn't required, it bundles a set of handy model additions that allow to do things like:

<% if current_user.can? :edit, :team %>
  <%= link_to 'Edit team', edit_team_path(@team) %>
<% end %>

Here :team refers to TeamsController, so the link will only be displayed if the current user is authorized to access the edit action in TeamsController. It also supports namespaces.

You can lock controllers by default, customize the redirection path and the alert message, etc.

It's very straightforward and easy, I hope you find it useful.

Leyes answered 26/10, 2014 at 17:47 Comment(1)
Note that using session[:role] to store what is effectively the user's access level is extremely risky for Rails apps because Rails stores session info in browser cookies by default. Users could easily promote themselves.Grume
S
2

Something that was suggested to me that we are now using is the petergate gem. Easy to use and very clean looking with a great rails feel.

Works well with devise.

Here is some examples from the readme.

If you're using devise you're in luck, otherwise you'll have to add following methods to your project:

user_signed_in?
current_user
after_sign_in_path_for(current_user)
authenticate_user! 

This comes in your User.rb. Adding more roles is as easy as adding them to the array.

petergate(roles: [:admin, :editor], multiple: false)

Instance Methods

user.role => :editor
user.roles => [:editor, :user]
user.roles=(v) #sets roles
user.available_roles => [:admin, :editor]
user.has_roles?(:admin, :editors) # returns true if user is any of roles passed in as params.

Controller access syntax.

access all: [:show, :index], user: {except: [:destroy]}, company_admin: :all
Silicic answered 14/3, 2017 at 20:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.