From 3ba18e43f656cfe58c795ae0a1bccb0a79a09399 Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Tue, 6 Nov 2018 15:15:26 +0100 Subject: [PATCH] ldap: don't crash on duplicated users (#27697) Keep roles on the more recently used user, then delete the other ones'. --- src/authentic2/backends/ldap_backend.py | 23 +++++++++++++++++------ tests/test_ldap.py | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/authentic2/backends/ldap_backend.py b/src/authentic2/backends/ldap_backend.py index 78f8c098..31f0161c 100644 --- a/src/authentic2/backends/ldap_backend.py +++ b/src/authentic2/backends/ldap_backend.py @@ -834,12 +834,23 @@ class LDAPBackend(object): external_id = self.build_external_id(eid_tuple, attributes) if not external_id: continue - try: - log.debug('lookup using external_id %r: %r', eid_tuple, external_id) - return LDAPUser.objects.prefetch_related('groups').get( - userexternalid__external_id__iexact=external_id, userexternalid__source=block['realm']) - except User.DoesNotExist: - pass + log.debug('lookup using external_id %r: %r', eid_tuple, external_id) + users = LDAPUser.objects.prefetch_related('groups').filter( + userexternalid__external_id__iexact=external_id, + userexternalid__source=block['realm']).order_by('-last_login') + # ordering of NULLs cannot be done through the ORM + users = sorted(users, reverse=True, key=lambda u: (u.last_login is None, u.last_login)) + if users: + user = users[0] + if len(users) > 1: + log.info('found %d users, collectings roles into the first one and deleting the other ones.', + len(users)) + for other in users[1:]: + for r in other.roles.all(): + user.roles.add(r) + other.delete() + return user + return None def lookup_existing_user(self, username, block, attributes): for lookup_type in block['lookups']: diff --git a/tests/test_ldap.py b/tests/test_ldap.py index f162be9a..2eb36eef 100644 --- a/tests/test_ldap.py +++ b/tests/test_ldap.py @@ -11,6 +11,7 @@ from django.contrib.auth import get_user_model from django.core.exceptions import ImproperlyConfigured from django.core import mail from django.utils.encoding import force_text +from django.utils import timezone from authentic2.a2_rbac.utils import get_default_ou from django_rbac.utils import get_ou_model @@ -416,6 +417,20 @@ def test_get_users(slapd, settings): assert save.call_count == 0 assert bulk_create.call_count == 0 + # create user with the same username, but case-different + save.reset_mock() + bulk_create.reset_mock() + u = ldap_backend.LDAPUser.objects.create(username=UID.capitalize()) + eid = ldap_backend.UserExternalId.objects.create(external_id=UID.capitalize(), + source='ldap', user=u) + # set user login time as if it loggued in + user = ldap_backend.LDAPUser.objects.get(username='%s@ldap'%UID) + user.last_login = timezone.now() + user.save() + assert ldap_backend.LDAPUser.objects.count() == 102 + users = list(ldap_backend.LDAPBackend.get_users()) + assert len(users) == 101 + assert ldap_backend.LDAPUser.objects.filter(username='%s' % UID.capitalize()).count() == 1 @pytest.mark.django_db def test_set_mandatory_roles(slapd, settings): -- 2.19.1