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。
- 示例:应用程序可以调用
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?
方法,
- 在该策略方法中,我还想检查
@blog
资源的授权访问。
希望问题很明显。由于资源 class 与控制器名称不同步,看来我无法执行上述操作。因此,如果我在各种不同的控制器中使用相同的资源,那我就不走运了。我将无法在那些不同的控制器上下文中检查资源的授权。这对 Pundit 来说是不可能的吗?
更新:
在控制器中,我也试过像这样直接调用策略方法:
SomeOtherBlogPolicy.new(current_user, @blog).show?
然而,调用引发了 Pundit::AuthorizationNotPerformedError
。所以看起来 authorize
方法中发生的事情比仅仅返回 true
或 false
还要多。
您可以通过以下方式为模型手动指定资源 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? %>
假设我正在使用 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。
- 示例:应用程序可以调用
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?
方法, - 在该策略方法中,我还想检查
@blog
资源的授权访问。
希望问题很明显。由于资源 class 与控制器名称不同步,看来我无法执行上述操作。因此,如果我在各种不同的控制器中使用相同的资源,那我就不走运了。我将无法在那些不同的控制器上下文中检查资源的授权。这对 Pundit 来说是不可能的吗?
更新:
在控制器中,我也试过像这样直接调用策略方法:
SomeOtherBlogPolicy.new(current_user, @blog).show?
然而,调用引发了 Pundit::AuthorizationNotPerformedError
。所以看起来 authorize
方法中发生的事情比仅仅返回 true
或 false
还要多。
您可以通过以下方式为模型手动指定资源 class:
class Blog
def self.policy_class
SomeOtherBlogPolicy
end
end
不幸的是,在调用 authorize
...
我最初写答案时是这样的。 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? %>