简体   繁体   English

Django rest 框架 ViewSet 中的 chainig 权限

[英]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]

and custom permission is:和自定义权限是:

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

first i dont get object permission but with help of @brian-destura at this question i fixed that part the previous question now the problem is when i chain 2 permission together it behave like AllowAny i check them one by one and both permissions work fine, one of them allow admin and one of them allow owner but when they are or together it mess everything up首先,我没有获得 object 许可,但是在@brian-destura 在这个问题上的帮助下,我修复了上一个问题的那部分,现在问题是当我将 2 个权限链接在一起时,它的行为就像AllowAny我一个一个检查它们,两个权限都可以正常工作,其中一个允许管理员,其中一个允许所有者,但是当他们在一起或在一起时,一切都搞砸了

When chaining permissions like当链接权限时

permission_classes = [AccountOwnerPermission, IsAdminUser]

it behaves like an AND operator between the permission classes它的行为类似于权限类之间的 AND 运算符

The best option is to create a new permission that allows either the permission logic.最好的选择是创建一个允许任一权限逻辑的新权限。

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

or this when the permissions used have long complex code to keep code DRY:或者当使用的权限有很长的复杂代码以保持代码干燥时:

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)

@Kyell already described the problem and his answer should be accepted @Kyell 已经描述了这个问题,他的回答应该被接受

But I'll try to add some details:但我会尝试添加一些细节:

  1. When we chain two permission classes DRF creates one new OR class:当我们链接两个权限类时,DRF 会创建一个新的OR class:
>>> 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 documentation says that has_object_permission (check permissions for exact object) method running after has_permission (check permissions for whole view class) Django 文档has_object_permission (检查确切对象的权限)方法在has_permission之后运行(检查整个视图类的权限)

  2. Let's see how looks these methods inside chained OR class:让我们看看这些方法在链式OR class 中的外观:

>>> 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)
        )

So we can see that DRF check both of has_permission and after that both of has_object_permission所以我们可以看到 DRF 检查了has_permission和之后的has_object_permission

has_permission check may be skipped because we run has_object_permission after. has_permission检查可能会被跳过,因为我们在之后运行has_object_permission

But!但! has_object_permission is not implemented inside IsAdminUser permission class but it is implemented inside parent BasePermission class and looks like: has_object_permission未在IsAdminUser权限 class 内部实现,但它在父BasePermission class 内部实现,如下所示:

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

So IsAdminUser always return True on has_object_permission .所以IsAdminUser总是在has_object_permission上返回True In usual cases IsAdminUser should fail on has_permission , but in your OR class has_permission passed because it is not implemented inside AccountOwnerPermission class在通常情况下IsAdminUser应该在has_permission上失败,但在您的OR class has_permission通过,因为它没有在AccountOwnerPermission中实现

Simplest solution will be to add has_permission method to AccountOwnerPermission class:最简单的解决方案是将has_permission方法添加到AccountOwnerPermission class:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM