Yusuke Ebihara's website
Dotfiles Blog RSS

Django Rest Frameworkでget_objectをオーバーライドする時はget_object_permissionに注意

2023/08/25
python django

目次

Django Rest Frameworkで以下のようなコードを書いていたが、Owner以外でもアクセスできしまうというバグが発生した。

class IsOwner(BasePermission):
    def has_permission(self, request, view):
        return request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        return obj.owner == request.user

class MyDetail(RetrieveAPIView):
    serializer_class = MySerializer
    permission_classes = [IsOwner]
    parser_classes = [JSONParser]

    def get_object(self):
        obj = get_object_or_404(MyModel, id=self.kwargs["id"])
        return obj

調べてみると、そもそも has_object_permission が呼び出されていない。

原因

調べてみると、 get_object をoverrideした場合は has_object_permission は呼び出されない仕様のようだ。 Django Rest Frameworkのドキュメント にも以下のような記述がある。

If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.

解決策

上記の通り、 check_object_permissions を明示的に呼び出す。

 class IsOwner(BasePermission):
     def has_permission(self, request, view):
         return request.user.is_authenticated
 
     def has_object_permission(self, request, view, obj):
         return obj.owner == request.user
 
 class MyDetail(RetrieveAPIView):
     serializer_class = MySerializer
     permission_classes = [IsOwner]
     parser_classes = [JSONParser]
 
     def get_object(self):
         obj = get_object_or_404(MyModel, id=self.kwargs["id"])
+        self.check_object_permissions(self.request, obj)
         return obj

テストを書いていたから気付けたものの、普通に危ない仕様。

References

コメント

Github Issue と連動しています。