tokens.py 2.0 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 make_hash(user, token_type):
  20. seeds = (
  21. user.pk,
  22. user.email,
  23. user.password,
  24. user.last_login.replace(microsecond=0, tzinfo=None),
  25. token_type,
  26. settings.SECRET_KEY,
  27. )
  28. return sha256('+'.join([unicode(s) for s in seeds])).hexdigest()[:8]
  29. def days_since_epoch():
  30. return int(time() / (25 * 3600))
  31. def make_checksum(obfuscated):
  32. return sha256('%s:%s' % (settings.SECRET_KEY, obfuscated)).hexdigest()[:8]
  33. def is_valid(user, token_type, token):
  34. checksum = token[:8]
  35. obfuscated = token[8:]
  36. if checksum != make_checksum(obfuscated):
  37. return False
  38. unobfuscated = base64.b64decode(obfuscated + '=' * (-len(obfuscated) % 4))
  39. user_hash = unobfuscated[:8]
  40. if user_hash != make_hash(user, token_type):
  41. return False
  42. creation_day = int(unobfuscated[8:])
  43. return creation_day + 5 >= days_since_epoch()
  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 reset token
  54. """
  55. PASSWORD_RESET_TOKEN = 'reset_password'
  56. def make_password_reset_token(user):
  57. return make(user, PASSWORD_RESET_TOKEN)
  58. def is_password_reset_token_valid(user, token):
  59. return is_valid(user, PASSWORD_RESET_TOKEN, token)