Skip to content

Instantly share code, notes, and snippets.

@bielfrontera
Created February 4, 2022 17:57
Show Gist options
  • Save bielfrontera/5f3e06dd9bd183dc62545ad8a46daeda to your computer and use it in GitHub Desktop.
Save bielfrontera/5f3e06dd9bd183dc62545ad8a46daeda to your computer and use it in GitHub Desktop.
Custom django authentication backend and middleware to extend the REMOTE_USER authentication of django.contrib.auth (see https://docs.djangoproject.com/en/3.2/howto/auth-remote-user/ )
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.auth.backends import RemoteUserBackend
class RemoteExtendedUserBackend(RemoteUserBackend):
"""
This backend can be used in conjunction with the ``RemoteUserMiddleware``
to handle authentication outside Django and update local user with external information
(name, email and groups).
Extends RemoteUserBackend (it creates the Django user if it does not exist,
as explained here: https://github.com/django/django/blob/main/django/contrib/auth/backends.py#L167),
updating the user with the information received from the remote headers.
Django user is only added to groups that already exist on the database (no groups are created).
A settings variable can be used to exclude some groups when updating the user.
"""
excluded_groups = set()
if hasattr(settings, 'REMOTE_AUTH_BACKEND_EXCLUDED_GROUPS'):
excluded_groups = set(settings.REMOTE_AUTH_BACKEND_EXCLUDED_GROUPS)
header_name = 'HTTP_REMOTE_NAME'
header_groups = 'HTTP_REMOTE_GROUPS'
header_email = 'HTTP_REMOTE_EMAIL'
def authenticate(self, request, remote_user):
user = super().authenticate(request, remote_user)
# original authenticate calls configure_user only
# when user is created. We need to call this method every time
# the user is authenticated in order to update its data.
if user:
self.configure_user(request, user)
return user
def configure_user(self, request, user):
"""
Complete the user from extra request.META information.
"""
if self.header_name in request.META:
user.last_name = request.META[self.header_name]
if self.header_email in request.META:
user.email = request.META[self.header_email]
if self.header_groups in request.META:
self.update_groups(user, request.META[self.header_groups])
user.save()
return user
def update_groups(self, user, remote_groups):
"""
Synchronizes groups the user belongs to with remote information.
Groups (existing django groups or remote groups) on excluded_groups are completely ignored.
No group will be created on the django database.
Disclaimer: this method is strongly inspired by the LDAPBackend from django-auth-ldap.
"""
current_group_names = frozenset(
user.groups.values_list("name", flat=True).iterator()
)
preserved_group_names = current_group_names.intersection(self.excluded_groups)
current_group_names = current_group_names - self.excluded_groups
target_group_names = frozenset(remote_groups.split(','))
target_group_names = target_group_names - self.excluded_groups
if target_group_names != current_group_names:
target_group_names = target_group_names.union(preserved_group_names)
existing_groups = list(
Group.objects.filter(name__in=target_group_names).iterator()
)
user.groups.set(existing_groups)
return
from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
class PersistentHttpRemoteUserMiddleware(PersistentRemoteUserMiddleware):
header = 'HTTP_REMOTE_USER'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment