Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

roles: keycloak role user mgt and kcadm updates #100

Merged
merged 3 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions sssd_test_framework/hosts/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ def __init__(self, *args, **kwargs) -> None:
self.client.setdefault("id_provider", "ldap")
self.client.setdefault("ldap_uri", f"ldap://{self.hostname}")

# Use different default for domain
danlavu marked this conversation as resolved.
Show resolved Hide resolved
if "domain" not in self.config and "dns_discovery_domain" in self.client:
self.domain = self.client["dns_discovery_domain"]

# Use different default for realm
if "realm" not in self.config:
self.realm = self.domain.upper()

@property
def features(self) -> dict[str, bool]:
"""
Expand Down
63 changes: 62 additions & 1 deletion sssd_test_framework/roles/ad.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from datetime import datetime
from typing import Any, TypeAlias

from pytest_mh.cli import CLIBuilderArgs
Expand Down Expand Up @@ -782,10 +783,13 @@ 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,
email: str | DeleteAttribute | None = None,
givenname: str | DeleteAttribute | None = None,
surname: str | DeleteAttribute | None = None,
) -> ADUser:
"""
Modify existing AD user.
Expand All @@ -797,6 +801,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
Expand All @@ -805,6 +811,10 @@ def modify(
:type shell: str | DeleteAttribute | None, optional
:param email: Email address, defaults to None
:type email: str | DeleteAttribute | None, optional
:param givenname: Given name of user, defaults to None
:type givenname: str | DeleteAttribute | None, optional
:param surname: Sur name of user, defaults to None
:type surname: str | DeleteAttribute | None, optional
:return: Self.
:rtype: ADUser
"""
Expand All @@ -816,7 +826,11 @@ def modify(
"loginShell": shell,
}

ad_attrs = {"emailAddress": email}
ad_attrs = {
"emailAddress": email,
"GivenName": givenname,
"SurName": surname,
}
all_attrs = {**unix_attrs, **ad_attrs}

clear = [key for key, value in all_attrs.items() if isinstance(value, DeleteAttribute)]
Expand All @@ -833,6 +847,53 @@ 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.

: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"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"Set-ADAccountExpiration {args}")

return self

def passkey_add(self, passkey_mapping: str) -> ADUser:
Expand Down
24 changes: 24 additions & 0 deletions sssd_test_framework/roles/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,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:
"""
Expand Down
54 changes: 50 additions & 4 deletions sssd_test_framework/roles/ipa.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,19 +455,26 @@ def add(
def modify(
self,
*,
first: str | None = None,
last: str | None = None,
uid: int | None = None,
gid: int | None = None,
password: str | None = None,
home: str | None = None,
gecos: str | None = None,
shell: str | None = None,
user_auth_type: str | list[str] | None = None,
idp: str | None = None,
idp_user_id: str | None = None,
password_expiration: str | None = None,
) -> IPAUser:
"""
Modify existing IPA user.

Parameters that are not set are ignored.

:param first: First name of user.
:type first: str | None, optional
:param last: Last name of user.
:type last: str | None, optional
:param uid: User id, defaults to None
:type uid: int | None, optional
:param gid: Primary group id, defaults to None
Expand All @@ -482,27 +489,66 @@ def modify(
:type shell: str | None, optional
:param user_auth_type: Types of supported user authentication, defaults to None
:type user_auth_type: str | list[str] | None, optional
:param idp: Name of external IdP configured in IPA for user.
:type idp: str | None, optional
:param idp_user_id: User ID used to map IPA user to external IdP user.
:type idp_user_id: str | None, optional
:param password_expiration: Date and time stamp for password expiration.
:type password_expiration: str | None, optional
:return: Self.
:rtype: IPAUser
"""
attrs = {
"first": (self.cli.option.VALUE, first),
"last": (self.cli.option.VALUE, last),
"uid": (self.cli.option.VALUE, uid),
"gidnumber": (self.cli.option.VALUE, gid),
"homedir": (self.cli.option.VALUE, home),
"gecos": (self.cli.option.VALUE, gecos),
"shell": (self.cli.option.VALUE, shell),
"password": (self.cli.option.SWITCH, True) if password is not None else None,
"user-auth-type": (self.cli.option.VALUE, user_auth_type),
"idp": (self.cli.option.VALUE, idp),
"idp-user-id": (self.cli.option.VALUE, idp_user_id),
"password-expiration": (self.cli.option.VALUE, password_expiration),
}

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.

:param passkey_mapping: Passkey mapping generated by ``sssctl passkey-register``
:param passkey_mapping: Passkey mapping generated by ``sssctl passkey-register``.
:type passkey_mapping: str
:return: Self.
:rtype: IPAUser
Expand All @@ -527,7 +573,7 @@ def passkey_add_register(
:type device: str
:param ioctl: Path to local umockdev ioctl file.
:type ioctl: str
:param script: Path to local umockdev script file
:param script: Path to local umockdev script file.
:type script: str
:return: Generated passkey mapping string.
:rtype: str
Expand Down
Loading
Loading