使用ModelViewSet和DjangoObjectPermissions。
Django REST框架似乎没有为"创建"调用check_object_permission。请求(POST)。 我需要检查用户是否可以在保存到数据库之前创建该对象(因为权限检查取决于对象值)
我想我需要覆盖"创建" ModelViewSet的方法,但我不知道如何从序列化器获取实例而不将其保存到数据库。
由于
编辑:
深入研究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)
答案 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)
在保存实例之前无法获取实例(see more)
最好的方法似乎是实现自定义权限(可能是子类rest_framework.permissions.BasePermission
或rest_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的可能性很低。