PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/win_useradd.py |
""" Module for managing Windows Users. .. important:: If you feel that Salt should be using this module to manage users on a minion, and it is using a different module (or gives an error similar to *'user.info' is not available*), see :ref:`here <module-provider-override>`. :depends: - pywintypes - win32api - win32con - win32net - win32netcon - win32profile - win32security - win32ts - wmi .. note:: This currently only works with local user accounts, not domain accounts """ import ctypes import logging import shlex import time from datetime import datetime import salt.utils.args import salt.utils.dateutils import salt.utils.platform import salt.utils.versions import salt.utils.win_reg import salt.utils.winapi from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) try: import pywintypes import win32api import win32con import win32net import win32netcon import win32profile import win32security import win32ts import wmi HAS_WIN32NET_MODS = True except ImportError: HAS_WIN32NET_MODS = False # Define the module's virtual name __virtualname__ = "user" def __virtual__(): """ Requires Windows and Windows Modules """ if not salt.utils.platform.is_windows(): return False, "Module win_useradd: Windows Only" if not HAS_WIN32NET_MODS: return False, "Module win_useradd: Missing Win32 Modules" return __virtualname__ def add( name, password=None, fullname=None, description=None, groups=None, home=None, homedrive=None, profile=None, logonscript=None, ): """ Add a user to the minion. Args: name (str): The username for the new account password (str, optional): User's password in plain text. fullname (str, optional): The user's full name. description (str, optional): A brief description of the user account. groups (str, optional): A list of groups to add the user to. (see chgroups) home (str, optional): The path to the user's home directory. homedrive (str, optional): The drive letter to assign to the home directory. Must be the Drive Letter followed by a colon. ie: U: profile (str, optional): An explicit path to a profile. Can be a UNC or a folder on the system. If left blank, windows uses its default profile directory. logonscript (str, optional): Path to a login script to run when the user logs on. Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.add name password """ user_info = {} if name: user_info["name"] = str(name) else: return False if password: user_info["password"] = str(password) else: user_info["password"] = None user_info["priv"] = win32netcon.USER_PRIV_USER user_info["home_dir"] = home user_info["comment"] = description user_info["flags"] = win32netcon.UF_SCRIPT user_info["script_path"] = logonscript try: win32net.NetUserAdd(None, 1, user_info) except win32net.error as exc: log.error("Failed to create user %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror update(name=name, homedrive=homedrive, profile=profile, fullname=fullname) ret = chgroups(name, groups) if groups else True return ret def update( name, password=None, fullname=None, description=None, home=None, homedrive=None, logonscript=None, profile=None, expiration_date=None, expired=None, account_disabled=None, unlock_account=None, password_never_expires=None, disallow_change_password=None, ): # pylint: disable=anomalous-backslash-in-string """ Updates settings for the Windows user. Name is the only required parameter. Settings will only be changed if the parameter is passed a value. .. versionadded:: 2015.8.0 Args: name (str): The username to update. password (str, optional): New user password in plain text. fullname (str, optional): The user's full name. description (str, optional): A brief description of the user account. home (str, optional): The path to the user's home directory. homedrive (str, optional): The drive letter to assign to the home directory. Must be the Drive Letter followed by a colon. ie: U: logonscript (str, optional): The path to the logon script. profile (str, optional): The path to the user's profile directory. expiration_date (date, optional): The date and time when the account expires. Can be a valid date/time string. To set to never expire pass the string 'Never'. expired (bool, optional): Pass `True` to expire the account. The user will be prompted to change their password at the next logon. Pass `False` to mark the account as 'not expired'. You can't use this to negate the expiration if the expiration was caused by the account expiring. You'll have to change the `expiration_date` as well. account_disabled (bool, optional): True disables the account. False enables the account. unlock_account (bool, optional): True unlocks a locked user account. False is ignored. password_never_expires (bool, optional): True sets the password to never expire. False allows the password to expire. disallow_change_password (bool, optional): True blocks the user from changing the password. False allows the user to change the password. Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.update bob password=secret profile=C:\\Users\\Bob home=\\server\\homeshare\\bob homedrive=U: """ # pylint: enable=anomalous-backslash-in-string # Make sure the user exists # Return an object containing current settings for the user try: user_info = win32net.NetUserGetInfo(None, str(name), 4) except win32net.error as exc: log.error("Failed to update user %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror # Check parameters to update # Update the user object with new settings if password: user_info["password"] = str(password) else: user_info["password"] = None if home: user_info["home_dir"] = home if homedrive: user_info["home_dir_drive"] = homedrive if description: user_info["comment"] = description if logonscript: user_info["script_path"] = logonscript if fullname: user_info["full_name"] = fullname if profile: user_info["profile"] = profile if expiration_date: if expiration_date == "Never": user_info["acct_expires"] = win32netcon.TIMEQ_FOREVER else: try: dt_obj = salt.utils.dateutils.date_cast(expiration_date) except (ValueError, RuntimeError): return f"Invalid Date/Time Format: {expiration_date}" user_info["acct_expires"] = int(dt_obj.timestamp()) if expired is not None: if expired: user_info["password_expired"] = 1 else: user_info["password_expired"] = 0 if account_disabled is not None: if account_disabled: user_info["flags"] |= win32netcon.UF_ACCOUNTDISABLE else: user_info["flags"] &= ~win32netcon.UF_ACCOUNTDISABLE if unlock_account is not None: # We can only unlock with this flag... we can't unlock if unlock_account: user_info["flags"] &= ~win32netcon.UF_LOCKOUT if password_never_expires is not None: if password_never_expires: user_info["flags"] |= win32netcon.UF_DONT_EXPIRE_PASSWD else: user_info["flags"] &= ~win32netcon.UF_DONT_EXPIRE_PASSWD if disallow_change_password is not None: if disallow_change_password: user_info["flags"] |= win32netcon.UF_PASSWD_CANT_CHANGE else: user_info["flags"] &= ~win32netcon.UF_PASSWD_CANT_CHANGE # Apply new settings try: win32net.NetUserSetInfo(None, str(name), 4, user_info) except win32net.error as exc: log.error("Failed to update user %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror return True def delete(name, purge=False, force=False): """ Remove a user from the minion Args: name (str): The name of the user to delete purge (bool, optional): Boolean value indicating that the user profile should also be removed when the user account is deleted. If set to True the profile will be removed. Default is False. force (bool, optional): Boolean value indicating that the user account should be deleted even if the user is logged in. True will log the user out and delete user. Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.delete name """ # Check if the user exists try: user_info = win32net.NetUserGetInfo(None, str(name), 4) except win32net.error as exc: log.error("User not found: %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror # Check if the user is logged in # Return a list of logged in users try: sess_list = win32ts.WTSEnumerateSessions() except win32ts.error as exc: log.error("No logged in users found") log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) # Is the user one that is logged in logged_in = False session_id = None for sess in sess_list: if ( win32ts.WTSQuerySessionInformation( None, sess["SessionId"], win32ts.WTSUserName ) == name ): session_id = sess["SessionId"] logged_in = True # If logged in and set to force, log the user out and continue # If logged in and not set to force, return false if logged_in: if force: try: win32ts.WTSLogoffSession( win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True ) except win32ts.error as exc: log.error("User not found: %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror else: log.error("User %s is currently logged in.", name) return False # Remove the User Profile directory if purge: try: sid = get_user_sid(name) win32profile.DeleteProfile(sid) except pywintypes.error as exc: (number, context, message) = exc.args if number == 2: # Profile Folder Not Found pass else: log.error("Failed to remove profile for %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror # And finally remove the user account try: win32net.NetUserDel(None, str(name)) except win32net.error as exc: log.error("Failed to delete user %s", name) log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) return exc.strerror return True def getUserSid(username): """ Deprecated function. Please use get_user_sid instead CLI Example: .. code-block:: bash salt '*' user.get_user_sid jsnuffy """ salt.utils.versions.warn_until( version=3009, message="'getUserSid' is being deprecated. Please use get_user_sid instead", ) return get_user_sid(username) def get_user_sid(username): """ Get the Security ID for the user Args: username (str): The username for which to look up the SID Returns: str: The user SID CLI Example: .. code-block:: bash salt '*' user.get_user_sid jsnuffy """ domain = win32api.GetComputerName() if username.find("\\") != -1: domain = username.split("\\")[0] username = username.split("\\")[-1] domain = domain.upper() return win32security.ConvertSidToStringSid( win32security.LookupAccountName(None, domain + "\\" + username)[0] ) def setpassword(name, password): """ Set the user's password Args: name (str): The username for which to set the password password (str): The new password Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.setpassword jsnuffy sup3rs3cr3t """ return update(name=name, password=password) def addgroup(name, group): """ Add user to a group Args: name (str): The username to add to the group group (str): The name of the group to which to add the user Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.addgroup jsnuffy 'Power Users' """ name = shlex.quote(str(name)) group = shlex.quote(group).lstrip("'").rstrip("'") user = info(name) if not user: return False if group in user["groups"]: return True cmd = f'net localgroup "{group}" {name} /add' ret = __salt__["cmd.run_all"](cmd, python_shell=True) return ret["retcode"] == 0 def removegroup(name, group): """ Remove user from a group Args: name (str): The username to remove from the group group (str): The name of the group from which to remove the user Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.removegroup jsnuffy 'Power Users' """ name = shlex.quote(str(name)) group = shlex.quote(group).lstrip("'").rstrip("'") user = info(name) if not user: return False if group not in user["groups"]: return True cmd = f'net localgroup "{group}" {name} /delete' ret = __salt__["cmd.run_all"](cmd, python_shell=True) return ret["retcode"] == 0 def chhome(name, home, **kwargs): """ Change the home directory of the user, pass True for persist to move files to the new home directory if the old home directory exist. Args: name (str): The name of the user whose home directory you wish to change home (str): The new location of the home directory Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.chhome foo \\\\fileserver\\home\\foo True """ kwargs = salt.utils.args.clean_kwargs(**kwargs) persist = kwargs.pop("persist", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if persist: log.info("Ignoring unsupported 'persist' argument to user.chhome") pre_info = info(name) if not pre_info: return False if home == pre_info["home"]: return True if not update(name=name, home=home): return False post_info = info(name) if post_info["home"] != pre_info["home"]: return post_info["home"] == home return False def chprofile(name, profile): """ Change the profile directory of the user Args: name (str): The name of the user whose profile you wish to change profile (str): The new location of the profile Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.chprofile foo \\\\fileserver\\profiles\\foo """ return update(name=name, profile=profile) def chfullname(name, fullname): """ Change the full name of the user Args: name (str): The username for which to change the full name fullname (str): The new value for the full name Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.chfullname user 'First Last' """ return update(name=name, fullname=fullname) def chgroups(name, groups, append=True): """ Change the groups this user belongs to, add append=False to make the user a member of only the specified groups Args: name (str): The username for which to change groups groups (str, list): A single group or a list of groups to assign to the user. For multiple groups this can be a comma delimited string or a list. append (bool, optional): True adds the passed groups to the user's current groups. False sets the user's groups to the passed groups only. Default is True. Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.chgroups jsnuffy Administrators,Users True """ if isinstance(groups, str): groups = groups.split(",") groups = [x.strip(" *") for x in groups] current_groups = set(list_groups(name)) expected_groups = set() name = shlex.quote(str(name)) if not append: # We don't want to append to the list, remove groups not in the new set # of groups for group in current_groups: group = shlex.quote(group).lstrip("'").rstrip("'") if group not in groups: cmd = f'net localgroup "{group}" {name} /delete' __salt__["cmd.run_all"](cmd, python_shell=True) else: expected_groups.add(group) else: # We're appending to the current list of groups. If they already match # then bail if current_groups == set(groups): return True else: expected_groups = current_groups.union(set(groups)) for group in groups: if group in current_groups: continue group = shlex.quote(group).lstrip("'").rstrip("'") cmd = f'net localgroup "{group}" {name} /add' out = __salt__["cmd.run_all"](cmd, python_shell=True) if out["retcode"] != 0: log.error(out["stdout"]) return False new_groups = set(list_groups(name)) return len(expected_groups - new_groups) == 0 def info(name): """ Return user information Args: name (str): Username for which to display information Returns: dict: A dictionary containing user information - fullname - username - SID - passwd (will always return None) - comment (same as description, left here for backwards compatibility) - description - active - logonscript - profile - home - homedrive - groups - password_changed - successful_logon_attempts - failed_logon_attempts - last_logon - account_disabled - account_locked - expiration_date - password_never_expires - disallow_change_password - gid CLI Example: .. code-block:: bash salt '*' user.info jsnuffy """ ret = {} items = {} try: items = win32net.NetUserGetInfo(None, str(name), 4) except win32net.error: pass if items: groups = [] try: groups = win32net.NetUserGetLocalGroups(None, str(name)) except win32net.error: pass ret["fullname"] = items["full_name"] ret["name"] = items["name"] ret["uid"] = win32security.ConvertSidToStringSid(items["user_sid"]) ret["passwd"] = items["password"] ret["comment"] = items["comment"] ret["description"] = items["comment"] ret["active"] = not bool(items["flags"] & win32netcon.UF_ACCOUNTDISABLE) ret["logonscript"] = items["script_path"] ret["profile"] = items["profile"] ret["failed_logon_attempts"] = items["bad_pw_count"] ret["successful_logon_attempts"] = items["num_logons"] secs = time.mktime(datetime.now().timetuple()) - items["password_age"] ret["password_changed"] = datetime.fromtimestamp(secs).strftime( "%Y-%m-%d %H:%M:%S" ) if items["last_logon"] == 0: ret["last_logon"] = "Never" else: ret["last_logon"] = datetime.fromtimestamp(items["last_logon"]).strftime( "%Y-%m-%d %H:%M:%S" ) # If the value is -1 or 0xFFFFFFFF, it is set to never expire if items["acct_expires"] == ctypes.c_ulong(win32netcon.TIMEQ_FOREVER).value: ret["expiration_date"] = "Never" else: ret["expiration_date"] = datetime.fromtimestamp( items["acct_expires"] ).strftime("%Y-%m-%d %H:%M:%S") ret["expired"] = items["password_expired"] == 1 if not ret["profile"]: ret["profile"] = _get_userprofile_from_registry(name, ret["uid"]) ret["home"] = items["home_dir"] ret["homedrive"] = items["home_dir_drive"] if not ret["home"]: ret["home"] = ret["profile"] ret["groups"] = groups if items["flags"] & win32netcon.UF_DONT_EXPIRE_PASSWD == 0: ret["password_never_expires"] = False else: ret["password_never_expires"] = True if items["flags"] & win32netcon.UF_ACCOUNTDISABLE == 0: ret["account_disabled"] = False else: ret["account_disabled"] = True if items["flags"] & win32netcon.UF_LOCKOUT == 0: ret["account_locked"] = False else: ret["account_locked"] = True if items["flags"] & win32netcon.UF_PASSWD_CANT_CHANGE == 0: ret["disallow_change_password"] = False else: ret["disallow_change_password"] = True ret["gid"] = "" return ret else: return {} def _get_userprofile_from_registry(user, sid): """ In case net user doesn't return the userprofile we can get it from the registry Args: user (str): The username, used in debug message sid (str): The sid to lookup in the registry Returns: str: Profile directory """ profile_dir = salt.utils.win_reg.read_value( hive="HKEY_LOCAL_MACHINE", key=f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{sid}", vname="ProfileImagePath", )["vdata"] log.debug('user %s with sid=%s profile is located at "%s"', user, sid, profile_dir) return profile_dir def list_groups(name): """ Return a list of groups the named user belongs to Args: name (str): The username for which to list groups Returns: list: A list of groups to which the user belongs CLI Example: .. code-block:: bash salt '*' user.list_groups foo """ ugrp = set() try: user = info(name)["groups"] except KeyError: return False for group in user: ugrp.add(group.strip(" *")) return sorted(list(ugrp)) def getent(refresh=False): """ Return the list of all info for all users Args: refresh (bool, optional): Refresh the cached user information. Useful when used from within a state function. Default is False. Returns: dict: A dictionary containing information about all users on the system CLI Example: .. code-block:: bash salt '*' user.getent """ if "user.getent" in __context__ and not refresh: return __context__["user.getent"] ret = [] for user in list_users(): stuff = {} user_info = info(user) stuff["gid"] = "" stuff["groups"] = user_info["groups"] stuff["home"] = user_info["home"] stuff["name"] = user_info["name"] stuff["passwd"] = user_info["passwd"] stuff["shell"] = "" stuff["uid"] = user_info["uid"] ret.append(stuff) __context__["user.getent"] = ret return ret def list_users(): """ Return a list of all users on Windows Returns: list: A list of all users on the system CLI Example: .. code-block:: bash salt '*' user.list_users """ res = 0 user_list = [] dowhile = True try: while res or dowhile: dowhile = False (users, _, res) = win32net.NetUserEnum( None, 0, win32netcon.FILTER_NORMAL_ACCOUNT, res, win32netcon.MAX_PREFERRED_LENGTH, ) for user in users: user_list.append(user["name"]) return user_list except win32net.error: pass def rename(name, new_name): """ Change the username for a named user Args: name (str): The username to change new_name (str): The new name for the current user Returns: bool: ``True`` if successful, otherwise ``False``. CLI Example: .. code-block:: bash salt '*' user.rename jsnuffy jshmoe """ # Load information for the current name current_info = info(name) if not current_info: raise CommandExecutionError(f"User '{name}' does not exist") # Look for an existing user with the new name new_info = info(new_name) if new_info: raise CommandExecutionError(f"User '{new_name}' already exists") # Rename the user account # Connect to WMI with salt.utils.winapi.Com(): c = wmi.WMI(find_classes=0) # Get the user object try: user = c.Win32_UserAccount(Name=name)[0] except IndexError: raise CommandExecutionError(f"User '{name}' does not exist") # Rename the user result = user.Rename(new_name)[0] # Check the result (0 means success) if not result == 0: # Define Error Dict error_dict = { 0: "Success", 1: "Instance not found", 2: "Instance required", 3: "Invalid parameter", 4: "User not found", 5: "Domain not found", 6: ( "Operation is allowed only on the primary domain controller of the" " domain" ), 7: "Operation is not allowed on the last administrative account", 8: ( "Operation is not allowed on specified special groups: user, admin," " local, or guest" ), 9: "Other API error", 10: "Internal error", } raise CommandExecutionError( "There was an error renaming '{}' to '{}'. Error: {}".format( name, new_name, error_dict[result] ) ) return info(new_name).get("name") == new_name def current(sam=False): """ Get the username that salt-minion is running under. If salt-minion is running as a service it should return the Local System account. If salt is running from a command prompt it should return the username that started the command prompt. .. versionadded:: 2015.5.6 Args: sam (bool, optional): False returns just the username without any domain notation. True returns the domain with the username in the SAM format. Ie: ``domain\\username`` Returns: str: Returns username CLI Example: .. code-block:: bash salt '*' user.current """ try: if sam: user_name = win32api.GetUserNameEx(win32con.NameSamCompatible) else: user_name = win32api.GetUserName() except pywintypes.error as exc: log.error("Failed to get current user") log.error("nbr: %s", exc.winerror) log.error("ctx: %s", exc.funcname) log.error("msg: %s", exc.strerror) raise CommandExecutionError("Failed to get current user", info=exc) if not user_name: raise CommandExecutionError("Failed to get current user") return user_name