Authentication failing for MongoEngineResource with ReferenceField
Question: The request to the embedded field of MongoEngineResource doesn't go through
Authentication process, if it contains reference field.

My case is the following:

  * there is a document Section, which consist of FieldDefinitions 
  * FieldDefinitions are EmbeddedDocuments
  * FieldDefinition contains `embedded_section` (optional), which references to the Section, and there is a signal that excludes self-referencing (e.g. embedded_section can only reference to the section, which doesn't contain the FieldDefinition)
  * this all is a part of moderator's interface, so i use authorization for all kinds of requests (get, post, patch, etc.)

Here is the code:

    
    
    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()
    

So, when i'm asking for a Section detail (e.g.
/api/v1/section/524df40502c8f109b07ed6ae/), everything goes smooth, and
`fields` attr is being displayed correctly in both cases of the presence and
absence of `embedded_section`.

But an attempt to refer to a specific field (e.g.
/api/v1/section/524df40502c8f109b07ed6ae/fields/0/) throws an error:

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

has_permission is a method of MongoUser, which inherits from Django auth.User.
In the first case described (Section detail) it does go through Authentication
and fills request.user with a proper user object, while in the second case
(Section field) it skips Authentication stage entirely, going straight to
Authorization.

Am i doing something wrong?

Here is a full traceback:

    
    
    {"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'
    "}
    


Answer: It is a known issue in django-tastypie-mongoengine: see
<https://github.com/wlanslovenija/django-tastypie-mongoengine/issues/71>

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

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

Those 3 issues are about the same problem:

The Authentication is being performed only before the action itself, but not
before actions on a resource prior to the target action.

Example (using the code in my question): the target action `update_detail` of
the instance of FieldDefinitionResource, which is a child of SectionResource.
Prior to updating a detail of FieldDefinitionResource, there is a
`read_detail` of SectionResource - and this is an action, for which django-
tastypie-mongoengine skips the Authentication stage. It results in the absence
of request.user, which in its turn prevents the work-flow from moving towards
the target action (update_detail of the child resource).

This applies for EmbeddedDocumentField, EmbeddedListField, ReferencedListField
and ReferenceField.

One possible workaround is to override the authorization for embedded /
referenced document:

    
    
    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!'))
    

But of course, it would be nice to have it solved in the future releases.

