123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- import base64
- from django.contrib.auth.hashers import PBKDF2PasswordHasher
- from django.utils import timezone
- from rest_framework import exceptions, HTTP_HEADER_ENCODING
- from rest_framework.authentication import (
- BaseAuthentication,
- get_authorization_header,
- TokenAuthentication as RestFrameworkTokenAuthentication,
- BasicAuthentication)
- from desecapi.models import Token
- from desecapi.serializers import AuthenticatedBasicUserActionSerializer, EmailPasswordSerializer
- class TokenAuthentication(RestFrameworkTokenAuthentication):
- model = Token
- def authenticate_credentials(self, key):
- key = Token.make_hash(key)
- user, token = super().authenticate_credentials(key)
- token.last_used = timezone.now()
- token.save()
- return user, token
- class BasicTokenAuthentication(BaseAuthentication):
- """
- HTTP Basic authentication that uses username and token.
- Clients should authenticate by passing the username and the token as a
- password in the "Authorization" HTTP header, according to the HTTP
- Basic Authentication Scheme
- Authorization: Basic dXNlcm5hbWU6dG9rZW4=
- For username "username" and password "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
- model = Token
- def authenticate(self, request):
- auth = get_authorization_header(request).split()
- if not auth or auth[0].lower() != b'basic':
- return None
- if len(auth) == 1:
- msg = 'Invalid basic auth token header. No credentials provided.'
- raise exceptions.AuthenticationFailed(msg)
- elif len(auth) > 2:
- msg = 'Invalid basic auth token header. Basic authentication string should not contain spaces.'
- raise exceptions.AuthenticationFailed(msg)
- return self.authenticate_credentials(auth[1])
- def authenticate_credentials(self, basic):
- invalid_token_message = 'Invalid basic auth token'
- try:
- username, key = base64.b64decode(basic).decode(HTTP_HEADER_ENCODING).split(':')
- user, token = TokenAuthentication().authenticate_credentials(key)
- domain_names = user.domains.values_list('name', flat=True)
- if username not in ['', user.email] and not username.lower() in domain_names:
- raise Exception
- except Exception:
- raise exceptions.AuthenticationFailed(invalid_token_message)
- if not user.is_active:
- raise exceptions.AuthenticationFailed(invalid_token_message)
- return user, token
- def authenticate_header(self, request):
- return 'Basic'
- class URLParamAuthentication(BaseAuthentication):
- """
- Authentication against username/password as provided in URL parameters.
- """
- model = Token
- def authenticate(self, request):
- """
- Returns a `User` if a correct username and password have been supplied
- using URL parameters. Otherwise returns `None`.
- """
- if 'username' not in request.query_params:
- msg = 'No username URL parameter provided.'
- raise exceptions.AuthenticationFailed(msg)
- if 'password' not in request.query_params:
- msg = 'No password URL parameter provided.'
- raise exceptions.AuthenticationFailed(msg)
- return self.authenticate_credentials(request.query_params['username'], request.query_params['password'])
- def authenticate_credentials(self, _, key):
- try:
- user, token = TokenAuthentication().authenticate_credentials(key)
- except self.model.DoesNotExist:
- raise exceptions.AuthenticationFailed('badauth')
- if not user.is_active:
- raise exceptions.AuthenticationFailed('badauth')
- return token.user, token
- class EmailPasswordPayloadAuthentication(BaseAuthentication):
- authenticate_credentials = BasicAuthentication.authenticate_credentials
- def authenticate(self, request):
- serializer = EmailPasswordSerializer(data=request.data)
- serializer.is_valid(raise_exception=True)
- return self.authenticate_credentials(serializer.data['email'], serializer.data['password'], request)
- class AuthenticatedBasicUserActionAuthentication(BaseAuthentication):
- """
- Authenticates a request based on whether the serializer determines the validity of the given verification code
- and additional data (using `serializer.is_valid()`). The serializer's input data will be determined by (a) the
- view's 'code' kwarg and (b) the request payload for POST requests.
- If the request is valid, the AuthenticatedAction instance will be attached to the request as `auth` attribute.
- """
- def authenticate(self, request):
- view = request.parser_context['view']
- serializer = AuthenticatedBasicUserActionSerializer(data=request.data, context=view.get_serializer_context())
- serializer.is_valid(raise_exception=True)
- return serializer.validated_data['user'], None
- class TokenHasher(PBKDF2PasswordHasher):
- algorithm = 'pbkdf2_sha256_iter1'
- iterations = 1
|