Search code examples
djangodjango-authenticationtastypie

Tastypie - Allow read-only perms for unauthenticated users while letting authorized write permissions


I am using Tastypie 0.9.11 and I want to have unauthenticated users be allowed read-only permissions to the API while on the same time, if a user authenticates with an API Key authentication, that user can perform add/change/delete operations on those same models.

Using API Key Authentication + Django Authorization doesn't satisfy requirement 1 (unauthenticated users can't access the API at all). Using No authentication doesn't let users authenticates with an API key (which doesn't satisfy requirement 2).

I bet there is a simple way to achieve this behaviour, please help.

Many thanks, Yuval Cohen


Solution

  • There are two things which you need to consider. Authentication and authorization.

    First you need to authenticate all users no matter the API key if the request method is GET, for all other methods use the ApiKeyAuthentication.

    Now, all authenticated users are subject to authorization. Here you also need to make sure that GET requests are always allowed. Something like this should get you started:

    from tastypie.resources import ModelResource
    from tastypie.authentication import ApiKeyAuthentication
    from tastypie.authorization import DjangoAuthorization
    
    class MyAuthentication(ApiKeyAuthentication):
        """
        Authenticates everyone if the request is GET otherwise performs
        ApiKeyAuthentication.
        """
    
        def is_authenticated(self, request, **kwargs):
            if request.method == 'GET':
                return True
            return super(MyAuthentication, self).is_authenticated(request, **kwargs)
    
    class MyAuthorization(DjangoAuthorization)
        """
        Authorizes every authenticated user to perform GET, for all others
        performs DjangoAuthorization.
        """
    
        def is_authorized(self, request, object=None):
            if request.method == 'GET':
                return True
            else:
                return super(MyAuthorization, self).is_authorized(request, object)
    
    class MyResource(ModelResource):
    
        class Meta:
            authentication = MyAuthentication()
            authorization = MyAuthorization()
    

    So basically your approach for using ApiKeyAuthentication and DjangoAuthorization only lacked special treatment for GET requests.