为什么返回 204 而不是 403?自定义权限不起作用

Why is it returning 204 instead of 403? Custom permissions does not working

我有一个 table 存储 parent - child 条记录。今天,我在编写单元测试时注意到一个问题。即使请求者不是 child 的 parent,他们也可以删除该记录。但在视图方面,如果用户不是记录的所有者,则删除按钮不可用。所以它在视图方面有效。如果我使用 postman 发出请求,无论谁拥有它,记录都会被删除。

如何检查用户是否是记录的所有者?我正在关注一个示例项目,该项目的创建者已获得类似许可。

Child 列出型号

class ChildList(models.Model):
    parent = models.ForeignKey(
        "account.ParentProfile",
        on_delete=models.CASCADE,
        related_name="parent_children",
        verbose_name=AccountStrings.ChildListString.parent_verbose_name)
    child = models.OneToOneField(
        "account.ChildProfile",
        on_delete=models.CASCADE,
        related_name="child_list",
        verbose_name=AccountStrings.ChildListString.child_verbose_name)

    def __str__(self):
        return f"{self.parent.user.first_name} {self.parent.user.last_name} - {self.child.user.first_name} {self.child.user.last_name}"

    class Meta:
        verbose_name = AccountStrings.ChildListString.meta_verbose_name
        verbose_name_plural = AccountStrings.ChildListString.meta_verbose_name_plural

Child 列出销毁视图

class ChildListItemDestroyAPIView(RetrieveDestroyAPIView):
    """
        Returns a destroy view by child id value.
    """
    queryset = ChildList.objects.all()
    serializer_class = ChildListSerializer
    permission_classes = [IsAuthenticated, IsParent, IsOwnChild]
    
    def get_object(self):
        queryset = self.get_queryset()
        obj = get_object_or_404(ChildList,child = self.kwargs["child_id"])
        return obj

权限

class IsOwnChild(BasePermission):
    """
        To edit or destroy a child list record, the user must be owner of that record.
    """
    def has_permission(self, request, view):
        return request.user.user_parent and request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        return (obj.parent == request.user.user_parent) or request.user.is_superuser
    message = AccountStrings.PermissionStrings.is_own_child_message

单元测试

class ChildListItemDestroyTests(APITestCase):
    login_url = reverse("token_obtain_pair")

    def setUp(self):
        self.username = "johndoe"
        self.password = "test1234"
        self.user_parent = User.objects.create_user(username=self.username, password=self.password, email = "email1@example.com", identity_number = "12345678910", user_type = 3)
        self.user_parent2 = User.objects.create_user(username= "davedoe", password=self.password, email = "email3@example.com", identity_number = "12345678912", user_type = 3)
        self.user_child = User.objects.create_user(username="janedoe", password=self.password, email = "email2@example.com", identity_number = "12345678911", user_type = 2)
        self.child_list_item = ChildList.objects.create(parent = self.user_parent.user_parent, child = self.user_child.user_child)
        self.test_jwt_authentication()

    def test_jwt_authentication(self, username="johndoe", password="test1234"):
        response = self.client.post(self.login_url, data={"username": username, "password": password})
        self.assertEqual(200, response.status_code)
        self.assertTrue("access" in json.loads(response.content))
        self.token = response.data["access"]
        self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + self.token)
  
    def test_child_list_item_delete(self):
        url = reverse("account:child_list_item_destroy", kwargs={"child_id": self.user_child.user_child.user_id})
        response = self.client.delete(url)
        self.assertEqual(204, response.status_code)
   
   
    def test_child_list_item_delete_is_own_child(self):
        self.test_jwt_authentication(username = "davedoe")
        url = reverse("account:child_list_item_destroy", kwargs={"child_id": self.user_child.user_child.user_id})
        response = self.client.delete(url)
        self.assertEqual(403, response.status_code)

我认为这是因为您重写了 get_object 方法。在 GenericApiView class 的 get_object 方法中,有以下内容:

# May raise a permission denied
self.check_object_permissions(self.request, obj)

所以你添加了新的权限,但是删除了权限检查机制。重新添加此行应该可以解决您的问题。

如何检查用户是否是记录的所有者? 您的 ChildList class 有一个指向配置文件的 pk。所以这里的记录是account.ParentProfile。在 test_child_list_item_delete_is_own_child

中得到 203 而不是 403 的原因

是因为在视图 permission_classes 中您正在传递 IsAuthenticated,这意味着任何具有有效访问令牌的用户都可以与该视图交互。

因此解决方案是自定义删除并获得这样的响应

class ChildListItemDestroyAPIView(RetrieveDestroyAPIView):
    """
        Returns a destroy view by child id value.
    """
    queryset = ChildList.objects.all()
    serializer_class = ChildListSerializer
    permission_classes = [IsAuthenticated, IsParent, IsOwnChild]
    
    def get_object(self):
        queryset = self.get_queryset()
        obj = get_object_or_404(ChildList,child = self.kwargs["child_id"])
        return obj

    def delete(self, *args, **kwargs): 
        pass 
        # business logic goes here 

    def get(self, *args, **kwargs): 
        pass 
        # business logic goes here