authentication.py 4.2 KB

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