简体   繁体   English

Django REST Framework - 允许员工访问所有端点

[英]Django REST Framework - Allow staff to access all endpoints

I'm building a DRF API, and I would like to allow staff members ( is_staff == True ) to access all of the REST endpoints, while still providing custom permission checking per ViewSet.我正在构建一个 DRF API,我希望允许工作人员 ( is_staff == True ) 访问所有 REST 端点,同时仍然为每个 ViewSet 提供自定义权限检查。 Ideally, this would be a global setting, but I'm not against setting it up per ViewSet.理想情况下,这将是一个全局设置,但我不反对按 ViewSet 进行设置。

Here are the things I've tried:以下是我尝试过的事情:


Option 1: Check on every custom permission选项 1:检查每个自定义权限

from rest_framework import permissions

class SomeModelPermission(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.user.is_staff:
            return True

        # other logic

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True

        # other logic

This works, but I'd rather not repeat so much code.这有效,但我不想重复这么多代码。


Option 2: Bitwise operators选项 2:按位运算符

I tried removing the is_staff logic from the custom permission above, and adding this to the ViewSet:我尝试从上面的自定义权限中删除is_staff逻辑,并将其添加到 ViewSet:

from rest_framework import permissions, viewsets

class SomeModelViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.IsAdminUser|SomeModelPermission,)

However, this actually does not enforce permissions as I'd like, because IsAdminUser inherits from BasePermission , which is defined as:但是,这实际上并没有像我希望的那样强制执行权限,因为IsAdminUser继承自BasePermission ,其定义为:

class BasePermission(object):
    def has_permission(self, request, view):
        return True

    def has_object_permission(self, request, view, obj):
        return True

IsAdminUser doesn't define its own has_object_permission , so it will always return True when checking object permissions, resulting in unintended object access. IsAdminUser并没有定义自己的has_object_permission ,所以在检查对象权限时总是返回True ,导致意外的对象访问。


Any ideas?有任何想法吗? I was hoping there would be some way I could set a global permissions check that would return True when the user is a staff member, and defer to the custom permissions otherwise.我希望有某种方法可以设置全局权限检查,当用户是工作人员时返回True ,否则遵循自定义权限。 But reading through how permissions are determined , I'm not sure that this is possible.但是阅读权限是如何确定的,我不确定这是可能的。

Bitwise solution:按位解:

How about creating your own IsAdminUser which also defines has_object_permission ?如何创建自己的IsAdminUser也定义了has_object_permission You could just inherit from the existing one:您可以从现有的继承:

from rest_framework.permissions import IsAdminUser as BaseIsAdminUser

class IsAdminUser(BaseIsAdminUser):
    def has_object_permission(self, request, view, obj):
        # Just reuse the same logic as `has_permission`...
        return self.has_permission(request, view)

Then you can do what you attempted above, with the bitwise operator:然后,您可以使用按位运算符执行上面尝试的操作:

from rest_framework import permissions, viewsets
from your_own_project.permissions import IsAdminUser

class SomeModelViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAdminUser|SomeModelPermission,)


Another solution:另一种解决方案:

A bit "hacky" in some ways, but you could try to create your own permission types on the fly.在某些方面有点“hacky”,但您可以尝试动态创建自己的权限类型。

So the end result would look something like:所以最终的结果应该是这样的:

class SomeModelViewSet(viewsets.ModelViewSet):
    permission_classes = skip_for_staff((SomeModelPermission, SomeOtherPermission, ...))

With the implementation something similar to:随着实现类似于:

class StaffAllowedMixin:
    def has_permission(self, request, view):
        if request.user.is_staff:
            return True
        return super().has_permission(request, view)

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True
        return super().has_object_permission(request, view, obj)


def skip_for_staff(permission_classes):
    # You can probably also use a comprehension here, but for clarity:
    staff_allowed_classes = []
    for permission_class in permissions(
       staff_allowed_classes.append(
           # Create a new type (class) with name StaffAllowed<...>
           type(f"StaffAllowed{permission_class}",
                # Inherit from the mixin above, and from the original class
                (StaffAllowedMixin, permission_class),
                # empty dictionary means you don't want to override any attributes
                {})
           )
    return tuple(staff_allowed_classes)

Essentially, for each permission class, you create a new class with the extra mixin that takes precedence and checks if the user is staff.本质上,对于每个权限类,您使用额外的 mixin 创建一个新类,该类优先并检查用户是否是员工。 But you do that on the fly, where your permissions are used, instead of having to predefine it for every permission.但是您可以在使用您的权限的情况下即时执行此操作,而不必为每个权限预定义它。

There is a permission class for admin users.管理员用户有一个权限类。 Here is an example of that:这是一个例子:

class deletecompletedreopenjobAPIView(RetrieveUpdateAPIView):
    queryset = Job.objects._all()
    serializer_class = JobCompletedDeleteStatusSerializer
    lookup_field = 'job_id'
    permission_classes = [IsOwnerOrReadOnly | **IsAdminUser**]
    authentication_classes = (authentication.TokenAuthentication,)

My custom IsOwnerOrReadOnly is as follows:我的自定义 IsOwnerOrReadOnly 如下:

class IsOwnerOrReadOnly(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return obj.user == request.user

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

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