diff options
| author | Jan Pazdziora <jpazdziora@redhat.com> | 2015-06-26 20:59:57 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-07-02 17:38:10 -0400 |
| commit | a570701e02e0bc09d977c8ae0b6ee987a1190039 (patch) | |
| tree | fd90e7ae329e79ea7d194a0d906d109e8a82bc4b | |
| parent | c6cce4de38662d49d5eaaf197b62937f6ce25be7 (diff) | |
Fixed #25029 -- Added PersistentRemoteUserMiddleware for login-page-only external authentication.
| -rw-r--r-- | AUTHORS | 1 | ||||
| -rw-r--r-- | django/contrib/auth/middleware.py | 16 | ||||
| -rw-r--r-- | docs/howto/auth-remote-user.txt | 25 | ||||
| -rw-r--r-- | docs/ref/middleware.txt | 8 | ||||
| -rw-r--r-- | docs/releases/1.9.txt | 4 | ||||
| -rw-r--r-- | tests/auth_tests/test_remote_user.py | 23 |
6 files changed, 75 insertions, 2 deletions
@@ -308,6 +308,7 @@ answer newbie questions, and generally made Django that much better: James Wheare <django@sparemint.com> Jannis Leidel <jannis@leidel.info> Janos Guljas + Jan Pazdziora Jan Rademaker Jarek Zgoda <jarek.zgoda@gmail.com> Jason Davies (Esaj) <http://www.jasondavies.com/> diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py index 02c7b26298..e13eb5374b 100644 --- a/django/contrib/auth/middleware.py +++ b/django/contrib/auth/middleware.py @@ -53,6 +53,7 @@ class RemoteUserMiddleware(object): # used in the request.META dictionary, i.e. the normalization of headers to # all uppercase and the addition of "HTTP_" prefix apply. header = "REMOTE_USER" + force_logout_if_no_header = True def process_request(self, request): # AuthenticationMiddleware is required so that request.user exists. @@ -69,7 +70,7 @@ class RemoteUserMiddleware(object): # If specified header doesn't exist then remove any existing # authenticated remote-user, or return (leaving request.user set to # AnonymousUser by the AuthenticationMiddleware). - if request.user.is_authenticated(): + if self.force_logout_if_no_header and request.user.is_authenticated(): self._remove_invalid_user(request) return # If the user is already authenticated and that user is the user we are @@ -118,3 +119,16 @@ class RemoteUserMiddleware(object): else: if isinstance(stored_backend, RemoteUserBackend): auth.logout(request) + + +class PersistentRemoteUserMiddleware(RemoteUserMiddleware): + """ + Middleware for Web-server provided authentication on logon pages. + + Like RemoteUserMiddleware but keeps the user authenticated even if + the header (``REMOTE_USER``) is not found in the request. Useful + for setups when the external authentication via ``REMOTE_USER`` + is only expected to happen on some "logon" URL and the rest of + the application wants to use Django's authentication mechanism. + """ + force_logout_if_no_header = False diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt index 39454c5f63..bef562d565 100644 --- a/docs/howto/auth-remote-user.txt +++ b/docs/howto/auth-remote-user.txt @@ -19,7 +19,8 @@ When the Web server takes care of authentication it typically sets the ``REMOTE_USER`` environment variable for use in the underlying application. In Django, ``REMOTE_USER`` is made available in the :attr:`request.META <django.http.HttpRequest.META>` attribute. Django can be configured to make -use of the ``REMOTE_USER`` value using the ``RemoteUserMiddleware`` and +use of the ``REMOTE_USER`` value using the ``RemoteUserMiddleware`` +or ``PersistentRemoteUserMiddleware``, and :class:`~django.contrib.auth.backends.RemoteUserBackend` classes found in :mod:`django.contrib.auth`. @@ -95,3 +96,25 @@ If your authentication mechanism uses a custom HTTP header and not If you need more control, you can create your own authentication backend that inherits from :class:`~django.contrib.auth.backends.RemoteUserBackend` and override one or more of its attributes and methods. + +.. _persistent-remote-user-middleware-howto: + +Using ``REMOTE_USER`` on login pages only +========================================= + +.. versionadded:: 1.9 + +The ``RemoteUserMiddleware`` authentication middleware assumes that the HTTP +request header ``REMOTE_USER`` is present with all authenticated requests. That +might be expected and practical when Basic HTTP Auth with ``htpasswd`` or other +simple mechanisms are used, but with Negotiate (GSSAPI/Kerberos) or other +resource intensive authentication methods, the authentication in the front-end +HTTP server is usually only set up for one or a few login URLs, and after +successful authentication, the application is supposed to maintain the +authenticated session itself. + +:class:`~django.contrib.auth.middleware.PersistentRemoteUserMiddleware` +provides support for this use case. It will maintain the authenticated session +until explicit logout by the user. The class can be used as a drop-in +replacement of :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +in the documentation above. diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index d256347577..733e701119 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -374,6 +374,14 @@ every incoming ``HttpRequest`` object. See :ref:`Authentication in Web requests Middleware for utilizing Web server provided authentication. See :doc:`/howto/auth-remote-user` for usage details. +.. class:: PersistentRemoteUserMiddleware + +.. versionadded:: 1.9 + +Middleware for utilizing Web server provided authentication when enabled only +on the login page. See :ref:`persistent-remote-user-middleware-howto` for usage +details. + .. class:: SessionAuthenticationMiddleware Allows a user's sessions to be invalidated when their password changes. See diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 3dc76dbd12..0b84b3c6d3 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -172,6 +172,10 @@ Minor features :func:`~django.contrib.auth.decorators.permission_required()` accepts all kinds of iterables, not only list and tuples. +* The new :class:`~django.contrib.auth.middleware.PersistentRemoteUserMiddleware` + makes it possible to use ``REMOTE_USER`` for setups where the header is only + populated on login pages instead of every request in the session. + :mod:`django.contrib.gis` ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/auth_tests/test_remote_user.py b/tests/auth_tests/test_remote_user.py index d0f3f2283f..a413b97ee4 100644 --- a/tests/auth_tests/test_remote_user.py +++ b/tests/auth_tests/test_remote_user.py @@ -232,3 +232,26 @@ class CustomHeaderRemoteUserTest(RemoteUserTest): 'auth_tests.test_remote_user.CustomHeaderMiddleware' ) header = 'HTTP_AUTHUSER' + + +class PersistentRemoteUserTest(RemoteUserTest): + """ + PersistentRemoteUserMiddleware keeps the user logged in even if the + subsequent calls do not contain the header value. + """ + middleware = 'django.contrib.auth.middleware.PersistentRemoteUserMiddleware' + require_header = False + + def test_header_disappears(self): + """ + A logged in user is kept logged in even if the REMOTE_USER header + disappears during the same browser session. + """ + User.objects.create(username='knownuser') + # Known user authenticates + response = self.client.get('/remote_user/', **{self.header: self.known_user}) + self.assertEqual(response.context['user'].username, 'knownuser') + # Should stay logged in if the REMOTE_USER header disappears. + response = self.client.get('/remote_user/') + self.assertEqual(response.context['user'].is_anonymous(), False) + self.assertEqual(response.context['user'].username, 'knownuser') |
