mirror of https://github.com/snachodog/mybuddy.git
Add forward auth by way of remote user
* Add forward auth related settings * Document forward auth settings * Rearrange code to match preference * Adjust forward auth configuration * Add tests for reverse proxy auth Closes #517 Co-authored-by: Christopher C. Wells <git-2022@chris-wells.net>
This commit is contained in:
parent
2690ab4876
commit
46159850c4
|
@ -1,4 +1,5 @@
|
|||
import time
|
||||
from os import getenv
|
||||
from time import time
|
||||
|
||||
import pytz
|
||||
|
||||
|
@ -6,6 +7,7 @@ from django.conf import settings
|
|||
from django.utils import timezone, translation
|
||||
from django.conf.locale.en import formats as formats_en_us
|
||||
from django.conf.locale.en_GB import formats as formats_en_gb
|
||||
from django.contrib.auth.middleware import RemoteUserMiddleware
|
||||
|
||||
|
||||
def update_en_us_date_formats():
|
||||
|
@ -134,12 +136,20 @@ class RollingSessionMiddleware:
|
|||
session_refresh = request.session.get("session_refresh")
|
||||
if session_refresh:
|
||||
try:
|
||||
delta = int(time.time()) - session_refresh
|
||||
delta = int(time()) - session_refresh
|
||||
except (ValueError, TypeError):
|
||||
delta = settings.ROLLING_SESSION_REFRESH + 1
|
||||
if delta > settings.ROLLING_SESSION_REFRESH:
|
||||
request.session["session_refresh"] = int(time.time())
|
||||
request.session["session_refresh"] = int(time())
|
||||
request.session.set_expiry(settings.SESSION_COOKIE_AGE)
|
||||
else:
|
||||
request.session["session_refresh"] = int(time.time())
|
||||
request.session["session_refresh"] = int(time())
|
||||
return self.get_response(request)
|
||||
|
||||
|
||||
class CustomRemoteUser(RemoteUserMiddleware):
|
||||
"""
|
||||
Middleware used for remote authentication when `REVERSE_PROXY_AUTH` is True.
|
||||
"""
|
||||
|
||||
header = getenv("PROXY_HEADER", "HTTP_REMOTE_USER")
|
||||
|
|
|
@ -143,6 +143,14 @@ LOGIN_URL = "babybuddy:login"
|
|||
|
||||
LOGOUT_REDIRECT_URL = "babybuddy:login"
|
||||
|
||||
REVERSE_PROXY_AUTH = bool(strtobool(os.environ.get("REVERSE_PROXY_AUTH") or "False"))
|
||||
|
||||
# Use remote user middleware when reverse proxy auth is enabled.
|
||||
if REVERSE_PROXY_AUTH:
|
||||
# Must appear AFTER AuthenticationMiddleware.
|
||||
MIDDLEWARE.append("babybuddy.middleware.CustomRemoteUser")
|
||||
AUTHENTICATION_BACKENDS.append("django.contrib.auth.backends.RemoteUserBackend")
|
||||
|
||||
|
||||
# Timezone
|
||||
# https://docs.djangoproject.com/en/4.0/topics/i18n/timezones/
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.core.management import call_command
|
||||
from django.test import Client as HttpClient, TestCase, modify_settings
|
||||
|
||||
|
||||
class ReverseProxyAuthTestCase(TestCase):
|
||||
"""
|
||||
Notes:
|
||||
- A class method cannot be used to establish the HTTP client because of the
|
||||
settings overrides required for these tests.
|
||||
- Overriding the `REVERSE_PROXY_AUTH` environment variable directly is not
|
||||
possible because environments variables are only evaluated once for the run.
|
||||
"""
|
||||
|
||||
def test_remote_user_authentication_disabled(self):
|
||||
call_command("migrate", verbosity=0)
|
||||
c = HttpClient()
|
||||
response = c.get("/welcome/", HTTP_REMOTE_USER="admin", follow=True)
|
||||
self.assertRedirects(response, "/login/?next=/welcome/")
|
||||
|
||||
@modify_settings(
|
||||
MIDDLEWARE={"append": "babybuddy.middleware.CustomRemoteUser"},
|
||||
AUTHENTICATION_BACKENDS={
|
||||
"append": "django.contrib.auth.backends.RemoteUserBackend"
|
||||
},
|
||||
)
|
||||
def test_remote_user_authentication_enabled(self):
|
||||
call_command("migrate", verbosity=0)
|
||||
c = HttpClient()
|
||||
response = c.get("/welcome/", HTTP_REMOTE_USER="admin")
|
||||
self.assertEqual(response.status_code, 200)
|
|
@ -38,6 +38,34 @@ Each entry must contain both the scheme (http, https) and fully-qualified domain
|
|||
- [`ALLOWED_HOSTS`](#allowed_hosts)
|
||||
- [`SECURE_PROXY_SSL_HEADER`](#secure_proxy_ssl_header)
|
||||
|
||||
## `PROXY_HEADER`
|
||||
|
||||
*Default:* `HTTP_REMOTE_USER`
|
||||
|
||||
Sets the header to read the authenticated username from when
|
||||
`REVERSE_PROXY_AUTH` has been enabled.
|
||||
|
||||
**Example value**
|
||||
|
||||
HTTP_X_AUTH_USER
|
||||
|
||||
**See also**
|
||||
|
||||
- [Django's documentation on the `REMOTE_USER` authentication method](https://docs.djangoproject.com/en/4.1/howto/auth-remote-user/)
|
||||
- [`REVERSE_PROXY_AUTH`](#reverse_proxy_auth)
|
||||
|
||||
## `REVERSE_PROXY_AUTH`
|
||||
|
||||
*Default:* `False`
|
||||
|
||||
Enable use of `PROXY_HEADER` to pass the username of an authenticated user.
|
||||
This setting should *only* be used with a properly configured reverse proxy to
|
||||
ensure the headers are not forwarded from sources other than your proxy.
|
||||
|
||||
**See also**
|
||||
|
||||
- [`PROXY_HEADER`](#proxy_header)
|
||||
|
||||
## `SECRET_KEY`
|
||||
|
||||
*Default:* `None`
|
||||
|
|
Loading…
Reference in New Issue