Pundit:将特定资源授权给不同名称的控制器

时间:2018-03-23 18:17:19

标签: ruby-on-rails ruby pundit

假设我使用Pundit gem进行授权。我有以下控制器:

class BlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :blog
  end
end

Pundit查看传入的参数:blog,从而推断出有一个BlogPolicy类。然后,它会查找带有问号的相应操作名称,例如:BlogPolicy#index?

如果我想在特定资源上查找授权,我会为check_authorization方法执行此操作:

  def check_authorization
    authorize @blog
  end

再次,没问题。 Pundit查看资源的类Blog,然后查找BlogPolicy类。

  • 示例:应用可以调用BlogPolicy#show?方法并传入@blog资源。

以下是问题:当我想要授权特定的控制器名称并对特定资源进行授权时,我该怎么办,但该资源不与控制器名称同步?

示例:

class SomeOtherBlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :some_other_blog, @blog
  end
end

上面的代码不起作用,但希望它能显示我想要做的事情。让我们假装这是SomeOtherBlogsController#show行动发生的事情。

  • 我正在尝试让专家找到SomeOtherBlogPolicy#show?方法,
  • 在该Policy方法中,我还要检查@blog资源的授权访问权限。

希望这个问题很明显。由于资源类不与控制器名称同步,似乎我无法执行上述操作。所以如果我在各种不同的控制器中使用相同的资源,我运气不好。我无法在各种控制器上下文中检查资源的授权。这与Pundit无关吗?

更新

在控制器中,我还尝试直接调用Policy方法:

SomeOtherBlogPolicy.new(current_user, @blog).show?

但是,调用它会引发Pundit::AuthorizationNotPerformedError。因此,authorize方法看起来更多,而不仅仅是返回truefalse

3 个答案:

答案 0 :(得分:2)

您可以通过以下方式手动指定模型的资源类:

class Blog
  def self.policy_class
    SomeOtherBlogPolicy
  end
end

不幸的是,在将authorize称为implemented as时,无法从控制器指定策略类:

 # Retrieves the policy for the given record, initializing it with the record
  # and current user and finally throwing an error if the user is not
  # authorized to perform the given action.
  #
  # @param record [Object] the object we're checking permissions of
  # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
  #   If omitted then this defaults to the Rails controller action name.
  # @raise [NotAuthorizedError] if the given query method returned false
  # @return [Object] Always returns the passed object record
  def authorize(record, query = nil)
    query ||= params[:action].to_s + "?"

    @_pundit_policy_authorized = true

    policy = policy(record)

    unless policy.public_send(query)
      raise NotAuthorizedError, query: query, record: record, policy: policy
    end

    record
  end

这可能是一个非常好的功能请求。

但正如您从上面的代码中可以看到,授权中确实没有那么多,您可以轻松创建自己的实现 - 您只需设置@_pundit_policy_authorized = true以避免令人讨厌的Pundit::AuthorizationNotPerformedError

def my_authorize(record, query = nil, policy_class: nil)
  # bail early and defer to default implementation
  return authorize(record, query) unless policy_class
  query ||= params[:action].to_s + "?"
  @_pundit_policy_authorized = true
  policy = policy_class.new(pundit_user, record)
  unless policy.public_send(query)
    raise NotAuthorizedError, query: query, record: record, policy: policy
  end
  record 
end

用法:

my_authorize(@blog, policy_class: SomeOtherBlogPolicy)

答案 1 :(得分:0)

这是Pundit版本2.0.0.中的内置功能。文档使用以下内容更新:

  

如有必要,您可以传递参数以覆盖策略类。例如:

def create
  @publication = find_publication # assume this method returns any model that behaves like a publication
  # @publication.class => Post
  authorize @publication, policy_class: PublicationPolicy
  @publication.publish!
  redirect_to @publication
end

答案 2 :(得分:0)

是否也可以在视图中覆盖策略类?我尝试这样做,但遇到错误unknown keyword: :policy_class

<% if policy(@publication, policy_class: PublicationPolicy).create? %>
相关问题