【问题标题】:Django REST Framework - Allow staff to access all endpointsDjango REST Framework - 允许员工访问所有端点
【发布时间】:2019-08-16 23:50:18
【问题描述】:

我正在构建一个 DRF API,我希望允许工作人员 (is_staff == True) 访问所有 REST 端点,同时仍为每个 ViewSet 提供自定义权限检查。理想情况下,这将是一个全局设置,但我不反对按 ViewSet 进行设置。

这是我尝试过的事情:


选项 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

这可行,但我不想重复这么多代码。


选项 2:按位运算符

我尝试从上面的自定义权限中删除 is_staff 逻辑,并将其添加到 ViewSet:

from rest_framework import permissions, viewsets

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

但是,这实际上并没有按照我的意愿强制执行权限,因为IsAdminUser 继承自BasePermission,其定义为:

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

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

IsAdminUser没有定义自己的has_object_permission,所以在检查对象权限的时候总会返回True,导致意外的对象访问。


有什么想法吗?我希望有某种方法可以设置全局权限检查,当用户是员工时返回True,否则遵循自定义权限。但是通过how permissions are determined阅读,我不确定这是否可能。

【问题讨论】:

    标签: python django permissions django-rest-framework


    【解决方案1】:

    按位解:

    如何创建自己的IsAdminUser,它也定义了has_object_permission?您可以从现有的继承:

    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)
    

    然后您可以使用按位运算符执行上述操作:

    from rest_framework import permissions, viewsets
    from your_own_project.permissions import IsAdminUser
    
    class SomeModelViewSet(viewsets.ModelViewSet):
        permission_classes = (IsAdminUser|SomeModelPermission,)
    
    

    另一种解决方案:

    在某些方面有点“hacky”,但您可以尝试动态创建自己的权限类型。

    所以最终结果看起来像:

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

    实现类似于:

    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)
    

    本质上,对于每个权限类,您都会创建一个新类,其中包含优先的额外 mixin 并检查用户是否为员工。 但是您可以在使用您的权限的地方即时执行此操作,而不必为每个权限预定义它。

    【讨论】:

      【解决方案2】:

      管理员用户有一个权限等级。 这是一个例子:

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

      我的自定义 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-24
        • 1970-01-01
        • 2021-11-30
        • 1970-01-01
        • 1970-01-01
        • 2019-02-12
        • 1970-01-01
        • 2019-11-23
        相关资源
        最近更新 更多