DRF视图集中的权限检查工作不正常



我正在实现一个API,其中我有嵌套的结构。

假设这是一个动物园,我可以调用GET /api/cage/来获取笼子的列表GET /api/cage/1/来获取笼子ID 1,但我可以使用GET /api/cage/1/animals/来获取那个笼子里的动物列表。

我遇到的问题是权限问题。只有当我能看到笼子本身的时候,我才能看到笼子里的动物。如果has_object_permission()在相关权限类中返回True,我应该能够看到框架本身。

出于某种原因,当我获取/api/cage/1/时,会调用has_object_permission(),但当我调用GET /api/cage/1/animals/时,has_permission()会被调用。使用has_permission(),我无法访问该对象以检查权限。我是不是错过了什么?我该怎么做?

我的笼子视图集看起来或多或少像这个

class CageViewSet(ModelViewSet):
    queryset = Cage.objects.all()
    serializer_class = CageSerializer
    permission_classes = [GeneralZooPermissions, ]
    authentication_classes = [ZooTicketCheck, ]
    def get_queryset(self):
        ... code to only list cages you have permission to see ...
    @detail_route(methods=['GET'])
    def animals(self, request, pk=None):
        return Request(AnimalSerializer(Animal.objects.filter(cage_id=pk), many=True).data)

我的GeneralZooPermissions类(目前)是这样的

class GeneralZooPermissions(BasePermission):
    def has_permission(self, request, view):
        return True
    def has_object_permission(self, request, view, obj):
        return request.user.has_perm('view_cage', obj)

这似乎是DRF中的一个错误。详细的路由不会调用正确的权限检查。我试着向DRF开发人员报告这个问题,但我的报告似乎已经消失了。不知道下一步该做什么。想法?

我与DRF发布的问题又回来了,我得到了回应。似乎只检查has_permission()而不检查has_object_permission()是预期行为。这对我没有帮助。在这一点上,必须做这样的事情:

class CustomPermission(BasePermission):
    def has_permission(self, request, view):
        """we need to do all permission checking here, since has_object_permission() is not guaranteed to be called"""
        if 'pk' in view.kwargs and view.kwargs['pk']:
            obj = view.get_queryset()[0]
            # check object permissions here
        else:
            # check model permissions here
    def has_object_permission(self, request, view, obj):
        """ nothing to do here, we already checked everything """
        return True

好吧,所以在阅读了一堆DRF的代码并在DRF GitHub页面上发布了一个问题之后。

似乎只有当视图调用get_object()来检索要操作的对象时,才会调用has_object_permission()

这是有意义的,因为无论如何都需要检索对象来检查权限,如果他们透明地这样做,就会添加额外的数据库查询。

回复我报告的人说,他们需要更新文件以反映这一点。所以,这个想法是,如果你想写一个自定义的详细路线,并让它正确检查权限,你需要做

class MyViewSet(ModelViewSet):
    queryset = MyModel.objects.all()
    ....
    permission_classes = (MyCustomPermissions, )
    
        @detail_route(methods=['GET', ])
        def custom(self, request, pk=None):
            my_obj = self.get_object() # do this and your permissions shall be checked
            return Response('whatever')

在我的情况下,我没有正确处理请求,所以我的URL是api/account/users,我的错误是我将前端的URL设置为api/account/,这是不正确的!

如果您想在执行另一个不调用get_object()的方法(例如POST方法)时定义权限,可以重写has_permission方法。也许这个答案会有所帮助(https://stackoverflow.com/a/52783914/12737833)

您可以做的另一件事是在POST方法中使用check_object_permissions,这样您就可以调用has_object_permission方法:

@action(detail=True, methods=["POST"])
def cool_post(self, request, pk=None, *args, **kwargs):
    your_obj = self.get_object()
    self.check_object_permissions(request, your_obj)

相关内容

  • 没有找到相关文章

最新更新