tokens.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. """
  2. Token creation
  3. Token is base encoded string containing three values:
  4. - days since unix epoch (so we can validate token expiration)
  5. - hash unique for current state of user model
  6. - token checksum for discovering manipulations
  7. """
  8. import base64
  9. from hashlib import sha256
  10. from time import time
  11. from django.conf import settings
  12. from django.utils import six
  13. from django.utils.encoding import force_bytes
  14. def make(user, token_type):
  15. user_hash = _make_hash(user, token_type)
  16. creation_day = _days_since_epoch()
  17. obfuscated = base64.b64encode(force_bytes('%s%s' % (user_hash, creation_day))).decode()
  18. obfuscated = obfuscated.rstrip('=')
  19. checksum = _make_checksum(obfuscated)
  20. return '%s%s' % (checksum, obfuscated)
  21. def is_valid(user, token_type, token):
  22. checksum = token[:8]
  23. obfuscated = token[8:]
  24. if checksum != _make_checksum(obfuscated):
  25. return False
  26. unobfuscated = base64.b64decode(obfuscated + '=' * (-len(obfuscated) % 4)).decode()
  27. user_hash = unobfuscated[:8]
  28. if user_hash != _make_hash(user, token_type):
  29. return False
  30. creation_day = int(unobfuscated[8:])
  31. return creation_day + 5 >= _days_since_epoch()
  32. def _make_hash(user, token_type):
  33. seeds = [
  34. user.pk,
  35. user.email,
  36. user.password,
  37. user.last_login.replace(microsecond=0, tzinfo=None),
  38. token_type,
  39. settings.SECRET_KEY,
  40. ]
  41. return sha256(force_bytes('+'.join([six.text_type(s) for s in seeds]))).hexdigest()[:8]
  42. def _days_since_epoch():
  43. return int(time() / (25 * 3600))
  44. def _make_checksum(obfuscated):
  45. return sha256(force_bytes('%s:%s' % (settings.SECRET_KEY, obfuscated))).hexdigest()[:8]
  46. ACTIVATION_TOKEN = 'activation'
  47. def make_activation_token(user):
  48. return make(user, ACTIVATION_TOKEN)
  49. def is_activation_token_valid(user, token):
  50. return is_valid(user, ACTIVATION_TOKEN, token)
  51. PASSWORD_CHANGE_TOKEN = 'change_password'
  52. def make_password_change_token(user):
  53. return make(user, PASSWORD_CHANGE_TOKEN)
  54. def is_password_change_token_valid(user, token):
  55. return is_valid(user, PASSWORD_CHANGE_TOKEN, token)