tokens.py 2.1 KB

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