mirror of https://github.com/django/django.git
Fixed #24097 -- Prevented AttributeError in redirect_to_login
Thanks Peter Schmidt for the report and the initial patch. Thanks to Oktay Sancak for writing the original failing test and Alvin Savoy for supporting contributing back to the community.
This commit is contained in:
parent
f5c3a8bff5
commit
d7bc37d611
|
@ -3,7 +3,6 @@ from django.conf import settings
|
|||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.decorators import available_attrs
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.six.moves.urllib.parse import urlparse
|
||||
from django.shortcuts import resolve_url
|
||||
|
||||
|
@ -21,9 +20,7 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
|
|||
if test_func(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
path = request.build_absolute_uri()
|
||||
# urlparse chokes on lazy objects in Python 3, force to str
|
||||
resolved_login_url = force_str(
|
||||
resolve_url(login_url or settings.LOGIN_URL))
|
||||
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
|
||||
# If the login url is the same scheme and net location then just
|
||||
# use the path as the "next" url.
|
||||
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from importlib import import_module
|
||||
import itertools
|
||||
import re
|
||||
|
@ -10,9 +13,9 @@ from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
|
|||
from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
|
||||
SetPasswordForm)
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.views import login as login_view
|
||||
from django.contrib.auth.views import login as login_view, redirect_to_login
|
||||
from django.core import mail
|
||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||
from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy
|
||||
from django.http import QueryDict, HttpRequest
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.utils.encoding import force_text
|
||||
|
@ -648,6 +651,10 @@ class LoginURLSettings(AuthViewsTestCase):
|
|||
expected = 'http://remote.example.com/login/?next=%s' % quoted_next
|
||||
self.assertLoginURLEquals(expected)
|
||||
|
||||
@override_settings(LOGIN_URL=reverse_lazy('login'))
|
||||
def test_lazy_login_url(self):
|
||||
self.assertLoginURLEquals('/login/?next=/login_required/')
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class LoginRedirectUrlTest(AuthViewsTestCase):
|
||||
|
@ -673,6 +680,21 @@ class LoginRedirectUrlTest(AuthViewsTestCase):
|
|||
self.assertLoginRedirectURLEqual('http://remote.example.com/welcome/')
|
||||
|
||||
|
||||
class RedirectToLoginTests(AuthViewsTestCase):
|
||||
"""Tests for the redirect_to_login view"""
|
||||
@override_settings(LOGIN_URL=reverse_lazy('login'))
|
||||
def test_redirect_to_login_with_lazy(self):
|
||||
login_redirect_response = redirect_to_login(next='/else/where/')
|
||||
expected = '/login/?next=/else/where/'
|
||||
self.assertEqual(expected, login_redirect_response.url)
|
||||
|
||||
@override_settings(LOGIN_URL=reverse_lazy('login'))
|
||||
def test_redirect_to_login_with_lazy_and_unicode(self):
|
||||
login_redirect_response = redirect_to_login(next='/else/where/झ/')
|
||||
expected = '/login/?next=/else/where/%E0%A4%9D/'
|
||||
self.assertEqual(expected, login_redirect_response.url)
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
class LogoutTest(AuthViewsTestCase):
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ from django.db.models.query import QuerySet
|
|||
from django.core import urlresolvers
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.functional import Promise
|
||||
|
||||
|
||||
def render_to_response(template_name, context=None,
|
||||
|
@ -182,6 +184,11 @@ def resolve_url(to, *args, **kwargs):
|
|||
if hasattr(to, 'get_absolute_url'):
|
||||
return to.get_absolute_url()
|
||||
|
||||
if isinstance(to, Promise):
|
||||
# Expand the lazy instance, as it can cause issues when it is passed
|
||||
# further to some Python functions like urlparse.
|
||||
to = force_text(to)
|
||||
|
||||
if isinstance(to, six.string_types):
|
||||
# Handle relative URLs
|
||||
if any(to.startswith(path) for path in ('./', '../')):
|
||||
|
|
|
@ -20,3 +20,7 @@ Bugfixes
|
|||
|
||||
* Fixed a crash in the CSRF middleware when handling non-ASCII referer header
|
||||
(:ticket:`23815`).
|
||||
|
||||
* Fixed a crash in the ``django.contrib.auth.redirect_to_login`` view when
|
||||
passing a :func:`~django.core.urlresolvers.reverse_lazy` result on Python 3
|
||||
(:ticket:`24097`).
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.core.urlresolvers import NoReverseMatch
|
||||
from django.core.urlresolvers import NoReverseMatch, reverse_lazy
|
||||
from django.contrib.auth.views import logout
|
||||
from django.shortcuts import resolve_url
|
||||
from django.test import TestCase, ignore_warnings, override_settings
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.utils import six
|
||||
|
||||
from .models import UnimportantThing
|
||||
|
||||
|
@ -56,6 +57,15 @@ class ResolveUrlTests(TestCase):
|
|||
resolved_url = resolve_url(logout)
|
||||
self.assertEqual('/accounts/logout/', resolved_url)
|
||||
|
||||
def test_lazy_reverse(self):
|
||||
"""
|
||||
Tests that passing the result of reverse_lazy is resolved to a real URL
|
||||
string.
|
||||
"""
|
||||
resolved_url = resolve_url(reverse_lazy('logout'))
|
||||
self.assertIsInstance(resolved_url, six.text_type)
|
||||
self.assertEqual('/accounts/logout/', resolved_url)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_valid_view_name(self):
|
||||
"""
|
||||
|
|
|
@ -2,5 +2,5 @@ from django.conf.urls import url
|
|||
from django.contrib.auth import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^accounts/logout/$', views.logout),
|
||||
url(r'^accounts/logout/$', views.logout, name='logout'),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue