Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hide parts of the view given a user role on Rails 4

I'm trying to hide parts of my views depending on the User role.

So let's say I want only admins to be able to destroy Products. Besides the code in the controller for preventing regular users from destroying records, I would do the following in the view:

<% if current_user.admin? %>
  <%= link_to 'Delete', product, method: :delete %>
<% end %>

The previous code works, but it's prone to errors of omission, which may cause regular users to see links to actions they are not allowed to execute.

Also, if I decide later on that a new role (e.g. "moderator") can delete Products, I would have to find the views that display a delete link and add the logic allowing moderators to see it.

And if there are many models that can be deleted only by admin users (e.g. Promotion, User) maitenance of all the ifs would be pretty challenging.

Is there a better way of doing it? Maybe using helpers, or something similar? I'm looking for something maybe like this:

<%= destroy_link 'Delete', product %> # Only admins can see it
<%= edit_link 'Edit', promotion %> # Again, only admins see this link
<%= show_link 'Show', comment %> # Everyone sees this one

I found these two questions that are similar to mine, but none of them answered my question:

Show and hide based on user role in rails

Ruby on Rails (3) hiding parts of the view

like image 646
Thiago Dias Avatar asked Jul 29 '13 20:07

Thiago Dias


2 Answers

I strongly recommend pundit.

It allows you to create "policies" for each model. For your Product model you might have a ProductPolicy that looks something like this

class ProductPolicy < ApplicationPolicy
  def delete?
    user.admin?
  end
end

In your view you can do something like this

<% if policy(@post).delete? %>
  <%= link_to 'Delete', product, method: :delete %>
<% end %>

If later on you want to add a moderator role, just modify the policy method

class ProductPolicy < ApplicationPolicy
  def delete?
    user.admin? || user.moderator?
  end
end
like image 141
deefour Avatar answered Sep 19 '22 10:09

deefour


So I kind of figured a way to move the IFs out of the view. First, I override the link_to helper in my application_helper.rb:

def link_to(text, path, options={})
  super(text, path, options) unless options[:admin] and !current_user.admin?
end

Then on my views I use it as:

<%= link_to 'Edit Product', product, admin: true, ... %>

This prevents regular users from seeing admin links, but for other html tags with content inside, such as divs, tables etc., an if would still be needed.

like image 37
Thiago Dias Avatar answered Sep 22 '22 10:09

Thiago Dias