Subhamoy S.
Subhamoy S.

Reputation: 6764

Passing user credentials from Tastypie to Django Model

I am using custom authentication in Django-Tastypie, which returns the username and password when authentication succeeds. In the ModelResource instance, once the authentication is successful, the username and password are available:

class TestResource(ModelResource):

    class Meta:
        queryset = Test.remote_supported.get_all(username,password)
        resource_name = 'test'
        filtering = {
            'id' : ALL,
        }
        detail_allowed_methods = ['get', 'post', 'patch']       
        authorization = Authorization()
        authentication = MyCustomAuth()
        always_return_data = True

    def __init__(self, api_name=None):
        self.username = None
        self.password = None
        super(TestResource, self).__init__(api_name)

    def is_authenticated(self, request):
        auth_result = self._meta.authentication.is_authenticated(request)

        if isinstance(auth_result, HttpResponse):
            raise ImmediateHttpResponse(response=auth_result)

        if not auth_result is True:
            raise ImmediateHttpResponse(response=http.HttpUnauthorized())

        # this is where I receive the username and password from my custom auth
        self.username, self.password = self._meta.authentication.get_credentials()

This code obviously does not work, because that username and that password are not available in the Metaclass, and even if it were, changing it would not affect only this instance, but all instances, which is not my intention, because the scope of username and password should be per RESTful query.

This is what my model looks like:

class RemoteAuthSupported(models.Manager):
    def get_all(self, username, password):
        # ... here I do some custom operations with the username and password
        return super(RemoteAuthSupported, self).get_query_set()  

class Test(models.Model):
    objects = models.Manager()
    remote_supported = RemoteAuthSupported()

    # ... the field declarations follow here ... #

The reason I am trying to do this is that I am using a non-ORM data source with my Django application, which requires authentication of its own, but with the same username and password. What would be the method of handling this parameter passing from a Tastypie ModelResource to a Django Model? I probably should mention here that there is no user model in use.

Upvotes: 0

Views: 413

Answers (2)

Subhamoy S.
Subhamoy S.

Reputation: 6764

I looked into the Tastypie documentation, and it seems like the following could be a solution, albeit amateurish:

class TestResource(ModelResource):

    class Meta:
        queryset = Test.remote_supported.get_all('dummyusername','dummypassword')
        resource_name = 'test'
        filtering = {
            'id' : ALL,
        }
        detail_allowed_methods = ['get', 'post', 'patch']       
        authorization = Authorization()
        authentication = MyCustomAuth()
        always_return_data = True

    def get_object_list(self, request):
        """
        This method calls a clone of the queryset declared in the Metaclass.
        It is called every time a query is executed.
        Here the actual username and password is passed to the model.
        """         
        return Site.remote_supported.get_all(self.username,self.password)

The problem was that the queryset declaration happened in the Metaclass only once, not per query, so the username and password acquired from the http request could not be passed on to the query in the Meta level. But since get_object_list() clones the declaration and performs the actual call with the updates argument values, this gets the job done. It is working for me, but I feel there should be a better solution for doing this.

Upvotes: 0

Maciej Gol
Maciej Gol

Reputation: 15864

What you could do is override the obj_get_list method that returns the list of resources for a request. Also, you could set the username and password to the request object itself, so the paramters get carried over the request-response path. Also, you'd need to set the queryset to all().

def obj_get_list(self, bundle, **kwargs):
    original = super(TestResource, self).obj_get_list(bundle, **kwargs)
    request = bundle.request
    return original.get_all(request.username, request.password)

Or the other way round - you could add custom authorization that would filter the objects list. The request attributes part still holds.

class MyAuth(Authorization):
   def authorized_read_list(self, objects, bundle):
       request = bundle.request
       return objects.get_all(request.username, request.password)

If you would rather imitate queryset using get_all than just alternating list endpoint, you could override the get_object_list.

def get_object_list(self, request):
    original = super(TestResource, self).get_object_list(request)
    return original.get_all(request.username, request.password)

Upvotes: 1

Related Questions