Django-guardian: user.has_perm("perm", obj) 行为异常

创建于 2011-09-03  ·  28评论  ·  资料来源: django-guardian/django-guardian

如果我使用标准user.has_perm("perm")方法,那么如果用户具有全局权限"perm" ,它将仅返回True #$ 。
如果使用user.has_perm("perm", obj) ,如果用户有权访问此特定对象,它将返回True
但它会返回False ,即使用户拥有全局权限"perm" ,这对我来说是非常意外的,因为我认为拥有全局权限应该让用户访问所有对象。 我对吗?

Enhancement

最有用的评论

嘿,您可以粘贴您的 _AUTHENTICATION_BACKENDS_ 设置吗?

正如您已经阅读过 Django 文档一样,您知道指定后端的顺序很重要。

我想您的应用程序中有以下内容:

AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

或这个:

from django.conf import global_settings
AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
) + global_settings.AUTHENTICATION_BACKENDS

只需确保首先指定默认后端即可。

你能确认这是问题所在吗? 如果没有,请添加更多信息(也许您也使用其他一些后端,或者其他一些应用程序猴子补丁_User.has_perm_ 方法?)。

所有28条评论

我挖掘了一点,发现每个权限后端都应该独立工作,所以不应该出现我上面描述的情况。 但是由于某种原因调用user.has_perm提供对象实例,看起来只检查对象级别的权限,并跳过全局权限检查。 我不知道,这种行为的原因是什么。 我正在使用 Django 1.2.5。

嘿,您可以粘贴您的 _AUTHENTICATION_BACKENDS_ 设置吗?

正如您已经阅读过 Django 文档一样,您知道指定后端的顺序很重要。

我想您的应用程序中有以下内容:

AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

或这个:

from django.conf import global_settings
AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
) + global_settings.AUTHENTICATION_BACKENDS

只需确保首先指定默认后端即可。

你能确认这是问题所在吗? 如果没有,请添加更多信息(也许您也使用其他一些后端,或者其他一些应用程序猴子补丁_User.has_perm_ 方法?)。

好吧,看来我太累了。 订单不应影响 _has_perm_ 结果。 因此,请添加有关您正在使用的应用程序设置的更多信息。 此外,您可以通过运行测试套件(_python manage.py test Guardian_)来确保 Guardian 正常工作。

哦,好的,我又读了一遍你的问题。 默认 _auth.ModelBackend_支持 _supports_object_permissions_(该属性为 _False_)。 根据 Django 文档,他们将从 1.4 添加对默认后端的支持。

因此,对于您的情况,行为是绝对正确和预期的。 默认后端被简单地省略。

在检查应用程序中的对象级别权限之前,您应该检查全局权限。 这是我能想到的最简单的解决方案。

关闭为_invalid_,但是如果您想用新评论重新打开它 - 请随意这样做!

好的,看起来你是对的。 但是不检查全局权限给我带来了一些严重的困难。 例如在guardian.decorators.permission_required中,正如我假设的那样,它应该扩展普通django.contrib.auth.decorators.permission_required的功能,并带有额外的对象级别权限检查。
问题是,我有一个使用全局权限的工作应用程序,我想向它添加一些额外的对象级权限,所以我已经将默认的permission_required装饰器更改为 django-guardian 的对象级装饰器,但是那么用户就失去了基于全局权限的访问权限。

对我来说这似乎是一个意外的行为,请您至少将此案例添加到文档中,因为如果不查看代码,它并不明显。

对我来说,这是一个设计缺陷,导致对代码进行额外检查,我需要验证全局和每个对象的权限。

@coagulant :从 Django 的身份验证中实际 _extend_ 装饰器并不灵活。 这个应用程序 intets 实现_对象权限_,而不是扩展原始权限。 即,如果您想使用仅使用监护人和对象级别权限的其他应用程序怎么办? 如果您只想在管理员中使用全局权限,而在授予普通用户的应用程序中使用对象级权限怎么办? 如果应用程序需要全局权限对象级权限才能让用户能够对对象执行某些操作,该怎么办? 有很多情况,监护人不会覆盖所有的。

另一方面,我可以承认这个特定的用例可能比其他用例更常见。 对于对此感兴趣的任何人-请提交另一个指定要求的问题。 我相信这可以很容易地实现,而无需背单词不兼容 - 即使用新的配置设置。

如果只添加了另一个 permission_required 装饰器,它会检查对象级权限和全局权限,这对我很有用。 那将涵盖我的问题。

@Dzejkob ,您能否检查一下最后一次提交并说这是否足够(我添加了接受全局权限的标志)。 另外,让我知道在装饰器本身扩展文档字符串是否足够,或者我应该添加更多示例/更具描述性的文档?

注意:提交在新分支:_feature/add-accept_global_perms-flag-for-decorators_

@lukaszb是的,我已经在我的项目中安装了 Guardian 的新分支版本,在permission_required装饰器accept_global_perms=True中添加了参数,它似乎可以正常工作。 因此,感谢您制作此功能。 我认为将其合并到后备箱中是个好主意。
关于文档,他们对我来说很清楚,所以我认为这就足够了。

这是很久以前修复的。

你好,我在尝试 jango-guardian 时发现了这个问题,我发现这种行为有问题/非常令人困惑。

我向用户添加了一个全局权限(我们称之为view_user )(我们称之为joe ),然后检查 Joe 是否可以查看特定用户(我们称之为other_user ) 并且令人惊讶的是它返回False

joe = User.objects.get(username="joe")
other_user = User.objects.get(username="other_user")
assign_perm("myapp.view_user", joe)
joe.has_perm("myapp.view_user") # True as expected
joe.has_perm("myapp.view_user", other_user) # False, whaaaat?

现在,我没有明确使用permission_required装饰器,因为我的视图集“自动”检查权限,因为我的REST_FRAMEWORK/DEFAULT_PERMISSION_CLASSESAUTHENTICATION_BACKENDSsettings.py . 那么我如何告诉监护人按预期行事?

(抱歉,如果我错过了一些非常愚蠢的东西,这是 djangoland 的第 2/3 天)

对不起今天的噪音,更有趣/令人困惑的是guardian.shortcuts.get_objects_for_user()默认情况下尊重 auth/global 权限( accept_global_perms )。

accept_global_perms: if True takes global permissions into account. 
[...]
Default is True.

虽然,这个问题被另一个问题所取代,但这里有很多见解,所以我在这里写信是为了重新引发讨论。 Python 之禅 说:

需要有一种——最好只有一种——明显的方法来做到这一点。

对我来说,当未指定本地(对象级别)时_回退到全局_。 该顺序不会改变结果(因为 Django returns False if obj is not None ),但可能会改变性能。

我确实同意对代码进行双重检查不是一个好的设计,并且在某种意义上也违反了 DRY 原则。 但是,Django 默认后端中可用的监护人实现功能也不是 DRY。 Django 允许多个后端,并按顺序逐一检查表明后端应该一起玩,而不是相互替换。 如果这是正确的,那么当obj is not None是_错误_时,Django 拒绝检查全局变量。 如果 Django 忽略了obj ,它可能是对象检查器的全局后备。

我认为我们应该向 Django 开一张票,询问授权后端是否以及如何相互配合,然后从那里开始。

话虽如此,我认为 Django 改变其行为是不太可能的(尽管我仍然认为我们应该问),所以现状表明,监护人需要发展以能够根据需要提供两者(哪个可以通过设置或函数的参数进行设置)。 但我认为默认行为应该是当 false_ 全面时 _local 回退到 global。

如果 Django 更喜欢三态权限系统,我宁愿喜欢它:True、False 和 None。 在这种情况下,本地检查器可以通过False _override_ 全局变量; 并且通过None每个后端可以说:“我不知道,问下一个”。 在这种情况下,Django 将在其中一个后端获得True or False后停止检查,并停止浪费处理能力。

这将赋予每个后端更多的权力:可以给出明确的答案,或者参考其他人。

@doganmeh Django 确实实现了三态权限系统(至少从 1.10 开始)。 选项是 True、None 和提高 PermissionDenied。 执行后者将导致 Django 停止检查并返回 False。

至于手头的问题,我认为这是一个 Contrib.Auth 问题。 那是处理“全局权限”的后端。 问题是他们已经建立了一个间接约定,带有 obj=None 的 has_perm 只检查“表权限”,而带有 obj 的 has_perm 只检查“行权限”。 他们不太可能改变这一点,但_应该_愿意扩展他们的 API 以支持两者的检查。

(至少我希望他们愿意添加行为来检查两者。这似乎是他们系统中的一个明显缺陷。)

您对“同时检查”API 有何建议? 我能想到的最好的是kwarg。

我在谈论一个明确的三态系统,其中 Permission 表上有一个 NullBooleanField。 的确,如果您将缺少True (或缺少权限)视为None ,则可以将其视为三态。 在这种情况下,监护人未指定的权限应视为None ,并且应将决定委托给全局。 不过,如果我有选择,我会采用显式设计。

@airstandley我猜你的意思是在user.has_perm中添加一个 kwarg,例如:

def has_perm(self, perm, obj=None, fallback=False)

我认为这会有所帮助。 在fallback=True的情况下,监护人将调用 Django 后端并返回全局。 不过,如果我有选择的话,我希望 Django 的回退由框架自然地处理,而不是通过劫持其内部。

@doganmeh对不起,我说错了。 那应该是“True, False , and raise PermissionDenied”。 不是:

选项是 True、None 和提高 PermissionDenied

后者是我如何看待我脑海中的回报......

我想我可能也误解了你的意思

如果 Django 更喜欢三态权限系统,我宁愿喜欢它

澄清一下,您是在专门讨论权限模型实现而不是整个系统?

我的观点是,身份验证后端系统确实允许您描述的确切捷径(至少对于has_permhas_module_perms api)。 它是明确的,即使不是直截了当的:
任何后端都可以自己做出决定(返回 True 或 PermissionDenied),或者将决定委托给链中的下一个支持者(返回 False)。 这是一个实施特定的决定,而不是系统本身的质量。

显式 ObjectPermissionBackend 对我的项目来说是个问题,所以我会选择不显式。 (“显性”使与其他权限检查后端的集成变得麻烦。)我怀疑,像您一样,其他人更希望它是显式的。 因此,将“明确性”作为设置对我来说很有意义。

@doganmeh
所以关于kwarg。

首先,我一直担心我们对于 auth 后端系统的行为是如何工作的看法不一致。 所以要清楚:
我的理解是,目的是后端一起工作。 如果一个应用程序想要使用 auth 的权限系统(即“全局权限”,虽然从技术上讲它们将是“表权限”,但是土豆,土豆)它会在 AUTH_BACKENDS 中安装 auth ModelBackend。 如果该应用程序想要使用监护人的 ObjectPermission 系统(即“行权限”),它会在 AUTH_BACKENDS 中不包含监护人的 ObjectPermissionBackend。
ModelBackend 将处理全局权限。
ObjectPermissionBackend 将处理对象权限。
如果用户只有对象权限,ModelBackend 永远不会为has_perm返回 True。 如果用户只有全局权限,ObjectPermissionBackend 永远不会为has_perm返回 True。 在这两种情况下,对 user.has_perm(perm, obj) 的调用仍应返回 true,因为用户确实拥有已安装后端的 _one_ 的权限。 (这是我们遇到了问题,因为 ModelBackend 在这个帐户上失败了)

好吧,鉴于这一切。

在兼容性不是问题的理想世界中,我也希望对 contrib.auth 的 ModelBackend 进行简单的更改。 如果用户具有 perm 指定的“全局权限”, ModelBackend.has_perm(user_obj, perm, obj=None)应该返回 True; 不管 obj 是否为 None 。

然而兼容性是一个问题,这就是为什么我问你是否有解决这个问题的办法。

我能想到的唯一 _backwards compatible_ 解决方案是向 API 添加 kwarg 或添加 AUTH 设置。

所以kwargs:
让我们称它为obj_shortcut ,因为这是我能想到的最好的方法。 object_shortcut将默认为 True。 如果object_shortcut为 True,则后端的行为应与 ModelBackend 现在一样:如果 obj 为 None,它们应该_only_检查“表/全局”权限,否则他们应该_only_检查“行/对象”权限。 但是,如果object_shortcut为 False,那么后端的行为应该像我们都喜欢的那样:当 object 不是 None 时,它​​们会检查 _both_ 全局和 object 权限。 然后像 Guardian 这样的附加组件总是可以提供一个带有has_perm方法的 mixin,其中object_shortcut=False作为默认值。 如果将 perm 表示的权限分配给用户, user.has_perm(perm, obj, object_shortcut=False)将正确返回 True,而对user.has_perm(perm, obj)的旧调用将继续返回 False。

设置将具有相同的结果,但基本上会导致ModelBackend._get_permissions中的切换。

if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
    return set()

会变成这样:

if not user_obj.is_active or user_obj.is_anonymous or (obj is not None and legacy_behaviour_setting_is _on):
    return set()

不确定哪个更好。 我认为设置更干净,但我确信 Django 开发人员更有资格确定这一点。

关键是问题可以修复,我坚信问题是 Django 错误,而不是 Guardian。

@airstandley你是对的, FalseNone无关紧要。 他们都表示我不知道。 最终,如果没有人知道,则不会授予许可。

我通常与您在同一页面上,尽管 django 强制后端以一种或另一种方式表现似乎会降低松散耦合性。 我认为它应该具有允许每个后端获取完成工作所需的信息的机制。 我同意你的关键字参数,但不仅仅是强迫一种或另一种方式。

这次谈话似乎是在浪费时间,但它帮助我向自己澄清,没有授予监护人许可不是否认,只是缺乏信息。

无论如何,我提出了一个拉取请求 #546。 请检查一下,让我知道你的想法。

我用 django 创建了一张票: https ://code.djangoproject.com/ticket/29012,想知道他们会说什么。

似乎还有其他一些带有 django 的票: https ://code.djangoproject.com/ticket/20218

我关闭了我的。

@doganmeh
我喜欢你的方向#546。 如果有帮助,我应该有时间查看其余的单元,并在本周末为这些后备写一些测试。

在切线上。 您关于“Django 强制后端”以特定方式表现的评论让我感到困惑,但它也给了我一个想法。 后端可以随心所欲地运行; 我最初对 Guardian 的 ObjectPermissionBackend 的担忧源于文档表明它被设计为与 Auth 的 ModelBackend 一起运行。 Guardian 可以提供多个后端,一个设计用于与 ModelBackend 一起工作,一个设计为单独工作。 (IE 一个只检查 Guardian 的 UserObjectPermission/GroupObjectPermission 表,另一个检查 UserObjectPermission/GroupObjectPermission 表和 Auth 的 Permission 表。)
就个人而言,我更喜欢当前的设置和 kwargs 方法。 我认为多后端方法的主要缺点是不清楚快捷方式和便利功能应该如何表现。

看到了 Django 开发邮件列表的帖子。 我希望他们接受这一点,或者同意以不兼容的方式改变行为。 目前对 API 的限制实在是太笨拙了。

你能在那里支持吗? 😊

从 Windows 10 的邮件发送

来自:airstandley
发送:2018 年 1 月 12 日星期五 12:06 PM
收件人:django-guardian/django-guardian
抄送:穆罕默德·多安; 提到
主题:回复:[django-guardian/django-guardian] user.has_perm("perm", obj) 行为异常 (#49)

看到了 Django 开发邮件列表的帖子。 我希望他们接受这一点,或者同意以不兼容的方式改变行为。 目前对 API 的限制实在是太笨拙了。

你收到这个是因为你被提到了。
直接回复此电子邮件,在 GitHub 上查看它,或将线程静音。

在处理这个问题大约一周后,我更加坚信每个后端只需要做一件事,那就是 Guardian 的对象权限。 为此,Django 必须更好地对待obj

我为此发送给 Django 的补丁: https ://github.com/django/django/pull/9581(如果可以,请发表评论)。 成功的,我们可以清理 Guardian 中任何地方的模型权限检索,只需调用默认后端即可。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

xuhcc picture xuhcc  ·  10评论

g-as picture g-as  ·  10评论

Allan-Nava picture Allan-Nava  ·  4评论

David-OConnor picture David-OConnor  ·  6评论

brianmay picture brianmay  ·  16评论