Added docs for waldo.
[philo.git] / philo / contrib / waldo / tokens.py
1 """
2 Based on :mod:`django.contrib.auth.tokens`. Supports the following settings:
3
4 :setting:`WALDO_REGISTRATION_TIMEOUT_DAYS`
5         The number of days a registration link will be valid before expiring. Default: 1.
6
7 :setting:`WALDO_EMAIL_TIMEOUT_DAYS`
8         The number of days an email change link will be valid before expiring. Default: 1.
9
10 """
11
12 from hashlib import sha1
13 from datetime import date
14
15 from django.conf import settings
16 from django.utils.http import int_to_base36, base36_to_int
17 from django.contrib.auth.tokens import PasswordResetTokenGenerator
18
19
20 REGISTRATION_TIMEOUT_DAYS = getattr(settings, 'WALDO_REGISTRATION_TIMEOUT_DAYS', 1)
21 EMAIL_TIMEOUT_DAYS = getattr(settings, 'WALDO_EMAIL_TIMEOUT_DAYS', 1)
22
23
24 class RegistrationTokenGenerator(PasswordResetTokenGenerator):
25         """Strategy object used to generate and check tokens for the user registration mechanism."""
26         
27         def check_token(self, user, token):
28                 """Check that a registration token is correct for a given user."""
29                 # If the user is active, the hash can't be valid.
30                 if user.is_active:
31                         return False
32                 
33                 # Parse the token
34                 try:
35                         ts_b36, hash = token.split('-')
36                 except ValueError:
37                         return False
38                 
39                 try:
40                         ts = base36_to_int(ts_b36)
41                 except ValueError:
42                         return False
43                 
44                 # Check that the timestamp and uid have not been tampered with.
45                 if self._make_token_with_timestamp(user, ts) != token:
46                         return False
47                 
48                 # Check that the timestamp is within limit
49                 if (self._num_days(self._today()) - ts) > REGISTRATION_TIMEOUT_DAYS:
50                         return False
51                 
52                 return True
53         
54         def _make_token_with_timestamp(self, user, timestamp):
55                 ts_b36 = int_to_base36(timestamp)
56                 
57                 # By hashing on the internal state of the user and using state that is
58                 # sure to change, we produce a hash that will be invalid as soon as it
59                 # is used.
60                 hash = sha1(settings.SECRET_KEY + unicode(user.id) + unicode(user.is_active) + user.last_login.strftime('%Y-%m-%d %H:%M:%S') + unicode(timestamp)).hexdigest()[::2]
61                 return '%s-%s' % (ts_b36, hash)
62
63
64 registration_token_generator = RegistrationTokenGenerator()
65
66
67 class EmailTokenGenerator(PasswordResetTokenGenerator):
68         """Strategy object used to generate and check tokens for a user email change mechanism."""
69         
70         def make_token(self, user, email):
71                 """Returns a token that can be used once to do an email change for the given user and email."""
72                 return self._make_token_with_timestamp(user, email, self._num_days(self._today()))
73         
74         def check_token(self, user, email, token):
75                 if email == user.email:
76                         return False
77                 
78                 # Parse the token
79                 try:
80                         ts_b36, hash = token.split('-')
81                 except ValueError:
82                         return False
83                 
84                 try:
85                         ts = base36_to_int(ts_b36)
86                 except ValueError:
87                         return False
88                 
89                 # Check that the timestamp and uid have not been tampered with.
90                 if self._make_token_with_timestamp(user, email, ts) != token:
91                         return False
92                 
93                 # Check that the timestamp is within limit
94                 if (self._num_days(self._today()) - ts) > EMAIL_TIMEOUT_DAYS:
95                         return False
96                 
97                 return True
98         
99         def _make_token_with_timestamp(self, user, email, timestamp):
100                 ts_b36 = int_to_base36(timestamp)
101                 
102                 hash = sha1(settings.SECRET_KEY + unicode(user.id) + user.email + email + unicode(timestamp)).hexdigest()[::2]
103                 return '%s-%s' % (ts_b36, hash)
104
105
106 email_token_generator = EmailTokenGenerator()