diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index b330c8660..3c24b061a 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -277,13 +277,12 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'utilities.middleware.ExceptionHandlingMiddleware', + 'utilities.middleware.RemoteUserMiddleware', 'utilities.middleware.LoginRequiredMiddleware', 'utilities.middleware.APIVersionMiddleware', 'extras.middleware.ObjectChangeMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware', ] -if REMOTE_AUTH_ENABLED: - MIDDLEWARE.append('utilities.middleware.RemoteUserMiddleware') ROOT_URLCONF = 'netbox.urls' @@ -308,10 +307,9 @@ TEMPLATES = [ # Set up authentication backends AUTHENTICATION_BACKENDS = [ + REMOTE_AUTH_BACKEND, 'utilities.auth_backends.ViewExemptModelBackend', ] -if REMOTE_AUTH_ENABLED: - AUTHENTICATION_BACKENDS.insert(0, REMOTE_AUTH_BACKEND) # Internationalization LANGUAGE_CODE = 'en-us' diff --git a/netbox/netbox/tests/test_authentication.py b/netbox/netbox/tests/test_authentication.py index 7c9f42bff..42cddb082 100644 --- a/netbox/netbox/tests/test_authentication.py +++ b/netbox/netbox/tests/test_authentication.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import Group, Permission, User +from django.conf import settings +from django.contrib.auth.models import Group, User from django.test import Client, TestCase from django.test.utils import override_settings from django.urls import reverse @@ -6,11 +7,17 @@ from django.urls import reverse class ExternalAuthenticationTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create(username='remoteuser1') + + def setUp(self): + self.client = Client() + @override_settings( - REMOTE_AUTH_ENABLED=True, LOGIN_REQUIRED=True ) - def test_remote_auth(self): + def test_remote_auth_disabled(self): """ Test enabling remote authentication with the default configuration. """ @@ -18,12 +25,31 @@ class ExternalAuthenticationTestCase(TestCase): 'HTTP_REMOTE_USER': 'remoteuser1', } - self.client = Client() + self.assertFalse(settings.REMOTE_AUTH_ENABLED) + self.assertEqual(settings.REMOTE_AUTH_HEADER, 'HTTP_REMOTE_USER') + + # Client should not be authenticated + response = self.client.get(reverse('home'), follow=True, **headers) + self.assertNotIn('_auth_user_id', self.client.session) + + @override_settings( + REMOTE_AUTH_ENABLED=True, + LOGIN_REQUIRED=True + ) + def test_remote_auth_enabled(self): + """ + Test enabling remote authentication with the default configuration. + """ + headers = { + 'HTTP_REMOTE_USER': 'remoteuser1', + } + + self.assertTrue(settings.REMOTE_AUTH_ENABLED) + self.assertEqual(settings.REMOTE_AUTH_HEADER, 'HTTP_REMOTE_USER') + response = self.client.get(reverse('home'), follow=True, **headers) self.assertEqual(response.status_code, 200) - - user = User.objects.get(username='remoteuser1') - self.assertEqual(int(self.client.session['_auth_user_id']), user.pk, msg='Authentication failed') + self.assertEqual(int(self.client.session.get('_auth_user_id')), self.user.pk, msg='Authentication failed') @override_settings( REMOTE_AUTH_ENABLED=True, @@ -38,40 +64,40 @@ class ExternalAuthenticationTestCase(TestCase): 'HTTP_FOO': 'remoteuser1', } - self.client = Client() + self.assertTrue(settings.REMOTE_AUTH_ENABLED) + self.assertEqual(settings.REMOTE_AUTH_HEADER, 'HTTP_FOO') + response = self.client.get(reverse('home'), follow=True, **headers) self.assertEqual(response.status_code, 200) - - user = User.objects.get(username='remoteuser1') - self.assertEqual(int(self.client.session['_auth_user_id']), user.pk, msg='Authentication failed') + self.assertEqual(int(self.client.session.get('_auth_user_id')), self.user.pk, msg='Authentication failed') @override_settings( REMOTE_AUTH_ENABLED=True, - REMOTE_AUTH_AUTO_CREATE_USER=False, + REMOTE_AUTH_AUTO_CREATE_USER=True, LOGIN_REQUIRED=True ) - def test_remote_auth_no_auto_create(self): + def test_remote_auth_auto_create(self): """ Test enabling remote authentication with automatic user creation disabled. """ headers = { - 'HTTP_REMOTE_USER': 'remoteuser1', + 'HTTP_REMOTE_USER': 'remoteuser2', } - self.client = Client() + self.assertTrue(settings.REMOTE_AUTH_ENABLED) + self.assertTrue(settings.REMOTE_AUTH_AUTO_CREATE_USER) + self.assertEqual(settings.REMOTE_AUTH_HEADER, 'HTTP_REMOTE_USER') - # First attempt should fail as the user does not exist - self.client.get(reverse('home'), **headers) - self.assertNotIn('_auth_user_id', self.client.session) - - # Create the user locally and try again - user = User.objects.create(username='remoteuser1') response = self.client.get(reverse('home'), follow=True, **headers) self.assertEqual(response.status_code, 200) - self.assertEqual(int(self.client.session['_auth_user_id']), user.pk, msg='Authentication failed') + + # Local user should have been automatically created + new_user = User.objects.get(username='remoteuser2') + self.assertEqual(int(self.client.session.get('_auth_user_id')), new_user.pk, msg='Authentication failed') @override_settings( REMOTE_AUTH_ENABLED=True, + REMOTE_AUTH_AUTO_CREATE_USER=True, REMOTE_AUTH_DEFAULT_GROUPS=['Group 1', 'Group 2'], LOGIN_REQUIRED=True ) @@ -80,9 +106,14 @@ class ExternalAuthenticationTestCase(TestCase): Test enabling remote authentication with the default configuration. """ headers = { - 'HTTP_REMOTE_USER': 'remoteuser1', + 'HTTP_REMOTE_USER': 'remoteuser2', } + self.assertTrue(settings.REMOTE_AUTH_ENABLED) + self.assertTrue(settings.REMOTE_AUTH_AUTO_CREATE_USER) + self.assertEqual(settings.REMOTE_AUTH_HEADER, 'HTTP_REMOTE_USER') + self.assertEqual(settings.REMOTE_AUTH_DEFAULT_GROUPS, ['Group 1', 'Group 2']) + # Create required groups groups = ( Group(name='Group 1'), @@ -91,19 +122,19 @@ class ExternalAuthenticationTestCase(TestCase): ) Group.objects.bulk_create(groups) - self.client = Client() response = self.client.get(reverse('home'), follow=True, **headers) self.assertEqual(response.status_code, 200) - user = User.objects.get(username='remoteuser1') - self.assertEqual(int(self.client.session['_auth_user_id']), user.pk, msg='Authentication failed') + new_user = User.objects.get(username='remoteuser2') + self.assertEqual(int(self.client.session.get('_auth_user_id')), new_user.pk, msg='Authentication failed') self.assertListEqual( [groups[0], groups[1]], - list(user.groups.all()) + list(new_user.groups.all()) ) @override_settings( REMOTE_AUTH_ENABLED=True, + REMOTE_AUTH_AUTO_CREATE_USER=True, REMOTE_AUTH_DEFAULT_PERMISSIONS=['dcim.add_site', 'dcim.change_site'], LOGIN_REQUIRED=True ) @@ -112,13 +143,17 @@ class ExternalAuthenticationTestCase(TestCase): Test enabling remote authentication with the default configuration. """ headers = { - 'HTTP_REMOTE_USER': 'remoteuser1', + 'HTTP_REMOTE_USER': 'remoteuser2', } - self.client = Client() + self.assertTrue(settings.REMOTE_AUTH_ENABLED) + self.assertTrue(settings.REMOTE_AUTH_AUTO_CREATE_USER) + self.assertEqual(settings.REMOTE_AUTH_HEADER, 'HTTP_REMOTE_USER') + self.assertEqual(settings.REMOTE_AUTH_DEFAULT_PERMISSIONS, ['dcim.add_site', 'dcim.change_site']) + response = self.client.get(reverse('home'), follow=True, **headers) self.assertEqual(response.status_code, 200) - user = User.objects.get(username='remoteuser1') - self.assertEqual(int(self.client.session['_auth_user_id']), user.pk, msg='Authentication failed') - self.assertTrue(user.has_perms(['dcim.add_site', 'dcim.change_site'])) + new_user = User.objects.get(username='remoteuser2') + self.assertEqual(int(self.client.session.get('_auth_user_id')), new_user.pk, msg='Authentication failed') + self.assertTrue(new_user.has_perms(['dcim.add_site', 'dcim.change_site'])) diff --git a/netbox/utilities/auth_backends.py b/netbox/utilities/auth_backends.py index 52c3454f1..6e968a241 100644 --- a/netbox/utilities/auth_backends.py +++ b/netbox/utilities/auth_backends.py @@ -37,7 +37,7 @@ class RemoteUserBackend(ViewExemptModelBackend, RemoteUserBackend_): """ @property def create_unknown_user(self): - return bool(settings.REMOTE_AUTH_AUTO_CREATE_USER) + return settings.REMOTE_AUTH_AUTO_CREATE_USER def configure_user(self, request, user): logger = logging.getLogger('netbox.authentication.RemoteUserBackend') diff --git a/netbox/utilities/middleware.py b/netbox/utilities/middleware.py index 8141be53f..d86be752b 100644 --- a/netbox/utilities/middleware.py +++ b/netbox/utilities/middleware.py @@ -42,6 +42,14 @@ class RemoteUserMiddleware(RemoteUserMiddleware_): def header(self): return settings.REMOTE_AUTH_HEADER + def process_request(self, request): + + # Bypass middleware if remote authentication is not enabled + if not settings.REMOTE_AUTH_ENABLED: + return + + return super().process_request(request) + class APIVersionMiddleware(object): """