diff --git a/authentik/sources/ldap/sync/users.py b/authentik/sources/ldap/sync/users.py index 1de08b508..42b6a6f60 100644 --- a/authentik/sources/ldap/sync/users.py +++ b/authentik/sources/ldap/sync/users.py @@ -10,6 +10,7 @@ from pytz import UTC from authentik.core.models import User from authentik.events.models import Event, EventAction from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer +from authentik.sources.ldap.sync.vendor.ad import UserAccountControl class UserLDAPSynchronizer(BaseLDAPSynchronizer): @@ -75,4 +76,8 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer): ) ak_user.set_unusable_password() ak_user.save() + if "userAccountControl" in attributes: + uac = UserAccountControl(attributes.get("userAccountControl")) + ak_user.is_active = not uac.ACCOUNTDISABLE + ak_user.save() return user_count diff --git a/authentik/sources/ldap/sync/vendor/__init__.py b/authentik/sources/ldap/sync/vendor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/sources/ldap/sync/vendor/ad.py b/authentik/sources/ldap/sync/vendor/ad.py new file mode 100644 index 000000000..9fceea003 --- /dev/null +++ b/authentik/sources/ldap/sync/vendor/ad.py @@ -0,0 +1,32 @@ +"""Active Directory specific""" +from enum import IntFlag + + +class UserAccountControl(IntFlag): + """UserAccountControl attribute for Active directory users""" + + # https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity + # /useraccountcontrol-manipulate-account-properties + + SCRIPT = 1 + ACCOUNTDISABLE = 2 + HOMEDIR_REQUIRED = 8 + LOCKOUT = 16 + PASSWD_NOTREQD = 32 + PASSWD_CANT_CHANGE = 64 + ENCRYPTED_TEXT_PWD_ALLOWED = 128 + TEMP_DUPLICATE_ACCOUNT = 256 + NORMAL_ACCOUNT = 512 + INTERDOMAIN_TRUST_ACCOUNT = 2048 + WORKSTATION_TRUST_ACCOUNT = 4096 + SERVER_TRUST_ACCOUNT = 8192 + DONT_EXPIRE_PASSWORD = 65536 + MNS_LOGON_ACCOUNT = 131072 + SMARTCARD_REQUIRED = 262144 + TRUSTED_FOR_DELEGATION = 524288 + NOT_DELEGATED = 1048576 + USE_DES_KEY_ONLY = 2097152 + DONT_REQ_PREAUTH = 4194304 + PASSWORD_EXPIRED = 8388608 + TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216 + PARTIAL_SECRETS_ACCOUNT = 67108864 diff --git a/authentik/sources/ldap/tests/mock_ad.py b/authentik/sources/ldap/tests/mock_ad.py index c94519f0f..3418e52af 100644 --- a/authentik/sources/ldap/tests/mock_ad.py +++ b/authentik/sources/ldap/tests/mock_ad.py @@ -54,6 +54,7 @@ def mock_ad_connection(password: str) -> Connection: "objectSid": "user0", "objectClass": "person", "distinguishedName": "cn=user0,ou=users,dc=goauthentik,dc=io", + "userAccountControl": 66050, }, ) # User without SID diff --git a/authentik/sources/ldap/tests/test_sync.py b/authentik/sources/ldap/tests/test_sync.py index 0d158b4db..d833b781f 100644 --- a/authentik/sources/ldap/tests/test_sync.py +++ b/authentik/sources/ldap/tests/test_sync.py @@ -72,7 +72,8 @@ class LDAPSyncTests(TestCase): with patch("authentik.sources.ldap.models.LDAPSource.connection", connection): user_sync = UserLDAPSynchronizer(self.source) user_sync.sync() - self.assertTrue(User.objects.filter(username="user0_sn").exists()) + user = User.objects.filter(username="user0_sn").first() + self.assertFalse(user.is_active) self.assertFalse(User.objects.filter(username="user1_sn").exists()) def test_sync_users_openldap(self):