S.M.
S.M.

Reputation: 21

what is the "rails way" for enabling an admin to create privileges for an existing user?

I'm writing a ruby on rails website for the first time. I have a User model and a Manager model. The user has_one Manager and a Manager belongs_to a User. The Manager model contains more info and flags regarding privileges. I want to allow an admin while viewing a User (show) to be able to make him a manager.

This is what I wrote (probably wrong):

In the view: <%= link_to 'Make Manager', new_manager_path(:id => @user.id) %>

In the controller:

def new
    @user = User.find(params[:id])

    @manager = @user.build_manager
end

resulting in a managers/new?id=X Url.

Upvotes: 0

Views: 71

Answers (1)

Mohamad
Mohamad

Reputation: 35349

I would separate roles and permissions from the User class. Here's why:

  1. Managers are users too. They share the same characteristics of Users: Email address, first name, last name, password, etc...
  2. What if a manager also has a higher level manager? You'll have create a ManagerManager class, and that's terrible. You might end up with a ManagerManagerManager.

You could use inheritance, but that would still be wrong. Managers are users except for their title and permissions, so extract these domains into their own classes. Then use an authorisation library to isolate permissions.

You can use Pundit or CanCan. I prefer Pundit because it's better maintained, and separates permissions into their own classes.

Once you have done that, allowing a manager to change a normal user to a manager becomes trivial and easy to test:

class UserPolicy
  attr_reader :user, :other_user

  def initialize(user, other_user)
    @user = user
    @other_user = other_user
  end

  def make_manager?
    user.manager?
  end
end

In your user class you can have something like:

def manager?
  title == 'manager?'
  # or
  # roles.include?('manager')
  # Or whatever way you choose to implement this
end

Now you can always rely on this policy, wherever you are in the application, to make a decision whether the current user can change another user's role. So, in your view, you can do something like this:

- if policy(@user).make_manager?
  = link_to "Make Manager", make_manager_path(@user)

Then, in the controller you would fetch the current user, and the user being acted upon, use the same policy to otherwise the action, and run the necessary updates. Something like:

def make_manager
  user = User.find(params[:id])
  authorize @user, :make_manager?
  user.update(role: 'manager')
  # or better, extract the method to the user class
  # user.make_manager!
end

So you can now see the advantage of taking this approach.

Upvotes: 2

Related Questions