From ab3531558aff902b760eadf03eb6e6ddb8492aff Mon Sep 17 00:00:00 2001 From: Luke Anderson Date: Fri, 21 Apr 2023 05:19:54 +0930 Subject: [PATCH] Closes #12226: Add Profile Data Headers to Remote Authentication Middleware (#12253) * Closes #12226: Add Profile Data Headers to Remote Authentication Middleware * Tweak documentation --------- Co-authored-by: jeremystretch --- .../administration/authentication/overview.md | 2 ++ docs/configuration/remote-authentication.md | 24 +++++++++++++++++++ netbox/netbox/configuration_example.py | 3 +++ netbox/netbox/middleware.py | 12 +++++++++- netbox/netbox/settings.py | 3 +++ netbox/netbox/tests/test_authentication.py | 23 ++++++++++++++++++ 6 files changed, 66 insertions(+), 1 deletion(-) diff --git a/docs/administration/authentication/overview.md b/docs/administration/authentication/overview.md index fca9eab5e..8a8b8f60b 100644 --- a/docs/administration/authentication/overview.md +++ b/docs/administration/authentication/overview.md @@ -26,6 +26,8 @@ REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend' Another option for remote authentication in NetBox is to enable HTTP header-based user assignment. The front end HTTP server (e.g. nginx or Apache) performs client authentication as a process external to NetBox, and passes information about the authenticated user via HTTP headers. By default, the user is assigned via the `REMOTE_USER` header, but this can be customized via the `REMOTE_AUTH_HEADER` configuration parameter. +Optionally, user profile information can be supplied by `REMOTE_USER_FIRST_NAME`, `REMOTE_USER_LAST_NAME` and `REMOTE_USER_EMAIL` headers. These are saved to the users profile during the authentication process. These headers can be customized like the `REMOTE_USER` header. + ### Single Sign-On (SSO) ```python diff --git a/docs/configuration/remote-authentication.md b/docs/configuration/remote-authentication.md index 1fda8d0d3..fd95adef5 100644 --- a/docs/configuration/remote-authentication.md +++ b/docs/configuration/remote-authentication.md @@ -79,6 +79,30 @@ When remote user authentication is in use, this is the name of the HTTP header w --- +## REMOTE_AUTH_USER_EMAIL + +Default: `'HTTP_REMOTE_USER_EMAIL'` + +When remote user authentication is in use, this is the name of the HTTP header which informs NetBox of the email address of the currently authenticated user. For example, to use the request header `X-Remote-User-Email` it needs to be set to `HTTP_X_REMOTE_USER_EMAIL`. (Requires `REMOTE_AUTH_ENABLED`.) + +--- + +## REMOTE_AUTH_USER_FIRST_NAME + +Default: `'HTTP_REMOTE_USER_FIRST_NAME'` + +When remote user authentication is in use, this is the name of the HTTP header which informs NetBox of the first name of the currently authenticated user. For example, to use the request header `X-Remote-User-First-Name` it needs to be set to `HTTP_X_REMOTE_USER_FIRST_NAME`. (Requires `REMOTE_AUTH_ENABLED`.) + +--- + +## REMOTE_AUTH_USER_LAST_NAME + +Default: `'HTTP_REMOTE_USER_LAST_NAME'` + +When remote user authentication is in use, this is the name of the HTTP header which informs NetBox of the last name of the currently authenticated user. For example, to use the request header `X-Remote-User-Last-Name` it needs to be set to `HTTP_X_REMOTE_USER_LAST_NAME`. (Requires `REMOTE_AUTH_ENABLED`.) + +--- + ## REMOTE_AUTH_SUPERUSER_GROUPS Default: `[]` (Empty list) diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py index 92f6133a3..4878ec520 100644 --- a/netbox/netbox/configuration_example.py +++ b/netbox/netbox/configuration_example.py @@ -193,6 +193,9 @@ PLUGINS = [] REMOTE_AUTH_ENABLED = False REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend' REMOTE_AUTH_HEADER = 'HTTP_REMOTE_USER' +REMOTE_AUTH_USER_FIRST_NAME = 'HTTP_REMOTE_USER_FIRST_NAME' +REMOTE_AUTH_USER_LAST_NAME = 'HTTP_REMOTE_USER_LAST_NAME' +REMOTE_AUTH_USER_EMAIL = 'HTTP_REMOTE_USER_EMAIL' REMOTE_AUTH_AUTO_CREATE_USER = True REMOTE_AUTH_DEFAULT_GROUPS = [] REMOTE_AUTH_DEFAULT_PERMISSIONS = {} diff --git a/netbox/netbox/middleware.py b/netbox/netbox/middleware.py index edf88a234..689a705be 100644 --- a/netbox/netbox/middleware.py +++ b/netbox/netbox/middleware.py @@ -87,7 +87,17 @@ class RemoteUserMiddleware(RemoteUserMiddleware_): else: user = auth.authenticate(request, remote_user=username) if user: - # User is valid. Set request.user and persist user in the session + # User is valid. + # Update the User's Profile if set by request headers + if settings.REMOTE_AUTH_USER_FIRST_NAME in request.META: + user.first_name = request.META[settings.REMOTE_AUTH_USER_FIRST_NAME] + if settings.REMOTE_AUTH_USER_LAST_NAME in request.META: + user.last_name = request.META[settings.REMOTE_AUTH_USER_LAST_NAME] + if settings.REMOTE_AUTH_USER_EMAIL in request.META: + user.email = request.META[settings.REMOTE_AUTH_USER_EMAIL] + user.save() + + # Set request.user and persist user in the session # by logging the user in. request.user = user auth.login(request, user) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 9e9ae5528..f88fc19eb 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -113,6 +113,9 @@ REMOTE_AUTH_DEFAULT_GROUPS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_GROUPS' REMOTE_AUTH_DEFAULT_PERMISSIONS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_PERMISSIONS', {}) REMOTE_AUTH_ENABLED = getattr(configuration, 'REMOTE_AUTH_ENABLED', False) REMOTE_AUTH_HEADER = getattr(configuration, 'REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER') +REMOTE_AUTH_USER_FIRST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_FIRST_NAME', 'HTTP_REMOTE_USER_FIRST_NAME') +REMOTE_AUTH_USER_LAST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_LAST_NAME', 'HTTP_REMOTE_USER_LAST_NAME') +REMOTE_AUTH_USER_EMAIL = getattr(configuration, 'REMOTE_AUTH_USER_EMAIL', 'HTTP_REMOTE_USER_EMAIL') REMOTE_AUTH_GROUP_HEADER = getattr(configuration, 'REMOTE_AUTH_GROUP_HEADER', 'HTTP_REMOTE_USER_GROUP') REMOTE_AUTH_GROUP_SYNC_ENABLED = getattr(configuration, 'REMOTE_AUTH_GROUP_SYNC_ENABLED', False) REMOTE_AUTH_SUPERUSER_GROUPS = getattr(configuration, 'REMOTE_AUTH_SUPERUSER_GROUPS', []) diff --git a/netbox/netbox/tests/test_authentication.py b/netbox/netbox/tests/test_authentication.py index ef4554b4b..790cb4bd8 100644 --- a/netbox/netbox/tests/test_authentication.py +++ b/netbox/netbox/tests/test_authentication.py @@ -151,6 +151,29 @@ class ExternalAuthenticationTestCase(TestCase): self.assertEqual(int(self.client.session.get( '_auth_user_id')), self.user.pk, msg='Authentication failed') + @override_settings( + REMOTE_AUTH_ENABLED=True, + LOGIN_REQUIRED=True + ) + def test_remote_auth_user_profile(self): + """ + Test remote authentication with user profile details. + """ + headers = { + 'HTTP_REMOTE_USER': 'remoteuser1', + 'HTTP_REMOTE_USER_FIRST_NAME': 'John', + 'HTTP_REMOTE_USER_LAST_NAME': 'Smith', + 'HTTP_REMOTE_USER_EMAIL': 'johnsmith@example.com', + } + + response = self.client.get(reverse('home'), follow=True, **headers) + self.assertEqual(response.status_code, 200) + + self.user = User.objects.get(username='remoteuser1') + self.assertEqual(self.user.first_name, "John", msg='User first name was not updated') + self.assertEqual(self.user.last_name, "Smith", msg='User last name was not updated') + self.assertEqual(self.user.email, "johnsmith@example.com", msg='User email was not updated') + @override_settings( REMOTE_AUTH_ENABLED=True, REMOTE_AUTH_AUTO_CREATE_USER=True,