10

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

假设它是一个动物园,我可以调用GET /api/cage/获取笼子列表GET /api/cage/1/以获取笼子 ID 1,但随后我可以GET /api/cage/1/animals/获取该笼子中的动物列表。

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

出于某种原因,has_object_permission()当我做 GET 时被调用/api/cage/1/,但has_permission()在我调用时被调用GET /api/cage/1/animals/。而且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
4

2 回答 2

24

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

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

这是有道理的,因为无论如何您都需要检索对象以检查权限,并且如果他们透明地执行此操作,则会添加额外的数据库查询。

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

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')
于 2016-05-23T17:29:45.200 回答
0

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

您可以做的另一件事是使用check_object_permissions您的方法内部POST,这样您就可以调用您的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)
于 2021-12-19T16:34:30.183 回答