2011-05-10 13:49:28 +04:00
|
|
|
"""
|
2011-05-17 03:27:27 +04:00
|
|
|
The :mod:`authentication` module provides a set of pluggable authentication classes.
|
2011-02-19 16:12:35 +03:00
|
|
|
|
2012-02-24 01:34:20 +04:00
|
|
|
Authentication behavior is provided by mixing the :class:`mixins.RequestMixin` class into a :class:`View` class.
|
2011-02-19 16:12:35 +03:00
|
|
|
"""
|
2011-05-10 13:49:28 +04:00
|
|
|
|
2011-01-24 21:59:23 +03:00
|
|
|
from django.contrib.auth import authenticate
|
2011-12-15 00:10:06 +04:00
|
|
|
from djangorestframework.compat import CsrfViewMiddleware
|
2011-01-24 21:59:23 +03:00
|
|
|
import base64
|
|
|
|
|
2011-05-10 13:49:28 +04:00
|
|
|
__all__ = (
|
2011-06-07 17:12:02 +04:00
|
|
|
'BaseAuthentication',
|
|
|
|
'BasicAuthentication',
|
2012-09-04 15:02:05 +04:00
|
|
|
'SessionAuthentication'
|
2011-05-10 13:49:28 +04:00
|
|
|
)
|
2011-02-19 13:26:27 +03:00
|
|
|
|
2011-05-10 13:49:28 +04:00
|
|
|
|
2011-06-07 17:12:02 +04:00
|
|
|
class BaseAuthentication(object):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
All authentication classes should extend BaseAuthentication.
|
|
|
|
"""
|
2011-01-24 21:59:23 +03:00
|
|
|
|
|
|
|
def authenticate(self, request):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
2011-05-19 11:49:57 +04:00
|
|
|
Authenticate the :obj:`request` and return a :obj:`User` or :const:`None`. [*]_
|
2011-12-29 17:31:12 +04:00
|
|
|
|
2011-05-17 12:15:35 +04:00
|
|
|
.. [*] The authentication context *will* typically be a :obj:`User`,
|
2011-05-17 03:27:27 +04:00
|
|
|
but it need not be. It can be any user-like object so long as the
|
2011-05-19 11:49:57 +04:00
|
|
|
permissions classes (see the :mod:`permissions` module) on the view can
|
|
|
|
handle the object and use it to determine if the request has the required
|
2011-12-29 17:31:12 +04:00
|
|
|
permissions or not.
|
|
|
|
|
2011-05-17 03:27:27 +04:00
|
|
|
This can be an important distinction if you're implementing some token
|
|
|
|
based authentication mechanism, where the authentication context
|
|
|
|
may be more involved than simply mapping to a :obj:`User`.
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
2011-01-24 21:59:23 +03:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
2011-06-07 17:12:02 +04:00
|
|
|
class BasicAuthentication(BaseAuthentication):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
2012-09-06 16:49:15 +04:00
|
|
|
Base class for HTTP Basic authentication.
|
|
|
|
Subclasses should implement `.authenticate_credentials()`.
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
|
2011-01-24 21:59:23 +03:00
|
|
|
def authenticate(self, request):
|
2011-05-17 12:15:35 +04:00
|
|
|
"""
|
2012-09-06 16:49:15 +04:00
|
|
|
Returns a `User` if a correct username and password have been supplied
|
|
|
|
using HTTP Basic authentication. Otherwise returns `None`.
|
2011-05-17 12:15:35 +04:00
|
|
|
"""
|
2011-04-05 05:40:18 +04:00
|
|
|
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
|
2011-12-29 17:31:12 +04:00
|
|
|
|
2011-01-24 21:59:23 +03:00
|
|
|
if 'HTTP_AUTHORIZATION' in request.META:
|
|
|
|
auth = request.META['HTTP_AUTHORIZATION'].split()
|
|
|
|
if len(auth) == 2 and auth[0].lower() == "basic":
|
2011-04-05 05:40:18 +04:00
|
|
|
try:
|
|
|
|
auth_parts = base64.b64decode(auth[1]).partition(':')
|
|
|
|
except TypeError:
|
|
|
|
return None
|
2011-12-29 17:31:12 +04:00
|
|
|
|
2011-04-05 05:40:18 +04:00
|
|
|
try:
|
2012-09-06 16:49:15 +04:00
|
|
|
userid, password = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
|
2011-04-05 05:40:18 +04:00
|
|
|
except DjangoUnicodeDecodeError:
|
|
|
|
return None
|
2011-12-29 17:31:12 +04:00
|
|
|
|
2012-09-06 16:49:15 +04:00
|
|
|
return self.authenticate_credentials(userid, password)
|
2011-12-29 17:31:12 +04:00
|
|
|
|
2012-09-06 16:49:15 +04:00
|
|
|
def authenticate_credentials(self, userid, password):
|
|
|
|
"""
|
|
|
|
Given the Basic authentication userid and password, authenticate
|
|
|
|
and return a user instance.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError('.authenticate_credentials() must be overridden')
|
|
|
|
|
|
|
|
|
|
|
|
class UserBasicAuthentication(BasicAuthentication):
|
|
|
|
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:
|
2012-09-06 17:50:43 +04:00
|
|
|
return (user, None)
|
2012-09-06 16:49:15 +04:00
|
|
|
|
2011-01-24 21:59:23 +03:00
|
|
|
|
2012-09-04 15:02:05 +04:00
|
|
|
class SessionAuthentication(BaseAuthentication):
|
2011-05-10 13:49:28 +04:00
|
|
|
"""
|
|
|
|
Use Django's session framework for authentication.
|
|
|
|
"""
|
|
|
|
|
2011-01-24 21:59:23 +03:00
|
|
|
def authenticate(self, request):
|
2011-05-17 12:15:35 +04:00
|
|
|
"""
|
2011-05-19 11:49:57 +04:00
|
|
|
Returns a :obj:`User` if the request session currently has a logged in user.
|
|
|
|
Otherwise returns :const:`None`.
|
2011-05-17 12:15:35 +04:00
|
|
|
"""
|
2012-04-11 20:38:47 +04:00
|
|
|
user = getattr(request._request, 'user', None)
|
2012-01-24 23:26:37 +04:00
|
|
|
|
2012-02-24 00:47:45 +04:00
|
|
|
if user and user.is_active:
|
2011-12-15 00:19:17 +04:00
|
|
|
# Enforce CSRF validation for session based authentication.
|
|
|
|
resp = CsrfViewMiddleware().process_view(request, None, (), {})
|
|
|
|
|
|
|
|
if resp is None: # csrf passed
|
2012-09-06 17:50:43 +04:00
|
|
|
return (user, None)
|
2011-04-27 21:07:28 +04:00
|
|
|
|
|
|
|
|
2012-09-08 00:12:33 +04:00
|
|
|
class TokenAuthentication(BaseAuthentication):
|
|
|
|
"""
|
|
|
|
Use a token model for authentication.
|
|
|
|
|
|
|
|
A custom token model may be used here, but must have the following minimum
|
|
|
|
properties:
|
|
|
|
|
|
|
|
* key -- The string identifying the token
|
|
|
|
* user -- The user to which the token belongs
|
|
|
|
* revoked -- The status of the token
|
|
|
|
|
|
|
|
The token key should be passed in as a string to the "Authorization" HTTP
|
|
|
|
header. For example:
|
|
|
|
|
|
|
|
Authorization: 0123456789abcdef0123456789abcdef
|
|
|
|
|
|
|
|
"""
|
|
|
|
model = None
|
|
|
|
|
|
|
|
def authenticate(self, request):
|
|
|
|
key = request.META.get('HTTP_AUTHORIZATION', '').strip()
|
|
|
|
|
|
|
|
if self.model is None:
|
|
|
|
from djangorestframework.tokenauth.models import BasicToken
|
|
|
|
self.model = BasicToken
|
|
|
|
|
|
|
|
try:
|
|
|
|
token = self.model.objects.get(key=key)
|
|
|
|
except self.model.DoesNotExist:
|
|
|
|
return None
|
|
|
|
|
|
|
|
if token.user.is_active and not token.revoked:
|
|
|
|
return (token.user, token)
|
|
|
|
|
|
|
|
# TODO: DigestAuthentication, OAuthAuthentication
|