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

IDview #146

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
280 changes: 280 additions & 0 deletions sssd_test_framework/roles/ipa.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ def test_example(client: Client, ipa: IPA):
"""
return IPASudoRule(self, name)

def idview(self, name:str) -> IPAIdView:
return IPAIdView(self, name)

#def idoverride(self, name:str) -> IPAIdOverride:
# return IPAIdOverride(self, name)

class IPAObject(BaseObject[IPAHost, IPA]):
"""
Expand Down Expand Up @@ -641,6 +646,203 @@ def passkey_remove(self, passkey_mapping: str) -> IPAUser:
self._exec("remove-passkey", [passkey_mapping])
return self

'''
def idoverrideuser(self, name:str) -> IPAUserIDOverride:
user_obj = IPAUser(self.role, name)
print("IN the idoverrideuser")
return IPAUserIDOverride(self.role, user_obj)

#return IPAUserIDOverride(self, name)
'''
def idoverride(self, name: str) -> IDOverride:
return IDOverride(self.role, self)

class IDOverride(IPAUser):
def __init__(self, role:IPA, user:IPAUser) -> None:
super().__init__(role, user.name)
#super().__init__(user.role, user.name)
self.name = user.name

def add(self,
idview_name:str,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use just name instead of idview_name, since it's a different class.

*,
description: str | None = None,
login: str | None = None,
uid: int | None = None,
gid: int | None = None,
gecos: str | None = None,
home_directory: str | None = None,
shell: str | None = None,
sshpubkey: str | None = None,
certificate: str | list[str] | None = None,
) -> IDOverride:

attrs = {
"--desc": (self.cli.option.VALUE, description),
"--login": (self.cli.option.VALUE, login),
"--uid": (self.cli.option.VALUE, uid),
"--gidnumber": (self.cli.option.VALUE, gid),
"--gecos": (self.cli.option.VALUE, gecos),
"--homedirectory": (self.cli.option.VALUE, home_directory),
"--shell": (self.cli.option.VALUE, shell),
"--sshpubkey": (self.cli.option.VALUE, sshpubkey),
"--certificate": (self.cli.option.VALUE, certificate),
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try this.

            if attrs is None:
                args = []
            
            attrs = self.cli.args(
                {
                    "desc": (self.cli.option.VALUE, description),
                    "login": (self.cli.option.VALUE, login),
                   "uid": (self.cli.option.VALUE, uid),
                    "gidnumber": (self.cli.option.VALUE, gid),
                    "gecos": (self.cli.option.VALUE, gecos),
                    "home_directory": (self.cli.option.VALUE, home_directory),
                    "shell": (self.cli.option.VALUE, shell),
                    "sshpubkey": (self.cli.option.VALUE, sshpubkey),
                    "certificate": (self.cli.option.VALUE, certificate),
                }
            )
         
            self.role.host.conn.exec(["ipa", "idoverrideuser-add", idview_name, self.name, *attrs])
            return self

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it did not work.

Command:
    ipa idoverrideuser-add newview1 user-4 desc login uid gidnumber gecos home_directory shell sshpubkey certificate
  CWD:
  Env:
  Output:
  Error output:
    ipa: ERROR: command 'idoverrideuser_add' takes at most 2 arguments

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    def __add(
        self,
        idview_name: str,
        *,
        description: str | None = None,
        login: str | None = None,
        uid: int | None = None,
        gid: int | None = None,
        gecos: str | None = None,
        home_directory: str | None = None,
        shell: str | None = None,
        sshpubkey: str | None = None,
        certificate: str | list[str] | None = None,
    ) -> IDOverride:

        attrs = {
            "desc": (self.cli.option.VALUE, description),
            "login": (self.cli.option.VALUE, login),
            "uid": (self.cli.option.VALUE, uid),
            "gidnumber": (self.cli.option.VALUE, gid),
            "gecos": (self.cli.option.VALUE, gecos),
            "homedirectory": (self.cli.option.VALUE, home_directory),
            "shell": (self.cli.option.VALUE, shell),
            "sshpubkey": (self.cli.option.VALUE, sshpubkey),
            "certificate": (self.cli.option.VALUE, certificate),
        }

        self.role.host.conn.exec(["ipa", "idoverrideuser-add", idview_name, self.name] + list(self.cli.args(attrs)))

        return self

Working now, the clibuilder adds -- to the command, the way you had it, it was doing ----uid. The _modify function, takes the dict and turns it into a list, so we had to do that too.

print(attrs.items())
# Extract only the actual values from (VALUE, actual_value) tuples
cli_args = [f'{k}="{v[1]}"' for k, v in attrs.items() if v is not None and v[1] is not None]

cmd = f'ipa idoverrideuser-add "{idview_name}" "{self.name}" ' + " ".join(cli_args)
print("Executing:", cmd)

self.host.conn.run(cmd)

return self

'''
# Construct command arguments dynamically
cmd = [f"ipa idoverrideuser-add {idview_name} {self.name}"]

if description:
cmd.append(f'--desc="{description}"')
if login:
cmd.append(f'--login="{login}"')
if uid:
cmd.append(f'--uid={uid}')
if gid:
cmd.append(f'--gidnumber={gid}')
if gecos:
cmd.append(f'--gecos="{gecos}"')
if home_directory:
cmd.append(f'--homedir="{home_directory}"')
if shell:
cmd.append(f'--shell="{shell}"')
if sshpubkey:
cmd.append(f'--sshpubkey="{sshpubkey}"')
if certificate:
if isinstance(certificate, list):
for cert in certificate:
cmd.append(f'--certificate="{cert}"')
else:
cmd.append(f'--certificate="{certificate}"')

# Run the corrected command
full_command = " ".join(cmd)
self.host.conn.run(full_command)

return self

'''
'''
class IPAUserIDOverride(IPAUser):
"""
IPA User ID override management.
"""

def __init__(self, role: IPA, user: IPAUser) -> None:
"""
:param role: IPA role object.
:type role: IPA
:param name: Username.
:type name: strc
"""
super().__init__(role, user.name)

self.name = user.name

"""Anchor name"""


def add_id_override(
self,
view_name: str,
*,
description: str | None = None,
login: str | None = None,
uid: int | None = None,
gid: int | None = None,
gecos: str | None = None,
home_directory: str | None = None,
shell: str | None = None,
ssh_public_key: str | None = None,
certificate: str | None = None,
) -> IPAUserIDOverride:
"""
Add an ID override for an IPA user.

:param uid: Override for user ID, defaults to None
:type uid: int | None, optional
:param gid: Override for group ID, defaults to None
:type gid: int | None, optional
:param gecos: Override for GECOS, defaults to None
:type gecos: str | None, optional
:param home: Override for home directory, defaults to None
:type home: str | None, optional
:param shell: Override for login shell, defaults to None
:type shell: str | None, optional
:return: Self.
:rtype: IPAUserIDOverride
"""


self.role.host.conn.run(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use the CLIBuilder to make it consistent with the rest of the code and you do not have to add conditionals.

https://pytest-mh.readthedocs.io/en/latest/articles/tips-and-tricks/building-command-line.html

f"ipa idoverrideuser-add {view_name} {self.name}"
+ (f' --desc="{description}"' if description else "")
+ (f' --login="{login}"' if login else "")
+ (f' --uid="{uid}"' if uid else "")
+ (f' --gidnumber="{gid}"' if gid else "")
+ (f' --gecos="{gecos}"' if gecos else "")
+ (f' --homedir="{home_directory}"' if home_directory else "")
+ (f' --shell="{shell}"' if shell else "")
+ (f' --sshpubkey="{ssh_public_key}"' if ssh_public_key else "")
+ (f' --certificate="{certificate}"' if certificate else "")
)
return self

def modify_id_override(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would rename it to modify and change the execution to host.conn.run or host.conn.exec so you don't have to sync the name of the function.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, after some testing the same function name is overloading the IPAUser.add() function. I know this was my suggestion, and I'm mistaken. We can rename the function to __add() to avoid the overloading.

self,
*,
uid: int | None = None,
gid: int | None = None,
gecos: str | None = None,
home: str | None = None,
shell: str | None = None,
) -> IPAUserIDOverride:
"""
Modify an ID override for an IPA user.

:param uid: Override for user ID, defaults to None
:type uid: int | None, optional
:param gid: Override for group ID, defaults to None
:type gid: int | None, optional
:param gecos: Override for GECOS, defaults to None
:type gecos: str | None, optional
:param home: Override for home directory, defaults to None
:type home: str | None, optional
:param shell: Override for login shell, defaults to None
:type shell: str | None, optional
:return: Self.
:rtype: IPAUserIDOverride
"""
attrs = {
"uid": (self.cli.option.VALUE, uid),
"gidnumber": (self.cli.option.VALUE, gid),
"gecos": (self.cli.option.VALUE, gecos),
"homedir": (self.cli.option.VALUE, home),
"shell": (self.cli.option.VALUE, shell),
}
self._modify(attrs)
return self

def delete_id_override(self) -> None:
"""
Delete the ID override for an IPA user.

:return: None
"""
self._delete()
'''

class IPAGroup(IPAObject):
"""
Expand Down Expand Up @@ -1597,3 +1799,81 @@ def __get_info(self, info: str | NFSExport | IPAAutomountMap | None) -> str | No
return info.name

return info

class IPAIdView(IPAObject):
"""
IPA id view management.
"""

def __init__(self, role:IPA, name:str) -> None:
super().__init__(role, name, command_group="idview")

def add(self,
*,
description: str | None = None,
) -> IPAIdView:
"""
Create new IPA ID view.

:return: Self.
:rtype: IPAIdView
"""
attrs: CLIBuilderArgs = {
"desc": (self.cli.option.VALUE, description),
}

self._add(attrs)
return self


def modify(
self,
*,
description: str | None = None,
rename: str | None = None,
) -> IPAIdView:
"""
Modify existing IPA ID view.

Parameters that are not set are ignored.

:param description: Description, defaults to None
:type description: str | None, optional
:param rename: Name of IPA ID view, defaults to None
:type rename: str | None, optional
:return: Self.
:rtype: IPAIdView
"""
attrs: CLIBuilderArgs = {
"desc": (self.cli.option.VALUE, description),
"rename": (self.cli.option.VALUE, rename),
}

self._modify(attrs)
return self

def apply(self, *, hosts: list[str] | None = None, hostgroups: str | None = None) -> IPAIdView:
"""
Applies ID View to specified hosts or current members of specified
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which id view takes priority, is it the last one applied? I'd put that information in the :description:

Applies id view to host or hostgroup. 

:description: ID View is a key. Any overlapping views will replace the value. 
:param hosts: Host or hosts to apply the ID view to, defaults to None.

hostgroups. If any other ID View is applied to the host, it is overridden.
:param hosts: Hosts to apply the ID View to, defaults to None
:type hosts: list[str] | None
:param hostgroups: Hostgroups to apply the ID View to, defaults to None
:type hostgroups: str | None
:return: IPAIdView object.
:rtype: IPAIdView
"""
if not hosts and not hostgroups:
raise ValueError(
"Either 'hosts' or 'hostgroups' must be provided.")

attrs: CLIBuilderArgs = {}
if hosts:
attrs["hosts"] = (self.cli.option.VALUE, hosts)
if hostgroups:
attrs["hostgroups"] = (self.cli.option.VALUE, hostgroups)

self._exec("apply", self.cli.args(attrs))
return self


Loading