Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions changelog/62087.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix useradd functions hard-coded relative command name
26 changes: 20 additions & 6 deletions salt/modules/useradd.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import salt.utils.data
import salt.utils.decorators.path
import salt.utils.files
import salt.utils.path
import salt.utils.stringutils
import salt.utils.user
from salt.exceptions import CommandExecutionError
Expand Down Expand Up @@ -99,6 +100,16 @@ def _build_gecos(gecos_dict):
).rstrip(",")


def _which(cmd):
"""
Utility function wrapper to error out early if a command is not found
"""
_cmd = salt.utils.path.which(cmd)
if not _cmd:
raise CommandExecutionError("Command '{}' cannot be found".format(cmd))
return _cmd


def _update_gecos(name, key, value, root=None):
"""
Common code to change a user's GECOS information
Expand All @@ -117,7 +128,8 @@ def _update_gecos(name, key, value, root=None):
gecos_data = copy.deepcopy(pre_info)
gecos_data[key] = value

cmd = ["usermod"]
cmd = [_which("usermod")]

if root is not None and __grains__["kernel"] != "AIX":
cmd.extend(("-R", root))
cmd.extend(("-c", _build_gecos(gecos_data), name))
Expand Down Expand Up @@ -209,7 +221,8 @@ def add(

salt '*' user.add name <uid> <gid> <groups> <home> <shell>
"""
cmd = ["useradd"]
cmd = [_which("useradd")]

if shell:
cmd.extend(["-s", shell])
if uid not in (None, ""):
Expand Down Expand Up @@ -342,7 +355,7 @@ def delete(name, remove=False, force=False, root=None):

salt '*' user.delete name remove=True force=True
"""
cmd = ["userdel"]
cmd = [_which("userdel")]

if remove:
cmd.append("-r")
Expand Down Expand Up @@ -421,7 +434,7 @@ def _chattrib(name, key, value, param, persist=False, root=None):
if value == pre_info[key]:
return True

cmd = ["usermod"]
cmd = [_which("usermod")]

if root is not None and __grains__["kernel"] != "AIX":
cmd.extend(("-R", root))
Expand Down Expand Up @@ -556,7 +569,8 @@ def chgroups(name, groups, append=False, root=None):
ugrps = set(list_groups(name))
if ugrps == set(groups):
return True
cmd = ["usermod"]

cmd = [_which("usermod")]

if __grains__["kernel"] != "OpenBSD":
if append and __grains__["kernel"] != "AIX":
Expand Down Expand Up @@ -719,7 +733,7 @@ def chloginclass(name, loginclass, root=None):
if loginclass == get_loginclass(name):
return True

cmd = ["usermod", "-L", loginclass, name]
cmd = [_which("usermod"), "-L", loginclass, name]

if root is not None and __grains__["kernel"] != "AIX":
cmd.extend(("-R", root))
Expand Down
17 changes: 17 additions & 0 deletions tests/pytests/functional/modules/test_user.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import pathlib

import pytest
import salt.modules.useradd as useradd
import salt.utils.platform
from salt.exceptions import CommandExecutionError
from saltfactories.utils import random_string
from tests.support.unit import skipIf

pytestmark = [
pytest.mark.skip_if_not_root,
Expand Down Expand Up @@ -72,3 +75,17 @@ def test_info_after_deletion(user, account):
ret = user.delete(account.username, **kwargs)
assert ret is True
assert not user.info(account.username)


@skipIf(
not (salt.utils.platform.is_darwin() or salt.utils.platform.is_windows()),
"This test should only run (and raise an expected exception) on Windows/Mac due to lack of useradd command",
)
def test_errors_out_when_no_useradd_exists(username):
"""
This test is fairly contrived, as we're calling the useradd module
directly instead of letting the loader grab the appropriate user
module for the given OS
"""
with pytest.raises(CommandExecutionError):
useradd.add(username)
Loading