commit c9f97bd7397a39ebcb9f8831b28d5ab3b4e57038
Author: Jonas Haag <jonas@lophus.org>
Date: Tue Jun 28 15:55:54 2011 +0200
auth password reset
diff --git a/django/contrib/admin/templates/registration/password_reset_email.html b/django/contrib/admin/templates/registration/password_reset_email.html
index de9dc79..665ea11 100644
|
a
|
b
|
|
| 3 | 3 | |
| 4 | 4 | {% trans "Please go to the following page and choose a new password:" %} |
| 5 | 5 | {% block reset_link %} |
| 6 | | {{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %} |
| | 6 | {{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %} |
| 7 | 7 | {% endblock %} |
| 8 | 8 | {% trans "Your username, in case you've forgotten:" %} {{ user.username }} |
| 9 | 9 | |
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index 3dcbd84..5854367 100644
|
a
|
b
|
from django.contrib.sites.models import get_current_site
|
| 5 | 5 | from django.template import Context, loader |
| 6 | 6 | from django import forms |
| 7 | 7 | from django.utils.translation import ugettext_lazy as _ |
| 8 | | from django.utils.http import int_to_base36 |
| | 8 | from django.utils.http import urlsafe_base64_encode |
| 9 | 9 | |
| 10 | 10 | class UserCreationForm(forms.ModelForm): |
| 11 | 11 | """ |
| … |
… |
class PasswordResetForm(forms.Form):
|
| 138 | 138 | 'email': user.email, |
| 139 | 139 | 'domain': domain, |
| 140 | 140 | 'site_name': site_name, |
| 141 | | 'uid': int_to_base36(user.id), |
| | 141 | 'uid': urlsafe_base64_encode(str(user.id)), |
| 142 | 142 | 'user': user, |
| 143 | 143 | 'token': token_generator.make_token(user), |
| 144 | 144 | 'protocol': use_https and 'https' or 'http', |
diff --git a/django/contrib/auth/tests/templates/registration/password_reset_email.html b/django/contrib/auth/tests/templates/registration/password_reset_email.html
index 1b9a482..baac2fc 100644
|
a
|
b
|
|
| 1 | | {{ protocol }}://{{ domain }}/reset/{{ uid }}-{{ token }}/ |
| 2 | | No newline at end of file |
| | 1 | {{ protocol }}://{{ domain }}/reset/{{ uid }}/{{ token }}/ |
diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py
index b03489c..40fdab0 100644
|
a
|
b
|
class PasswordResetTest(AuthViewsTestCase):
|
| 100 | 100 | |
| 101 | 101 | def test_confirm_invalid_user(self): |
| 102 | 102 | # Ensure that we get a 200 response for a non-existant user, not a 404 |
| 103 | | response = self.client.get('/reset/123456-1-1/') |
| | 103 | response = self.client.get('/reset/123456/1-1/') |
| 104 | 104 | self.assertEqual(response.status_code, 200) |
| 105 | 105 | self.assertTrue("The password reset link was invalid" in response.content) |
| 106 | 106 | |
| 107 | 107 | def test_confirm_overflow_user(self): |
| 108 | 108 | # Ensure that we get a 200 response for a base36 user id that overflows int |
| 109 | | response = self.client.get('/reset/zzzzzzzzzzzzz-1-1/') |
| | 109 | response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/') |
| 110 | 110 | self.assertEqual(response.status_code, 200) |
| 111 | 111 | self.assertTrue("The password reset link was invalid" in response.content) |
| 112 | 112 | |
diff --git a/django/contrib/auth/urls.py b/django/contrib/auth/urls.py
index 42b4e8f..ed8db75 100644
|
a
|
b
|
urlpatterns = patterns('',
|
| 11 | 11 | (r'^password_change/done/$', 'django.contrib.auth.views.password_change_done'), |
| 12 | 12 | (r'^password_reset/$', 'django.contrib.auth.views.password_reset'), |
| 13 | 13 | (r'^password_reset/done/$', 'django.contrib.auth.views.password_reset_done'), |
| 14 | | (r'^reset/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 'django.contrib.auth.views.password_reset_confirm'), |
| | 14 | (r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 'django.contrib.auth.views.password_reset_confirm'), |
| 15 | 15 | (r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete'), |
| 16 | 16 | ) |
| 17 | 17 | |
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index eba83a2..7ecfd5c 100644
|
a
|
b
|
from django.core.urlresolvers import reverse
|
| 5 | 5 | from django.http import HttpResponseRedirect, QueryDict |
| 6 | 6 | from django.shortcuts import render_to_response |
| 7 | 7 | from django.template import RequestContext |
| 8 | | from django.utils.http import base36_to_int |
| | 8 | from django.utils.http import urlsafe_base64_decode |
| 9 | 9 | from django.utils.translation import ugettext as _ |
| 10 | 10 | from django.views.decorators.cache import never_cache |
| 11 | 11 | from django.views.decorators.csrf import csrf_protect |
| … |
… |
def password_reset_done(request,
|
| 173 | 173 | |
| 174 | 174 | # Doesn't need csrf_protect since no-one can guess the URL |
| 175 | 175 | @never_cache |
| 176 | | def password_reset_confirm(request, uidb36=None, token=None, |
| | 176 | def password_reset_confirm(request, uidb64=None, token=None, |
| 177 | 177 | template_name='registration/password_reset_confirm.html', |
| 178 | 178 | token_generator=default_token_generator, |
| 179 | 179 | set_password_form=SetPasswordForm, |
| … |
… |
def password_reset_confirm(request, uidb36=None, token=None,
|
| 183 | 183 | View that checks the hash in a password reset link and presents a |
| 184 | 184 | form for entering a new password. |
| 185 | 185 | """ |
| 186 | | assert uidb36 is not None and token is not None # checked by URLconf |
| | 186 | assert uidb64 is not None and token is not None # checked by URLconf |
| 187 | 187 | if post_reset_redirect is None: |
| 188 | 188 | post_reset_redirect = reverse('django.contrib.auth.views.password_reset_complete') |
| 189 | 189 | try: |
| 190 | | uid_int = base36_to_int(uidb36) |
| 191 | | user = User.objects.get(id=uid_int) |
| 192 | | except (ValueError, User.DoesNotExist): |
| | 190 | uid = urlsafe_base64_decode(str(uidb64)) |
| | 191 | user = User.objects.get(id=uid) |
| | 192 | except (TypeError, ValueError, User.DoesNotExist): |
| 193 | 193 | user = None |
| 194 | 194 | |
| 195 | 195 | if user is not None and token_generator.check_token(user, token): |
diff --git a/django/utils/http.py b/django/utils/http.py
index c93a338..a839fa8 100644
|
a
|
b
|
|
| | 1 | import base64 |
| 1 | 2 | import calendar |
| 2 | 3 | import datetime |
| 3 | 4 | import re |
| 4 | 5 | import sys |
| 5 | 6 | import urllib |
| 6 | 7 | import urlparse |
| | 8 | from binascii import Error as BinasciiError |
| 7 | 9 | from email.Utils import formatdate |
| 8 | 10 | |
| 9 | 11 | from django.utils.encoding import smart_str, force_unicode |
| … |
… |
def int_to_base36(i):
|
| 168 | 170 | factor -= 1 |
| 169 | 171 | return ''.join(base36) |
| 170 | 172 | |
| | 173 | def urlsafe_base64_encode(s): |
| | 174 | return base64.urlsafe_b64encode(s).rstrip('\n=') |
| | 175 | |
| | 176 | def urlsafe_base64_decode(s): |
| | 177 | assert isinstance(s, str) |
| | 178 | try: |
| | 179 | return base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, '=')) |
| | 180 | except (LookupError, BinasciiError), e: |
| | 181 | raise ValueError(e) |
| | 182 | |
| 171 | 183 | def parse_etags(etag_str): |
| 172 | 184 | """ |
| 173 | 185 | Parses a string with one or several etags passed in If-None-Match and |