From cc7c16af98e4c9a7146c248c8567aac7a50e0fbb Mon Sep 17 00:00:00 2001 From: Frantisek Holop Date: Thu, 9 Apr 2020 13:04:49 +0200 Subject: [PATCH] Fixed #31443 -- Fixed login redirection in auth mixins when LOGIN_URL is off-site URL. --- django/contrib/auth/mixins.py | 21 ++++++++++++++++++++- tests/auth_tests/test_mixins.py | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/django/contrib/auth/mixins.py b/django/contrib/auth/mixins.py index e83e0ee805..02f9d23f07 100644 --- a/django/contrib/auth/mixins.py +++ b/django/contrib/auth/mixins.py @@ -1,7 +1,10 @@ +from urllib.parse import urlparse + from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.views import redirect_to_login from django.core.exceptions import ImproperlyConfigured, PermissionDenied +from django.shortcuts import resolve_url class AccessMixin: @@ -41,7 +44,23 @@ class AccessMixin: def handle_no_permission(self): if self.raise_exception or self.request.user.is_authenticated: raise PermissionDenied(self.get_permission_denied_message()) - return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name()) + + path = self.request.build_absolute_uri() + resolved_login_url = resolve_url(self.get_login_url()) + # If the login url is the same scheme and net location then use the + # path as the "next" url. + login_scheme, login_netloc = urlparse(resolved_login_url)[:2] + current_scheme, current_netloc = urlparse(path)[:2] + if ( + (not login_scheme or login_scheme == current_scheme) and + (not login_netloc or login_netloc == current_netloc) + ): + path = self.request.get_full_path() + return redirect_to_login( + path, + resolved_login_url, + self.get_redirect_field_name(), + ) class LoginRequiredMixin(AccessMixin): diff --git a/tests/auth_tests/test_mixins.py b/tests/auth_tests/test_mixins.py index ca3a249d75..4f396fc208 100644 --- a/tests/auth_tests/test_mixins.py +++ b/tests/auth_tests/test_mixins.py @@ -94,6 +94,20 @@ class AccessMixinTests(TestCase): self.assertEqual(response.status_code, 302) self.assertEqual(response.url, '/accounts/login/?next=/rand') + def test_access_mixin_permission_denied_remote_login_url(self): + class AView(AlwaysFalseView): + login_url = 'https://www.remote.example.com/login' + + view = AView.as_view() + request = self.factory.get('/rand') + request.user = AnonymousUser() + response = view(request) + self.assertEqual(response.status_code, 302) + self.assertEqual( + response.url, + 'https://www.remote.example.com/login?next=http%3A//testserver/rand', + ) + @mock.patch.object(models.User, 'is_authenticated', False) def test_stacked_mixins_not_logged_in(self): user = models.User.objects.create(username='joe', password='qwerty')