diff --git a/authentik/sources/ldap/sync/vendor/freeipa.py b/authentik/sources/ldap/sync/vendor/freeipa.py index 3a356011ed..47f11774c8 100644 --- a/authentik/sources/ldap/sync/vendor/freeipa.py +++ b/authentik/sources/ldap/sync/vendor/freeipa.py @@ -27,7 +27,7 @@ class FreeIPA(BaseLDAPSynchronizer): """Check krbLastPwdChange""" if "krbLastPwdChange" not in attributes: return - pwd_last_set: datetime = attributes.get("krbLastPwdChange", datetime.now()) + pwd_last_set: datetime = flatten(attributes.get("krbLastPwdChange", datetime.now())) pwd_last_set = pwd_last_set.replace(tzinfo=UTC) if created or pwd_last_set >= user.password_change_date: self._task.info(f"'{user.username}': Reset user's password") diff --git a/authentik/sources/ldap/tests/test_sync.py b/authentik/sources/ldap/tests/test_sync.py index b68addccc3..1fe3208550 100644 --- a/authentik/sources/ldap/tests/test_sync.py +++ b/authentik/sources/ldap/tests/test_sync.py @@ -1,5 +1,6 @@ """LDAP Source tests""" +from datetime import UTC, datetime from unittest.mock import MagicMock, patch from django.db.models import Q @@ -27,6 +28,7 @@ from authentik.sources.ldap.sync.membership import ( MembershipLDAPSynchronizer, ) from authentik.sources.ldap.sync.users import UserLDAPSynchronizer +from authentik.sources.ldap.sync.vendor.freeipa import FreeIPA from authentik.sources.ldap.tasks import ldap_sync, ldap_sync_page from authentik.sources.ldap.tests.mock_ad import mock_ad_connection from authentik.sources.ldap.tests.mock_freeipa import mock_freeipa_connection @@ -446,6 +448,24 @@ class LDAPSyncTests(TestCase): posix_group = Group.objects.filter(name="group-posix").first() self.assertTrue(posix_group.users.filter(name="user-posix").exists()) + def test_sync_users_freeipa_krb_last_pwd_change_list(self): + """Ensure list-valued krbLastPwdChange is handled correctly.""" + connection = MagicMock(return_value=mock_freeipa_connection(LDAP_PASSWORD)) + with patch("authentik.sources.ldap.models.LDAPSource.connection", connection): + user = User.objects.create(username=generate_id()) + user.set_password("test-password") + user.save() + + freeipa_sync = FreeIPA(self.source, Task()) + freeipa_sync.check_pwd_last_set( + attributes={"krbLastPwdChange": [datetime.now(UTC)]}, + user=user, + created=True, + ) + + user.refresh_from_db() + self.assertFalse(user.has_usable_password()) + def test_sync_group_hierarchy_ad(self): """Test group hierarchy sync""" self.source.base_dn = "dc=t,dc=goauthentik,dc=io"