我也许误解了Pundit的政策,但我面临着一个问题,UserPolicy
与SongPolicy
发生了冲突。
如果AssertUserPolicy
中的语句时忽略了SongPolicy
中的内容,会发生什么情况:
Pundit::NotAuthorizedError in SongsController#edit
not allowed to edit? this User
def authorization
authorize Current.user
end
这个问题是在为用户引入新角色后出现的,但我认为我可能没有正确配置它,出于某种原因,在SongsController
中只查看UserPolicy
来Assert授权。
我有两个控制器检查用户是否登录(require_user_logged_in
),另一个控制器检查Pundit的策略(authorization
):
第一次authorization
方法如下所示:
def authorization
authorize Current.user
end
有一个应用程序级别的策略类ApplicationPolicy
:
# frozen_string_literal: true
class ApplicationPolicy
attr_reader :user, :params, :record
# Allows params to be part of policies.
def initialize(context, record)
if context.is_a?(Hash)
@user = context[:user]
@params = context[:params]
else
@user = context
@params = {}
end
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
class Scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
raise NotImplementedError, "You must define #resolve in #{self.class}"
end
private
attr_reader :user, :scope
end
end
UserPolicy
保护用户视图:
class UserPolicy < ApplicationPolicy
class Scope < Scope
end
def index?
user.has_role?(:admin)
end
def show?
# Access if admin or the same user only.
user.has_role?(:admin) || is_same_user?
end
def create?
index?
end
def new?
create?
end
def update?
index? || is_same_user?
end
def edit?
update? # This is called when accessing a view for `SongsController`.
end
def destroy?
index? || is_same_user?
end
def delete?
destroy?
end
private
# Used to keep a user from editing another.
# Admins should be allowed to edit all users.
def is_same_user?
# Check if user being accessed is the one being logged in.
params[:id].to_s == Current.user.username.to_s
end
end
而SongPolicy
:
class SongPolicy < ApplicationPolicy
class Scope < Scope
end
def index?
end
def show?
end
def create?
user.has_role?(:admin) || user.has_role?(:collaborator) # This is ignored.
end
def new?
create?
end
def update?
create?
end
def edit?
create?
end
def destroy?
user.has_role?(:admin)
end
def delete?
destroy?
end
end
不知道还有什么可以尝试在这里,我敢肯定我错过了一些东西,如果有人更多的知识Maven可以让我知道他们的想法,为什么一个政策的声明可以泄漏到另一个,这将是非常有帮助的。
2条答案
按热度按时间xtfmy6hx1#
你在当前用户上调用
authorize
,这是一个User
,所以Pundit将推断UserPolicy
策略,除非你提供一个Song
记录,否则它不会自动推断SongPolicy
策略,即使你在SongController
控制器中。如果要使用不同的策略,则需要通过
authorize(policy_class:)
提供。像这样的隐式授权通常是一种代码味道。理想情况下,您应该根据当前用户上下文显式授权当前
Song
记录。sigwle7e2#
我的观点是你对权威的方法有误解。特别是你把授权的主体和授权的客体扭曲了。让我们试着解释一下。
您有一个参与者要对对象应用操作。对象可能已被授权接收操作。
默认情况下,权威人士总是认为演员是
current_user
。操作是策略上的一种方法。
对象是您正在使用的资源;在最普通的情况下,它可以是ActiveRecord对象-但也不是必须如此。
Pundit的
authorize
方法用简单的英语来说就是“授权资源 bar 接收来自当前用户的动作 foo”,而你要做的是“授权当前用户在资源 bar 上应用动作 foo”。有什么区别?授权的主体和客体互换了。IMO,在进行授权过程中,您应该回答以下问题:“此对象是否有权接收参与者的此操作?”
注意:参与者隐式为
current_user
注意:如果未声明action,则它将隐式为
action_name
这将解决问题“是否授权此特定用户从当前用户接收:edit?”
根据推理,这是我认为适合您的示例场景的正确方法:
我不建议依赖回调,我宁愿编写更显式的代码
这段程式码会解析问题“这首特定的歌曲是否已受权从目前的使用者接收:edit?”。
关于使用自定义
policy_class
的警告像在
利用该代码,将通过调用
SongPolicy#edit?
来进行授权,但是record
将有规律地被设置为Current.user
的值;让我们假设其中
in_my_playlist?
是Song
的示例方法:你就不会有也许你没有做你想做的事情。
关于在逻辑中使用
Current.user
的警告如果
Current.user
正在使用http://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html,并且整个应用程序都依赖于该单例,那么您可能希望重新定义Pundit的默认用户,如本文所述否则,您的业务逻辑和授权逻辑最终将依赖于两个可能不同的事实来源。