Source code for reemote.operations.builtin.user

# Copyright (c) 2025 Kim Jarvis TPF Software Services S.A. kim.jarvis@tpfsystems.com 
# This software is licensed under the MIT License. See the LICENSE file for details.
#
from reemote.command import Command


[docs] class User: """ A class to encapsulate the functionality of managing user accounts in Unix-like operating systems. It allows users to create, modify, and remove user accounts with various attributes and options. Attributes: name (str): Name of the user to create, remove or modify. state (str): Whether the account should exist or not ('present' or 'absent'). uid (int): Optionally sets the UID of the user. group (str): Optionally sets the user's primary group. groups (list): A list of supplementary groups which the user is also a member of. append (bool): If true, add the user to the groups specified in groups. comment (str): Optionally sets the description (aka GECOS) of user account. home (str): Optionally set the user's home directory. shell (str): Optionally set the user's shell. password (str): If provided, set the user's password. create_home (bool): Whether to create a home directory for the user. move_home (bool): If set to true when used with home, attempt to move the user's old home directory. system (bool): When creating an account, setting this to true makes the user a system account. force (bool): Forces removal of the user and associated directories. remove (bool): Attempts to remove directories associated with the user. generate_ssh_key (bool): Whether to generate a SSH key for the user. ssh_key_bits (int): Optionally specify number of bits in SSH key to create. ssh_key_file (str): Optionally specify the SSH key filename. ssh_key_comment (str): Optionally define the comment for the SSH key. ssh_key_passphrase (str): Set a passphrase for the SSH key. ssh_key_type (str): Optionally specify the type of SSH key to generate. update_password (str): Controls when to update passwords ('always' or 'on_create'). expires (float): An expiry time for the user in epoch. password_lock (bool): Lock the password. password_expire_max (int): Maximum number of days between password change. password_expire_min (int): Minimum number of days between password change. password_expire_warn (int): Number of days of warning before password expires. password_expire_account_disable (int): Number of days after a password expires until the account is disabled. local (bool): Forces the use of "local" command alternatives. skeleton (str): Optionally set a home skeleton directory. umask (str): Sets the umask of the user. uid_min (int): Sets the UID_MIN value for user creation. uid_max (int): Sets the UID_MAX value for user creation. seuser (str): Optionally sets the seuser type on SELinux enabled systems. login_class (str): Optionally sets the user's login class. authorization (str): Sets the authorization of the user. role (str): Sets the role of the user. profile (str): Sets the profile of the user. hidden (bool): Optionally hide the user from the login window and system preferences (macOS only). **Examples:** .. code:: python # Add the user 'johnd' with a specific uid and a primary group of 'admin' r = yield User(name="johnd", comment="John Doe", uid=1040, group="admin") # Create a user 'johnd' with a home directory r = yield User(name="johnd", create_home=True) # Add the user 'james' with a bash shell, appending the group 'admins' and 'developers' r = yield User(name="james", shell="/bin/bash", groups=["admins", "developers"], append=True) # Remove the user 'johnd' r = yield User(name="johnd", state="absent", remove=True) # Create a 2048-bit SSH key for user jsmith r = yield User(name="jsmith", generate_ssh_key=True, ssh_key_bits=2048, ssh_key_file=".ssh/id_rsa") Usage: This class is designed to be used in a generator-based workflow where commands are yielded for execution. Notes: - Commands are constructed based on the provided parameters and the target operating system. - The implementation uses underlying system tools like useradd, usermod, userdel, pw, dscl depending on the platform. """ def __init__(self, name: str, state: str = "present", uid: int = None, group: str = None, groups: list = None, append: bool = False, comment: str = None, home: str = None, shell: str = None, password: str = None, create_home: bool = True, move_home: bool = False, system: bool = False, force: bool = False, remove: bool = False, generate_ssh_key: bool = False, ssh_key_bits: int = None, ssh_key_file: str = None, ssh_key_comment: str = None, ssh_key_passphrase: str = None, ssh_key_type: str = "rsa", update_password: str = "always", expires: float = None, password_lock: bool = None, password_expire_max: int = None, password_expire_min: int = None, password_expire_warn: int = None, password_expire_account_disable: int = None, local: bool = False, skeleton: str = None, umask: str = None, uid_min: int = None, uid_max: int = None, seuser: str = None, login_class: str = None, authorization: str = None, role: str = None, profile: str = None, hidden: bool = None): self.name = name self.state = state self.uid = uid self.group = group self.groups = groups or [] self.append = append self.comment = comment self.home = home self.shell = shell self.password = password self.create_home = create_home self.move_home = move_home self.system = system self.force = force self.remove = remove self.generate_ssh_key = generate_ssh_key self.ssh_key_bits = ssh_key_bits self.ssh_key_file = ssh_key_file self.ssh_key_comment = ssh_key_comment self.ssh_key_passphrase = ssh_key_passphrase self.ssh_key_type = ssh_key_type self.update_password = update_password self.expires = expires self.password_lock = password_lock self.password_expire_max = password_expire_max self.password_expire_min = password_expire_min self.password_expire_warn = password_expire_warn self.password_expire_account_disable = password_expire_account_disable self.local = local self.skeleton = skeleton self.umask = umask self.uid_min = uid_min self.uid_max = uid_max self.seuser = seuser self.login_class = login_class self.authorization = authorization self.role = role self.profile = profile self.hidden = hidden def __repr__(self): return (f"User(name={self.name!r}, " f"state={self.state!r}, " f"uid={self.uid!r}, " f"group={self.group!r}, " f"groups={self.groups!r}, " f"append={self.append!r}, " f"comment={self.comment!r}, " f"home={self.home!r}, " f"shell={self.shell!r}, " f"password={'***' if self.password else None!r}, " f"create_home={self.create_home!r}, " f"move_home={self.move_home!r}, " f"system={self.system!r}, " f"force={self.force!r}, " f"remove={self.remove!r}, " f"generate_ssh_key={self.generate_ssh_key!r}, " f"ssh_key_bits={self.ssh_key_bits!r}, " f"ssh_key_file={self.ssh_key_file!r}, " f"ssh_key_comment={self.ssh_key_comment!r}, " f"ssh_key_passphrase={'***' if self.ssh_key_passphrase else None!r}, " f"ssh_key_type={self.ssh_key_type!r}, " f"update_password={self.update_password!r}, " f"expires={self.expires!r}, " f"password_lock={self.password_lock!r}, " f"password_expire_max={self.password_expire_max!r}, " f"password_expire_min={self.password_expire_min!r}, " f"password_expire_warn={self.password_expire_warn!r}, " f"password_expire_account_disable={self.password_expire_account_disable!r}, " f"local={self.local!r}, " f"skeleton={self.skeleton!r}, " f"umask={self.umask!r}, " f"uid_min={self.uid_min!r}, " f"uid_max={self.uid_max!r}, " f"seuser={self.seuser!r}, " f"login_class={self.login_class!r}, " f"authorization={self.authorization!r}, " f"role={self.role!r}, " f"profile={self.profile!r}, " f"hidden={self.hidden!r})") def execute(self): # Build the command based on the parameters if self.state == "absent": cmd = f"userdel" if self.remove: cmd += " --remove" if self.force: cmd += " --force" cmd += f" {self.name}" else: cmd = f"useradd" if self.state == "present" else f"usermod" # Add common options if self.uid is not None: cmd += f" --uid {self.uid}" if self.group: cmd += f" --gid {self.group}" if self.groups: groups_str = ",".join(self.groups) if self.append: cmd += f" --append --groups {groups_str}" else: cmd += f" --groups {groups_str}" if self.comment: cmd += f" --comment '{self.comment}'" if self.home: cmd += f" --home {self.home}" if self.shell: cmd += f" --shell {self.shell}" if self.password and self.update_password == "always": cmd += f" --password '{self.password}'" if self.create_home: cmd += " --create-home" if self.move_home: cmd += " --move-home" if self.system: cmd += " --system" if self.expires is not None: cmd += f" --expiredate {self.expires}" if self.password_lock is not None: if self.password_lock: cmd += " --lock" else: cmd += " --unlock" # Add user name cmd += f" {self.name}" # Handle SSH key generation if self.generate_ssh_key: ssh_cmd = "ssh-keygen" if self.ssh_key_bits: ssh_cmd += f" -b {self.ssh_key_bits}" if self.ssh_key_file: ssh_cmd += f" -f {self.ssh_key_file}" if self.ssh_key_comment: ssh_cmd += f" -C '{self.ssh_key_comment}'" if self.ssh_key_passphrase: ssh_cmd += f" -P '{self.ssh_key_passphrase}'" else: ssh_cmd += " -P ''" if self.ssh_key_type: ssh_cmd += f" -t {self.ssh_key_type}" # Execute SSH key generation after user creation yield Command(ssh_cmd) # print(f"{self}") r = yield Command(cmd) r.changed = True