我正在构建DRF API,我希望允许工作人员(is_staff == True
)访问所有REST端点,同时仍按ViewSet提供自定义权限检查。理想情况下,这是一个全局设置,但我不反对根据ViewSet进行设置。
这是我尝试过的事情:
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
这行得通,但我不想重复太多代码。
我尝试从上面的自定义权限中删除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,我不确定是否有可能。
答案 0 :(得分: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)
然后,您可以使用按位运算符完成上面的尝试:
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)
本质上,对于每个权限类,您都将创建一个具有额外混合优先级的新类,并优先检查用户是否为职员。 但是您可以在使用权限的情况下即时执行此操作,而不必为每个权限都预先定义它。
答案 1 :(得分: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