【问题标题】:Objects with permissions assigned by django-guardian not visible in admin具有由 django-guardian 分配的权限的对象在管理员中不可见
【发布时间】:2012-06-11 07:59:54
【问题描述】:

我使用django-guardian 来管理每个对象的权限。

对于给定的用户,我授予一个对象的所有权限:

joe = User.objects.get(username="joe")

mytask = Task.objects.get(pk=1)

assign('add_task', joe, mytask)
assign('change_task', joe, mytask)
assign('delete_task', joe, mytask)

我得到了,正如预期的那样:

In [57]: joe.has_perm("add_task", mytask)
Out[57]: True

In [58]: joe.has_perm("change_task", mytask)
Out[58]: True

In [59]: joe.has_perm("delete_task", mytask)
Out[59]: True

在 admin.py 中,我还让 TaskAdmin 继承自 GuardedModelAdmin 而不是 admin.ModelAdmin

现在,当我使用 joe 连接到我的网站时,在管理员上我得到:

You don't have permission to edit anything 

我不应该能够编辑对象 mytask 吗?

我是否必须使用内置的基于模型的权限系统设置一些权限?

我错过了什么吗?

编辑

我尝试添加选项 user_can_access_owned_objects_only,这应该可以解决我的问题,但我仍然无法在我的管理员中看到任何内容...

class TaskAdmin(GuardedModelAdmin):

    user_can_access_owned_objects_only = True

    pass

admin.site.register(Task, TaskAdmin)

谢谢

【问题讨论】:

    标签: django permissions django-admin django-authentication django-guardian


    【解决方案1】:

    为了只看到当前用户拥有的实例,我给了他所有权限

    add_task=Permission.objects.get(codename="add_task")
    change_task=Permission.objects.get(codename="change_task")
    delete_task=Permission.objects.get(codename="delete_task")
    
    joe.user_permissions.add(add_task)
    joe.user_permissions.add(change_task)
    joe.user_permissions.add(delete_task)
    

    然后我使用 Guardian.shortcuts.assign 对几个实例设置权限,并在管理员中过滤查询集:

    class TaskAdmin(admin.ModelAdmin):
    
        def queryset(self, request):
                if request.user.is_superuser:
                    return super(TaskAdmin, self).queryset(request)
                return get_objects_for_user(user=request.user, perms=['add_task', 'change_task', 'delete_task'], klass=Task)
    

    它远非完美,但我找不到任何其他解决方案。

    【讨论】:

      【解决方案2】:

      Django Admin,特别是它处理 change 权限,有点粗粒度。内部方法ModelAdmin.has_change_permission() 涵盖了对view 权限的检查,实际上是缺少的。

      其次,GuardedModelAdmin 带来了所有权检查(通过user_can_access_owned_objects_only)和用于管理行级权限的表单。它不向 Django Admin 提供任何其他行级访问策略。

      对于Django Admin中典型的行级权限场景,我想建议代码,这里我引入了一个可选的'view'权限:

      class ExtendedGuardedModelAdmin(GuardedModelAdmin):
          def queryset(self, request):
              qs = super(ExtendedGuardedModelAdmin, self).queryset(request)
              # Check global permission
              if super(ExtendedGuardedModelAdmin, self).has_change_permission(request) \
                  or (not self.list_editable and self.has_view_permission(request)):
                      return qs
              # No global, filter by row-level permissions. also use view permission if the changelist is not editable
              if self.list_editable:
                  return get_objects_for_user(request.user, [self.opts.get_change_permission()], qs)
              else:
                  return get_objects_for_user(request.user, [self.opts.get_change_permission(), self.get_view_permission(
      )], qs, any_perm=True)
      
          def has_change_permission(self, request, obj=None):
              if super(ExtendedGuardedModelAdmin, self).has_change_permission(request, obj):
                  return True
              if obj is None:
                  # Here check global 'view' permission or if there is any changeable items
                  return self.has_view_permission(request) or self.queryset(request).exists()
              else:
                  # Row-level checking
                  return request.user.has_perm(self.opts.get_change_permission(), obj)
      
          def get_view_permission(self):
              return 'view_%s' % self.opts.object_name.lower()
      
          def has_view_permission(self, request, obj=None):
              return request.user.has_perm(self.opts.app_label + '.' + self.get_view_permission(), obj)
      
          def has_delete_permission(self, request, obj=None):
              return super(ExtendedGuardedModelAdmin, self).has_delete_permission(request, obj) \
                      or (obj is not None and request.user.has_perm(self.opts.get_delete_permission(), obj))
      

      通过这种方式,您可以实现更灵活的权限检查,用户权限现在是全局的,用户 obj 权限是基于行级的:

      • joe.user_permissions.add(add_task)
        joe 可以添加新任务(没有行级“添加”权限)
      • joe.user_permissions.add(change_task)
        joe 可以更改所有任务
      • joe.user_permissions.add(delete_task)
        joe 可以删除所有任务
      • assign(Task._meta.get_change_permission(), joe, obj)
        joe 可以更改 Task obj,查看包含 obj 的更改列表以及其他可更改的任务。
      • assign(Task._meta.get_delete_permission(), joe, obj)
        joe 可以删除任务 obj
      • assign('view_task', joe, obj)
        [可选] joe 可以查看任务 obj(您可能希望在自定义的管理员视图页面检查此权限)
      • joe.user_permissions.add(Permission.objects.get(codename='view_task', ...))
        [可选] joe 可以查看更改列表中的所有任务,只要更改列表不可内联编辑。如果 joe 可以在没有更改权限的情况下从 raw_id_fields 中提取项目,这将非常有用。
      • ...

      如果“查看”权限对您无用,您可以安全地忽略它。

      目前django.contrib.admin.util.get_deleted_objects在权限检查时不支持obj,如果需要在删除过程中检查行级权限,请将get_deleted_objects修改为if not user.has_perm(p):if not user.has_perm(p, obj):。相关票是13539 & 16862

      【讨论】:

      • 你应该在 self.has_view_permission 中传递 'request' 而不是 'request.user'
      • 可能是版本更改相关的问题,但遇到“选项”对象没有属性“get_change_permission”
      猜你喜欢
      • 1970-01-01
      • 2011-11-13
      • 1970-01-01
      • 1970-01-01
      • 2011-06-07
      • 1970-01-01
      • 1970-01-01
      • 2014-06-21
      • 2013-08-20
      相关资源
      最近更新 更多