【问题标题】:Using Ruby on Rails 5, what is an efficient way to check an array of ActiveRecord results for particular values?使用 Ruby on Rails 5,检查 ActiveRecord 结果数组的特定值的有效方法是什么?
【发布时间】:2018-09-15 12:21:59
【问题描述】:

在我的应用程序中,我有一个包含 id, account_id, user_id, resource_type, resource_id, and role 列的权限表。使用 Pundit,当用户尝试访问特定资源时,我需要检查许多可能的权限记录,以便他们查看/编辑页面。我正在寻找最有效的方法来获取所有Permissions.where(account_id: params[:account_id] && user_id: current_user.id),然后循环遍历它们以获得所有可以授予访问权限的值:

is_user_account_owner?
is_user_account_admin?
is_user_resource_moderator?
is_user_parent_resource_admin?
is_user_parent_resource_moderator?
is_user_parent_resource_viewer?
is_user_resource_viewer?

如果这些检查中的任何一个为真,则用户可以查看资源并且不需要进行其余检查。每次页面浏览都会发生这种情况,因此我将不胜感激任何关于如何使这种高效/可扩展的建议,特别是想要一些关于如何在此处实现缓存的建议。

【问题讨论】:

  • 这真的取决于权限是如何实现的。此外,除了查询缓存之外,您不能真正使用缓存,因为每个客户端的结果都是唯一的。
  • 您能否描述更多您认为我在描述我如何实现权限时遗漏的内容?很基本; pundit per action 查找 Permissions 记录,如果存在某些值,则授予/拒绝访问
  • 你能展示你的权威实现吗?你不能(也不应该)在策略中正常访问params[:account_id],所以我不清楚你是怎么写的。
  • 什么是“特定资源”?任何属于Account 的东西?数据是如何建模的?
  • 为什么会这样?如果我在政策受到打击之前从 params 中提取或仅设置 @account_id = params[:account_id] 有什么区别?我会向您展示我的权威代码,但我还没有编写它,因为在我开始编码之前我仍然处于思维导图过程中我将如何实现它

标签: ruby-on-rails ruby activerecord permissions access-control


【解决方案1】:

我按照我想要的方式完成了这项工作,而不是像我的问题要求的那样循环浏览查询结果。

由于我的应用程序具有多租户设置,因此 Pundit 必须有权访问在 Params 中指定的 account_id,以便我可以在可能没有记录时在我的 Post#index 等位置进行权限检查传递给 pundit 以从中获取 account_id。

我敢肯定,Tom Lord 很沮丧,我已经使用 Pundits additional context 功能始终传入我的 @account 变量,该变量在 params[:account_id] 的每个请求上设置,current_user上下文:

application_controller.rb:

def pundit_user
  UserContext.new(current_user, @account)
end

政策/user_context.rb:

class UserContext
  attr_reader :user, :account

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

政策/application_policy.rb:

class ApplicationPolicy
  attr_reader :request_account, :user, :record

  def initialize(user_context, record)
    @user = user_context.user
    @account = user_context.account
    @record = record
  end

...

因此,现在在我的个人策略中,我可以执行我想要确定 current_user 是否具有任何可以授予对操作的访问权限的任何权限记录以及一个查询:

政策/note_policy.rb:

class NotePolicy < ApplicationPolicy

def index?
    Permission.where({account_id: @account, user_id: user.id}) \
    .where({resource: 'Account', role: :owner}) \
    .or(Permission.where({account_id: @account, user_id: user.id}) \
        .where({resource: 'Account', role: :admin})) \
    .exists?
  end

  def show?
    Permission.where({account_id: record.account_id, user_id: user.id}) \
    .where({resource: 'Account', role: :owner}) \
    .or(Permission.where({account_id: record.account_id, user_id: user.id}) \
        .where({resource: 'Account', role: :admin})) \
    .or(Permission.where({account_id: record.account_id, user_id: user.id}) \
        .where({resource: 'Note', role: :viewer})) \
    .exists?
  end

以及在 notes_controller.rb 中对授权的调用:

  def index
    authorize Note
    @notes = @account.notes.all
  end


  def show
    @note = @account.notes.find(params[:id])
    authorize @note
  end

我敢肯定,这不是最干净的方法,但它确实有效。如果有任何明显的安全问题,我很乐意听取反馈。

【讨论】:

  • 与其说让 我的 沮丧,倒不如说是 Pundit 的作者,他非常 clearly wrote in that link 认为这可能不是建模它的最佳方式...跨度>
  • 事实上,您实际上是如何在在控制器中调用此策略授权的?您上面的答案中缺少的那行代码是您认为没有理由以这种方式使用 Pundit 的关键原因。
  • 在 index 动作中,你的“admin”逻辑没有意义——如果存在这样的权限,那么 everyone 可以执行该动作,如果不存在这样的记录,那么逻辑是多余的。无论哪种方式,至关重要的是,您如何确定响应?这是 Pundit 应该在这里使用的,但您没有显示它的代码。
  • 至于show? 操作,您的逻辑又是错误的。我建议您检查实际生成的 SQL 并编写一些单元测试以查看实际允许谁查看资源。然而,至少在这种情况下,您似乎正在尝试正确使用 Pundit(不出所料,您不需要虚假的 @account 变量!)
  • 已更新以显示我的最新查询(已经发现了您描述的查询错误),并且现在显示了控制器对授权的调用。响应的范围是通过 accounts.notes 关系。
猜你喜欢
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 2012-11-20
  • 1970-01-01
  • 1970-01-01
  • 2017-12-14
相关资源
最近更新 更多