Skip to content
Snippets Groups Projects
Verified Commit 42232a5c authored by Nik | Klampfradler's avatar Nik | Klampfradler Committed by Tom Teichler
Browse files

Implement LDAP mass import with Celery job and management command

Also introduces some loggin for LDAP synchronisation
parent e2fc70d0
No related branches found
No related tags found
1 merge request!4Resolve "Mass import of users"
from django.core.management.base import BaseCommand
from ...tasks import ldap_import
class Command(BaseCommand):
def handle(self, *args, **options):
ldap_import()
from aleksis.core.util.core_helpers import celery_optional
from .util.ldap_sync import mass_ldap_import
@celery_optional
def ldap_import():
mass_ldap_import()
import logging
import re
from django.apps import apps
......@@ -9,6 +10,9 @@ from django.utils.translation import gettext as _
from constance import config
logger = logging.getLogger(__name__)
def setting_name_from_field(model, field):
""" Generate a constance setting name from a model field """
......@@ -82,7 +86,7 @@ def apply_templates(value, patterns, templates, separator="|"):
return value
def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **kwargs):
def ldap_sync_from_user(sender, instance, created, **kwargs):
""" Synchronise Person meta-data and groups from ldap_user on User update. """
# Semaphore to guard recursive saves within this signal
......@@ -97,6 +101,7 @@ def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **
# Check if there is an existing person connected to the user.
if Person.objects.filter(user=instance).exists():
person = instance.person
logger.info("Existing person %s already linked to user %s" % (str(person), instance.username))
else:
# Build filter criteria depending on config
matches = {}
......@@ -110,9 +115,11 @@ def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **
person = Person.objects.get(**matches)
except Person.DoesNotExist:
# Bail out of further processing
logger.info("No matching person for user %s" % instance.username)
return
person.user = instance
logger.info("Matching person %s linked to user %s" % (str(person), instance.username))
# Synchronise additional fields if enabled
for field in syncable_fields(Person):
......@@ -132,6 +139,7 @@ def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **
value = from_ldap(value, field)
setattr(person, field.name, value)
logger.debug("Field %s set to %s for %s" % (field.name, str(value), str(person)))
if config.ENABLE_LDAP_GROUP_SYNC:
# Resolve Group objects from LDAP group objects
......@@ -162,18 +170,56 @@ def ldap_sync_from_user(sender, instance, created, raw, using, update_fields, **
"name": name
}
)
logger.info("%s LDAP group %s for Django group %s" % (
("Created" if created else "Updated"),
ldap_group[1][config.LDAP_GROUP_SYNC_FIELD_NAME][0],
name
))
group_objects.append(group)
# Replace linked groups of logged-in user completely
person.member_of.set(group_objects)
logger.info("Replaced group memberships of %s" % str(person))
try:
person.save()
except Exception:
# Exceptions here are silenced because the synchronisation is optional
except Exception as e:
# Exceptions here are logged only because the synchronisation is optional
# FIXME throw warning to user instead
pass
logger.error("Could not save person %s:\n%s" % (str(person), str(e)))
# Remove semaphore
del instance._skip_signal
def mass_ldap_import():
""" Utility code for mass import from ldap """
from django_auth_ldap.backend import LDAPBackend, _LDAPUser # noqa
# Abuse pre-configured search object to find all users by letting * pass
backend = LDAPBackend()
res = backend.settings.USER_SEARCH.execute(_LDAPUser(backend, "").connection, {"user": "*"}, escape=False)
# Guess LDAP username field from user filter
uid_field = re.search(r"([a-zA-Z]+)=%\(user\)s", backend.settings.USER_SEARCH.filterstr).group(1)
uids = [entry[1][uid_field][0] for entry in res]
for uid in uids:
# Prepare an empty LDAPUser object with the target username
ldap_user = _LDAPUser(backend, username=uid)
# Find out whether the User object would be created, but do not save
_, created = backend.get_or_build_user(uid, backend.ldap_to_django_username(ldap_user))
logger.info("Will %s user %s in Django" % ("create" if created else "update", uid))
# Run creation and/or population, like the auth backend would
ldap_user._get_or_create_user()
user = ldap_user._user
# Trigger sync run like on login signal
ldap_sync_from_user(user.__class__, user, created)
logger.info("Successfully imported user %s" % uid)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment