mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
Add flags to create local users and groups
This commit is contained in:
parent
05d3295eba
commit
ced3436053
8 changed files with 340 additions and 75 deletions
1
changelog/64256.added.md
Normal file
1
changelog/64256.added.md
Normal file
|
@ -0,0 +1 @@
|
|||
Added flags to create local users and groups
|
|
@ -48,11 +48,11 @@ def _which(cmd):
|
|||
"""
|
||||
_cmd = salt.utils.path.which(cmd)
|
||||
if not _cmd:
|
||||
raise CommandExecutionError("Command '{}' cannot be found".format(cmd))
|
||||
raise CommandExecutionError(f"Command '{cmd}' cannot be found")
|
||||
return _cmd
|
||||
|
||||
|
||||
def add(name, gid=None, system=False, root=None, non_unique=False):
|
||||
def add(name, gid=None, system=False, root=None, non_unique=False, local=False):
|
||||
"""
|
||||
.. versionchanged:: 3006.0
|
||||
|
||||
|
@ -75,21 +75,26 @@ def add(name, gid=None, system=False, root=None, non_unique=False):
|
|||
|
||||
.. versionadded:: 3006.0
|
||||
|
||||
local
|
||||
Specifically add the group locally rather than through remote providers (e.g. LDAP)
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' group.add foo 3456
|
||||
"""
|
||||
cmd = [_which("groupadd")]
|
||||
cmd = [_which("lgroupadd" if local else "groupadd")]
|
||||
if gid:
|
||||
cmd.append("-g {}".format(gid))
|
||||
if non_unique:
|
||||
cmd.append(f"-g {gid}")
|
||||
if non_unique and not local:
|
||||
cmd.append("-o")
|
||||
if system and __grains__["kernel"] != "OpenBSD":
|
||||
cmd.append("-r")
|
||||
|
||||
if root is not None:
|
||||
if root is not None and not local:
|
||||
cmd.extend(("-R", root))
|
||||
|
||||
cmd.append(name)
|
||||
|
@ -99,7 +104,7 @@ def add(name, gid=None, system=False, root=None, non_unique=False):
|
|||
return not ret["retcode"]
|
||||
|
||||
|
||||
def delete(name, root=None):
|
||||
def delete(name, root=None, local=False):
|
||||
"""
|
||||
Remove the named group
|
||||
|
||||
|
@ -109,15 +114,21 @@ def delete(name, root=None):
|
|||
root
|
||||
Directory to chroot into
|
||||
|
||||
local (Only on systems with lgroupdel available):
|
||||
Ensure the group account is removed locally ignoring global
|
||||
account management (default is False).
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' group.delete foo
|
||||
"""
|
||||
cmd = [_which("groupdel")]
|
||||
cmd = [_which("lgroupdel" if local else "groupdel")]
|
||||
|
||||
if root is not None:
|
||||
if root is not None and not local:
|
||||
cmd.extend(("-R", root))
|
||||
|
||||
cmd.append(name)
|
||||
|
@ -349,11 +360,11 @@ def deluser(name, username, root=None):
|
|||
retcode = __salt__["cmd.retcode"](cmd, python_shell=False)
|
||||
elif __grains__["kernel"] == "OpenBSD":
|
||||
out = __salt__["cmd.run_stdout"](
|
||||
"id -Gn {}".format(username), python_shell=False
|
||||
f"id -Gn {username}", python_shell=False
|
||||
)
|
||||
cmd = [_which("usermod"), "-S"]
|
||||
cmd.append(",".join([g for g in out.split() if g != str(name)]))
|
||||
cmd.append("{}".format(username))
|
||||
cmd.append(f"{username}")
|
||||
retcode = __salt__["cmd.retcode"](cmd, python_shell=False)
|
||||
else:
|
||||
log.error("group.deluser is not yet supported on this platform")
|
||||
|
@ -459,7 +470,7 @@ def _getgrnam(name, root=None):
|
|||
comps[2] = int(comps[2])
|
||||
comps[3] = comps[3].split(",") if comps[3] else []
|
||||
return grp.struct_group(comps)
|
||||
raise KeyError("getgrnam(): name not found: {}".format(name))
|
||||
raise KeyError(f"getgrnam(): name not found: {name}")
|
||||
|
||||
|
||||
def _getgrall(root=None):
|
||||
|
|
|
@ -106,7 +106,7 @@ def _which(cmd):
|
|||
"""
|
||||
_cmd = salt.utils.path.which(cmd)
|
||||
if not _cmd:
|
||||
raise CommandExecutionError("Command '{}' cannot be found".format(cmd))
|
||||
raise CommandExecutionError(f"Command '{cmd}' cannot be found")
|
||||
return _cmd
|
||||
|
||||
|
||||
|
@ -157,6 +157,7 @@ def add(
|
|||
nologinit=False,
|
||||
root=None,
|
||||
usergroup=None,
|
||||
local=False,
|
||||
):
|
||||
"""
|
||||
Add a user to the minion
|
||||
|
@ -215,13 +216,18 @@ def add(
|
|||
usergroup
|
||||
Create and add the user to a new primary group of the same name
|
||||
|
||||
local (Only on systems with luseradd available)
|
||||
Specifically add the user locally rather than possibly through remote providers (e.g. LDAP)
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.add name <uid> <gid> <groups> <home> <shell>
|
||||
"""
|
||||
cmd = [_which("useradd")]
|
||||
cmd = [_which("luseradd" if local else "useradd")]
|
||||
|
||||
if shell:
|
||||
cmd.extend(["-s", shell])
|
||||
|
@ -230,9 +236,10 @@ def add(
|
|||
if gid not in (None, ""):
|
||||
cmd.extend(["-g", gid])
|
||||
elif usergroup:
|
||||
cmd.append("-U")
|
||||
if __grains__["kernel"] != "Linux":
|
||||
log.warning("'usergroup' is only supported on GNU/Linux hosts.")
|
||||
if not local:
|
||||
cmd.append("-U")
|
||||
if __grains__["kernel"] != "Linux":
|
||||
log.warning("'usergroup' is only supported on GNU/Linux hosts.")
|
||||
elif groups is not None and name in groups:
|
||||
defs_file = "/etc/login.defs"
|
||||
if __grains__["kernel"] != "OpenBSD":
|
||||
|
@ -269,14 +276,15 @@ def add(
|
|||
# /etc/usermgmt.conf not present: defaults will be used
|
||||
pass
|
||||
|
||||
# Setting usergroup to False adds the -N command argument. If
|
||||
# Setting usergroup to False adds a command argument. If
|
||||
# usergroup is None, no arguments are added to allow useradd to go
|
||||
# with the defaults defined for the OS.
|
||||
if usergroup is False:
|
||||
cmd.append("-N")
|
||||
cmd.append("-n" if local else "-N")
|
||||
|
||||
if createhome:
|
||||
cmd.append("-m")
|
||||
if not local:
|
||||
cmd.append("-m")
|
||||
elif __grains__["kernel"] != "NetBSD" and __grains__["kernel"] != "OpenBSD":
|
||||
cmd.append("-M")
|
||||
|
||||
|
@ -302,7 +310,7 @@ def add(
|
|||
|
||||
cmd.append(name)
|
||||
|
||||
if root is not None and __grains__["kernel"] != "AIX":
|
||||
if root is not None and not local and __grains__["kernel"] != "AIX":
|
||||
cmd.extend(("-R", root))
|
||||
|
||||
ret = __salt__["cmd.run_all"](cmd, python_shell=False)
|
||||
|
@ -333,7 +341,7 @@ def add(
|
|||
return True
|
||||
|
||||
|
||||
def delete(name, remove=False, force=False, root=None):
|
||||
def delete(name, remove=False, force=False, root=None, local=False):
|
||||
"""
|
||||
Remove a user from the minion
|
||||
|
||||
|
@ -349,23 +357,34 @@ def delete(name, remove=False, force=False, root=None):
|
|||
root
|
||||
Directory to chroot into
|
||||
|
||||
local (Only on systems with luserdel available):
|
||||
Ensure the user account is removed locally ignoring global
|
||||
account management (default is False).
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.delete name remove=True force=True
|
||||
"""
|
||||
cmd = [_which("userdel")]
|
||||
cmd = [_which("luserdel" if local else "userdel")]
|
||||
|
||||
if remove:
|
||||
cmd.append("-r")
|
||||
|
||||
if force and __grains__["kernel"] != "OpenBSD" and __grains__["kernel"] != "AIX":
|
||||
if (
|
||||
force
|
||||
and __grains__["kernel"] != "OpenBSD"
|
||||
and __grains__["kernel"] != "AIX"
|
||||
and not local
|
||||
):
|
||||
cmd.append("-f")
|
||||
|
||||
cmd.append(name)
|
||||
|
||||
if root is not None and __grains__["kernel"] != "AIX":
|
||||
if root is not None and __grains__["kernel"] != "AIX" and not local:
|
||||
cmd.extend(("-R", root))
|
||||
|
||||
ret = __salt__["cmd.run_all"](cmd, python_shell=False)
|
||||
|
@ -429,7 +448,7 @@ def _chattrib(name, key, value, param, persist=False, root=None):
|
|||
"""
|
||||
pre_info = info(name, root=root)
|
||||
if not pre_info:
|
||||
raise CommandExecutionError("User '{}' does not exist".format(name))
|
||||
raise CommandExecutionError(f"User '{name}' does not exist")
|
||||
|
||||
if value == pre_info[key]:
|
||||
return True
|
||||
|
@ -911,7 +930,7 @@ def rename(name, new_name, root=None):
|
|||
salt '*' user.rename name new_name
|
||||
"""
|
||||
if info(new_name, root=root):
|
||||
raise CommandExecutionError("User '{}' already exists".format(new_name))
|
||||
raise CommandExecutionError(f"User '{new_name}' already exists")
|
||||
|
||||
return _chattrib(name, "name", new_name, "-l", root=root)
|
||||
|
||||
|
|
|
@ -40,12 +40,21 @@ import salt.utils.platform
|
|||
import salt.utils.win_functions
|
||||
|
||||
|
||||
def _changes(name, gid=None, addusers=None, delusers=None, members=None):
|
||||
def _get_root_args(local):
|
||||
"""
|
||||
Retrieve args to use for group.info calls depending on platform and the local flag
|
||||
"""
|
||||
if not local or salt.utils.platform.is_windows():
|
||||
return {}
|
||||
return {"root": "/"}
|
||||
|
||||
|
||||
def _changes(name, gid=None, addusers=None, delusers=None, members=None, local=False):
|
||||
"""
|
||||
Return a dict of the changes required for a group if the group is present,
|
||||
otherwise return False.
|
||||
"""
|
||||
lgrp = __salt__["group.info"](name)
|
||||
lgrp = __salt__["group.info"](name, **_get_root_args(local))
|
||||
if not lgrp:
|
||||
return False
|
||||
|
||||
|
@ -108,6 +117,7 @@ def present(
|
|||
delusers=None,
|
||||
members=None,
|
||||
non_unique=False,
|
||||
local=False,
|
||||
):
|
||||
r"""
|
||||
.. versionchanged:: 3006.0
|
||||
|
@ -146,6 +156,12 @@ def present(
|
|||
|
||||
.. versionadded:: 3006.0
|
||||
|
||||
local (Only on systems with lgroupadd available):
|
||||
Create the group account locally ignoring global account management
|
||||
(default is False).
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
@ -173,7 +189,7 @@ def present(
|
|||
"name": name,
|
||||
"changes": {},
|
||||
"result": True,
|
||||
"comment": "Group {} is present and up to date".format(name),
|
||||
"comment": f"Group {name} is present and up to date",
|
||||
}
|
||||
|
||||
if members is not None and (addusers is not None or delusers is not None):
|
||||
|
@ -193,11 +209,11 @@ def present(
|
|||
] = "Error. Same user(s) can not be added and deleted simultaneously"
|
||||
return ret
|
||||
|
||||
changes = _changes(name, gid, addusers, delusers, members)
|
||||
changes = _changes(name, gid, addusers, delusers, members, local=local)
|
||||
if changes:
|
||||
ret["comment"] = "The following group attributes are set to be changed:\n"
|
||||
for key, val in changes.items():
|
||||
ret["comment"] += "{}: {}\n".format(key, val)
|
||||
ret["comment"] += f"{key}: {val}\n"
|
||||
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
|
@ -222,7 +238,7 @@ def present(
|
|||
sys.modules[__salt__["test.ping"].__module__].__context__.pop(
|
||||
"group.getent", None
|
||||
)
|
||||
changes = _changes(name, gid, addusers, delusers, members)
|
||||
changes = _changes(name, gid, addusers, delusers, members, local=local)
|
||||
if changes:
|
||||
ret["result"] = False
|
||||
ret["comment"] += "Some changes could not be applied"
|
||||
|
@ -234,7 +250,7 @@ def present(
|
|||
# The group is not present, make it!
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
ret["comment"] = "Group {} set to be added".format(name)
|
||||
ret["comment"] = f"Group {name} set to be added"
|
||||
return ret
|
||||
|
||||
grps = __salt__["group.getent"]()
|
||||
|
@ -255,7 +271,17 @@ def present(
|
|||
return ret
|
||||
|
||||
# Group is not present, make it.
|
||||
if __salt__["group.add"](name, gid=gid, system=system, non_unique=non_unique):
|
||||
if salt.utils.platform.is_windows():
|
||||
add_args = {}
|
||||
else:
|
||||
add_args = {"local": local}
|
||||
if __salt__["group.add"](
|
||||
name,
|
||||
gid=gid,
|
||||
system=system,
|
||||
non_unique=non_unique,
|
||||
**add_args,
|
||||
):
|
||||
# if members to be added
|
||||
grp_members = None
|
||||
if members:
|
||||
|
@ -268,9 +294,9 @@ def present(
|
|||
sys.modules[__salt__["test.ping"].__module__].__context__.pop(
|
||||
"group.getent", None
|
||||
)
|
||||
ret["comment"] = "New group {} created".format(name)
|
||||
ret["changes"] = __salt__["group.info"](name)
|
||||
changes = _changes(name, gid, addusers, delusers, members)
|
||||
ret["comment"] = f"New group {name} created"
|
||||
ret["changes"] = __salt__["group.info"](name, **_get_root_args(local))
|
||||
changes = _changes(name, gid, addusers, delusers, members, local=local)
|
||||
if changes:
|
||||
ret["result"] = False
|
||||
ret["comment"] = (
|
||||
|
@ -280,11 +306,11 @@ def present(
|
|||
ret["changes"] = {"Failed": changes}
|
||||
else:
|
||||
ret["result"] = False
|
||||
ret["comment"] = "Failed to create new group {}".format(name)
|
||||
ret["comment"] = f"Failed to create new group {name}"
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name):
|
||||
def absent(name, local=False):
|
||||
"""
|
||||
Ensure that the named group is absent
|
||||
|
||||
|
@ -292,6 +318,13 @@ def absent(name):
|
|||
name (str):
|
||||
The name of the group to remove
|
||||
|
||||
local (Only on systems with lgroupdel available):
|
||||
|
||||
Ensure the group account is removed locally ignoring global
|
||||
account management (default is False).
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
@ -301,20 +334,24 @@ def absent(name):
|
|||
group.absent
|
||||
"""
|
||||
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
||||
grp_info = __salt__["group.info"](name)
|
||||
grp_info = __salt__["group.info"](name, **_get_root_args(local))
|
||||
if grp_info:
|
||||
# Group already exists. Remove the group.
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
ret["comment"] = "Group {} is set for removal".format(name)
|
||||
ret["comment"] = f"Group {name} is set for removal"
|
||||
return ret
|
||||
ret["result"] = __salt__["group.delete"](name)
|
||||
if salt.utils.platform.is_windows():
|
||||
del_args = {}
|
||||
else:
|
||||
del_args = {"local": local}
|
||||
ret["result"] = __salt__["group.delete"](name, **del_args)
|
||||
if ret["result"]:
|
||||
ret["changes"] = {name: ""}
|
||||
ret["comment"] = "Removed group {}".format(name)
|
||||
ret["comment"] = f"Removed group {name}"
|
||||
return ret
|
||||
else:
|
||||
ret["comment"] = "Failed to remove group {}".format(name)
|
||||
ret["comment"] = f"Failed to remove group {name}"
|
||||
return ret
|
||||
else:
|
||||
ret["comment"] = "Group not present"
|
||||
|
|
|
@ -47,6 +47,15 @@ def _group_changes(cur, wanted, remove=False):
|
|||
return False
|
||||
|
||||
|
||||
def _get_root_args(local):
|
||||
"""
|
||||
Retrieve args to use for user.info calls depending on platform and the local flag
|
||||
"""
|
||||
if not local or salt.utils.platform.is_windows():
|
||||
return {}
|
||||
return {"root": "/"}
|
||||
|
||||
|
||||
def _changes(
|
||||
name,
|
||||
uid=None,
|
||||
|
@ -79,6 +88,7 @@ def _changes(
|
|||
allow_uid_change=False,
|
||||
allow_gid_change=False,
|
||||
password_lock=None,
|
||||
local=False,
|
||||
):
|
||||
"""
|
||||
Return a dict of the changes required for a user if the user is present,
|
||||
|
@ -94,7 +104,7 @@ def _changes(
|
|||
if "shadow.info" in __salt__:
|
||||
lshad = __salt__["shadow.info"](name)
|
||||
|
||||
lusr = __salt__["user.info"](name)
|
||||
lusr = __salt__["user.info"](name, **_get_root_args(local))
|
||||
if not lusr:
|
||||
return False
|
||||
|
||||
|
@ -274,6 +284,7 @@ def present(
|
|||
allow_uid_change=False,
|
||||
allow_gid_change=False,
|
||||
password_lock=None,
|
||||
local=False,
|
||||
):
|
||||
"""
|
||||
Ensure that the named user is present with the specified properties
|
||||
|
@ -449,6 +460,12 @@ def present(
|
|||
Date that account expires, represented in days since epoch (January 1,
|
||||
1970).
|
||||
|
||||
local (Only on systems with luseradd available):
|
||||
Create the user account locally ignoring global account management
|
||||
(default is False).
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
The below parameters apply to windows only:
|
||||
|
||||
win_homedrive (Windows Only)
|
||||
|
@ -530,14 +547,14 @@ def present(
|
|||
"name": name,
|
||||
"changes": {},
|
||||
"result": True,
|
||||
"comment": "User {} is present and up to date".format(name),
|
||||
"comment": f"User {name} is present and up to date",
|
||||
}
|
||||
|
||||
# the comma is used to separate field in GECOS, thus resulting into
|
||||
# salt adding the end of fullname each time this function is called
|
||||
for gecos_field in [fullname, roomnumber, workphone]:
|
||||
if isinstance(gecos_field, str) and "," in gecos_field:
|
||||
ret["comment"] = "Unsupported char ',' in {}".format(gecos_field)
|
||||
ret["comment"] = f"Unsupported char ',' in {gecos_field}"
|
||||
ret["result"] = False
|
||||
return ret
|
||||
|
||||
|
@ -614,6 +631,7 @@ def present(
|
|||
allow_uid_change,
|
||||
allow_gid_change,
|
||||
password_lock=password_lock,
|
||||
local=local,
|
||||
)
|
||||
except CommandExecutionError as exc:
|
||||
ret["result"] = False
|
||||
|
@ -629,14 +647,14 @@ def present(
|
|||
val = "XXX-REDACTED-XXX"
|
||||
elif key == "group" and not remove_groups:
|
||||
key = "ensure groups"
|
||||
ret["comment"] += "{}: {}\n".format(key, val)
|
||||
ret["comment"] += f"{key}: {val}\n"
|
||||
return ret
|
||||
# The user is present
|
||||
if "shadow.info" in __salt__:
|
||||
lshad = __salt__["shadow.info"](name)
|
||||
if __grains__["kernel"] in ("OpenBSD", "FreeBSD"):
|
||||
lcpre = __salt__["user.get_loginclass"](name)
|
||||
pre = __salt__["user.info"](name)
|
||||
pre = __salt__["user.info"](name, **_get_root_args(local))
|
||||
|
||||
# Make changes
|
||||
|
||||
|
@ -727,11 +745,9 @@ def present(
|
|||
# NOTE: list(changes) required here to avoid modifying dictionary
|
||||
# during iteration.
|
||||
for key in [
|
||||
x
|
||||
for x in list(changes)
|
||||
if x != "groups" and "user.ch{}".format(x) in __salt__
|
||||
x for x in list(changes) if x != "groups" and f"user.ch{x}" in __salt__
|
||||
]:
|
||||
__salt__["user.ch{}".format(key)](name, changes.pop(key))
|
||||
__salt__[f"user.ch{key}"](name, changes.pop(key))
|
||||
|
||||
# Do group changes last
|
||||
if "groups" in changes:
|
||||
|
@ -742,7 +758,7 @@ def present(
|
|||
"Unhandled changes: {}".format(", ".join(changes))
|
||||
)
|
||||
|
||||
post = __salt__["user.info"](name)
|
||||
post = __salt__["user.info"](name, **_get_root_args(local))
|
||||
spost = {}
|
||||
if "shadow.info" in __salt__ and lshad["passwd"] != password:
|
||||
spost = __salt__["shadow.info"](name)
|
||||
|
@ -762,7 +778,7 @@ def present(
|
|||
if __grains__["kernel"] in ("OpenBSD", "FreeBSD") and lcpost != lcpre:
|
||||
ret["changes"]["loginclass"] = lcpost
|
||||
if ret["changes"]:
|
||||
ret["comment"] = "Updated user {}".format(name)
|
||||
ret["comment"] = f"Updated user {name}"
|
||||
changes = _changes(
|
||||
name,
|
||||
uid,
|
||||
|
@ -795,6 +811,7 @@ def present(
|
|||
allow_uid_change=True,
|
||||
allow_gid_change=True,
|
||||
password_lock=password_lock,
|
||||
local=local,
|
||||
)
|
||||
# allow_uid_change and allow_gid_change passed as True to avoid race
|
||||
# conditions where a uid/gid is modified outside of Salt. If an
|
||||
|
@ -802,7 +819,7 @@ def present(
|
|||
# first time we ran _changes().
|
||||
|
||||
if changes:
|
||||
ret["comment"] = "These values could not be changed: {}".format(changes)
|
||||
ret["comment"] = f"These values could not be changed: {changes}"
|
||||
ret["result"] = False
|
||||
return ret
|
||||
|
||||
|
@ -810,7 +827,7 @@ def present(
|
|||
# The user is not present, make it!
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
ret["comment"] = "User {} set to be added".format(name)
|
||||
ret["comment"] = f"User {name} set to be added"
|
||||
return ret
|
||||
if groups and present_optgroups:
|
||||
groups.extend(present_optgroups)
|
||||
|
@ -837,6 +854,7 @@ def present(
|
|||
"createhome": createhome,
|
||||
"nologinit": nologinit,
|
||||
"loginclass": loginclass,
|
||||
"local": local,
|
||||
"usergroup": usergroup,
|
||||
}
|
||||
else:
|
||||
|
@ -853,8 +871,8 @@ def present(
|
|||
}
|
||||
result = __salt__["user.add"](**params)
|
||||
if result is True:
|
||||
ret["comment"] = "New user {} created".format(name)
|
||||
ret["changes"] = __salt__["user.info"](name)
|
||||
ret["comment"] = f"New user {name} created"
|
||||
ret["changes"] = __salt__["user.info"](name, **_get_root_args(local))
|
||||
if not createhome:
|
||||
# pwd incorrectly reports presence of home
|
||||
ret["changes"]["home"] = ""
|
||||
|
@ -880,7 +898,7 @@ def present(
|
|||
if spost["passwd"] != "":
|
||||
ret[
|
||||
"comment"
|
||||
] = "User {} created but failed to empty password".format(name)
|
||||
] = f"User {name} created but failed to empty password"
|
||||
ret["result"] = False
|
||||
ret["changes"]["password"] = ""
|
||||
if date is not None:
|
||||
|
@ -987,12 +1005,12 @@ def present(
|
|||
if isinstance(result, str):
|
||||
ret["comment"] = result
|
||||
else:
|
||||
ret["comment"] = "Failed to create new user {}".format(name)
|
||||
ret["comment"] = f"Failed to create new user {name}"
|
||||
ret["result"] = False
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name, purge=False, force=False):
|
||||
def absent(name, purge=False, force=False, local=False):
|
||||
"""
|
||||
Ensure that the named user is absent
|
||||
|
||||
|
@ -1007,30 +1025,40 @@ def absent(name, purge=False, force=False):
|
|||
If the user is logged in, the absent state will fail. Set the force
|
||||
option to True to remove the user even if they are logged in. Not
|
||||
supported in FreeBSD and Solaris, Default is ``False``.
|
||||
|
||||
local (Only on systems with luserdel available):
|
||||
Ensure the user account is removed locally ignoring global account management
|
||||
(default is False).
|
||||
|
||||
.. versionadded:: 3007.0
|
||||
"""
|
||||
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
||||
|
||||
lusr = __salt__["user.info"](name)
|
||||
lusr = __salt__["user.info"](name, **_get_root_args(local))
|
||||
if lusr:
|
||||
# The user is present, make it not present
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
ret["comment"] = "User {} set for removal".format(name)
|
||||
ret["comment"] = f"User {name} set for removal"
|
||||
return ret
|
||||
beforegroups = set(salt.utils.user.get_group_list(name))
|
||||
ret["result"] = __salt__["user.delete"](name, purge, force)
|
||||
if salt.utils.platform.is_windows():
|
||||
del_args = {}
|
||||
else:
|
||||
del_args = {"local": local}
|
||||
ret["result"] = __salt__["user.delete"](name, purge, force, **del_args)
|
||||
aftergroups = {g for g in beforegroups if __salt__["group.info"](g)}
|
||||
if ret["result"]:
|
||||
ret["changes"] = {}
|
||||
for g in beforegroups - aftergroups:
|
||||
ret["changes"]["{} group".format(g)] = "removed"
|
||||
ret["changes"][f"{g} group"] = "removed"
|
||||
ret["changes"][name] = "removed"
|
||||
ret["comment"] = "Removed user {}".format(name)
|
||||
ret["comment"] = f"Removed user {name}"
|
||||
else:
|
||||
ret["result"] = False
|
||||
ret["comment"] = "Failed to remove user {}".format(name)
|
||||
ret["comment"] = f"Failed to remove user {name}"
|
||||
return ret
|
||||
|
||||
ret["comment"] = "User {} is not present".format(name)
|
||||
ret["comment"] = f"User {name} is not present"
|
||||
|
||||
return ret
|
||||
|
|
|
@ -27,9 +27,10 @@ def test_add():
|
|||
with patch(
|
||||
"salt.utils.path.which",
|
||||
MagicMock(side_effect=[None, "/bin/groupadd", "/bin/groupadd"]),
|
||||
):
|
||||
) as which_mock:
|
||||
with pytest.raises(CommandExecutionError):
|
||||
groupadd.add("test", 100)
|
||||
which_mock.assert_called_once_with("groupadd")
|
||||
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}):
|
||||
|
@ -40,6 +41,42 @@ def test_add():
|
|||
assert groupadd.add("test", 100, True) is True
|
||||
|
||||
|
||||
def test_add_local():
|
||||
"""
|
||||
Tests if specified group was added with local flag
|
||||
"""
|
||||
with patch(
|
||||
"salt.utils.path.which",
|
||||
MagicMock(return_value="/bin/lgroupadd"),
|
||||
) as which_mock:
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}):
|
||||
assert groupadd.add("test", 100, local=True) is True
|
||||
which_mock.assert_called_once_with("lgroupadd")
|
||||
mock.assert_called_once_with(
|
||||
["/bin/lgroupadd", "-g 100", "test"], python_shell=False
|
||||
)
|
||||
|
||||
|
||||
def test_add_local_with_params():
|
||||
"""
|
||||
Tests if specified group was added with local flag and extra parameters
|
||||
"""
|
||||
with patch(
|
||||
"salt.utils.path.which",
|
||||
MagicMock(return_value="/bin/lgroupadd"),
|
||||
):
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}):
|
||||
assert (
|
||||
groupadd.add("test", 100, local=True, non_unique=True, root="ignored")
|
||||
is True
|
||||
)
|
||||
mock.assert_called_once_with(
|
||||
["/bin/lgroupadd", "-g 100", "test"], python_shell=False
|
||||
)
|
||||
|
||||
|
||||
def test_info():
|
||||
"""
|
||||
Tests the return of group information
|
||||
|
@ -104,15 +141,45 @@ def test_delete():
|
|||
with patch(
|
||||
"salt.utils.path.which",
|
||||
MagicMock(side_effect=[None, "/bin/groupdel"]),
|
||||
):
|
||||
) as which_mock:
|
||||
with pytest.raises(CommandExecutionError):
|
||||
groupadd.delete("test")
|
||||
which_mock.assert_called_once_with("groupdel")
|
||||
|
||||
mock_ret = MagicMock(return_value={"retcode": 0})
|
||||
with patch.dict(groupadd.__salt__, {"cmd.run_all": mock_ret}):
|
||||
assert groupadd.delete("test") is True
|
||||
|
||||
|
||||
def test_delete_local():
|
||||
"""
|
||||
Tests if the specified group was deleted with a local flag
|
||||
"""
|
||||
with patch(
|
||||
"salt.utils.path.which",
|
||||
MagicMock(return_value="/bin/lgroupdel"),
|
||||
) as which_mock:
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}):
|
||||
assert groupadd.delete("test", local=True) is True
|
||||
which_mock.assert_called_once_with("lgroupdel")
|
||||
mock.assert_called_once_with(["/bin/lgroupdel", "test"], python_shell=False)
|
||||
|
||||
|
||||
def test_delete_local_with_params():
|
||||
"""
|
||||
Tests if the specified group was deleted with a local flag and params
|
||||
"""
|
||||
with patch(
|
||||
"salt.utils.path.which",
|
||||
MagicMock(return_value="/bin/lgroupdel"),
|
||||
):
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}):
|
||||
assert groupadd.delete("test", local=True, root="ignored") is True
|
||||
mock.assert_called_once_with(["/bin/lgroupdel", "test"], python_shell=False)
|
||||
|
||||
|
||||
def test_adduser():
|
||||
"""
|
||||
Tests if specified user gets added in the group.
|
||||
|
|
|
@ -26,8 +26,9 @@ def test_add():
|
|||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch(
|
||||
"salt.utils.path.which", MagicMock(return_value="/sbin/useradd")
|
||||
), patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
) as which_mock, patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
assert useradd.add("Salt") is True
|
||||
which_mock.assert_called_once_with("useradd")
|
||||
mock.assert_called_once_with(["/sbin/useradd", "-m", "Salt"], python_shell=False)
|
||||
|
||||
# command found and unsuccessful run
|
||||
|
@ -48,13 +49,33 @@ def test_add():
|
|||
mock.assert_not_called()
|
||||
|
||||
|
||||
def test_add_local():
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch(
|
||||
"salt.utils.path.which", MagicMock(return_value="/sbin/luseradd")
|
||||
) as which_mock, patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
assert useradd.add("Salt", local=True) is True
|
||||
which_mock.assert_called_once_with("luseradd")
|
||||
mock.assert_called_once_with(["/sbin/luseradd", "Salt"], python_shell=False)
|
||||
|
||||
|
||||
def test_add_local_with_params():
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch(
|
||||
"salt.utils.path.which", MagicMock(return_value="/sbin/luseradd")
|
||||
), patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
assert useradd.add("Salt", local=True, usergroup=False, root="ignored") is True
|
||||
mock.assert_called_once_with(["/sbin/luseradd", "-n", "Salt"], python_shell=False)
|
||||
|
||||
|
||||
def test_delete():
|
||||
# command found and successful run
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch(
|
||||
"salt.utils.path.which", MagicMock(return_value="/sbin/userdel")
|
||||
), patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
) as which_mock, patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
assert useradd.delete("Salt") is True
|
||||
which_mock.assert_called_once_with("userdel")
|
||||
mock.assert_called_once_with(["/sbin/userdel", "Salt"], python_shell=False)
|
||||
|
||||
# command found and unsuccessful run
|
||||
|
@ -75,6 +96,28 @@ def test_delete():
|
|||
mock.assert_not_called()
|
||||
|
||||
|
||||
def test_delete_local():
|
||||
# command found and successful run
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch(
|
||||
"salt.utils.path.which", MagicMock(return_value="/sbin/luserdel")
|
||||
) as which_mock, patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
assert useradd.delete("Salt", local=True) is True
|
||||
which_mock.assert_called_once_with("luserdel")
|
||||
mock.assert_called_once_with(["/sbin/luserdel", "Salt"], python_shell=False)
|
||||
|
||||
|
||||
def test_delete_local_with_params():
|
||||
# command found and successful run
|
||||
mock = MagicMock(return_value={"retcode": 0})
|
||||
with patch(
|
||||
"salt.utils.path.which", MagicMock(return_value="/sbin/luserdel")
|
||||
) as which_mock, patch.dict(useradd.__salt__, {"cmd.run_all": mock}):
|
||||
assert useradd.delete("Salt", local=True, force=True, root="ignored") is True
|
||||
which_mock.assert_called_once_with("luserdel")
|
||||
mock.assert_called_once_with(["/sbin/luserdel", "Salt"], python_shell=False)
|
||||
|
||||
|
||||
def test_chgroups():
|
||||
# groups matched - no command run
|
||||
mock = MagicMock()
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import pytest
|
||||
|
||||
import salt.states.group as group
|
||||
from tests.support.mock import MagicMock, patch
|
||||
import salt.utils.platform
|
||||
from tests.support.mock import MagicMock, call, patch
|
||||
|
||||
__context__ = {}
|
||||
|
||||
|
@ -42,6 +43,43 @@ def test_present_with_non_unique_gid():
|
|||
}
|
||||
|
||||
|
||||
def test_present_with_local():
|
||||
group_add_mock = MagicMock(return_value=True)
|
||||
group_info_mock = MagicMock(return_value={"things": "stuff"})
|
||||
with patch(
|
||||
"salt.states.group._changes", MagicMock(side_effect=[False, {}, False])
|
||||
) as changes_mock, patch.dict(
|
||||
group.__salt__,
|
||||
{
|
||||
"group.getent": MagicMock(
|
||||
side_effect=[[{"name": "salt", "gid": 1}], [{"name": "salt", "gid": 1}]]
|
||||
)
|
||||
},
|
||||
), patch.dict(
|
||||
group.__salt__, {"group.add": group_add_mock}
|
||||
), patch.dict(
|
||||
group.__salt__, {"group.chgid": MagicMock(return_value=True)}
|
||||
), patch.dict(
|
||||
group.__salt__, {"group.info": group_info_mock}
|
||||
):
|
||||
ret = group.present("salt", gid=1, non_unique=True, local=True)
|
||||
assert ret["result"]
|
||||
assert changes_mock.call_args_list == [
|
||||
call("salt", 1, None, None, None, local=True),
|
||||
call("salt", 1, None, None, None, local=True),
|
||||
]
|
||||
if salt.utils.platform.is_windows():
|
||||
group_info_mock.assert_called_once_with("salt")
|
||||
group_add_mock.assert_called_once_with(
|
||||
"salt", gid=1, system=False, non_unique=True
|
||||
)
|
||||
else:
|
||||
group_info_mock.assert_called_once_with("salt", root="/")
|
||||
group_add_mock.assert_called_once_with(
|
||||
"salt", gid=1, system=False, local=True, non_unique=True
|
||||
)
|
||||
|
||||
|
||||
def test_present_with_existing_group_and_non_unique_gid():
|
||||
with patch(
|
||||
"salt.states.group._changes",
|
||||
|
@ -76,3 +114,24 @@ def test_present_with_existing_group_and_non_unique_gid():
|
|||
"name": "salt",
|
||||
"result": False,
|
||||
}
|
||||
|
||||
|
||||
def test_absent_with_local():
|
||||
group_delete_mock = MagicMock(return_value=True)
|
||||
group_info_mock = MagicMock(return_value={"things": "stuff"})
|
||||
with patch.dict(group.__salt__, {"group.delete": group_delete_mock}), patch.dict(
|
||||
group.__salt__, {"group.info": group_info_mock}
|
||||
):
|
||||
ret = group.absent("salt", local=True)
|
||||
assert ret == {
|
||||
"changes": {"salt": ""},
|
||||
"comment": "Removed group salt",
|
||||
"name": "salt",
|
||||
"result": True,
|
||||
}
|
||||
if salt.utils.platform.is_windows():
|
||||
group_info_mock.assert_called_once_with("salt")
|
||||
group_delete_mock.assert_called_once_with("salt")
|
||||
else:
|
||||
group_info_mock.assert_called_once_with("salt", root="/")
|
||||
group_delete_mock.assert_called_once_with("salt", local=True)
|
||||
|
|
Loading…
Add table
Reference in a new issue