Opened 5 weeks ago
Closed 4 weeks ago
#36002 closed Cleanup/optimization (fixed)
RemoteUserMiddleware/PersistentRemoteUserMiddleware docstrings and code comments refer to "headers" rather than request.META keys
Reported by: | Anders Einar Hilden | Owned by: | Anders Einar Hilden |
---|---|---|---|
Component: | contrib.auth | Version: | dev |
Severity: | Normal | Keywords: | RemoteUserMiddleware, PersistentRemoteUserMiddleware |
Cc: | Anders Einar Hilden | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
File
django/django/contrib/auth/middleware.py (https://github.com/django/django/blob/main/django/contrib/auth/middleware.py)
Middleware
RemoteUserMiddleware and PersistentRemoteUserMiddleware
Error
The docstring and comments state that the middlewares by default uses a request header named REMOTE_USER for authentication. This is wrong, as it by default uses the REMOTE_USER environment variable.
Using git blame, all of these texts appear to be from when the respective middlewares were added to Django, 16 and 9 years ago. While it might have been correct 16/9 years ago, it is not correct for the current code base.
While the howto for RemoteUserMiddleware imho could use some work, it appears to use the correct terms. https://docs.djangoproject.com/en/5.1/howto/auth-remote-user/#configuration
From RemoteUserMiddleware docstring (line 94)
If request.user is not authenticated, then this middleware attempts to authenticate the username passed in the ``REMOTE_USER`` request header. If authentication is successful, the user is automatically logged in to persist the user in the session. The header used is configurable and defaults to ``REMOTE_USER``. Subclass this class and change the ``header`` attribute if you need to use a different header.
In the comment starting at line 119, it is referenced to as a header again.
# 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"
From PersistentRemoteUserMiddleware (line 258)
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.
For testing
A view that dumps meta and the user:
from django.http import HttpResponse import pprint def dump_request(request): rs = pprint.pformat(request.META, indent=2) + f"\nrequest.user: {request.user}" return HttpResponse(rs)
Start runserver with a REMOTE_USER environment variable
REMOTE_USER=set-in@env.com ./manage.py runserver
Send a request the the request header REMOTE_USER set. Note that the header is with a dash (-), not underline (_), as runserver and other WSGI servers do not allow headers with underscores, as not to be confused with environment variables)
curl -s -H 'REMOTE-USER: set-in@header.com' http://localhost:8000/dump_request | grep -E 'REMOTE_USER|request.user'
observe that the authenticated user is the email from the environment variable.
'HTTP_REMOTE_USER': 'set-in@header.com', 'REMOTE_USER': 'set-in@env.com', request.user: set-in@env.com
Stop runserver and restart w/o environment variable
./manage.py runserver
observe that the user is not authenticated.
curl -s -H 'REMOTE-USER: set-in@header.com' http://localhost:8006/dump_request | grep -E 'REMOTE_USER|request.user'
'HTTP_REMOTE_USER': 'set-in@header.com', request.user: AnonymousUser
For the adventrus bugreader, subclass RemoteUserMiddleware and make it actually respect a HTTP request header named Remote-User.
from django.contrib.auth.middleware import RemoteUserMiddleware class CustomRemoteUserMiddleware(RemoteUserMiddleware): header = "HTTP_REMOTE_USER"
curl -s -H 'REMOTE-USER: set-in@header.com' http://localhost:8000/dump_request | grep -E 'REMOTE_USER|request.user'
'HTTP_REMOTE_USER': 'set-in@header.com', 'REMOTE_USER': 'set-in@env.com', request.user: set-in@header.com
Change History (13)
comment:1 by , 5 weeks ago
Has patch: | set |
---|
comment:2 by , 5 weeks ago
Was informed a PR was the way to go, so made a PR: https://github.com/django/django/pull/18920
comment:3 by , 5 weeks ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:4 by , 5 weeks ago
Resolution: | → invalid |
---|---|
Status: | assigned → closed |
HttpRequest.META is a dictionary of headers. On WSGI, these are initiated from environment variables.
I think both the docs and the doc strings are correct. The docs refer to how the header is likely to be set (from the environment variable) and the docstring is on what it actually is looking at (the header).
comment:5 by , 5 weeks ago
I do not concurre with that evaluation. The docstring & comments spesifically say "the REMOTE_USER
request header" - But a request header can never be named "REMOTE_USER", if you send a header named "REMOTE-USER", it will be named HTTP_REMOTE_USER in the dict, not REMOTE_USER. Thus the default can only be passed as a direct envirionment variable, it can never come from a header.
comment:6 by , 5 weeks ago
For more context, this issue was raised after a lengthy support session after a user assumed passing a header named REMOTE-USER would work, because that was spesifically what the docstring for the middleware said would work.
comment:7 by , 5 weeks ago
There is a configurable header
attribute and in the docs there is an example configuring it
from django.contrib.auth.middleware import RemoteUserMiddleware class CustomHeaderMiddleware(RemoteUserMiddleware): header = "HTTP_AUTHUSER"
By default, the value is "REMOTE_USER"
. This header can only be set via an environment variable.
I think the comment above the header
also hints at this
# 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"
As there are different ways to set headers, I don't think it's wrong to refer to this as a header 🤔
We might be able to make the docstring clearer, but I'm not convinced that it's clearly wrong
comment:8 by , 5 weeks ago
Patch needs improvement: | set |
---|---|
Resolution: | invalid |
Status: | closed → new |
Summary: | Errors in RemoteUserMiddleware/PersistentRemoteUserMiddleware docstrings and code comments → RemoteUserMiddleware/PersistentRemoteUserMiddleware docstrings and code comments refer to "headers" rather than request.META keys |
Triage Stage: | Unreviewed → Accepted |
Accepting after further investigation
Also looks like some clarification in the HttpRequest.META docs would be beneficial to clarify that these are not exclusively headers and contain things like request Meta variables but that discussion should perhaps happen on the forum to iron things out
comment:9 by , 5 weeks ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
Hi, This is my first time trying to contribute and I would like to work on this ticket to get familiar with. I currently am not familiar with the methods and the ticketing system, So i might be prone to making beginners mistakes in the protocol. Appreciate the understanding
comment:10 by , 5 weeks ago
Welcome devian-321- while i created this ticket, I have also created a PR for resolving it (linked) - i have just not been able to incorporate the comments from the PR discussion yet.
I would reccommend looking at another ticket :)
comment:11 by , 5 weeks ago
Owner: | changed from | to
---|
comment:12 by , 4 weeks ago
Patch needs improvement: | unset |
---|---|
Triage Stage: | Accepted → Ready for checkin |
I have suggested changes in https://github.com/Kagee/django/commit/ba199155ae9b82693b7d2b0f7dfd7e2fb3f036e2. The contributor howtos feel unclear if i should have just pushed this as a PR or waiting until i get some response here.