9

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

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

TL;DR:我无法将便笺编辑限制为便笺的创建者,同时仍允许任何用户对便笺发表评论。

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

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 创建了一个注释并且用户 B 尝试对该注释进行评论,那么美味派发送(或模拟)一个 POST 请求来编辑该注释。该尝试被拒绝,因为用户 B 没有创建注释,因此创建注释失败。

问题是这样的:有没有办法:

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

提前感谢您的任何见解。

编辑: 我有一个很大的技巧可以做到这一点。我相当确定它是安全的,但我并不肯定;我将尝试构建一些查询以确保。我没有使用fields.ForeignKeyinComment与 相关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

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

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

4

2 回答 2

4

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

判断 a 是否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').

于 2012-05-07T21:44:42.010 回答
1

一个简单的解决方案应该是在内部检查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/comment”开头-因此您只是将评论访问权限列入白名单,而不是注释以外的任何内容。然而,同样的原则也适用。小心这种基于文本的请求路径比较,以避免有人简单地将字符串附加/附加到您的 url 以绕过您的授权的情况。希望前置更受限制,因为它需要在 urls.py 中点击正确的 url,这就是我startswith在这里使用的原因。当然,您必须调整路径字符串以匹配您的美味 url。

于 2012-01-16T15:15:02.030 回答