Skip to content

Commit

Permalink
Update RemoteUser middleware to use native async logic (needs tests and
Browse files Browse the repository at this point in the history
docs)
  • Loading branch information
bigfootjon committed Mar 31, 2024
1 parent e8bb170 commit 6ace5c4
Showing 1 changed file with 72 additions and 2 deletions.
74 changes: 72 additions & 2 deletions django/contrib/auth/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def process_request(self, request):
request.auser = partial(auser, request)


class RemoteUserMiddleware(MiddlewareMixin):
class RemoteUserMiddleware:
"""
Middleware for utilizing web-server-provided authentication.
Expand All @@ -48,13 +48,22 @@ 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
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):
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, "user"):
raise ImproperlyConfigured(
Expand Down Expand Up @@ -92,6 +101,51 @@ def process_request(self, request):
# 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[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
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
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):
"""
Expand Down Expand Up @@ -122,6 +176,22 @@ def _remove_invalid_user(self, request):
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):
"""
Expand Down

0 comments on commit 6ace5c4

Please sign in to comment.