From d18f600d4e13c3e04cd7225058b0dfaa3a4b2b1c Mon Sep 17 00:00:00 2001 From: Dan Lavu Date: Mon, 2 Dec 2024 16:12:49 -0500 Subject: [PATCH] roles: added password policy utilities to ipa role --- sssd_test_framework/roles/ipa.py | 138 +++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/sssd_test_framework/roles/ipa.py b/sssd_test_framework/roles/ipa.py index 62afe57..92615af 100644 --- a/sssd_test_framework/roles/ipa.py +++ b/sssd_test_framework/roles/ipa.py @@ -26,6 +26,7 @@ "IPAAutomountLocation", "IPAAutomountMap", "IPAAutomountKey", + "IPAPasswordPolicy", ] @@ -197,6 +198,32 @@ def test_example_group(client: Client, ipa: IPA): """ return IPAGroup(self, name) + @property + def password(self, name: str = "ipausers") -> IPAPasswordPolicy: + """ + Domain password policy management. + + .. code-block:: python + :caption: Example usage + + @pytest.mark.topology(KnownTopology.IPA + def test_example(client: Client, ipa: IPA): + # Enable password complexity + ipa.password.complexity(enable=True) + + # Set 3 login attempts and 30 seconds lockout duration + ipa.password.lockout(attempts=3, duration=30) + + # Set password length requirement to 12 characters + ipa.password.requirement(length=12) + + :param name: Name of target password policy group, defaults to 'ipausers'. + :type name: str, defaults to 'ipausers' + :return: New IPAPasswordPolicy object. + :rtype: IPAPasswordPolicy + """ + return IPAPasswordPolicy(self, name) + def netgroup(self, name: str) -> IPANetgroup: """ Get netgroup object. @@ -552,6 +579,17 @@ def expire(self, expiration: str | None = "19700101000000Z") -> IPAUser: return self + @property + def password_change_at_logon(self) -> IPAUser: + """ + Force user to change password next logon. + + :return: Self. + :rtype: IPAUser + """ + self.host.conn.run(f"ipa user-mod {self.name} --setattr=krbPasswordExpiration=20010203203734Z") + return self + def passkey_add(self, passkey_mapping: str) -> IPAUser: """ Add passkey mapping to the user. @@ -1597,3 +1635,103 @@ def __get_info(self, info: str | NFSExport | IPAAutomountMap | None) -> str | No return info.name return info + + +class IPAPasswordPolicy(IPAObject): + """ + Password policy management. + """ + + def __init__(self, role: IPA, name: str): + """ + :param role: IPA host object. + :type role: IPAHost + :param name: Name of target object. + :type name: str + """ + super().__init__(role, name, command_group="pwpolicy") + + def complexity(self, enable: bool) -> IPAPasswordPolicy: + """ + Enable or disable password complexity. + + :param enable: Enable or disable password complexity. + :type enable: bool + :return: IPAPasswordPolicy object. + :rtype: IPAPasswordPolicy + """ + if enable: + attrs: CLIBuilderArgs = { + "dictcheck": (self.cli.option.VALUE, "True"), + "usercheck": (self.cli.option.VALUE, "True"), + "minlength": (self.cli.option.VALUE, 8), + "minclasses": (self.cli.option.VALUE, 5), + "priority": (self.cli.option.VALUE, 1), + } + self._add(attrs) + else: + _attrs: CLIBuilderArgs = { + "dictcheck": (self.cli.option.VALUE, "False"), + "usercheck": (self.cli.option.VALUE, "False"), + "minlength": (self.cli.option.VALUE, 0), + "minclasses": (self.cli.option.VALUE, 0), + "priority": (self.cli.option.VALUE, 1), + } + self._modify(_attrs) + + return self + + def lockout(self, duration: int, attempts: int) -> IPAPasswordPolicy: + """ + Set lockout duration and login attempts. + + :param duration: Duration of lockout in seconds. + :type duration: int + :param attempts: Number of login attempts. + :type attempts: int + :return: IPAPasswordPolicy object. + :rtype: IPAPasswordPolicy + """ + attrs: CLIBuilderArgs = { + "lockouttime": (self.cli.option.VALUE, str(duration)), + "maxfail": (self.cli.option.VALUE, str(attempts)), + } + self._add(attrs) + + return self + + def age(self, minimum: int, maximum: int) -> IPAPasswordPolicy: + """ + 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: IPAPasswordPolicy object. + :rtype: IPAPasswordPolicy + """ + attrs: CLIBuilderArgs = { + "minlife": (self.cli.option.VALUE, str(minimum)), + "maxlife": (self.cli.option.VALUE, str(maximum)), + } + + self._add(attrs) + + return self + + def requirements(self, length: int) -> IPAPasswordPolicy: + """ + Set password requirements, like length. + + :param length: Required password character count. + :type length: int + :return: IPAPasswordPolicy object. + :rtype: IPAPasswordPolicy + """ + attrs: CLIBuilderArgs = { + "minlength": (self.cli.option.VALUE, length), + } + self._add(attrs) + + return self