Skip to content

Commit

Permalink
Closes netbox-community#12226: Add Profile Data Headers to Remote Aut…
Browse files Browse the repository at this point in the history
…hentication Middleware
  • Loading branch information
stuntguy3000 committed Apr 14, 2023
1 parent c1c98f9 commit 05e2d96
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 19 deletions.
2 changes: 2 additions & 0 deletions docs/administration/authentication/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions docs/configuration/remote-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,30 @@ When remote user authentication is in use, this is the name of the HTTP header w

---

## 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_USER_LAST_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_SUPERUSER_GROUPS

Default: `[]` (Empty list)
Expand Down
3 changes: 3 additions & 0 deletions netbox/netbox/configuration_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@
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 = {}
Expand Down
12 changes: 11 additions & 1 deletion netbox/netbox/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,17 @@ def process_request(self, request):
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)
Expand Down
3 changes: 3 additions & 0 deletions netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@
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_FULL_NAME', 'HTTP_REMOTE_USER_FIRST_NAME')
REMOTE_AUTH_USER_LAST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_FULL_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', [])
Expand Down
18 changes: 0 additions & 18 deletions netbox/netbox/tests/test_api.py

This file was deleted.

23 changes: 23 additions & 0 deletions netbox/netbox/tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,29 @@ def test_remote_auth_custom_header(self):
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': '[email protected]',
}

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, "[email protected]", msg='User email was not updated')

@override_settings(
REMOTE_AUTH_ENABLED=True,
REMOTE_AUTH_AUTO_CREATE_USER=True,
Expand Down

0 comments on commit 05e2d96

Please sign in to comment.