authentication.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import base64
  2. from rest_framework import exceptions, HTTP_HEADER_ENCODING
  3. from rest_framework.authentication import (
  4. BaseAuthentication,
  5. get_authorization_header,
  6. TokenAuthentication as RestFrameworkTokenAuthentication,
  7. BasicAuthentication)
  8. from desecapi.models import Token
  9. from desecapi.serializers import EmailPasswordSerializer
  10. class TokenAuthentication(RestFrameworkTokenAuthentication):
  11. model = Token
  12. class BasicTokenAuthentication(BaseAuthentication):
  13. """
  14. HTTP Basic authentication that uses username and token.
  15. Clients should authenticate by passing the username and the token as a
  16. password in the "Authorization" HTTP header, according to the HTTP
  17. Basic Authentication Scheme
  18. Authorization: Basic dXNlcm5hbWU6dG9rZW4=
  19. For username "username" and password "token".
  20. """
  21. # A custom token model may be used, but must have the following properties.
  22. #
  23. # * key -- The string identifying the token
  24. # * user -- The user to which the token belongs
  25. model = Token
  26. def authenticate(self, request):
  27. auth = get_authorization_header(request).split()
  28. if not auth or auth[0].lower() != b'basic':
  29. return None
  30. if len(auth) == 1:
  31. msg = 'Invalid basic auth token header. No credentials provided.'
  32. raise exceptions.AuthenticationFailed(msg)
  33. elif len(auth) > 2:
  34. msg = 'Invalid basic auth token header. Basic authentication string should not contain spaces.'
  35. raise exceptions.AuthenticationFailed(msg)
  36. return self.authenticate_credentials(auth[1])
  37. def authenticate_credentials(self, basic):
  38. invalid_token_message = 'Invalid basic auth token'
  39. try:
  40. user, key = base64.b64decode(basic).decode(HTTP_HEADER_ENCODING).split(':')
  41. token = self.model.objects.get(key=key)
  42. domain_names = token.user.domains.values_list('name', flat=True)
  43. if user not in ['', token.user.email] and not user.lower() in domain_names:
  44. raise Exception
  45. except Exception:
  46. raise exceptions.AuthenticationFailed(invalid_token_message)
  47. if not token.user.is_active:
  48. raise exceptions.AuthenticationFailed(invalid_token_message)
  49. return token.user, token
  50. def authenticate_header(self, request):
  51. return 'Basic'
  52. class URLParamAuthentication(BaseAuthentication):
  53. """
  54. Authentication against username/password as provided in URL parameters.
  55. """
  56. model = Token
  57. def authenticate(self, request):
  58. """
  59. Returns a `User` if a correct username and password have been supplied
  60. using URL parameters. Otherwise returns `None`.
  61. """
  62. if 'username' not in request.query_params:
  63. msg = 'No username URL parameter provided.'
  64. raise exceptions.AuthenticationFailed(msg)
  65. if 'password' not in request.query_params:
  66. msg = 'No password URL parameter provided.'
  67. raise exceptions.AuthenticationFailed(msg)
  68. return self.authenticate_credentials(request.query_params['username'], request.query_params['password'])
  69. def authenticate_credentials(self, _, key):
  70. try:
  71. token = self.model.objects.get(key=key)
  72. except self.model.DoesNotExist:
  73. raise exceptions.AuthenticationFailed('badauth')
  74. if not token.user.is_active:
  75. raise exceptions.AuthenticationFailed('badauth')
  76. return token.user, token
  77. class EmailPasswordPayloadAuthentication(BasicAuthentication):
  78. def authenticate(self, request):
  79. serializer = EmailPasswordSerializer(data=request.data)
  80. if not serializer.is_valid():
  81. return None, None
  82. return self.authenticate_credentials(serializer.data['email'], serializer.data['password'], request)