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

Pundit: Authorize specific resource to differently named controller

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

class BlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :blog
  end
end

Pundit 查看传入的参数 :blog,因此推断存在 BlogPolicy class。然后它会查找带有问号的相应操作名称,例如:BlogPolicy#index?.

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

  def check_authorization
    authorize @blog
  end

同样,没问题。 Pundit 查看资源的 class Blog,然后查找 BlogPolicy class。

问题来了:当我想在特定控制器名称上授权并在特定资源上授权,但该资源与控制器名称不同步时,我该怎么办?

示例:

class SomeOtherBlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :some_other_blog, @blog
  end
end

上面的代码不起作用,但希望它显示了我正在尝试做的事情。让我们假设这发生在 SomeOtherBlogsController#show 动作上。

希望问题很明显。由于资源 class 与控制器名称不同步,看来我无法执行上述操作。因此,如果我在各种不同的控制器中使用相同的资源,那我就不走运了。我将无法在那些不同的控制器上下文中检查资源的授权。这对 Pundit 来说是不可能的吗?

更新:

在控制器中,我也试过像这样直接调用策略方法:

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

然而,调用引发了 Pundit::AuthorizationNotPerformedError。所以看起来 authorize 方法中发生的事情比仅仅返回 truefalse 还要多。

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

class Blog
  def self.policy_class
    SomeOtherBlogPolicy
  end
end

不幸的是,在调用 authorize...

时无法从控制器指定策略 class

我最初写答案时是这样的。 v2.00 添加了一个 policy_class 选项来授权:

authorize(@blog, policy_class: SomeOtherBlogPolicy)

因此不再需要我原来答案中的解决方法。

现在这是 Pundit 版本中的内置功能 2.0.0. 文档更新如下:

You can pass an argument to override the policy class if necessary. For example:

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

是否也可以覆盖视图中的策略 class?我尝试这样做但出现错误 unknown keyword: :policy_class

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