app/django/contrib/auth/tokens.py
changeset 323 ff1a9aa48cfd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/django/contrib/auth/tokens.py	Tue Oct 14 16:00:59 2008 +0000
@@ -0,0 +1,66 @@
+from datetime import date
+from django.conf import settings
+from django.utils.http import int_to_base36, base36_to_int
+
+class PasswordResetTokenGenerator(object):
+    """
+    Stratgy object used to generate and check tokens for the password
+    reset mechanism.
+    """
+    def make_token(self, user):
+        """
+        Returns a token that can be used once to do a password reset
+        for the given user.
+        """
+        return self._make_token_with_timestamp(user, self._num_days(self._today()))
+
+    def check_token(self, user, token):
+        """
+        Check that a password reset token is correct for a given user.
+        """
+        # Parse the tokem
+        try:
+            ts_b36, hash = token.split("-")
+        except ValueError:
+            return False
+
+        try:
+            ts = base36_to_int(ts_b36)
+        except ValueError:
+            return False
+
+        # Check that the timestamp/uid has not been tampered with
+        if self._make_token_with_timestamp(user, ts) != token:
+            return False
+
+        # Check the timestamp is within limit
+        if (self._num_days(self._today()) - ts) > settings.PASSWORD_RESET_TIMEOUT_DAYS:
+            return False
+
+        return True
+
+    def _make_token_with_timestamp(self, user, timestamp):
+        # timestamp is number of days since 2001-1-1.  Converted to
+        # base 36, this gives us a 3 digit string until about 2121
+        ts_b36 = int_to_base36(timestamp)
+
+        # By hashing on the internal state of the user and using state
+        # that is sure to change (the password salt will change as soon as
+        # the password is set, at least for current Django auth, and
+        # last_login will also change), we produce a hash that will be
+        # invalid as soon as it is used.
+        # We limit the hash to 20 chars to keep URL short
+        from django.utils.hashcompat import sha_constructor
+        hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) +
+                               user.password + unicode(user.last_login) +
+                               unicode(timestamp)).hexdigest()[::2]
+        return "%s-%s" % (ts_b36, hash)
+
+    def _num_days(self, dt):
+        return (dt - date(2001,1,1)).days
+
+    def _today(self):
+        # Used for mocking in tests
+        return date.today()
+
+default_token_generator = PasswordResetTokenGenerator()