如何将额外的上下文传递给Pundit范围?

时间:2018-05-09 03:54:30

标签: ruby-on-rails ruby pundit

我有一个系统,其中User可以与许多Portals相关联,但用户的权限可能会因门户网站而异。

例如,用户可能能够在一个门户中看到未发布的帖子,但在另一个门户中却看不到。

对于像show?这样的方法,我可以抓住记录中的门户网站。

def show?
  portal = record.portal
  # logic to check whether, for this particular portal, 
  # this user has permission to view this record
end

但是,该解决方案不适用于策略范围。

有没有办法可以把门户网站传递给控制器​​中的policy_scope方法?

我在这个地方看到的一个解决方案是为用户设置(临时)属性,以便策略方法可以使用它。 e.g。

# model
class User < ActiveRecord::Base
  attr_accessor :current_portal
  ...
end

# controller
posts = portal.posts
current_user.current_portal = current_portal
policy_scope posts

# policy Scope
def resolve
  portal = user.current_portal
  # logic to scope these records by user's portal permissions
end

然而,这似乎是一种解决方法,我绝对可以想到其他情况,我希望能够为授权逻辑提供更多的上下文,我不希望这种解决方法成为一个坏习惯。

有没有人有任何建议?

1 个答案:

答案 0 :(得分:0)

Pundit授权类是普通的旧ruby类。使用pundit authorize方法,您可以传递要授权的对象和可选查询。例如,当您使用授权方法

authorize @product, :update?

authorize方法将调用update?类上的ProductPolicy方法。如果没有传递查询,控制器动作名称+?标记将被设置为查询。看看Pundit authorize method definition

因此,为了将额外的参数传递给Pundit授权方法,您应该使用额外的参数覆盖authorize方法:

module Pundit
  def authorize(record, query=nil, options=nil)
    query ||= params[:action].to_s + "?"

    @_pundit_policy_authorized = true

    policy = policy(record)

    if options.nil?
      raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
    else
      raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query, options)
    end
  end
end

然后在您的策略类中,您可以使用此额外参数

class ProductPolicy
  ...
  def update?(options)
    ...
  end

  def new?
    ...
  end
  ...
end

如您所见,您可以使用接受额外选项参数的策略方法。

然后,您可以通过以下方式之一在控制器中使用authorize方法:

authorize @product
authorize @product, nil, { owner: User.find(1) }
authorize @product, :some_method?, { owner: User.find(1) }
authorize @product, :some_method?