tokens.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  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.encoding import force_bytes
  13. def make(user, token_type):
  14. user_hash = _make_hash(user, token_type)
  15. creation_day = _days_since_epoch()
  16. obfuscated = base64.b64encode(
  17. force_bytes("%s%s" % (user_hash, creation_day))
  18. ).decode()
  19. obfuscated = obfuscated.rstrip("=")
  20. checksum = _make_checksum(obfuscated)
  21. return "%s%s" % (checksum, obfuscated)
  22. def is_valid(user, token_type, token):
  23. checksum = token[:8]
  24. obfuscated = token[8:]
  25. if checksum != _make_checksum(obfuscated):
  26. return False
  27. unobfuscated = base64.b64decode(obfuscated + "=" * (-len(obfuscated) % 4)).decode()
  28. user_hash = unobfuscated[:8]
  29. if user_hash != _make_hash(user, token_type):
  30. return False
  31. creation_day = int(unobfuscated[8:])
  32. return creation_day + 5 >= _days_since_epoch()
  33. def _make_hash(user, token_type):
  34. seeds = [
  35. user.pk,
  36. user.email,
  37. user.password,
  38. user.last_login.replace(microsecond=0, tzinfo=None),
  39. token_type,
  40. settings.SECRET_KEY,
  41. ]
  42. return sha256(force_bytes("+".join([str(s) for s in seeds]))).hexdigest()[:8]
  43. def _days_since_epoch():
  44. return int(time() / (25 * 3600))
  45. def _make_checksum(obfuscated):
  46. return sha256(force_bytes("%s:%s" % (settings.SECRET_KEY, obfuscated))).hexdigest()[
  47. :8
  48. ]
  49. ACTIVATION_TOKEN = "activation"
  50. def make_activation_token(user):
  51. return make(user, ACTIVATION_TOKEN)
  52. def is_activation_token_valid(user, token):
  53. return is_valid(user, ACTIVATION_TOKEN, token)
  54. PASSWORD_CHANGE_TOKEN = "change_password"
  55. def make_password_change_token(user):
  56. return make(user, PASSWORD_CHANGE_TOKEN)
  57. def is_password_change_token_valid(user, token):
  58. return is_valid(user, PASSWORD_CHANGE_TOKEN, token)