leapp-repository/SOURCES/0057-Ignore-external-accounts-in-etc-passwd.patch
2023-03-29 09:01:41 +00:00

183 lines
7.1 KiB
Diff

From 6ada6553eadc08fbbaf69d54129e6d3cc0c214e3 Mon Sep 17 00:00:00 2001
From: PeterMocary <petermocary@gmail.com>
Date: Fri, 26 Aug 2022 15:44:50 +0200
Subject: [PATCH 57/63] Ignore external accounts in /etc/passwd
The /etc/passwd can contain special entries to selectively incorporate entries
from another service source such as NIS or LDAP. These entries don't need to
contain all the fields that are normally present in the /etc/passwd entry and
would cause the upgrade failure in facts phase.
---
.../systemfacts/libraries/systemfacts.py | 48 ++++++++---
.../systemfacts/tests/test_systemfacts.py | 85 ++++++++++++++++++-
2 files changed, 121 insertions(+), 12 deletions(-)
diff --git a/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py b/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py
index e34cb86b..d1eeb28c 100644
--- a/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py
+++ b/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py
@@ -60,13 +60,26 @@ def anyhasprefix(value, prefixes):
@aslist
def _get_system_users():
+ skipped_user_names = []
for p in pwd.getpwall():
- yield User(
- name=p.pw_name,
- uid=p.pw_uid,
- gid=p.pw_gid,
- home=p.pw_dir
- )
+ # The /etc/passwd can contain special entries from another service source such as NIS or LDAP. These entries
+ # start with + or - sign and might not contain all the mandatory fields, thus are skipped along with other
+ # invalid entries for now. The UID and GID fields are always defined by pwd to 0 even when not specifiead in
+ # /etc/passwd.
+ if p.pw_name != '' and not p.pw_name.startswith(('+', '-')) and p.pw_dir:
+ yield User(
+ name=p.pw_name,
+ uid=p.pw_uid,
+ gid=p.pw_gid,
+ home=p.pw_dir
+ )
+ else:
+ skipped_user_names.append(p.pw_name)
+
+ if skipped_user_names:
+ api.current_logger().debug("These users from /etc/passwd that are special entries for service "
+ "like NIS, or don't contain all mandatory fields won't be included "
+ "in UsersFacts: {}".format(skipped_user_names))
def get_system_users_status():
@@ -76,12 +89,25 @@ def get_system_users_status():
@aslist
def _get_system_groups():
+ skipped_group_names = []
for g in grp.getgrall():
- yield Group(
- name=g.gr_name,
- gid=g.gr_gid,
- members=g.gr_mem
- )
+ # The /etc/group can contain special entries from another service source such as NIS or LDAP. These entries
+ # start with + or - sign and might not contain all the mandatory fields, thus are skipped along with other
+ # invalid entries for now. The GID field is always defined by pwd to 0 even when not specifiead in
+ # /etc/group.
+ if g.gr_name != '' and not g.gr_name.startswith(('+', '-')):
+ yield Group(
+ name=g.gr_name,
+ gid=g.gr_gid,
+ members=g.gr_mem
+ )
+ else:
+ skipped_group_names.append(g.gr_name)
+
+ if skipped_group_names:
+ api.current_logger().debug("These groups from /etc/group that are special entries for service "
+ "like NIS, or don't contain all mandatory fields won't be included "
+ "in GroupsFacts: {}".format(skipped_group_names))
def get_system_groups_status():
diff --git a/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py b/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py
index f94003d5..badf174c 100644
--- a/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py
+++ b/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py
@@ -1,4 +1,11 @@
-from leapp.libraries.actor.systemfacts import anyendswith, anyhasprefix, aslist
+import grp
+import pwd
+
+import pytest
+
+from leapp.libraries.actor.systemfacts import _get_system_groups, _get_system_users, anyendswith, anyhasprefix, aslist
+from leapp.libraries.common.testutils import logger_mocked
+from leapp.libraries.stdlib import api
from leapp.snactor.fixture import current_actor_libraries
@@ -33,3 +40,79 @@ def test_aslist(current_actor_libraries):
r = local()
assert isinstance(r, list) and r[0] and r[2] and not r[1]
+
+
+@pytest.mark.parametrize(
+ ('etc_passwd_names', 'etc_passwd_directory', 'skipped_user_names'),
+ [
+ (['root', 'unbound', 'dbus'], '/', []),
+ (['root', '+@scanners', 'dbus', '-@usrc', ''], '/', ['+@scanners', '-@usrc', '']),
+ (['root', '+@scanners', 'dbus'], '', ['root', '+@scanners', 'dbus']),
+ ]
+)
+def test_get_system_users(monkeypatch, etc_passwd_names, etc_passwd_directory, skipped_user_names):
+
+ class MockedPwdEntry(object):
+ def __init__(self, pw_name, pw_uid, pw_gid, pw_dir):
+ self.pw_name = pw_name
+ self.pw_uid = pw_uid
+ self.pw_gid = pw_gid
+ self.pw_dir = pw_dir
+
+ etc_passwd_contents = []
+ for etc_passwd_name in etc_passwd_names:
+ etc_passwd_contents.append(MockedPwdEntry(etc_passwd_name, 0, 0, etc_passwd_directory))
+
+ monkeypatch.setattr(pwd, 'getpwall', lambda: etc_passwd_contents)
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+
+ _get_system_users()
+
+ if skipped_user_names:
+ assert len(api.current_logger().dbgmsg) == 1
+
+ for skipped_user_name in skipped_user_names:
+ assert skipped_user_name in api.current_logger().dbgmsg[0]
+
+ for user_name in etc_passwd_names:
+ if user_name not in skipped_user_names:
+ assert user_name not in api.current_logger().dbgmsg[0]
+ else:
+ assert not api.current_logger().dbgmsg
+
+
+@pytest.mark.parametrize(
+ ('etc_group_names', 'skipped_group_names'),
+ [
+ (['cdrom', 'floppy', 'tape'], []),
+ (['cdrom', '+@scanners', 'floppy', '-@usrc', ''], ['+@scanners', '-@usrc', '']),
+ ]
+)
+def test_get_system_groups(monkeypatch, etc_group_names, skipped_group_names):
+
+ class MockedGrpEntry(object):
+ def __init__(self, gr_name, gr_gid, gr_mem):
+ self.gr_name = gr_name
+ self.gr_gid = gr_gid
+ self.gr_mem = gr_mem
+
+ etc_group_contents = []
+ for etc_group_name in etc_group_names:
+ etc_group_contents.append(MockedGrpEntry(etc_group_name, 0, []))
+
+ monkeypatch.setattr(grp, 'getgrall', lambda: etc_group_contents)
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+
+ _get_system_groups()
+
+ if skipped_group_names:
+ assert len(api.current_logger().dbgmsg) == 1
+
+ for skipped_group_name in skipped_group_names:
+ assert skipped_group_name in api.current_logger().dbgmsg[0]
+
+ for group_name in etc_group_names:
+ if group_name not in skipped_group_names:
+ assert group_name not in api.current_logger().dbgmsg[0]
+ else:
+ assert not api.current_logger().dbgmsg
--
2.39.0