mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-06 02:18:26 +02:00
Add support for authentication proxies (#321)
* add support for auth proxies * Improve docs
This commit is contained in:
6
bookmarks/middlewares.py
Normal file
6
bookmarks/middlewares.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.middleware import RemoteUserMiddleware
|
||||
|
||||
|
||||
class CustomRemoteUserMiddleware(RemoteUserMiddleware):
|
||||
header = settings.LD_AUTH_PROXY_USERNAME_HEADER
|
46
bookmarks/tests/test_auth_proxy_support.py
Normal file
46
bookmarks/tests/test_auth_proxy_support.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
from django.test import TestCase, modify_settings
|
||||
from django.urls import reverse
|
||||
from bookmarks.models import User
|
||||
from bookmarks.middlewares import CustomRemoteUserMiddleware
|
||||
|
||||
|
||||
class AuthProxySupportTest(TestCase):
|
||||
# Reproducing configuration from the settings logic here
|
||||
# ideally this test would just override the respective options
|
||||
@modify_settings(
|
||||
MIDDLEWARE={'append': 'bookmarks.middlewares.CustomRemoteUserMiddleware'},
|
||||
AUTHENTICATION_BACKENDS={'prepend': 'django.contrib.auth.backends.RemoteUserBackend'}
|
||||
)
|
||||
def test_auth_proxy_authentication(self):
|
||||
user = User.objects.create_user('auth_proxy_user', 'user@example.com', 'password123')
|
||||
|
||||
headers = {'REMOTE_USER': user.username}
|
||||
response = self.client.get(reverse('bookmarks:index'), **headers)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Reproducing configuration from the settings logic here
|
||||
# ideally this test would just override the respective options
|
||||
@modify_settings(
|
||||
MIDDLEWARE={'append': 'bookmarks.middlewares.CustomRemoteUserMiddleware'},
|
||||
AUTHENTICATION_BACKENDS={'prepend': 'django.contrib.auth.backends.RemoteUserBackend'}
|
||||
)
|
||||
def test_auth_proxy_with_custom_header(self):
|
||||
with patch.object(CustomRemoteUserMiddleware, 'header', new_callable=PropertyMock) as mock:
|
||||
mock.return_value = 'Custom-User'
|
||||
user = User.objects.create_user('auth_proxy_user', 'user@example.com', 'password123')
|
||||
|
||||
headers = {'Custom-User': user.username}
|
||||
response = self.client.get(reverse('bookmarks:index'), **headers)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_auth_proxy_is_disabled_by_default(self):
|
||||
user = User.objects.create_user('auth_proxy_user', 'user@example.com', 'password123')
|
||||
|
||||
headers = {'REMOTE_USER': user.username}
|
||||
response = self.client.get(reverse('bookmarks:index'), **headers, follow=True)
|
||||
|
||||
self.assertRedirects(response, '/login/?next=%2Fbookmarks')
|
@@ -57,4 +57,23 @@ Allows to set a custom port for the UWSGI server running in the container. While
|
||||
Values: `String` | Default = None
|
||||
|
||||
Allows configuring the context path of the website. Useful for setting up Nginx reverse proxy.
|
||||
The context path must end with a slash. For example: `linkding/`
|
||||
The context path must end with a slash. For example: `linkding/`
|
||||
|
||||
### `LD_ENABLE_AUTH_PROXY`
|
||||
|
||||
Values: `True`, `False` | Default = `False`
|
||||
|
||||
Enables support for authentication proxies such as Authelia.
|
||||
This effectively disables credentials-based authentication and instead authenticates users if a specific request header contains a known username.
|
||||
You must make sure that your proxy (nginx, Traefik, Caddy, ...) forwards this header from your auth proxy to linkding. Check the documentation of your auth proxy and your reverse proxy on how to correctly set this up.
|
||||
|
||||
Note that this does not automatically create new users, you still need to create users as described in the README, and users need to have the same username as in the auth proxy.
|
||||
|
||||
Enabling this setting also requires configuring the following options:
|
||||
- `LD_AUTH_PROXY_USERNAME_HEADER` - The name of the request header that the auth proxy passes to the proxied application (linkding in this case), so that the application can identify the user.
|
||||
Check the documentation of your auth proxy to get this information.
|
||||
Note that the request headers are rewritten in linkding: all HTTP headers are prefixed with `HTTP_`, all letters are in uppercase, and dashes are replaced with underscores.
|
||||
For example, for Authelia, which passes the `Remote-User` HTTP header, the `LD_AUTH_PROXY_USERNAME_HEADER` needs to be configured as `HTTP_REMOTE_USER`.
|
||||
- `LD_AUTH_PROXY_LOGOUT_URL` - The URL that linkding should redirect to after a logout.
|
||||
By default, the logout redirects to the login URL, which means the user will be automatically authenticated again.
|
||||
Instead, you might want to configure the logout URL of the auth proxy here.
|
||||
|
@@ -182,3 +182,20 @@ MAX_ATTEMPTS = 5
|
||||
# specced systems like Raspberries. Should be OK as tasks are not time critical.
|
||||
BACKGROUND_TASK_RUN_ASYNC = True
|
||||
BACKGROUND_TASK_ASYNC_THREADS = 2
|
||||
|
||||
# Enable authentication proxy support if configured
|
||||
LD_ENABLE_AUTH_PROXY = os.getenv('LD_ENABLE_AUTH_PROXY', False) in (True, 'True', '1')
|
||||
LD_AUTH_PROXY_USERNAME_HEADER = os.getenv('LD_AUTH_PROXY_USERNAME_HEADER', 'REMOTE_USER')
|
||||
LD_AUTH_PROXY_LOGOUT_URL = os.getenv('LD_AUTH_PROXY_LOGOUT_URL', None)
|
||||
|
||||
if LD_ENABLE_AUTH_PROXY:
|
||||
# Add middleware that automatically authenticates requests that have a known username
|
||||
# in the LD_AUTH_PROXY_USERNAME_HEADER request header
|
||||
MIDDLEWARE.append('bookmarks.middlewares.CustomRemoteUserMiddleware')
|
||||
# Configure auth backend that does not require a password credential
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.RemoteUserBackend',
|
||||
]
|
||||
# Configure logout URL
|
||||
if LD_AUTH_PROXY_LOGOUT_URL:
|
||||
LOGOUT_REDIRECT_URL = LD_AUTH_PROXY_LOGOUT_URL
|
||||
|
@@ -44,12 +44,7 @@ LOGGING = {
|
||||
'level': 'ERROR', # Set to DEBUG to log all SQL calls
|
||||
'handlers': ['console'],
|
||||
},
|
||||
'bookmarks.services.tasks': { # Log task output
|
||||
'level': 'DEBUG',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'bookmarks.services.importer': { # Log importer debug output
|
||||
'bookmarks': { # Log importer debug output
|
||||
'level': 'DEBUG',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
|
Reference in New Issue
Block a user