From f2fc104f067221f7621537a2daa40c13239522e3 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Wed, 5 Nov 2025 16:16:21 +0200 Subject: [PATCH] sysaccount: make sure nsaccountlock is always present Commands sysaccount-enable/sysaccount-disable allow to lock/unlock the account. This is exposed via operational attribute `nsAccountLock` which has to be explicitly requested to query the state. However, if account wasn't explicitly disabled before, `nsAccountLock` will not be returned by the LDAP server. Ensure the nsaccountlock attribute is always retrieved, validated, and normalized across sysaccount operations. - Invoke validate_nsaccountlock in create and modify pre-callbacks to enforce valid values - Invoke convert_nsaccountlock in create, modify, list, reset, and policy post-callbacks to normalize the nsaccountlock attribute and default to False in case it is absent. Fixes: https://pagure.io/freeipa/issue/9842 Signed-off-by: Alexander Bokovoy Reviewed-By: Rob Crittenden Reviewed-By: Thomas Woerner --- ipaserver/plugins/sysaccounts.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ipaserver/plugins/sysaccounts.py b/ipaserver/plugins/sysaccounts.py index e2dd820ef..a67d8b2fb 100644 --- a/ipaserver/plugins/sysaccounts.py +++ b/ipaserver/plugins/sysaccounts.py @@ -15,6 +15,7 @@ from .baseldap import ( LDAPSearch, LDAPRetrieve, LDAPQuery) +from .baseuser import validate_nsaccountlock, convert_nsaccountlock from ipalib import _, ngettext from ipalib import constants from ipalib import output @@ -285,6 +286,7 @@ class sysaccount_add(LDAPCreate): error=_('Either --password or --random is required') ) check_userpassword(entry_attrs, **options) + validate_nsaccountlock(entry_attrs) return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): @@ -296,6 +298,7 @@ class sysaccount_add(LDAPCreate): except errors.NotGroupMember: pass self.add_message(SystemAccountUsage(uid=keys[0], dn=dn)) + convert_nsaccountlock(entry_attrs) return dn @@ -353,12 +356,15 @@ class sysaccount_mod(LDAPUpdate): if 'privileged' not in options: self.allow_empty_update = False + validate_nsaccountlock(entry_attrs) + return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) fill_randompassword(entry_attrs, **options) entry_attrs['privileged'] = getattr(context, 'privileged') + convert_nsaccountlock(entry_attrs) return dn @@ -382,6 +388,7 @@ class sysaccount_find(LDAPSearch): self.obj.get_password_attributes(ldap, entry_attrs.dn, entry_attrs) self.obj.handle_reset(self, self, ldap, entry_attrs.dn, entry_attrs, **options) + convert_nsaccountlock(entry_attrs) return truncated @@ -398,6 +405,7 @@ class sysaccount_show(LDAPRetrieve): self.obj.get_password_attributes(ldap, dn, entry_attrs) self.obj.handle_reset(self, self, ldap, dn, entry_attrs, **options) + convert_nsaccountlock(entry_attrs) return dn @@ -413,6 +421,7 @@ class sysaccount_policy(LDAPRetrieve): def post_callback(self, ldap, dn, entry_attrs, *keys, **options): self.obj.handle_reset(self, self, ldap, dn, entry_attrs, **options) + convert_nsaccountlock(entry_attrs) return dn