django - 将所有 Guardian 用户对象权限从一个 Django 用户重新分配给另一个 Django 用户

标签 django django-guardian

我有一个 Django 系统,它使用 Guardian 的用户对象权限进行访问控制。我想编写一个函数,查找与“旧”用户关联的所有内容类型的所有权限,并将它们分配给"new"用户。我无法直接查询 UserObjectPermission 因为我们的系统使用直接 foreign key models出于性能原因。

这是一个我想要的示例

from django.contrib.auth.models import User
from guardian.shortcuts import get_objects_for_user, assign_perm

def update_permissions_for_user(old_user, new_user):
    # Retrieve all USER permission objects for the old user
    # this throws exception because it requires a perms param that I do not want to provide.  I want all objects across all models
    old_user_permissions = get_objects_for_user(old_user, use_groups=False, any_perm=False, with_superuser=False, accept_global_perms=False)

    # Iterate through each permission object and update it to point to the new user
    for permission in old_user_permissions:
        assign_perm(permission.codename, new_user, permission)
        remove_perm(permission.codename, old_user, permission)

    
# Example usage:
old_user = User.objects.get(username='old_username')  # Replace 'old_username' with the actual username
new_user = User.objects.get(username='new_username')  # Replace 'new_username' with the actual username

update_permissions_for_user(old_user, new_user)

如果我们不使用直接外键模型,代码将简单地是:

user_objects_permission_qs = UserObjectPermission.objects.filter(user=current_user)
affected_rows = user_objects_permission_qs.update(user=new_user)

但是,我想要的示例的问题是 get_objects_for_user需要一个 perms 参数。并且,根据卫报文档

If more than one permission is present within sequence, their content type must be the same or MixedContentTypeError exception would be raised.

因此这不适用于具有不同内容类型的对象。有什么建议么?我还考虑过使用 get_user_perms但这也需要一个对象参数。

这是我们创建直接外键模型的方法

class MyModelUserObjectPermission(UserObjectPermissionBase):
    content_object = models.ForeignKey(MyModel, on_delete=models.CASCADE)

后脚本

@willeM_ Van Onsem 解决方案在 SQLite 中运行良好,但在 MySQL 中引发以下错误。

此外,我们必须显式检查 UserObjectPermission 类(因为对于某些模型,我们仍在使用该类),并由于该模型具有不同的架构而以不同方式对待该模型

You can't specify target table 'tablename' for update in FROM clause"

我们必须将 ~Exists 更改为 .exclude()。在 MySQL 中,您无法在同一语句中连接要更新的表

最佳答案

在这种情况下,我们可能可以“批量”执行此操作,例如每个 UserObjectPermissionBase 的子类一个查询。然而,有一个可能的警告:如果我们想将权限分配给新用户,并且该用户已经获得了该权限。

单个模型的查询如下所示:

<em>MyModelUserObjectPermission</em>.objects.filter(user=old_user).update(<b>user=new_user</b>)

但如果新用户的权限已存在,则此操作将会失败,因为这样存在唯一性约束,该约束将会失败。

但是我们可以look before we leap [wiki] ,其中:

from django.db.models import Exists, OuterRef

<em>MyModelUserObjectPermission</em>.objects.filter(
    ~Exists(
        <em>MyModelUserObjectPermission</em>.objects.filter(
            user=new_user,
            content_object=OuterRef('content_object'),
            permission=OuterRef('permission'),
        )
    ),
    user=old_user,
).update(user=new_user)
<em>MyModelUserObjectPermission</em>.objects.filter(user=old_user).delete()

因此,这将首先检查我们是否可以更改它,以便同一对象、权限和新用户不存在 MyModelUserObjectPermission。如果是这种情况,我们会更新它。在第二个查询中,我们删除了仍然存在的旧用户的权限。

现在我们唯一需要做的就是对所有模型执行此操作,我们可以通过查找UserObjectPermissionBase的子类来完成此操作:

from django.db.models import Exists, OuterRef
from guardian.models.models import UserObjectPermissionBase


def change_perm_for_klass(klass, old_user, new_user):
    klass.objects.filter(
        ~Exists(
            klass.objects.filter(
                user=new_user,
                content_object=OuterRef('content_object'),
                permission=OuterRef('permission'),
            )
        ),
        user=old_user,
    ).update(user=new_user)
    klass.objects.filter(user=old_user).delete()


def update_permissions_for_user(old_user, new_user):
    subclasses = set()
    new_gen = {UserObjectPermissionBase}
    while new_gen:
        subclasses.update(new_gen)
        <b>new_gen = {sc for k in new_gen for sc in k.__subclasses__()}</b>
    for klass in subclasses:
        if not klass._meta.abstract:
            change_perm_for_klass(klass, old_user, new_user)

在这里,我们首先划分类层次结构,查找 UserObjectPermissionBase 的所有子类,接下来我们将为所有这些不存在的模型调用 change_perm_for_klass(…)摘要。

当然,只有通过定义 UserObjectPermissionBase 的子类来实现 django-guardian 时,该逻辑才有效。

关于django - 将所有 Guardian 用户对象权限从一个 Django 用户重新分配给另一个 Django 用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77431461/

相关文章:

django - Django-Guardian 可以做什么 Django Auth 权限无法做到的事情?

javascript - NoReverseMatch 与 Django url

Django 模型、信号和电子邮件发送延迟

python - 使用输入编辑模型。值错误

python - django-guardian和django-rules可以一起使用吗?

django - 如何在自定义用户模型中使用 django-guardian

python - 这是 Django 中的错误还是什么?注销 Django 身份验证系统...会删除所有 session 吗?

javascript - Firefox 处理 xxx.submit(),Safari 不处理……可以做什么?

python - 克隆 django-guardian 中的所有对象权限/从 ForeignKey 继承

python - 使用 django-rest-framework 设置对象级权限