From 38716eb822c224da4a6a5b75e0384642f8eee30f Mon Sep 17 00:00:00 2001 From: klonfish Date: Sat, 4 May 2019 00:59:03 +0200 Subject: [PATCH] Add synchronous user sync --- README.md | 5 +++++ bam/apps.py | 3 +++ bam/ldap_sync.py | 11 ++++++++++- bam/signals.py | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 bam/signals.py diff --git a/README.md b/README.md index f59e9f6..5eefad8 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,11 @@ ln -sr bam PROJECT_DIRECTORY/bam } ``` + * `BAM_LDAP_SYNCHRONOUS_SYNC_ENABLED`: If `True`, changes are immediately + propagated to LDAP when they are made through the web frontend. If `False`, + the `sync_users` management command has to be used to perform this task for + all users. (Optional, default: `True`) + ## ToDo ### Essential diff --git a/bam/apps.py b/bam/apps.py index 0c2403a..2cb6556 100644 --- a/bam/apps.py +++ b/bam/apps.py @@ -3,3 +3,6 @@ from django.apps import AppConfig class BamConfig(AppConfig): name = 'bam' + + def ready(self): + import bam.signals diff --git a/bam/ldap_sync.py b/bam/ldap_sync.py index 9671aee..48ab7a6 100644 --- a/bam/ldap_sync.py +++ b/bam/ldap_sync.py @@ -77,7 +77,7 @@ class LDAPUserEntry(): pw_hash = transform_password(self.user.password) attrs[cls.passw_attr] = pw_hash - + return {k: [v.encode('utf8')] for k, v in attrs.items()} def get(self): @@ -150,3 +150,12 @@ class LDAPUserSyncer(): actions.append(action) return actions + + def remove_user(self, user): + actions = [] + for base_dn in self.base_dn_map: + user_entry = LDAPUserEntry(user, self.conn, base_dn) + action = user_entry.delete_if_present() + actions.append(action) + + return actions diff --git a/bam/signals.py b/bam/signals.py new file mode 100644 index 0000000..ea7dd72 --- /dev/null +++ b/bam/signals.py @@ -0,0 +1,38 @@ +from django.dispatch import receiver +from django.db.models.signals import post_save, post_delete, m2m_changed +from django.contrib.auth import get_user_model +from django.conf import settings +from .ldap_sync import make_ldap_conn, LDAPAction, LDAPUserSyncer + +User = get_user_model() + +def synchronous_user_sync(user, remove=False): + sync_enabled = getattr(settings, 'BAM_LDAP_SYNCHRONOUS_SYNC_ENABLED', True) + if sync_enabled: + uri = settings.BAM_LDAP_URI + bind_dn = settings.BAM_LDAP_BIND_DN + secret = settings.BAM_LDAP_SECRET + base_dn_map = settings.BAM_LDAP_BASE_DN_MAP + ldap_conn = make_ldap_conn(uri, bind_dn, secret) + + try: + syncer = LDAPUserSyncer(ldap_conn, base_dn_map) + if remove: + syncer.remove_user(user) + else: + syncer.sync_user(user) + finally: + ldap_conn.unbind_s() + +@receiver(post_save, sender=User) +def handle_user_save(sender, instance, **kwargs): + synchronous_user_sync(instance) + +@receiver(post_delete, sender=User) +def handle_user_delete(sender, instance, **kwargs): + synchronous_user_sync(instance, remove=True) + +@receiver(m2m_changed, sender=User.groups.through) +def handle_user_groups_change(sender, instance, action, **kwargs): + if action in ('post_add', 'post_remove'): + synchronous_user_sync(instance)