How to manage users and their roles as an admin using acl9

792 Views Asked by At

Wondering what the best approach would be for managing user roles using acl9?

Here's the need:

  • Admins should be able to update a user's role.

Here are the issues:

  1. How can we list all subject roles? :admin, :manager
  2. To keep the api endpoints RESTful, I would like to pass the role param in the user_controller's update method ideally.
  3. How can I authorize just for the role property so that, the owner of the user object can still modify their first_name, last_name fields, but not their role field? Only admins are allowed.

I can check for params manually outside of the access_control block:

def secure_params
    if current_user.has_role?(:admin)
        params.require(:user).permit(:first_name, :last_name, :role)
    else
        params.require(:user).permit(:first_name, :last_name)
    end
end

but thought I would check and see if there is a cleaner solution.

Lastly, is it possible and wise to use something like RoleTypes.ADMIN, RoleTypes.MANAGER instead of :admin? If so, what's the best way to do this and is that Class accessible throughout a rails app?

2

There are 2 best solutions below

0
On BEST ANSWER

How can we list all subject roles? :admin, :manager

The Role model is just a normal model, so you can just query this like you would anything else:

Role.uniq.pluck :name

To keep the api endpoints RESTful, I would like to pass the role param in the user_controller's update method ideally.

Generally, you're much better off using a separate controller for performing administration, and put it in the Admin:: namespace, and use namespace :admin do routes, etc..

Then in that controller you can use a normal access_control block to make sure no one else can get in:

class Admin::UsersController < ApplicationController

  access_control do
    allow :admin
  end

  # ...

end

So then, yeah, you can set/update a role in a number of ways, but seeing as a user can have many roles it's probably best not to have a single :role param, but rather to use:

class User < ActiveRecord::Base
  acts_as_authorization_subject
  accepts_nested_attributes_for :roles
end

So then in your controller you would have:

def user_params
  params.require(:user).permit(:first_name, :last_name, roles_attributes: [:name])
end

Then in your form you can do something like:

= form_for :user do |f|
  = f.text_field :first_name
  = f.text_field :last_name
  = f.fields_for :roles do |fr|
    = fr.text_field :name

Note that I'm assuming you have just simple roles here, not roles on an object, if you have object roles then you'll need something a bit trickier to capture authorizable_id and authorizable_type for each role, and some way to select the object on which to act.

How can I authorize just for the role property so that, the owner of the user object can still modify their first_name, last_name fields, but not their role field? Only admins are allowed.

Hopefully you're already answering this one yourself now - by using different controllers for the admin interface and the user's own interface.

Lastly, is it possible and wise to use something like RoleTypes.ADMIN, RoleTypes.MANAGER instead of :admin?

No, the symbols are simpler and better, although it's quite common to do something like:

class Role < ActiveRecord::Base
  def self.permitted_roles
    %i/admin manager foo bar wee/
  end
end

So that you can then use Role.permitted_roles to construct an array of checkboxes, or a dropdown, or something like that.

2
On

Edit: Totally missed the acl9 bit in the first sentence - sorry for that!

That being said, my answer is still decently applicable; I would have a helper method or use acl9's has_role? method and alter the view based on this. Ie:

>> <% if @user.has_role?(:foo, nil) %> 
>>    <p>
>>    <%= f.label :usertype %><br />
>>    <%= f.check_box :usertype %>
>>   </p>
>> <% end %>

I would use a helper method to actually set the user role using the .has_role! method ie: user.has_role! :some_role, [optional scope], where user is the user being assigned the role.

For more info, check out this (from the acl9 docs): https://github.com/be9/acl9/wiki/Tutorial%3A-securing-a-controller


Ok, so if what I think you're saying is correct, you want a way to have content and/or profile information editable by users but user roles editable by an admin.

First off, I'm going to assume role is a part of your user schema (if its not, I suggest you consider moving it there). If so, I would suggest adding a method to your user model may_edit?(content) that returns a boolean depending on the user role or id. Ie:

def may_edit?(content) item.user.id == self.id end

Where the content is found via a find_by(id) in the controller. You could then use an if in the controller to dictate which content will appear on the show:

<% if current_user.role == admin %> <%= some button or link or dropdown render %> <% end %>

<% if current_user.may_edit?(content) %> <%= some button or link or dropdown render %> <% end %>

Or something along those lines. You can also write an if..else as a separate helper method or in the may_edit? method (I'd suggest making it/them model methods). The controller ideally shouldn't be the one deciding who can and can't perform this kind of thing. If you're set on doing it in the controller, you might want to look into :before_filter at the top of your controller, ie:

:before_filter admin?, except [:foo, :bar]

I'm not sure which user id you want to pass in the params, but either the editor/admin's or the original poster's ids can be found - the current_user will be logged in session as session[:user_id] and the original poster can be found via the content being edited, since it belongs to a user and can be found via a ``User.find_by(content.user_id).

Finally, are you asking how to see a list of all user roles? If so, you can use a dropdown menu, but you'll have to either hardcode them somewhere in your project or iterate through every user and aggregate their roles, although the latter is far from ideal and very inefficient.

Hope this helps, and good luck!