diff options
| author | Jon Janzen <jon@jonjanzen.com> | 2024-03-31 12:29:10 -0700 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2024-10-07 14:19:41 +0200 |
| commit | 50f89ae850f6b4e35819fe725a08c7e579bfd099 (patch) | |
| tree | 856a0e954e0be928c55f6070f2ac8b766459b3e7 /django/contrib/auth/middleware.py | |
| parent | 4cad317ff1f9a79d54c1d5b12f1ccbd260ca009f (diff) | |
Fixed #35303 -- Implemented async auth backends and utils.
Diffstat (limited to 'django/contrib/auth/middleware.py')
| -rw-r--r-- | django/contrib/auth/middleware.py | 85 |
1 files changed, 81 insertions, 4 deletions
diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py index cb409ee778..85f58ec9a5 100644 --- a/django/contrib/auth/middleware.py +++ b/django/contrib/auth/middleware.py @@ -1,6 +1,8 @@ from functools import partial from urllib.parse import urlsplit +from asgiref.sync import iscoroutinefunction, markcoroutinefunction + from django.conf import settings from django.contrib import auth from django.contrib.auth import REDIRECT_FIELD_NAME, load_backend @@ -88,7 +90,7 @@ class LoginRequiredMiddleware(MiddlewareMixin): ) -class RemoteUserMiddleware(MiddlewareMixin): +class RemoteUserMiddleware: """ Middleware for utilizing web-server-provided authentication. @@ -102,13 +104,27 @@ class RemoteUserMiddleware(MiddlewareMixin): different header. """ + sync_capable = True + async_capable = True + + def __init__(self, get_response): + if get_response is None: + raise ValueError("get_response must be provided.") + self.get_response = get_response + self.is_async = iscoroutinefunction(get_response) + if self.is_async: + markcoroutinefunction(self) + super().__init__() + # Name of request header to grab username from. This will be the key as # 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): + def __call__(self, request): + if self.is_async: + return self.__acall__(request) # AuthenticationMiddleware is required so that request.user exists. if not hasattr(request, "user"): raise ImproperlyConfigured( @@ -126,13 +142,13 @@ class RemoteUserMiddleware(MiddlewareMixin): # AnonymousUser by the AuthenticationMiddleware). if self.force_logout_if_no_header and request.user.is_authenticated: self._remove_invalid_user(request) - return + return self.get_response(request) # If the user is already authenticated and that user is the user we are # getting passed in the headers, then the correct user is already # persisted in the session and we don't need to continue. if request.user.is_authenticated: if request.user.get_username() == self.clean_username(username, request): - return + return self.get_response(request) else: # An authenticated user is associated with the request, but # it does not match the authorized user in the header. @@ -146,6 +162,51 @@ class RemoteUserMiddleware(MiddlewareMixin): # by logging the user in. request.user = user auth.login(request, user) + return self.get_response(request) + + async def __acall__(self, request): + # AuthenticationMiddleware is required so that request.user exists. + if not hasattr(request, "user"): + raise ImproperlyConfigured( + "The Django remote user auth middleware requires the" + " authentication middleware to be installed. Edit your" + " MIDDLEWARE setting to insert" + " 'django.contrib.auth.middleware.AuthenticationMiddleware'" + " before the RemoteUserMiddleware class." + ) + try: + username = request.META["HTTP_" + self.header] + except KeyError: + # 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 self.force_logout_if_no_header: + user = await request.auser() + if user.is_authenticated: + await self._aremove_invalid_user(request) + return await self.get_response(request) + user = await request.auser() + # If the user is already authenticated and that user is the user we are + # getting passed in the headers, then the correct user is already + # persisted in the session and we don't need to continue. + if user.is_authenticated: + if user.get_username() == self.clean_username(username, request): + return await self.get_response(request) + else: + # An authenticated user is associated with the request, but + # it does not match the authorized user in the header. + await self._aremove_invalid_user(request) + + # We are seeing this user for the first time in this session, attempt + # to authenticate the user. + user = await auth.aauthenticate(request, remote_user=username) + if user: + # User is valid. Set request.user and persist user in the session + # by logging the user in. + request.user = user + await auth.alogin(request, user) + + return await self.get_response(request) def clean_username(self, username, request): """ @@ -176,6 +237,22 @@ class RemoteUserMiddleware(MiddlewareMixin): if isinstance(stored_backend, RemoteUserBackend): auth.logout(request) + async def _aremove_invalid_user(self, request): + """ + Remove the current authenticated user in the request which is invalid + but only if the user is authenticated via the RemoteUserBackend. + """ + try: + stored_backend = load_backend( + await request.session.aget(auth.BACKEND_SESSION_KEY, "") + ) + except ImportError: + # Backend failed to load. + await auth.alogout(request) + else: + if isinstance(stored_backend, RemoteUserBackend): + await auth.alogout(request) + class PersistentRemoteUserMiddleware(RemoteUserMiddleware): """ |
