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

3
我正在构建一个 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,否则使用自定义权限。但是阅读了如何确定权限,我不确定这是否可能。
2个回答

2

按位解决方案:

你可以创建自己的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)

那么你可以使用位运算符来完成你上面尝试的操作:

Original Answer翻译成"最初的回答"

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

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

另一种解决方案:

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

因此,最终结果会类似于:

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来优先考虑并检查用户是否为员工。 但是,您可以在使用权限时动态进行此操作,而无需为每个权限预定义它。"最初的回答"

0

管理员用户有一个权限类。 以下是一个示例:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接