From 379d1b68135be3978e91f39c64274104b4046dd2 Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Fri, 27 Mar 2020 22:59:27 +0000
Subject: [PATCH] Dynamically generate constance configs for sync fields

---
 aleksis/apps/ldap/apps.py           |  5 +++-
 aleksis/apps/ldap/settings.py       | 15 +---------
 aleksis/apps/ldap/util/ldap_sync.py | 46 ++++++++++++++++++++++++-----
 3 files changed, 44 insertions(+), 22 deletions(-)

diff --git a/aleksis/apps/ldap/apps.py b/aleksis/apps/ldap/apps.py
index 88f4909..66bec2e 100644
--- a/aleksis/apps/ldap/apps.py
+++ b/aleksis/apps/ldap/apps.py
@@ -3,7 +3,7 @@ from django.db.models.signals import post_save
 
 from aleksis.core.util.apps import AppConfig
 
-from .util.ldap_sync import ldap_sync_from_user
+from .util.ldap_sync import ldap_sync_from_user, update_constance_config_fields
 
 class LDAPConfig(AppConfig):
     name = "aleksis.apps.ldap"
@@ -11,5 +11,8 @@ class LDAPConfig(AppConfig):
 
     def ready(self) -> None:
         super().ready()
+
+        update_constance_config_fields()
+
         User = get_user_model()
         post_save.connect(ldap_sync_from_user, sender=User)
diff --git a/aleksis/apps/ldap/settings.py b/aleksis/apps/ldap/settings.py
index 47f9cd7..5bc325f 100644
--- a/aleksis/apps/ldap/settings.py
+++ b/aleksis/apps/ldap/settings.py
@@ -1,3 +1,4 @@
+from django.apps import apps
 from django.utils.translation import gettext_lazy as _
 
 CONSTANCE_ADDITIONAL_FIELDS = {
@@ -23,13 +24,6 @@ CONSTANCE_CONFIG = {
         _("LDAP sync matching fields"),
         "matching-fields-select",
     ),
-    "LDAP_SYNC_FIELD_STREET": (None, _("Field for street"), str),
-    "LDAP_SYNC_FIELD_HOUSENUMBER": (None, _("Field for house number"), str),
-    "LDAP_SYNC_FIELD_POSTAL_CODE": (None, _("Field for postal code"), str),
-    "LDAP_SYNC_FIELD_PLACE": (None, _("Field for place"), str),
-    "LDAP_SYNC_FIELD_PHONE_NUMBER": (None, _("Field for phone number"), str),
-    "LDAP_SYNC_FIELD_MOBILE_NUMBER": (None, _("Field for mobile number"), str),
-    "LDAP_SYNC_FIELD_DATE_OF_BIRTH": (None, _("Field for date of birth"), str),
     "ENABLE_LDAP_GROUP_SYNC": (True, _("Enable ldap group sync"), bool),
     "LDAP_GROUP_SYNC_FIELD_SHORT_NAME": ("cn", _("Field for short name of group"), str),
     "LDAP_GROUP_SYNC_FIELD_NAME": ("cn", _("Field for name of group"), str),
@@ -39,13 +33,6 @@ CONSTANCE_CONFIG_FIELDSETS = {
         "ENABLE_LDAP_SYNC",
         "LDAP_SYNC_ON_UPDATE",
         "LDAP_MATCHING_FIELDS",
-        "LDAP_SYNC_FIELD_STREET",
-        "LDAP_SYNC_FIELD_HOUSENUMBER",
-        "LDAP_SYNC_FIELD_POSTAL_CODE",
-        "LDAP_SYNC_FIELD_PLACE",
-        "LDAP_SYNC_FIELD_PHONE_NUMBER",
-        "LDAP_SYNC_FIELD_MOBILE_NUMBER",
-        "LDAP_SYNC_FIELD_DATE_OF_BIRTH",
         "ENABLE_LDAP_GROUP_SYNC",
         "LDAP_GROUP_SYNC_FIELD_SHORT_NAME",
         "LDAP_GROUP_SYNC_FIELD_NAME",
diff --git a/aleksis/apps/ldap/util/ldap_sync.py b/aleksis/apps/ldap/util/ldap_sync.py
index 3873507..be0239e 100644
--- a/aleksis/apps/ldap/util/ldap_sync.py
+++ b/aleksis/apps/ldap/util/ldap_sync.py
@@ -1,9 +1,37 @@
 from django.apps import apps
+from django.conf import settings
 from django.contrib.auth import get_user_model
 
 from constance import config
 
 
+def setting_name_from_field(model, field):
+    """ Generate a constance setting name from a model field """
+
+    return "LDAP_ADDITIONAL_FIELD_%s_%s" % (model._meta.label, field.name)
+
+
+def update_constance_config_fields():
+    """ Auto-generate sync field settings from models """
+
+    Person = apps.get_model("core", "Person")
+    for model in (Person,):
+        # Collect fields that are matchable
+        setting_names = []
+        for field in model._meta.fields:
+            if field.editable and not field.auto_created:
+                setting_name = setting_name_from_field(model, field)
+                setting_desc = field.verbose_name
+
+                settings.CONSTANCE_CONFIG[setting_name] = ("", setting_desc, str)
+                setting_names.append(setting_name)
+
+        # Add separate constance section if settings were generated
+        if setting_names:
+            fieldset_name = "LDAP-Sync: Additional fields for %s" % model._meta.verbose_name
+            settings.CONSTANCE_CONFIG_FIELDSETS[fieldset_name] = setting_names
+
+
 def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **kwargs):
     """ Synchronise Person meta-data and groups from ldap_user on User update. """
 
@@ -28,6 +56,17 @@ def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **
                 return
 
             person.user = instance
+
+            # Synchronise additional fields if enabled
+            for field in Person._meta.fields:
+                if field.editable and not field.auto_created:
+                    setting_name = setting_name_from_field(Person, field)
+
+                    # Try sync if constance setting for this field is non-empty
+                    ldap_field = getattr(config, setting_name, "")
+                    if ldap_field and ldap_field in instance.ldap_user.attrs.data:
+                        setattr(person, field._meta.name, instance.ldap_user.attrs.data[ldap_field])
+
             person.save()
 
         if config.ENABLE_LDAP_GROUP_SYNC:
@@ -51,10 +90,3 @@ def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **
 
             # Sync additional fields if enabled in config.
             ldap_user = instance.ldap_user
-            person.street = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_STREET]
-            person.housenumber = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_HOUSENUMBER]
-            person.postal_code = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_POSTAL_CODE]
-            person.place = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_PLACE]
-            person.phone_number = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_PHONE_NUMBER]
-            person.mobile_number = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_MOBILE_NUMBER]
-            person.date_of_birth = ldap_user.attrs.data[config.LDAP_SYNC_FIELD_DATE_OF_BIRTH]
-- 
GitLab