chainig permission in Django rest framework ViewSet


class UserViewSet(viewsets.ModelViewSet):
def list(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
pass
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
self.check_object_permissions(request, user)
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
def get_permissions(self):
if self.action == "list":
permission_classes = [
IsAdminUser,
]
elif self.action == "create":
permission_classes = [AllowAny]
else:
permission_classes = [AccountOwnerPermission | IsAdminUser ]
return [permission() for permission in permission_classes]

自定义权限为:

class AccountOwnerPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print(object)
print(request.user)
return obj == request.user

首先,我没有得到对象许可,但在这个问题上@brian-Destura 的帮助下,我修复了上一个问题的那部分 现在的问题是,当我将 2 个权限链接在一起时,它的行为就像AllowAny一样,我一一检查它们,两个权限都工作正常,其中一个允许管理员,一个允许所有者,但是当它们在一起或在一起时,它会搞砸一切

当链接权限时,例如

permission_classes = [AccountOwnerPermission, IsAdminUser]

它的行为类似于权限类之间的 AND 运算符

最佳选择是创建允许权限逻辑的新权限。

class AdminOrAccountOwnerPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user or request.user.id_admin

或者当使用的权限具有长而复杂的代码以保持代码干燥时:

class AdminOrAccountOwnerPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return AccountOwnerPermission().has_object_permission(request, view, obj) or IsAdminUser().has_object_permission(request, view, obj)

编辑: 解决评论中的问题,它表现得像AllowAny的原因。AccountOwnerPermissionhas_object_permission但没有has_permission。另一方面,IsAdminUserhas_permission但没有实施has_object_permission

当这些函数未实现时,函数默认返回True(从BasePermission)。因此,当运行has_permission时,AccountOwnerPermission总是返回True当运行has_object_permission时,IsAdminUser总是返回True

实现AccountOwnerPermission.has_permission将给出预期的行为。

@Kyell已经描述了这个问题,他的答案应该被接受

但我会尝试添加一些细节:

  1. 当我们链接两个权限类时,DRF 会创建一个新的OR类:
>>> from rest_framework.permissions import IsAdminUser 
>>> or_class = [IsAdminUser | IsAdminUser]
>>> len(or_class)
1
>>> print(or_class)
[<rest_framework.permissions.OperandHolder object at 0x1096d5fa0>]
>>> 
  1. Django 文档说has_object_permission(检查确切对象的权限)方法在has_permission之后运行(检查整个视图类的权限)

  2. 让我们看看这些方法在链式OR类中的外观:

>>> import inspect
>>> or_instance = or_class[0]()
>>> print(inspect.getsource(or_instance.has_permission))
def has_permission(self, request, view):
return (
self.op1.has_permission(request, view) or
self.op2.has_permission(request, view)
)
>>> print(inspect.getsource(or_instance.has_object_permission))
def has_object_permission(self, request, view, obj):
return (
self.op1.has_object_permission(request, view, obj) or
self.op2.has_object_permission(request, view, obj)
)

因此,我们可以看到DRF检查了has_permission,之后两者都has_object_permission

has_permission检查可能会被跳过,因为我们在之后运行has_object_permission

但!has_object_permission不是在权限类IsAdminUser实现的,而是在父类BasePermission类中实现的,如下所示:

class BasePermission(metaclass=BasePermissionMetaclass):
def has_object_permission(self, request, view, obj):
return True

所以IsAdminUser总是在has_object_permission上返回True.在通常情况下,IsAdminUser应该在has_permission上失败,但在你的OR类中has_permission通过,因为它没有在类内实现AccountOwnerPermission

最简单的解决方案是将has_permission方法添加到类AccountOwnerPermission

class AccountOwnerPermission(permissions.BasePermission):
def has_permission(self, request, view, obj):
return False

相关内容

  • 没有找到相关文章

最新更新