diff --git a/authentik/sources/ldap/sync/vendor/freeipa.py b/authentik/sources/ldap/sync/vendor/freeipa.py index a56b569f1..2eff854f6 100644 --- a/authentik/sources/ldap/sync/vendor/freeipa.py +++ b/authentik/sources/ldap/sync/vendor/freeipa.py @@ -20,6 +20,7 @@ class FreeIPA(BaseLDAPSynchronizer): def sync(self, attributes: dict[str, Any], user: User, created: bool): self.check_pwd_last_set(attributes, user, created) + self.check_nsaccountlock(attributes, user) def check_pwd_last_set(self, attributes: dict[str, Any], user: User, created: bool): """Check krbLastPwdChange""" @@ -37,3 +38,14 @@ class FreeIPA(BaseLDAPSynchronizer): ) user.set_unusable_password() user.save() + + def check_nsaccountlock(self, attributes: dict[str, Any], user: User): + """https://www.port389.org/docs/389ds/howto/howto-account-inactivation.html""" + # This is more of a 389-ds quirk rather than FreeIPA, but FreeIPA uses + # 389-ds and this will trigger regardless + if "nsaccountlock" not in attributes: + return + is_active = attributes.get("nsaccountlock", False) + if is_active != user.is_active: + user.is_active = is_active + user.save() diff --git a/authentik/sources/ldap/sync/vendor/ms_ad.py b/authentik/sources/ldap/sync/vendor/ms_ad.py index c14e9f944..84b8fc7df 100644 --- a/authentik/sources/ldap/sync/vendor/ms_ad.py +++ b/authentik/sources/ldap/sync/vendor/ms_ad.py @@ -78,5 +78,7 @@ class MicrosoftActiveDirectory(BaseLDAPSynchronizer): # /useraccountcontrol-manipulate-account-properties uac_bit = attributes.get("userAccountControl", 512) uac = UserAccountControl(uac_bit) - user.is_active = UserAccountControl.ACCOUNTDISABLE not in uac - user.save() + is_active = UserAccountControl.ACCOUNTDISABLE not in uac + if is_active != user.is_active: + user.is_active = is_active + user.save()