tokens.py 2.1 KB

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