Commit be0ad629 authored by Claude Paroz's avatar Claude Paroz
Browse files

Fixed #21911 -- Made admin views redirect to login when needed

Historically, the Django admin used to pass through the request
from an unauthorized access to the login view directly. Now we
are using a proper redirection, which is also preventing
inadvertantly changing data when POSTing login data to an admin
view when user is already authorized.
Thanks Marc Tamlyn and Tim Graham for the reviews.
parent 5848bea9
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from functools import update_wrapper
from django.http import Http404, HttpResponseRedirect
from django.contrib.admin import ModelAdmin, actions
from django.contrib.auth import logout as auth_logout, REDIRECT_FIELD_NAME
from django.contrib.auth.views import redirect_to_login
from django.views.decorators.csrf import csrf_protect
from django.db.models.base import ModelBase
from django.apps import apps
@@ -198,7 +199,10 @@ class AdminSite(object):
                if request.path == reverse('admin:logout', current_app=self.name):
                    index_path = reverse('admin:index', current_app=self.name)
                    return HttpResponseRedirect(index_path)
                return self.login(request)
                return redirect_to_login(
                    request.get_full_path(),
                    reverse('admin:login', current_app=self.name)
                )
            return view(request, *args, **kwargs)
        if not cacheable:
            inner = never_cache(inner)
@@ -329,6 +333,11 @@ class AdminSite(object):
        """
        Displays the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            index_path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(index_path)

        from django.contrib.auth.views import login
        # Since this module gets imported in the application's root package,
        # it cannot import models from other applications at the module level,
+11 −1
Original line number Diff line number Diff line
@@ -830,6 +830,8 @@ a :exc:`~exceptions.ValueError` when encountering them, you will have to
install pytz_. You may be affected by this problem if you use Django's time
zone-related date formats or :mod:`django.contrib.syndication`.

.. _pytz: https://pypi.python.org/pypi/pytz/

``remove()`` and ``clear()`` methods of related managers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@@ -858,7 +860,15 @@ Fixing the issues introduced some backward incompatible changes:
  may not be an issue depending on your database and your data itself.
  See :ref:`this note <nested-queries-performance>` for more details.

.. _pytz: https://pypi.python.org/pypi/pytz/
Admin login redirection strategy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Historically, the Django admin site passed the request from an unauthorized or
unauthenticated user directly to the login view, without HTTP redirection. In
Django 1.7, this behavior changed to conform to a more traditional workflow
where any unauthorized request to an admin page will be redirected (by HTTP
status code 302) to the login page, with the ``next`` parameter set to the
referring path. The user will be redirected there after a successful login.

Miscellaneous
~~~~~~~~~~~~~
+70 −54

File changed.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Diff line number Diff line
@@ -1968,12 +1968,12 @@ class TestEtagWithAdmin(TestCase):
    def test_admin(self):
        with self.settings(USE_ETAGS=False):
            response = self.client.get('/test_admin/admin/')
            self.assertEqual(response.status_code, 200)
            self.assertEqual(response.status_code, 302)
            self.assertFalse(response.has_header('ETag'))

        with self.settings(USE_ETAGS=True):
            response = self.client.get('/test_admin/admin/')
            self.assertEqual(response.status_code, 200)
            self.assertEqual(response.status_code, 302)
            self.assertTrue(response.has_header('ETag'))