如何在Rails 4上给定用户角色隐藏部分视图

问题描述 投票:5回答:4

我试图隐藏部分视图,具体取决于用户角色。

所以,假设我只希望管理员能够销毁产品。除了控制器中用于防止常规用户销毁记录的代码外,我还会在视图中执行以下操作:

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

前面的代码有效,但它容易出现遗漏错误,这可能导致普通用户看到不允许执行的操作的链接。

此外,如果我稍后决定新角色(例如“主持人”)可以删除产品,我将不得不找到显示删除链接的视图并添加允许主持人查看它的逻辑。

如果有许多模型只能由管理员用户删除(例如促销,用户),那么所有ifs的维护将非常具有挑战性。

有没有更好的方法呢?也许使用帮手,或类似的东西?我正在寻找像这样的东西:

<%= 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

我发现这两个问题与我的相似,但没有人回答我的问题:

Show and hide based on user role in rails

Ruby on Rails (3) hiding parts of the view

ruby-on-rails views admin helpers
4个回答
0
投票

使用CanCan和Role gems,还需要一种方法来检查路由并查看“current_user”是否有权根据其角色访问该路由 - 然后根据该角色显示/隐藏。

这样可以节省用户单击事物并告知他们无法看到它 - 或者我们必须编写每个项目“if”逻辑指定哪些角色可以查看哪些列表项(客户将定期更改,因为角色已更改/改进) )在一个菜单中的每个链接周围(考虑一个包含50多个嵌套在html格式组中的项目的引导菜单),这是疯了。

如果我们必须在每个菜单项周围放置if-logic,那么让我们通过检查我们在Ability文件中定义的角色/权限,为每个项目使用完全相同的逻辑。

但是在我们的菜单列表中,我们有路由助手 - 而不是“控制器/方法”信息,那么如何测试用户是否能够点击为每个链接中的“路径”指定的控制器操作?

获取路径的控制器和方法(动作)(我的示例使用'users_path'route-helper)...

Rails.application.routes.recognize_path(app.users_path)
        => {:controller=>"users", :action=>"index"}

获取控制器名称

    Rails.application.routes.recognize_path(app.users_path)[:controller]
        => "users"

能力使用模型进行细分,因此从控制器名称转换为模型(假设使用默认命名)...

    Rails.application.routes.recognize_path(app.users_path)[:controller].classify
        => "User"

获取动作名称

    Rails.application.routes.recognize_path(app.users_path)[:action]
        => "index"

既然“可以?”对于每个菜单项,我们得到的方法需要一个符号表示动作,而常量表示模型:

    path_hash = Rails.application.routes.recognize_path(app.users_path)
    model = path_hash[:controller].classify.constantize
    action = path_hash[:action].to_sym

然后使用我们现有的Abilty系统来检查current_user是否可以访问它,我们必须将动作作为符号传递,并将模型作为常量传递,所以......

    <% if can? action model %>
        <%= link_to "Users List", users_path %>
    <% end %>

现在,我们可以更改谁可以从Ability文件中看到此资源和链接,而不会再次弄乱菜单。但是为了使这个更清洁,我在app-controller中为每个菜单项提取出查找:

def get_path_parts(path)
  path_hash = Rails.application.routes.recognize_path(path)
  model_name = path_hash[:controller].classify.constantize
  action_name = path_hash[:action].to_sym
  return [model_name, action_name]
end
helper_method :get_path_parts

...所以我可以在视图中执行此操作(为了简单起见,我从链接中取出了所有html格式,这里):

<% path_parts = get_path_parts(users_path); if can?(path_parts[1], path_parts[0]) %>
    <%= link_to "Users Listing", users_path %>
<% end %>

...并且为了使这不是整天打字这些每菜单项if-wraps,我使用正则表达式查找/替换捕获和通配符来包围菜单项列表中的每个列表项一次传递。

它远非理想,我可以做更多的事情来使它变得更好,但我没有空余时间来编写角色/ CanCan系统的其余部分。我希望这部分可以帮助别人。


6
投票

我强烈推荐pundit

它允许您为每个模型创建“策略”。对于你的Product模型,你可能有一个看起来像这样的ProductPolicy

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

在你看来,你可以做这样的事情

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

如果以后要添加moderator角色,只需修改策略方法即可

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

1
投票

CanCan是另一个宝石,可让您为每个用户角色定义“能力”。在视图中,您可以使用类似if can? :delete, @post的内容来检查用户是否可以删除该特定帖子。


1
投票

所以我想出了一种将IF移出视图的方法。首先,我在application_helper.rb中覆盖link_to帮助器:

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

然后我的观点我用它:

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

这可以防止普通用户看到管理员链接,但对于其他内容有内容的html标签,例如div,表等,仍然需要if。

© www.soinside.com 2019 - 2024. All rights reserved.