""" Provides a set of pluggable authentication policies. """ from django.contrib.auth import authenticate from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError from rest_framework import exceptions from rest_framework.compat import CsrfViewMiddleware from rest_framework.authtoken.models import Token import base64 class BaseAuthentication(object): """ All authentication classes should extend BaseAuthentication. """ def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ raise NotImplementedError(".authenticate() must be overridden.") class BasicAuthentication(BaseAuthentication): """ HTTP Basic authentication against username/password. """ def authenticate(self, request): """ Returns a `User` if a correct username and password have been supplied using HTTP Basic authentication. Otherwise returns `None`. """ if 'HTTP_AUTHORIZATION' in request.META: auth = request.META['HTTP_AUTHORIZATION'].split() if len(auth) == 2 and auth[0].lower() == "basic": try: auth_parts = base64.b64decode(auth[1]).partition(':') except TypeError: return None try: userid = smart_unicode(auth_parts[0]) password = smart_unicode(auth_parts[2]) except DjangoUnicodeDecodeError: return None return self.authenticate_credentials(userid, password) def authenticate_credentials(self, userid, password): """ Authenticate the userid and password against username and password. """ user = authenticate(username=userid, password=password) if user is not None and user.is_active: return (user, None) class SessionAuthentication(BaseAuthentication): """ Use Django's session framework for authentication. """ def authenticate(self, request): """ Returns a `User` if the request session currently has a logged in user. Otherwise returns `None`. """ # Get the underlying HttpRequest object http_request = request._request user = getattr(http_request, 'user', None) # Unauthenticated, CSRF validation not required if not user or not user.is_active: return # Enforce CSRF validation for session based authentication. class CSRFCheck(CsrfViewMiddleware): def _reject(self, request, reason): # Return the failure reason instead of an HttpResponse return reason reason = CSRFCheck().process_view(http_request, None, (), {}) if reason: # CSRF failed, bail with explicit error message raise exceptions.PermissionDenied('CSRF Failed: %s' % reason) # CSRF passed with authenticated user return (user, None) class TokenAuthentication(BaseAuthentication): """ Simple token based authentication. Clients should authenticate by passing the token key in the "Authorization" HTTP header, prepended with the string "Token ". For example: Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a """ model = Token """ A custom token model may be used, but must have the following properties. * key -- The string identifying the token * user -- The user to which the token belongs """ def authenticate(self, request): auth = request.META.get('HTTP_AUTHORIZATION', '').split() if len(auth) == 2 and auth[0].lower() == "token": key = auth[1] try: token = self.model.objects.get(key=key) except self.model.DoesNotExist: return None if token.user.is_active: return (token.user, token) # TODO: OAuthAuthentication