Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix groups with duplicate GIDs are not returned by get_group_list #62378

Merged
merged 18 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d23403d
fixes saltstack/salt#62377 groups with duplicate GIDs are not returne…
nicholasmhughes Jul 25, 2022
9cd495e
don't run the os.getgrouplist function inside the comprehension
nicholasmhughes Jul 25, 2022
9cd0fb4
support creating groups with non-unique GIDs for testing purposes
nicholasmhughes Jul 25, 2022
1b37b80
handle gid default differently
nicholasmhughes Jul 25, 2022
d821033
fix setting non-unique gid and tests
nicholasmhughes Jul 26, 2022
1bce522
fix tests that don't use gid for groups
nicholasmhughes Jul 26, 2022
f85685b
add non_unique to mac and freebsd group add functions
nicholasmhughes Jul 27, 2022
e11e079
macos and freebsd do not have well-formed duplicate gid support
nicholasmhughes Jul 27, 2022
90dd467
always helpful to actually include the import
nicholasmhughes Jul 28, 2022
fb4e369
Merge branch 'master' into fix-duplicate-group-handling
nicholasmhughes Jul 28, 2022
c9d55f8
skip doesn't seem to be working for some reason
nicholasmhughes Jul 31, 2022
9247ed2
use skipif instead of skipIf
nicholasmhughes Aug 1, 2022
4c355e3
add non_unique to group.present state
nicholasmhughes Aug 2, 2022
87fafab
Merge branch 'master' into fix-duplicate-group-handling
nicholasmhughes Aug 2, 2022
fc0b42f
fix isort pre-commit
nicholasmhughes Aug 2, 2022
4fe2902
fix isort pre-commit
nicholasmhughes Aug 2, 2022
c3e463f
handle modification of an existing group to a non-unique gid
nicholasmhughes Aug 8, 2022
1f46275
test modification of an existing group to a non-unique gid
nicholasmhughes Aug 8, 2022
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/62377.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix groups with duplicate GIDs are not returned by get_group_list
2 changes: 1 addition & 1 deletion salt/modules/aix_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __virtual__():
)


def add(name, gid=None, system=False, root=None):
def add(name, gid=None, system=False, root=None, **kwargs):
"""
Add the specified group

Expand Down
25 changes: 22 additions & 3 deletions salt/modules/groupadd.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ def __virtual__():
)


def add(name, gid=None, system=False, root=None):
def add(name, gid=None, system=False, root=None, non_unique=False):
"""
.. versionchanged:: 3006.0

Add the specified group

name
Expand All @@ -56,6 +58,11 @@ def add(name, gid=None, system=False, root=None):
root
Directory to chroot into

non_unique
Allow creating groups with duplicate (non-unique) GIDs

.. versionadded:: 3006.0

CLI Example:

.. code-block:: bash
Expand All @@ -65,6 +72,8 @@ def add(name, gid=None, system=False, root=None):
cmd = ["groupadd"]
if gid:
cmd.append("-g {}".format(gid))
if non_unique:
cmd.append("-o")
if system and __grains__["kernel"] != "OpenBSD":
cmd.append("-r")

Expand Down Expand Up @@ -200,8 +209,10 @@ def _chattrib(name, key, value, param, root=None):
return info(name, root=root).get(key) == value


def chgid(name, gid, root=None):
def chgid(name, gid, root=None, non_unique=False):
"""
.. versionchanged:: 3006.0

Change the gid for a named group

name
Expand All @@ -213,13 +224,21 @@ def chgid(name, gid, root=None):
root
Directory to chroot into

non_unique
Allow modifying groups with duplicate (non-unique) GIDs

.. versionadded:: 3006.0

CLI Example:

.. code-block:: bash

salt '*' group.chgid foo 4376
"""
return _chattrib(name, "gid", gid, "-g", root=root)
param = "-g"
if non_unique:
param = "-og"
return _chattrib(name, "gid", gid, param, root=root)


def adduser(name, username, root=None):
Expand Down
13 changes: 13 additions & 0 deletions salt/modules/mac_group.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Manage groups on Mac OS 10.7+
"""
import logging

import salt.utils.functools
import salt.utils.itertools
Expand All @@ -13,6 +14,8 @@
except ImportError:
pass

log = logging.getLogger(__name__)


# Define the module's virtual name
__virtualname__ = "group"
Expand All @@ -35,8 +38,16 @@ def __virtual__():

def add(name, gid=None, **kwargs):
"""
.. versionchanged:: 3006.0

Add the specified group

name
Name of the new group

gid
Use GID for the new group

CLI Example:

.. code-block:: bash
Expand All @@ -55,6 +66,8 @@ def add(name, gid=None, **kwargs):
)
if gid is not None and not isinstance(gid, int):
raise SaltInvocationError("gid must be an integer")
if "non_unique" in kwargs:
Ch3LL marked this conversation as resolved.
Show resolved Hide resolved
log.warning("The non_unique parameter is not supported on this platform.")
# check if gid is already in use
gid_list = _list_gids()
if str(gid) in gid_list:
Expand Down
10 changes: 10 additions & 0 deletions salt/modules/pw_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@ def __virtual__():

def add(name, gid=None, **kwargs):
"""
.. versionchanged:: 3006.0

Add the specified group

name
Name of the new group

gid
Use GID for the new group

CLI Example:

.. code-block:: bash
Expand All @@ -49,6 +57,8 @@ def add(name, gid=None, **kwargs):
kwargs = salt.utils.args.clean_kwargs(**kwargs)
if salt.utils.data.is_true(kwargs.pop("system", False)):
log.warning("pw_group module does not support the 'system' argument")
if "non_unique" in kwargs:
log.warning("The non_unique parameter is not supported on this platform.")
if kwargs:
log.warning("Invalid kwargs passed to group.add")

Expand Down
23 changes: 19 additions & 4 deletions salt/states/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,18 @@ def _changes(name, gid=None, addusers=None, delusers=None, members=None):
return change


def present(name, gid=None, system=False, addusers=None, delusers=None, members=None):
def present(
name,
gid=None,
system=False,
addusers=None,
delusers=None,
members=None,
non_unique=False,
):
r"""
.. versionchanged:: 3006.0

Ensure that a group is present

Args:
Expand Down Expand Up @@ -131,6 +141,11 @@ def present(name, gid=None, system=False, addusers=None, delusers=None, members=
Replace existing group members with a list of new members. Cannot be
used in conjunction with addusers or delusers.

non_unique (bool):
Allow creating groups with duplicate (non-unique) GIDs

.. versionadded:: 3006.0

Example:

.. code-block:: yaml
Expand Down Expand Up @@ -190,7 +205,7 @@ def present(name, gid=None, system=False, addusers=None, delusers=None, members=

for key, val in changes.items():
if key == "gid":
__salt__["group.chgid"](name, gid)
__salt__["group.chgid"](name, gid, non_unique=non_unique)
continue
if key == "addusers":
for user in val:
Expand Down Expand Up @@ -224,7 +239,7 @@ def present(name, gid=None, system=False, addusers=None, delusers=None, members=

grps = __salt__["group.getent"]()
# Test if gid is free
if gid is not None:
if gid is not None and not non_unique:
gid_group = None
for lgrp in grps:
if lgrp["gid"] == gid:
Expand All @@ -240,7 +255,7 @@ def present(name, gid=None, system=False, addusers=None, delusers=None, members=
return ret

# Group is not present, make it.
if __salt__["group.add"](name, gid=gid, system=system):
if __salt__["group.add"](name, gid=gid, system=system, non_unique=non_unique):
# if members to be added
grp_members = None
if members:
Expand Down
6 changes: 4 additions & 2 deletions salt/utils/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,11 @@ def get_group_list(user, include_default=True):
# Try os.getgrouplist, available in python >= 3.3
log.trace("Trying os.getgrouplist for '%s'", user)
try:
user_group_list = os.getgrouplist(user, pwd.getpwnam(user).pw_gid)
group_names = [
grp.getgrgid(grpid).gr_name
for grpid in os.getgrouplist(user, pwd.getpwnam(user).pw_gid)
_group.gr_name
for _group in grp.getgrall()
if _group.gr_gid in user_group_list
]
except Exception: # pylint: disable=broad-except
pass
Expand Down
39 changes: 39 additions & 0 deletions tests/pytests/functional/utils/user/test_get_group_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import logging

import pytest

import salt.utils.platform
import salt.utils.user

log = logging.getLogger(__name__)

pytestmark = [
pytest.mark.destructive_test,
pytest.mark.skip_if_not_root,
pytest.mark.skip_on_windows,
]


@pytest.fixture(scope="module")
def user():
with pytest.helpers.create_account(create_group=True) as _account:
yield _account


@pytest.fixture(scope="module")
def dupegroup(user):
grpid = user.group.info.gid
with pytest.helpers.create_group(
name="dupegroup", gid=grpid, members=user.username
) as _group:
yield _group


@pytest.mark.skipif(
salt.utils.platform.is_darwin() or salt.utils.platform.is_freebsd(),
reason="This test should not run on FreeBSD and Mac due to lack of duplicate GID support",
)
def test_get_group_list_with_duplicate_gid_group(user, dupegroup):
group_list = salt.utils.user.get_group_list(user.username)
assert user.group.info.name in group_list
assert dupegroup.name in group_list
78 changes: 78 additions & 0 deletions tests/pytests/unit/states/test_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pytest

import salt.states.group as group
from tests.support.mock import MagicMock, patch

__context__ = {}


def ping():
...


@pytest.fixture
def configure_loader_modules():
return {group: {"__salt__": {"test.ping": ping}, "__opts__": {"test": False}}}


def test_present_with_non_unique_gid():
with patch(
"salt.states.group._changes", MagicMock(side_effect=[False, {}, False])
), patch.dict(
group.__salt__,
{"group.getent": MagicMock(side_effect=[[], [{"name": "salt", "gid": 1}]])},
), patch.dict(
group.__salt__, {"group.add": MagicMock(return_value=True)}
), patch.dict(
group.__salt__, {"group.info": MagicMock(return_value={"things": "stuff"})}
):
ret = group.present("salt", gid=1, non_unique=True)
assert ret == {
"changes": {"things": "stuff"},
"comment": "New group salt created",
"name": "salt",
"result": True,
}
ret = group.present("salt", gid=1, non_unique=False)
assert ret == {
"changes": {},
"comment": "Group salt is not present but gid 1 is already taken by group salt",
"name": "salt",
"result": False,
}


def test_present_with_existing_group_and_non_unique_gid():
with patch(
"salt.states.group._changes",
MagicMock(side_effect=[{"gid": 1}, {}, {"gid": 1}, {"gid": 1}]),
), patch.dict(
group.__salt__,
{
"group.getent": MagicMock(
side_effect=[[{"name": "salt", "gid": 1}], [{"name": "salt", "gid": 1}]]
)
},
), patch.dict(
group.__salt__, {"group.add": MagicMock(return_value=True)}
), patch.dict(
group.__salt__, {"group.chgid": MagicMock(return_value=True)}
), patch.dict(
group.__salt__, {"group.info": MagicMock(return_value={"things": "stuff"})}
):
ret = group.present("salt", gid=1, non_unique=True)
assert ret == {
"changes": {"Final": "All changes applied successfully"},
"comment": "The following group attributes are set to be changed:\ngid: 1\n",
"name": "salt",
"result": True,
}
ret = group.present("salt", gid=1, non_unique=False)
assert ret == {
"changes": {"Failed": {"gid": 1}},
"comment": "The following group attributes are set to be changed:\n"
"gid: 1\n"
"Some changes could not be applied",
"name": "salt",
"result": False,
}
15 changes: 12 additions & 3 deletions tests/support/pytest/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ def remove_stale_proxy_minion_cache_file(proxy_minion, minion_id=None):
class TestGroup:
sminion = attr.ib(repr=False)
name = attr.ib()
gid = attr.ib(default=None)
members = attr.ib(default=None)
_delete_group = attr.ib(init=False, repr=False, default=False)

@sminion.default
Expand All @@ -209,12 +211,19 @@ def info(self):
def __enter__(self):
group = self.sminion.functions.group.info(self.name)
if not group:
ret = self.sminion.functions.group.add(self.name)
ret = self.sminion.functions.group.add(
self.name, gid=self.gid, non_unique=True
)
assert ret
self._delete_group = True
log.debug("Created system group: %s", self)
else:
log.debug("Reusing exising system group: %s", self)
if self.members:
ret = self.sminion.functions.group.members(
self.name, members_list=self.members
)
assert ret
# Run tests
return self

Expand All @@ -231,8 +240,8 @@ def __exit__(self, *_):

@pytest.helpers.register
@contextmanager
def create_group(name=attr.NOTHING, sminion=attr.NOTHING):
with TestGroup(sminion=sminion, name=name) as group:
def create_group(name=attr.NOTHING, sminion=attr.NOTHING, gid=None, members=None):
with TestGroup(sminion=sminion, name=name, gid=gid, members=members) as group:
yield group


Expand Down