假设我使用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?
方法,@blog
资源的授权访问权限。希望这个问题很明显。由于资源类不与控制器名称同步,似乎我无法执行上述操作。所以如果我在各种不同的控制器中使用相同的资源,我运气不好。我无法在各种控制器上下文中检查资源的授权。这与Pundit无关吗?
更新:
在控制器中,我还尝试直接调用Policy方法:
SomeOtherBlogPolicy.new(current_user, @blog).show?
但是,调用它会引发Pundit::AuthorizationNotPerformedError
。因此,authorize
方法看起来更多,而不仅仅是返回true
或false
。
答案 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? %>