Commit 1184d077 authored by Tim Graham's avatar Tim Graham
Browse files

Fixed #14881 -- Modified password reset to work with a non-integer UserModel.pk.

uid is now base64 encoded in password reset URLs/views. A backwards compatible
password_reset_confirm view/URL will allow password reset links generated before
this change to continue to work. This view will be removed in Django 1.7.

Thanks jonash for the initial patch and claudep for the review.
parent b6a87f5c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@

{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb36=uid token=token %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}

+3 −2
Original line number Diff line number Diff line
@@ -6,8 +6,9 @@ from django import forms
from django.forms.util import flatatt
from django.template import loader
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_bytes
from django.utils.html import format_html, format_html_join
from django.utils.http import int_to_base36
from django.utils.http import urlsafe_base64_encode
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext, ugettext_lazy as _
@@ -243,7 +244,7 @@ class PasswordResetForm(forms.Form):
                'email': user.email,
                'domain': domain,
                'site_name': site_name,
                'uid': int_to_base36(user.pk),
                'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                'user': user,
                'token': token_generator.make_token(user),
                'protocol': 'https' if use_https else 'http',
+1 −1
Original line number Diff line number Diff line
{{ protocol }}://{{ domain }}/reset/{{ uid }}-{{ token }}/
 No newline at end of file
{{ protocol }}://{{ domain }}/reset/{{ uid }}/{{ token }}/
+22 −2
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ from django.core import mail
from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import QueryDict, HttpRequest
from django.utils.encoding import force_text
from django.utils.http import urlquote
from django.utils.http import int_to_base36, urlsafe_base64_decode, urlquote
from django.utils._os import upath
from django.test import TestCase
from django.test.utils import override_settings, patch_logger
@@ -91,7 +91,7 @@ class AuthViewNamedURLTests(AuthViewsTestCase):
            ('password_reset', [], {}),
            ('password_reset_done', [], {}),
            ('password_reset_confirm', [], {
                'uidb36': 'aaaaaaa',
                'uidb64': 'aaaaaaa',
                'token': '1111-aaaaa',
            }),
            ('password_reset_complete', [], {}),
@@ -193,6 +193,16 @@ class PasswordResetTest(AuthViewsTestCase):
        # redirect to a 'complete' page:
        self.assertContains(response, "Please enter your new password")

    def test_confirm_valid_base36(self):
        # Remove in Django 1.7
        url, path = self._test_confirm_start()
        path_parts = path.strip("/").split("/")
        # construct an old style (base36) URL by converting the base64 ID
        path_parts[1] = int_to_base36(int(urlsafe_base64_decode(path_parts[1])))
        response = self.client.get("/%s/%s-%s/" % tuple(path_parts))
        # redirect to a 'complete' page:
        self.assertContains(response, "Please enter your new password")

    def test_confirm_invalid(self):
        url, path = self._test_confirm_start()
        # Let's munge the token in the path, but keep the same length,
@@ -204,11 +214,21 @@ class PasswordResetTest(AuthViewsTestCase):

    def test_confirm_invalid_user(self):
        # Ensure that we get a 200 response for a non-existant user, not a 404
        response = self.client.get('/reset/123456/1-1/')
        self.assertContains(response, "The password reset link was invalid")

    def test_confirm_invalid_user_base36(self):
        # Remove in Django 1.7
        response = self.client.get('/reset/123456-1-1/')
        self.assertContains(response, "The password reset link was invalid")

    def test_confirm_overflow_user(self):
        # Ensure that we get a 200 response for a base36 user id that overflows int
        response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/')
        self.assertContains(response, "The password reset link was invalid")

    def test_confirm_overflow_user_base36(self):
        # Remove in Django 1.7
        response = self.client.get('/reset/zzzzzzzzzzzzz-1-1/')
        self.assertContains(response, "The password reset link was invalid")

+2 −3
Original line number Diff line number Diff line
@@ -67,10 +67,10 @@ urlpatterns = urlpatterns + patterns('',
    (r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')),
    (r'^password_reset/custom_redirect/$', 'django.contrib.auth.views.password_reset', dict(post_reset_redirect='/custom/')),
    (r'^password_reset/custom_redirect/named/$', 'django.contrib.auth.views.password_reset', dict(post_reset_redirect='password_reset')),
    (r'^reset/custom/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
    (r'^reset/custom/(?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',
        dict(post_reset_redirect='/custom/')),
    (r'^reset/custom/named/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
    (r'^reset/custom/named/(?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',
        dict(post_reset_redirect='password_reset')),
    (r'^password_change/custom/$', 'django.contrib.auth.views.password_change', dict(post_change_redirect='/custom/')),
@@ -88,4 +88,3 @@ urlpatterns = urlpatterns + patterns('',
    (r'^custom_request_auth_login/$', custom_request_auth_login),
    url(r'^userpage/(.+)/$', userpage, name="userpage"),
)
Loading