在Django rest框架中创建之前检查对象权限

时间:2016-11-14 14:44:09

标签: python django-rest-framework

使用ModelViewSet和DjangoObjectPermissions。

Django REST框架似乎没有为"创建"调用check_object_permission。请求(POST)。 我需要检查用户是否可以在保存到数据库之前创建该对象(因为权限检查取决于对象值)

我想我需要覆盖"创建" ModelViewSet的方法,但我不知道如何从序列化器获取实例而不将其保存到数据库。

  1. 如何从序列化程序获取对象实例而不保存到数据库?
  2. 或者如何让DRF检查POST /创建请求的对象权限?
  3. 由于

    编辑:

    深入研究DRF代码后,我能够在没有保存的情况下获取实例:

    def create(self, request, *args, **kwargs):
        serializer = WorkedHourSerializer(data=request.data)
        if serializer.is_valid():
            instance = MyModel(**serializer.validated_data)
    

    但Django拒绝检查perm是否有没有主键的对象,所以我必须强制一个:

            instance.id = 0
            self.check_object_permissions(request, instance)
    

4 个答案:

答案 0 :(得分:1)

我的解决方案是创建一个Mixin以应用于ModelViewSet,它使用request.data创建的实例执行check_object_permission,而不是在将数据保存到数据库之前从数据库中检索对象:

import uuid


class CheckObjectPermissionBeforeSaveMixin():

    def create(self, request, *args, **kwargs):
        self.check_instance_from_data_permission(request)
        return super(CheckObjectPermissionBeforeSaveMixin, self).create(request, *args, **kwargs)

    def update(self, request, *args, **kwargs):
        self.check_instance_from_data_permission(request)
        return super(CheckObjectPermissionBeforeSaveMixin, self).update(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        self.check_instance_from_data_permission(request)
        return super(CheckObjectPermissionBeforeSaveMixin, self).destroy(request, *args, **kwargs)

    def check_instance_from_data_permission(self, request):
        instance = self.get_instance_from_data(request.data)
        if instance:
            self.check_object_permissions(request, instance)

    def get_instance_from_data(self, data):
        ModelClass = self.serializer_class.Meta.model
        serializer = self.get_serializer(data=data)
        if serializer.is_valid():
            instance = ModelClass(**serializer.validated_data)
            instance.id = data.get('id') or uuid.uuid4().hex  # Django's has_perm need a primary key to be set...
            return instance
        return None


class MyModelViewSet(CheckObjectPermissionBeforeSaveMixin, viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

答案 1 :(得分:1)

在我看来,这是一个巨大的安全漏洞。保护它的另一种方法 - 如果没有生成好的HTML响应,如果您正在使用它和REST API视图/ ViewSets并使用对象级权限,则编写自定义模型序列化器:

document.getElementById("select").value = valueToBeSelected; 

答案 2 :(得分:0)

  1. 在保存实例之前无法获取实例(see more

  2. 最好的方法似乎是实现自定义权限(可能是子类rest_framework.permissions.BasePermissionrest_framework.permissions.IsAuthenticated)并在has_permission(self, request, view)中添加权限检查的逻辑({{3 }})。这样,您将访问request.user,然后您就可以确定该用户是否有权创建该对象。

答案 3 :(得分:0)

有关安全漏洞的事情。

Django DRF权限系统可以很好地对与其他对象相关的对象实施权限...

...但是您的应用程序中可能会有我称之为“根对象”的对象:仅与用户ID相关的对象。

在测试我的应用程序时,我注意到,一个登录的用户可以为另一个用户创建这样的“根对象”。

具有安全流程的示例:

models.py:

class MyObject(models.Model):
    name = models.CharField(max_length=20)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

permissions.py: 您将在关联的视图集的权限类中使用IsOwner

class IsOwner(permissions.BasePermission):
    def has_permission(self, request, view):
        obj = MyObject.objects.get(pk=view.kwargs['something'])
        return obj.owner == request.user

虽然这将限制访问属于已登录用户的对象,但不会禁止已登录用户创建属于另一个用户的对象。

我曾经让前端填充“所有者”属性。 像这样发布(很容易猜出用户1存在):

{
    "name": "A prank object",
    "owner": 1,
}

对象级别权限(has_object_permission)在这里不能使用。只是不会被调用。

因此,恶意用户可能会创建“恶作剧对象”,从而破坏您的用户体验。

实际上,Django DRF给出了simple solution

在您的viewset.py中:

def perform_create(self, serializer):
     serializer.save(owner=self.request.user)

并且,由于owner属性现在由Django DRF管理,因此可以从序列化器Meta中排除此字段。因此,您的REST API暴露程度较低。

然后,无论潜在的恶意用户作为所有者发布什么,它将被其自己的ID取代,因此不会打扰其他用户...

好吧,最好使用UUID作为用户ID而不是增量整数,因为猜测其他用户ID的可能性很低。