Django Tastypie中ForeignKey使用POST的问题
我正在使用django tastypie构建一个简单的API。我的想法是我有两个资源:Django Tastypie中ForeignKey使用POST的问题,django,api,tastypie,Django,Api,Tastypie,我正在使用django tastypie构建一个简单的API。我的想法是我有两个资源: 表示用户留下的注释的注释资源。只有创建注释的用户才能对其进行编辑 评论资源。任何用户都可以在任何注释上留下注释 TL;DR:我无法将注释编辑限制在注释的创建者,同时仍允许任何用户对注释进行评论 我正在使用以下设置进行身份验证: class CreatedByEditAuthorization(Authorization): def is_authorized(self, request, obje
- 表示用户留下的注释的注释资源。只有创建注释的用户才能对其进行编辑李>
- 评论资源。任何用户都可以在任何注释上留下注释
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()
因此,在这里,当创建对象时,我会自动将当前用户附加到由创建的属性,并将其链接到适当的授权
注释
资源很简单,只有一个外键
到注释
资源
问题在于:如果用户A创建了一个便笺,而用户B试图对该便笺发表评论,则tastypie会发送(或模拟)一个POST请求来编辑该便笺。该尝试被拒绝,因为用户B没有创建注释,因此创建注释失败
问题是:有没有办法:
防止tastypie使用POST创建与便笺资源的反向关系或
更改授权方案,使便笺只能由其创建者编辑,但通常可以创建注释
提前感谢您提供的任何见解
编辑:
我有一个大的胖黑客可以做到这一点。我相当肯定它是安全的,但我不能肯定;我将尝试构造一些查询以确保。我创建了一个自定义字段,而不是在注释中使用字段。ForeignKey
与注释相关:
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
查询匹配,而不是与PUT
或POST
匹配的UPDATE
)。如果使用不当,这真的很难看,而且可能不安全,我希望有更好的解决方案
编辑2:从读取tastypie源,据我所知,没有办法通过实际发送的查询过滤授权。一个简单的解决方案应该是在apply\u限制
内检查请求是否为注释资源或注释资源。e、 差不多
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
然后,当用户直接访问注释资源,而不是通过其他相关资源(如注释)时,您只限制同一用户访问注释
更新:或者更安全的方法是检查请求是否以“api/v1/comment”开头,而不是以“note”开头。然而,同样的原则也适用。在对请求路径进行这种基于文本的比较时要小心,以避免出现有人简单地在url中添加字符串以绕过您的授权的情况。希望预结束更加有限,因为它需要在url.py中找到正确的url,因此我在这里使用了startswith
。当然,您必须调整路径字符串以匹配您的tastypie URL。根据以下讨论:
确定是否可以更新资源
的方法是可以更新
。因此,要以“正确”的方式进行此操作,您需要创建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')
两个问题-您是否使用contrib.comments?您正在使用身份验证和授权吗?我有一个非常类似的设置(没有seem CommentResource),在另一个用户对象上发布新评论时效果很好。@JamesO没有,我们的评论比contrib.comments提供的要丰富一些(还有其他与具有相同问题的帖子相关联的数据)。我们目前只使用内置身份验证()(即每个人都经过身份验证)。您是否已将此作为问题发布到django tastypie:?如果每次您创建与父记录相关的内容时,它都真正尝试更新父记录,这更像是一个bug,而不是一个功能。@JordanReiter see-tastypie似乎保存了所有相关字段,甚至有评论说它只是“以防万一”。@Yoav我们知道是否有必要调用save_related?这似乎是一个比我建议的更大的攻击;对于相应的资源
,应在Meta
中处理表级权限。此外,正如您所说,基于文本的比较可能是危险的(事实上,如果您与full=True
——在这种情况下,URL可能不包含/comment/)的注释有关联,也会导致错误)。从捆绑包传递回来的内容的意义上讲,您是对的,路径实际上是您拥有的唯一相关数据。不确定我的建议比将请求方法从POST更改为GET(更不用说添加的代码行)更严重。在我提出的解决方案中,授权被处理在正确的位置(即apply_limits),并且无论full=True与否,都可以执行url检查。我们正在检查请求URL,而不是任何捆绑数据。我的解决方案也许可以通过在Comments资源obj_create内的request对象中添加某种标志,然后在Notes apply_limits方法中检查它来改进一点。这是一个更大的缺陷,因为它破坏了模块化。表级授权应在