我正在使用django-tastypie构建一个简单的API。我的想法是有两个资源:
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
资源很简单,只有ForeignKey
到Note
资源。
问题是:如果用户A创建了一个Note并且用户B尝试对该Note进行评论,则tastypie发送(或模拟)POST请求以编辑该Note。该尝试被拒绝,因为用户B没有创建注释,因此创建注释失败。
问题是:有没有办法:
提前感谢任何见解。
修改
我有一个可以实现这个目标的大胖子。我很确定这是安全的,但我并不积极;我会尝试构建一些查询以确保。我没有使用fields.ForeignKey
中的Comment
与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
查询匹配,而不是匹配UPDATE
到PUT
或POST
)。如果使用不当,这真的很难看并且可能不安全,我希望有更好的解决方案。
编辑2:从阅读tastypie来源,据我所知,无法通过实际发送的查询来过滤授权。
答案 0 :(得分:4)
根据对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')
。
答案 1 :(得分:1)
一个简单的解决方案应该是apply_limits
内部检查请求是针对注意资源还是注释资源。例如
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 资源而不是通过其他相关资源(例如评论
更新:或稍微更安全的选项是检查请求不以'api / v1 / comment'开头 - 所以你只是将评论访问列入白名单而不是除了注意之外的任何。然而,同样的原则也适用。请注意这种基于文本的请求路径比较,以避免有人只是将字符串附加/预先添加到您的网址以绕过您的授权。希望prepending更有限,因为它需要在urls.py中找到正确的url,因此我在这里使用了startswith
。当然,你必须调整路径字符串以匹配你的tastypie网址。