Code

Ticket #16385: c9f97bd7397a39ebcb9f8831b28d5ab3b4e57038.diff

File c9f97bd7397a39ebcb9f8831b28d5ab3b4e57038.diff, 7.0 KB (added by jonash, 3 years ago)

Auth password reset string pk

Line 
1commit c9f97bd7397a39ebcb9f8831b28d5ab3b4e57038
2Author: Jonas Haag <jonas@lophus.org>
3Date:   Tue Jun 28 15:55:54 2011 +0200
4
5    auth password reset
6
7diff --git a/django/contrib/admin/templates/registration/password_reset_email.html b/django/contrib/admin/templates/registration/password_reset_email.html
8index de9dc79..665ea11 100644
9--- a/django/contrib/admin/templates/registration/password_reset_email.html
10+++ b/django/contrib/admin/templates/registration/password_reset_email.html
11@@ -3,7 +3,7 @@
12 
13 {% trans "Please go to the following page and choose a new password:" %}
14 {% block reset_link %}
15-{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %}
16+{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
17 {% endblock %}
18 {% trans "Your username, in case you've forgotten:" %} {{ user.username }}
19 
20diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
21index 3dcbd84..5854367 100644
22--- a/django/contrib/auth/forms.py
23+++ b/django/contrib/auth/forms.py
24@@ -5,7 +5,7 @@ from django.contrib.sites.models import get_current_site
25 from django.template import Context, loader
26 from django import forms
27 from django.utils.translation import ugettext_lazy as _
28-from django.utils.http import int_to_base36
29+from django.utils.http import urlsafe_base64_encode
30 
31 class UserCreationForm(forms.ModelForm):
32     """
33@@ -138,7 +138,7 @@ class PasswordResetForm(forms.Form):
34                 'email': user.email,
35                 'domain': domain,
36                 'site_name': site_name,
37-                'uid': int_to_base36(user.id),
38+                'uid': urlsafe_base64_encode(str(user.id)),
39                 'user': user,
40                 'token': token_generator.make_token(user),
41                 'protocol': use_https and 'https' or 'http',
42diff --git a/django/contrib/auth/tests/templates/registration/password_reset_email.html b/django/contrib/auth/tests/templates/registration/password_reset_email.html
43index 1b9a482..baac2fc 100644
44--- a/django/contrib/auth/tests/templates/registration/password_reset_email.html
45+++ b/django/contrib/auth/tests/templates/registration/password_reset_email.html
46@@ -1 +1 @@
47-{{ protocol }}://{{ domain }}/reset/{{ uid }}-{{ token }}/
48\ No newline at end of file
49+{{ protocol }}://{{ domain }}/reset/{{ uid }}/{{ token }}/
50diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py
51index b03489c..40fdab0 100644
52--- a/django/contrib/auth/tests/views.py
53+++ b/django/contrib/auth/tests/views.py
54@@ -100,13 +100,13 @@ class PasswordResetTest(AuthViewsTestCase):
55 
56     def test_confirm_invalid_user(self):
57         # Ensure that we get a 200 response for a non-existant user, not a 404
58-        response = self.client.get('/reset/123456-1-1/')
59+        response = self.client.get('/reset/123456/1-1/')
60         self.assertEqual(response.status_code, 200)
61         self.assertTrue("The password reset link was invalid" in response.content)
62 
63     def test_confirm_overflow_user(self):
64         # Ensure that we get a 200 response for a base36 user id that overflows int
65-        response = self.client.get('/reset/zzzzzzzzzzzzz-1-1/')
66+        response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/')
67         self.assertEqual(response.status_code, 200)
68         self.assertTrue("The password reset link was invalid" in response.content)
69 
70diff --git a/django/contrib/auth/urls.py b/django/contrib/auth/urls.py
71index 42b4e8f..ed8db75 100644
72--- a/django/contrib/auth/urls.py
73+++ b/django/contrib/auth/urls.py
74@@ -11,7 +11,7 @@ urlpatterns = patterns('',
75     (r'^password_change/done/$', 'django.contrib.auth.views.password_change_done'),
76     (r'^password_reset/$', 'django.contrib.auth.views.password_reset'),
77     (r'^password_reset/done/$', 'django.contrib.auth.views.password_reset_done'),
78-    (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'),
79+    (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'),
80     (r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete'),
81 )
82 
83diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
84index eba83a2..7ecfd5c 100644
85--- a/django/contrib/auth/views.py
86+++ b/django/contrib/auth/views.py
87@@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
88 from django.http import HttpResponseRedirect, QueryDict
89 from django.shortcuts import render_to_response
90 from django.template import RequestContext
91-from django.utils.http import base36_to_int
92+from django.utils.http import urlsafe_base64_decode
93 from django.utils.translation import ugettext as _
94 from django.views.decorators.cache import never_cache
95 from django.views.decorators.csrf import csrf_protect
96@@ -173,7 +173,7 @@ def password_reset_done(request,
97 
98 # Doesn't need csrf_protect since no-one can guess the URL
99 @never_cache
100-def password_reset_confirm(request, uidb36=None, token=None,
101+def password_reset_confirm(request, uidb64=None, token=None,
102                            template_name='registration/password_reset_confirm.html',
103                            token_generator=default_token_generator,
104                            set_password_form=SetPasswordForm,
105@@ -183,13 +183,13 @@ def password_reset_confirm(request, uidb36=None, token=None,
106     View that checks the hash in a password reset link and presents a
107     form for entering a new password.
108     """
109-    assert uidb36 is not None and token is not None # checked by URLconf
110+    assert uidb64 is not None and token is not None # checked by URLconf
111     if post_reset_redirect is None:
112         post_reset_redirect = reverse('django.contrib.auth.views.password_reset_complete')
113     try:
114-        uid_int = base36_to_int(uidb36)
115-        user = User.objects.get(id=uid_int)
116-    except (ValueError, User.DoesNotExist):
117+        uid = urlsafe_base64_decode(str(uidb64))
118+        user = User.objects.get(id=uid)
119+    except (TypeError, ValueError, User.DoesNotExist):
120         user = None
121 
122     if user is not None and token_generator.check_token(user, token):
123diff --git a/django/utils/http.py b/django/utils/http.py
124index c93a338..a839fa8 100644
125--- a/django/utils/http.py
126+++ b/django/utils/http.py
127@@ -1,9 +1,11 @@
128+import base64
129 import calendar
130 import datetime
131 import re
132 import sys
133 import urllib
134 import urlparse
135+from binascii import Error as BinasciiError
136 from email.Utils import formatdate
137 
138 from django.utils.encoding import smart_str, force_unicode
139@@ -168,6 +170,16 @@ def int_to_base36(i):
140         factor -= 1
141     return ''.join(base36)
142 
143+def urlsafe_base64_encode(s):
144+    return base64.urlsafe_b64encode(s).rstrip('\n=')
145+
146+def urlsafe_base64_decode(s):
147+    assert isinstance(s, str)
148+    try:
149+        return base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, '='))
150+    except (LookupError, BinasciiError), e:
151+        raise ValueError(e)
152+
153 def parse_etags(etag_str):
154     """
155     Parses a string with one or several etags passed in If-None-Match and