Created
February 4, 2022 17:57
-
-
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/ )
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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