繁体   English   中英

在Django-Tastypie中使用POST的ForeignKey问题

[英]Problems with ForeignKey using POST in Django-Tastypie

我正在使用django-tastypie构建一个简单的API。 我的想法是有两个资源:

  • 表示用户留下的注释的Note资源。 只有创建了Note的用户才能编辑它。
  • 评论资源。 任何用户都可以在任何备注上留下评论。

TL; DR:我无法将Note编辑限制为Note的创建者,同时仍然允许任何用户对Note进行评论。

我使用以下设置进行身份验证:

class CreatedByEditAuthorization(Authorization):
    def is_authorized(self, request, object=None, **kwargs):
        return True

    def apply_limits(self, request, object_list):
        if request and request.method != 'GET' and hasattr(request, 'user'):
            return object_list.filter(created_by=request.user)
        return object_list

简而言之,用户仅被授权编辑与created_by属性相等的对象(他们只能编辑他们创建的对象)。

这连结如下:

class NoteResource(ModelResource):
    comments = fields.ToManyField('myapp.api.resources.CommentResource', 'comments', null=True, blank=True)
    created_by = fields.ToOneField('account.api.resources.UserResource', 'created_by')

    def obj_create(self, bundle, request, **kwargs):
        return super(HapResource, self).obj_create(bundle, request, created_by=request.user)

    class Meta:
        queryset = Note.objects.all()
        allowed_methods = ['get', 'put', 'post']
        authorization = CreatedByEditAuthorization()

所以在这里,当创建一个对象时,我会自动将当前用户附加到created_by属性并将其链接到正确的授权。

一个Comment资源简单,只是有一个ForeignKeyNote资源。

问题是这样的:如果用户A创建了一个Note而用户B试图对该Note进行评论,则tastypie发送(或模拟)一个POST请求来编辑该Note。 由于用户B没有创建Note,因此该尝试被拒绝,因此创建注释失败。

问题是:有没有办法:

  1. 防止tastypie使用POST创建与Note资源的反向关系
  2. 更改授权方案,以便Notes只能由其创建者编辑,但是通常可以创建注释吗?

提前感谢任何见解。

编辑:我有一个很大的黑客可以实现这一目标。 我很确定这是安全的,但我并不积极; 我会尝试构建一些查询以确保。 而不是使用的fields.ForeignKeyComment涉及到Note ,我创建一个自定义字段:

class SafeForeignKey(fields.ForeignKey):
    def build_related_resource(self, value, request=None, related_obj=None, related_name=None):
        temp = request.method
        if isinstance(value, basestring):
            request.method = 'GET'
        ret = super(SafeForeignKey, self).build_related_resource(value, request, related_obj, related_name)
        request.method = temp
        return ret

每次我们尝试构造这个相关资源时,我们将请求标记为GET (因为我们希望它与SELECT查询匹配,而不是与PUTPOST匹配的UPDATE )。 如果使用不当,这真的很难看并且可能不安全,我希望有更好的解决方案。

编辑2:从阅读tastypie源代码,据我所知,没有办法通过实际发送的查询来过滤授权。

根据https://github.com/toastdriven/django-tastypie/issues/480#issuecomment-5561036上的讨论:

确定Resource可以更新的方法是can_update 因此,要以“正确”的方式使这项工作,您需要创建NoteResource的子类:

class SafeNoteResource(NoteResource):
    def can_update(self):
        return False
    class Meta:
        queryset = Note.objects.all()
        allowed_methods = ['get']
        authorization = Authorization()
        # You MUST set this to the same resource_name as NoteResource
        resource_name = 'note'

然后让CommentResource以标准方式链接到笔记: note = fields.ForeignKey(SafeNoteResource, 'note')

一个简单的解决方案应该是检查apply_limits内部,无论请求是针对Note资源还是Comment资源。 例如

def apply_limits(self, request, object_list):
    if request and request.method != 'GET' and hasattr(request, 'user') and getattr(request, 'path','').startswith('/api/v1/note'):
        return object_list.filter(created_by=request.user)
    return object_list

然后,当用户直接访问Note资源而不是通过其他相关资源(例如Comments)时,您只限制对同一用户的Notes访问。

更新:或稍微更安全的选择将是检查的要求符合“API / V1 /评论”开始-所以你只是白名单的评论权限,而不是不是音符的任何其他。 然而,同样的原则也适用。 请注意这种基于文本的请求路径比较,以避免有人只是将字符串附加/预先添加到您的网址以绕过您的授权。 希望prepending更有限,因为它需要在urls.py中找到正确的url,因此我在这里使用startswith 当然,你必须调整路径字符串以匹配你的tastypie网址。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM