diff --git a/sssd_test_framework/roles/ad.py b/sssd_test_framework/roles/ad.py index c57b9b6..1306c16 100644 --- a/sssd_test_framework/roles/ad.py +++ b/sssd_test_framework/roles/ad.py @@ -2,6 +2,7 @@ from __future__ import annotations +from datetime import datetime from typing import Any, TypeAlias from pytest_mh.cli import CLIBuilderArgs @@ -777,6 +778,7 @@ def modify( *, uid: int | DeleteAttribute | None = None, gid: int | DeleteAttribute | None = None, + password: str | DeleteAttribute | None = "Secret123", home: str | DeleteAttribute | None = None, gecos: str | DeleteAttribute | None = None, shell: str | DeleteAttribute | None = None, @@ -794,6 +796,8 @@ def modify( :type uid: int | DeleteAttribute | None, optional :param gid: Primary group id, defaults to None :type gid: int | DeleteAttribute | None, optional + :param password: Password (cannot be None), defaults to 'Secret123' + :type password: str, optional :param home: Home directory, defaults to None :type home: str | DeleteAttribute | None, optional :param gecos: GECOS, defaults to None @@ -839,6 +843,64 @@ def modify( self._modify(attrs) + # Password changes require a different command group so special + # handling is needed + if password is not None: + self.reset(str(password)) + + return self + + def reset( + self, + password: str = "Secret123", + ) -> ADUser: + """ + Reset user password + + Parameters that are not set are ignored. + + :param password: Password, defaults to 'Secret123' + :type password: str, optional + :return: Self. + :rtype: ADUser + """ + attrs: CLIBuilderArgs = { + **self._identity, + "Reset": (self.cli.option.SWITCH, True), + "NewPassword": (self.cli.option.PLAIN, f'(ConvertTo-SecureString "{password}" -AsPlainText -force)'), + } + + args = " ".join(self.cli.args(attrs, quote_value=True)) + self.role.host.conn.run( + f""" + Import-Module ActiveDirectory + Set-ADAccountPassword {args} + """ + ) + + return self + + def expire(self, expiration: str = "19700101000000") -> ADUser: + """ + Set user password expiration date and time + + :param expiration: Date and time for user password expiration, defaults to 19700101000000Z + :type expirataion: str, optional + :return: Self. + :rtype: ADUser + """ + expire = datetime.strptime(expiration, "%Y%m%d%H%M%S") + expire_format = expire.strftime("%m/%d/%Y %H:%M:%S") + + attrs: CLIBuilderArgs = {**self._identity, "DateTime": (self.cli.option.VALUE, f"{expire_format}")} + + args = " ".join(self.cli.args(attrs, quote_value=True)) + self.role.host.conn.run( + f""" + Import-Module ActiveDirectory + Set-ADAccountExpiration {args} + """ + ) return self def passkey_add(self, passkey_mapping: str) -> ADUser: diff --git a/sssd_test_framework/roles/generic.py b/sssd_test_framework/roles/generic.py index b1892f6..4f2c21e 100644 --- a/sssd_test_framework/roles/generic.py +++ b/sssd_test_framework/roles/generic.py @@ -368,6 +368,30 @@ def modify( """ pass + @abstractmethod + def reset(self, password: str | None = "Secret123") -> GenericUser: + """ + Reset user password + + :param password: Password, defaults to 'Secret123' + :type password: str, optional + :return: Self. + :rtype: IPAUser + """ + pass + + @abstractmethod + def expire(self, expiration: str | None = "19700101000000") -> GenericUser: + """ + Set user password expiration date and time + + :param expiration: Date and time for user password expiration, defaults to 19700101000000 + :type expirataion: str, optional + :return: Self. + :rtype: IPAUser + """ + pass + @abstractmethod def delete(self) -> None: """ diff --git a/sssd_test_framework/roles/ipa.py b/sssd_test_framework/roles/ipa.py index 47376a8..71f36e9 100644 --- a/sssd_test_framework/roles/ipa.py +++ b/sssd_test_framework/roles/ipa.py @@ -518,6 +518,34 @@ def modify( self._modify(attrs, input=password) return self + def reset(self, password: str | None = "Secret123") -> IPAUser: + """ + Reset user password + + :param password: Password, defaults to 'Secret123' + :type password: str, optional + :return: Self. + :rtype: IPAUser + """ + pwinput = f"{password}\n{password}" + self.role.host.conn.run(f"ipa passwd {self.name}", input=pwinput) + self.expire("20380101120000Z") + + return self + + def expire(self, expiration: str | None = "19700101000000Z") -> IPAUser: + """ + Set user password expiration date and time + + :param expiration: Date and time for user password expiration, defaults to 19700101000000 + :type expirataion: str, optional + :return: Self. + :rtype: IPAUser + """ + self.modify(password_expiration=expiration) + + return self + def passkey_add(self, passkey_mapping: str) -> IPAUser: """ Add passkey mapping to the user. diff --git a/sssd_test_framework/roles/ldap.py b/sssd_test_framework/roles/ldap.py index 017d19e..f983a6e 100644 --- a/sssd_test_framework/roles/ldap.py +++ b/sssd_test_framework/roles/ldap.py @@ -3,6 +3,7 @@ from __future__ import annotations import base64 +from datetime import datetime from enum import Enum from typing import Any, Generic, TypeVar @@ -630,7 +631,9 @@ class LDAPUser(LDAPObject[LDAPHost, LDAP]): LDAP user management. """ - def __init__(self, role: LDAP, name: str, basedn: LDAPObject | str | None = "ou=users", rdn_attr: str | None = "cn") -> None: + def __init__( + self, role: LDAP, name: str, basedn: LDAPObject | str | None = "ou=users", rdn_attr: str | None = "cn" + ) -> None: """ :param role: LDAP role object. :type role: LDAP @@ -763,6 +766,8 @@ def modify( :type uid: int | DeleteAttribute | None, optional :param gid: Primary group id, defaults to None :type gid: int | DeleteAttribute | None, optional + :param password: Password, defaults to 'Secret123' + :type password: str, optional :param home: Home directory, defaults to None :type home: str | DeleteAttribute | None, optional :param gecos: GECOS, defaults to None @@ -808,6 +813,39 @@ def modify( self._set(attrs) return self + def reset(self, password: str | None = "Secret123") -> LDAPUser: + """ + Reset user password + + :param password: Password, defaults to 'Secret123' + :type password: str, optional + :return: Self. + :rtype: LDAPUser + """ + self.modify(password=password) + return self + + def expire(self, expiration: str = "19700101000000") -> LDAPUser: + """ + Set user password expiration date and time + + :param expiration: Date and time for user password expiration, defaults to 19700101000000 + :type expirataion: str, optional + :return: Self. + :rtype: IPAUser + """ + + start = datetime.now() + end = datetime.strptime(expiration, "%Y%m%d%H%M%S") + time_diff = end - start + expires_in = int(time_diff.total_seconds()) + if expires_in < 0: + expires_in = 0 + + self.modify(shadowMax=expires_in) + + return self + def passkey_add(self, passkey_mapping: str) -> LDAPUser: """ Add passkey mapping to the user.