【问题标题】:Authentication failing for MongoEngineResource with ReferenceField使用 ReferenceField 对 MongoEngineResource 进行身份验证失败
【发布时间】:2013-11-16 15:01:03
【问题描述】:

对 MongoEngineResource 的嵌入字段的请求如果包含引用字段,则不经过身份验证过程。

我的情况如下:

  • 有一个文档Section,由FieldDefinitions组成
  • FieldDefinitions 是 EmbeddedDocuments
  • FieldDefinition 包含embedded_section(可选),它引用了Section,并且有一个信号排除了自引用(例如embedded_section 只能引用该section,它不包含FieldDefinition)
  • 这都是版主界面的一部分,所以我对各种请求(获取、发布、补丁等)使用授权

代码如下:

from tastypie_mongoengine.resources import MongoEngineResource
from tastypie.authentication import ApiKeyAuthentication
from apps.api.auth import CustomAuthorization

class FieldDefinitionResource(MongoEngineResource):
    embedded_section = ReferenceField(attribute='embedded_section',
                                      to='myproject.apps.api.resources.SectionResource',
                                      full=True, null=True)

    class Meta:
        object_class = models.FieldDefinition # mongoengine EmbeddedDocument
        authentication = ApiKeyAuthentication()
        authorization = CustomAuthorization()

class SectionResource(MongoEngineResource):
    fields = EmbeddedListField(attribute='fields',
                               of='myproject.apps.api.resources.FieldDefinitionResource',
                               full=True, null=True)
    class Meta:
        object_class = models.Section # mongoengine Document
        authentication = ApiKeyAuthentication()
        authorization = CustomAuthorization()

所以,当我询问章节详细信息(例如 /api/v1/section/524df40502c8f109b07ed6ae/)时,一切都很顺利,fields attr 在存在和不存在 @ 的两种情况下都能正确显示987654324@.

但尝试引用特定字段(例如 /api/v1/section/524df40502c8f109b07ed6ae/fields/0/)会引发错误:

error_message: "'AnonymousUser' object has no attribute 'has_permission'"

has_permission 是 MongoUser 的一个方法,继承自 Django auth.User。在描述的第一种情况(Section detail)中,它确实通过 Authentication 并使用适当的用户对象填充 request.user,而在第二种情况(Section 字段)中,它完全跳过 Authentication 阶段,直接进入 Authorization。

我做错了吗?

这是一个完整的追溯:

{"error_message": "'AnonymousUser' object has no attribute 'has_permission'", "traceback": "Traceback (most recent call last):

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie/resources.py", line 195, in wrapper
    response = callback(request, *args, **kwargs)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie_mongoengine/resources.py", line 277, in dispatch_subresource
    return resource.dispatch(request=request, **kwargs)

  File "/vagrant/myproject/myproject/apps/api/resources.py", line 248, in dispatch
    super(FieldDefinitionResource, self).dispatch(request_type, request, **kwargs)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie_mongoengine/resources.py", line 776, in dispatch
    self.instance = self._safe_get(bundle, **kwargs)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie_mongoengine/resources.py", line 768, in _safe_get
    return self.parent.cached_obj_get(bundle=bundle, **filters)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie/resources.py", line 1113, in cached_obj_get
    cached_bundle = self.obj_get(bundle=bundle, **kwargs)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie_mongoengine/resources.py", line 528, in obj_get
    return super(MongoEngineResource, self).obj_get(bundle=bundle, **kwargs)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie/resources.py", line 2069, in obj_get
    self.authorized_read_detail(object_list, bundle)

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/tastypie/resources.py", line 589, in authorized_read_detail
    auth_result = self._meta.authorization.read_detail(object_list, bundle)

  File "/vagrant/myproject/myproject/apps/api/auth.py", line 201, in read_detail
    bundle.request.user.has_permission('read_detail',

  File "/var/www/vhosts/myproject/local/lib/python2.7/site-packages/django/utils/functional.py", line 205, in inner
    return func(self._wrapped, *args)

AttributeError: 'AnonymousUser' object has no attribute 'has_permission'
"}

【问题讨论】:

    标签: django mongoengine tastypie


    【解决方案1】:

    这是 django-tastypie-mongoengine 中的一个已知问题:请参阅 https://github.com/wlanslovenija/django-tastypie-mongoengine/issues/71

    https://github.com/wlanslovenija/django-tastypie-mongoengine/issues/72

    https://github.com/wlanslovenija/django-tastypie-mongoengine/issues/70

    这三个问题是同一个问题:

    身份验证仅在操作本身之前执行,而不是在目标操作之前对资源的操作之前执行。

    示例(使用我的问题中的代码):FieldDefinitionResource 实例的目标操作update_detail,它是SectionResource 的子项。在更新 FieldDefinitionResource 的详细信息之前,有一个 SectionResource 的read_detail - 这是一个动作,django-tastypie-mongoengine 跳过了 Authentication 阶段。它会导致 request.user 的缺失,进而阻止工作流向目标操作(子资源的 update_detail)移动。

    这适用于 EmbeddedDocumentField、EmbeddedListField、ReferencedListField 和 ReferenceField。

    一种可能的解决方法是覆盖嵌入/引用文档的授权:

    class CustomAuthorization(Authorization):
        def read_detail(self, object_list, bundle):
    
            # Double-check anonymous users, because operations
            # on embedded fields do not pass through authentication.
            if bundle.request.user.is_anonymous():
                MyAuthentication().is_authenticated(bundle.request)
    
            # Now authorize.
            try:
                return bundle.request.user.has_permission(object_list, 'read_detail')
            except AttributeError:
                raise Unauthorized(_('You have to authenticate first!'))
    

    当然,如果在未来的版本中解决它会很好。

    【讨论】:

    • (相关包的维护者):请提出带有测试的拉取请求。我本人仅将包用于公共只读 API,因此身份验证完全未经测试且开发不足。如你看到的。 :-)
    猜你喜欢
    • 2019-05-29
    • 1970-01-01
    • 2021-08-05
    • 2018-11-02
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    相关资源
    最近更新 更多