Mini Shell
"""
Manage users with the useradd command
.. 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>`.
"""
import copy
import logging
import salt.utils.data
import salt.utils.user
from salt.exceptions import CommandExecutionError
try:
import pwd
HAS_PWD = True
except ImportError:
HAS_PWD = False
log = logging.getLogger(__name__)
# Define the module's virtual name
__virtualname__ = "user"
def __virtual__():
"""
Set the user module if the kernel is SunOS
"""
if __grains__["kernel"] == "SunOS" and HAS_PWD:
return __virtualname__
return (
False,
"The solaris_user execution module failed to load: "
"only available on Solaris systems with pwd module installed.",
)
def _get_gecos(name):
"""
Retrieve GECOS field info and return it in dictionary form
"""
gecos_field = pwd.getpwnam(name).pw_gecos.split(",", 3)
if not gecos_field:
return {}
else:
# Assign empty strings for any unspecified trailing GECOS fields
while len(gecos_field) < 4:
gecos_field.append("")
return {
"fullname": str(gecos_field[0]),
"roomnumber": str(gecos_field[1]),
"workphone": str(gecos_field[2]),
"homephone": str(gecos_field[3]),
}
def _build_gecos(gecos_dict):
"""
Accepts a dictionary entry containing GECOS field names and their values,
and returns a full GECOS comment string, to be used with usermod.
"""
return "{},{},{},{}".format(
gecos_dict.get("fullname", ""),
gecos_dict.get("roomnumber", ""),
gecos_dict.get("workphone", ""),
gecos_dict.get("homephone", ""),
)
def _update_gecos(name, key, value):
"""
Common code to change a user's GECOS information
"""
if not isinstance(value, str):
value = str(value)
pre_info = _get_gecos(name)
if not pre_info:
return False
if value == pre_info[key]:
return True
gecos_data = copy.deepcopy(pre_info)
gecos_data[key] = value
cmd = ["usermod", "-c", _build_gecos(gecos_data), name]
__salt__["cmd.run"](cmd, python_shell=False)
post_info = info(name)
return _get_gecos(name).get(key) == value
def add(
name,
uid=None,
gid=None,
groups=None,
home=None,
shell=None,
unique=True,
fullname="",
roomnumber="",
workphone="",
homephone="",
createhome=True,
**kwargs,
):
"""
Add a user to the minion
CLI Example:
.. code-block:: bash
salt '*' user.add name <uid> <gid> <groups> <home> <shell>
"""
if salt.utils.data.is_true(kwargs.pop("system", False)):
log.warning("solaris_user module does not support the 'system' argument")
if kwargs:
log.warning("Invalid kwargs passed to user.add")
if isinstance(groups, str):
groups = groups.split(",")
cmd = ["useradd"]
if shell:
cmd.extend(["-s", shell])
if uid:
cmd.extend(["-u", uid])
if gid:
cmd.extend(["-g", gid])
if groups:
cmd.extend(["-G", ",".join(groups)])
if createhome:
cmd.append("-m")
if home is not None:
cmd.extend(["-d", home])
if not unique:
cmd.append("-o")
cmd.append(name)
if __salt__["cmd.retcode"](cmd, python_shell=False) != 0:
return False
else:
# At this point, the user was successfully created, so return true
# regardless of the outcome of the below functions. If there is a
# problem wth changing any of the user's info below, it will be raised
# in a future highstate call. If anyone has a better idea on how to do
# this, feel free to change it, but I didn't think it was a good idea
# to return False when the user was successfully created since A) the
# user does exist, and B) running useradd again would result in a
# nonzero exit status and be interpreted as a False result.
if fullname:
chfullname(name, fullname)
if roomnumber:
chroomnumber(name, roomnumber)
if workphone:
chworkphone(name, workphone)
if homephone:
chhomephone(name, homephone)
return True
def delete(name, remove=False, force=False):
"""
Remove a user from the minion
CLI Example:
.. code-block:: bash
salt '*' user.delete name remove=True force=True
"""
if salt.utils.data.is_true(force):
log.warning(
"userdel does not support force-deleting user while user is logged in"
)
cmd = ["userdel"]
if remove:
cmd.append("-r")
cmd.append(name)
return __salt__["cmd.retcode"](cmd, python_shell=False) == 0
def getent(refresh=False):
"""
Return the list of all info for all users
CLI Example:
.. code-block:: bash
salt '*' user.getent
"""
if "user.getent" in __context__ and not refresh:
return __context__["user.getent"]
ret = []
for data in pwd.getpwall():
ret.append(info(data.pw_name))
__context__["user.getent"] = ret
return ret
def chuid(name, uid):
"""
Change the uid for a named user
CLI Example:
.. code-block:: bash
salt '*' user.chuid foo 4376
"""
pre_info = info(name)
if not pre_info:
raise CommandExecutionError(f"User '{name}' does not exist")
if uid == pre_info["uid"]:
return True
cmd = ["usermod", "-u", uid, name]
__salt__["cmd.run"](cmd, python_shell=False)
return info(name).get("uid") == uid
def chgid(name, gid):
"""
Change the default group of the user
CLI Example:
.. code-block:: bash
salt '*' user.chgid foo 4376
"""
pre_info = info(name)
if not pre_info:
raise CommandExecutionError(f"User '{name}' does not exist")
if gid == pre_info["gid"]:
return True
cmd = ["usermod", "-g", gid, name]
__salt__["cmd.run"](cmd, python_shell=False)
return info(name).get("gid") == gid
def chshell(name, shell):
"""
Change the default shell of the user
CLI Example:
.. code-block:: bash
salt '*' user.chshell foo /bin/zsh
"""
pre_info = info(name)
if not pre_info:
raise CommandExecutionError(f"User '{name}' does not exist")
if shell == pre_info["shell"]:
return True
cmd = ["usermod", "-s", shell, name]
__salt__["cmd.run"](cmd, python_shell=False)
return info(name).get("shell") == shell
def chhome(name, home, persist=False):
"""
Set a new home directory for an existing user
name
Username to modify
home
New home directory to set
persist : False
Set to ``True`` to prevent configuration files in the new home
directory from being overwritten by the files from the skeleton
directory.
CLI Example:
.. code-block:: bash
salt '*' user.chhome foo /home/users/foo True
"""
pre_info = info(name)
if not pre_info:
raise CommandExecutionError(f"User '{name}' does not exist")
if home == pre_info["home"]:
return True
cmd = ["usermod", "-d", home]
if persist:
cmd.append("-m")
cmd.append(name)
__salt__["cmd.run"](cmd, python_shell=False)
return info(name).get("home") == home
def chgroups(name, groups, append=False):
"""
Change the groups to which a user belongs
name
Username to modify
groups
List of groups to set for the user. Can be passed as a comma-separated
list or a Python list.
append : False
Set to ``True`` to append these groups to the user's existing list of
groups. Otherwise, the specified groups will replace any existing
groups for the user.
CLI Example:
.. code-block:: bash
salt '*' user.chgroups foo wheel,root True
"""
if isinstance(groups, str):
groups = groups.split(",")
ugrps = set(list_groups(name))
if ugrps == set(groups):
return True
if append:
groups.update(ugrps)
cmd = ["usermod", "-G", ",".join(groups), name]
return __salt__["cmd.retcode"](cmd, python_shell=False) == 0
def chfullname(name, fullname):
"""
Change the user's Full Name
CLI Example:
.. code-block:: bash
salt '*' user.chfullname foo "Foo Bar"
"""
return _update_gecos(name, "fullname", fullname)
def chroomnumber(name, roomnumber):
"""
Change the user's Room Number
CLI Example:
.. code-block:: bash
salt '*' user.chroomnumber foo 123
"""
return _update_gecos(name, "roomnumber", roomnumber)
def chworkphone(name, workphone):
"""
Change the user's Work Phone
CLI Example:
.. code-block:: bash
salt '*' user.chworkphone foo "7735550123"
"""
return _update_gecos(name, "workphone", workphone)
def chhomephone(name, homephone):
"""
Change the user's Home Phone
CLI Example:
.. code-block:: bash
salt '*' user.chhomephone foo "7735551234"
"""
return _update_gecos(name, "homephone", homephone)
def info(name):
"""
Return user information
CLI Example:
.. code-block:: bash
salt '*' user.info root
"""
ret = {}
try:
data = pwd.getpwnam(name)
ret["gid"] = data.pw_gid
ret["groups"] = list_groups(name)
ret["home"] = data.pw_dir
ret["name"] = data.pw_name
ret["passwd"] = data.pw_passwd
ret["shell"] = data.pw_shell
ret["uid"] = data.pw_uid
# Put GECOS info into a list
gecos_field = data.pw_gecos.split(",", 3)
# Assign empty strings for any unspecified GECOS fields
while len(gecos_field) < 4:
gecos_field.append("")
ret["fullname"] = gecos_field[0]
ret["roomnumber"] = gecos_field[1]
ret["workphone"] = gecos_field[2]
ret["homephone"] = gecos_field[3]
except KeyError:
return {}
return ret
def list_groups(name):
"""
Return a list of groups the named user belongs to
CLI Example:
.. code-block:: bash
salt '*' user.list_groups foo
"""
return salt.utils.user.get_group_list(name)
def list_users():
"""
Return a list of all users
CLI Example:
.. code-block:: bash
salt '*' user.list_users
"""
return sorted(user.pw_name for user in pwd.getpwall())
def rename(name, new_name):
"""
Change the username for a named user
CLI Example:
.. code-block:: bash
salt '*' user.rename name new_name
"""
current_info = info(name)
if not current_info:
raise CommandExecutionError(f"User '{name}' does not exist")
new_info = info(new_name)
if new_info:
raise CommandExecutionError(f"User '{new_name}' already exists")
cmd = ["usermod", "-l", new_name, name]
__salt__["cmd.run"](cmd, python_shell=False)
return info(new_name).get("name") == new_name
Zerion Mini Shell 1.0