summaryrefslogtreecommitdiff
path: root/django/contrib/auth/middleware.py
diff options
context:
space:
mode:
authorJon Janzen <jon@jonjanzen.com>2024-03-31 12:29:10 -0700
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-10-07 14:19:41 +0200
commit50f89ae850f6b4e35819fe725a08c7e579bfd099 (patch)
tree856a0e954e0be928c55f6070f2ac8b766459b3e7 /django/contrib/auth/middleware.py
parent4cad317ff1f9a79d54c1d5b12f1ccbd260ca009f (diff)
Fixed #35303 -- Implemented async auth backends and utils.
Diffstat (limited to 'django/contrib/auth/middleware.py')
-rw-r--r--django/contrib/auth/middleware.py85
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):
"""