From e63f374929c684b1cf0b42c00bf37498084eb733 Mon Sep 17 00:00:00 2001 From: Dan Lavu Date: Tue, 3 Dec 2024 17:49:32 -0500 Subject: [PATCH] roles: added password policy utilities to ldap role --- sssd_test_framework/roles/ldap.py | 123 ++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/sssd_test_framework/roles/ldap.py b/sssd_test_framework/roles/ldap.py index c28dacc..b870349 100644 --- a/sssd_test_framework/roles/ldap.py +++ b/sssd_test_framework/roles/ldap.py @@ -31,6 +31,7 @@ "LDAPAutomount", "LDAPAutomountMap", "LDAPAutomountKey", + "LDAPPasswordPolicy", ] @@ -84,6 +85,9 @@ def __init__(self, *args, **kwargs) -> None: self.aci: LDAPACI = LDAPACI(self) """Manage LDAP ACI records.""" + self.password: LDAPPasswordPolicy = LDAPPasswordPolicy(self) + """Manage LDAP password policies.""" + self.automount: LDAPAutomount[LDAPHost, LDAP] = LDAPAutomount[LDAPHost, LDAP](self) """ Manage automount maps and keys. @@ -846,6 +850,19 @@ def expire(self, expiration: str = "19700101000000") -> LDAPUser: return self + @property + def password_change_at_logon(self) -> LDAPUser: + """ + Force user to change password next logon. + + :return: Self. + :rtype: LDAPUser + """ + + self.role.ldap.modify("cn=config", replace={"passwordMustChange": "on"}) + + return self + def passkey_add(self, passkey_mapping: str) -> LDAPUser: """ Add passkey mapping to the user. @@ -1759,3 +1776,109 @@ def __get_info(self, info: str | NFSExport | LDAPAutomountMap | DeleteAttribute return info.name return info + + +class LDAPPasswordPolicy(object): + """ + Password policy management. + """ + + def __init__(self, role: LDAP) -> None: + """ + :param role: LDAP role object. + :type role: LDAP + """ + self.role: LDAP = role + self.ldap: LDAPUtils = self.role.ldap + self.config: str = "cn=config" + + role.aci.add('(targetattr="userpassword")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///self";)') + + def get(self, name: str) -> str: + """ + Return password policy value. + + :param name: Password policy setting name. + :type name: str + :return: Password policy value. + :rtype: str + """ + result = ( + self.ldap.conn.search_s(self.config, ldap.SCOPE_BASE, attrlist=[name]) + .pop()[1] + .get(name)[0] + .decode("utf-8") + ) + return result + + def set(self, policy: dict[str, str]) -> None: + """ + Set password policy value. + + :param policy: Password policy key and values. + :type policy: dict[str, str] + """ + for k, v in policy.items(): + if self.get(k) != v: + self.ldap.modify(self.config, replace={k: v}) + + def complexity(self, enable: bool) -> LDAPPasswordPolicy: + """ + Enable or disable password complexity. + + :param enable: Enable or disable password complexity. + :type enable: bool + :return: LDAPPasswordPolicy object. + :rtype: LDAPPasswordPolicy + """ + if enable: + self.set({"passwordCheckSyntax": "on"}) + return self + + def lockout(self, duration: int, attempts: int) -> LDAPPasswordPolicy: + """ + Set lockout duration and login attempts. + + :param duration: Duration of lockout in seconds, converted to minutes. + :type duration: int + :param attempts: Number of login attempts. + :type attempts: int + :return: LDAPPasswordPolicy object. + :rtype: LDAPPasswordPolicy + """ + self.set( + {"passwordLockout": "on", "passwordMaxFailure": str(attempts), "passwordLockoutDuration": str(duration)} + ) + return self + + def age(self, minimum: int, maximum: int) -> LDAPPasswordPolicy: + """ + Set maximum and minimum password age. + + :param minimum: Minimum password age in seconds, converted to days. + :type minimum: int + :param maximum: Maximum password age in seconds, converted to days. + :type maximum: int + :return: LDAPPasswordPolicy object. + :rtype: LDAPPasswordPolicy + """ + self.set( + { + "passwordExp": "on", + "passwordMaxAge": str(maximum), + "passwordMinAge": str(minimum), + }, + ) + return self + + def requirements(self, length: int) -> LDAPPasswordPolicy: + """ + Set password requirements, like length. + + :param length: Required password character count. + :type length: int + :return: LDAPPasswordPolicy object. + :rtype: LDAPPasswordPolicy + """ + self.set({"passwordCheckSyntax": "on", "passwordMinLength": str(length)}) + return self