diff --git a/tests/system/framework/hosts/shadow.py b/tests/system/framework/hosts/shadow.py index 0696ec0de4..ba76ef9676 100644 --- a/tests/system/framework/hosts/shadow.py +++ b/tests/system/framework/hosts/shadow.py @@ -398,21 +398,27 @@ def __setitem__(self, key: str, value: str) -> None: self.logger.info(f"Setting {key}={value} in {self.path} on {self.host.hostname}") sep = self.separator - grep_match = "=" if sep == "=" else "\\s" - awk_match = "=" if sep == "=" else "\\\\s" + if sep == "=": + grep_pattern = f"^{key}=" + grep_comment_pattern = f"^# *{key}=" + awk_pattern = "=" + else: + grep_pattern = f"^{key}\\s+" + grep_comment_pattern = f"^# *{key}\\s+" + awk_pattern = "[[:space:]]+" # Escape special characters for awk escaped_value = value.replace("/", "\\/") self.host.conn.run( f""" - if grep -q '^{key}{grep_match}' {self.path}; then + if grep -qE '{grep_pattern}' {self.path}; then awk -v key="{key}" -v val="{escaped_value}" \\ - '{{if ($0 ~ "^" key "{awk_match}") print key "{sep}" val; else print $0}}' \\ + '{{if ($0 ~ "^" key "{awk_pattern}") print key "{sep}" val; else print $0}}' \\ {self.path} > {self.path}.tmp && mv {self.path}.tmp {self.path} - elif grep -q '^#\\s*{key}{grep_match}' {self.path}; then + elif grep -qE '{grep_comment_pattern}' {self.path}; then awk -v key="{key}" -v val="{escaped_value}" \\ - '{{if ($0 ~ "^#\\\\s*" key "{awk_match}") print key "{sep}" val; else print $0}}' \\ + '{{if ($0 ~ "^# *" key "{awk_pattern}") print key "{sep}" val; else print $0}}' \\ {self.path} > {self.path}.tmp && mv {self.path}.tmp {self.path} else echo '{key}{sep}{value}' >> {self.path} @@ -432,5 +438,8 @@ def __delitem__(self, key: str) -> None: self._ensure_exists() self.logger.info(f"Removing {key} from {self.path} on {self.host.hostname}") - match = "=" if self.separator == "=" else "\\s" - self.host.conn.run(f"sed -i 's/^{key}{match}.*/#&/' {self.path}", log_level=ProcessLogLevel.Error) + if self.separator == "=": + pattern = f"^{key}=" + else: + pattern = f"^{key}\\s+" + self.host.conn.run(f"sed -iE 's/{pattern}.*/#&/' {self.path}", log_level=ProcessLogLevel.Error) diff --git a/tests/system/tests/test_groupadd.py b/tests/system/tests/test_groupadd.py index a9655805d2..fa81d4049f 100644 --- a/tests/system/tests/test_groupadd.py +++ b/tests/system/tests/test_groupadd.py @@ -512,3 +512,92 @@ def test_groupadd__invalid_arguments(shadow: Shadow, args: str): shadow.groupadd(args) assert exc_info.value.rc == 2, f"Expected return code 2(invalid usage), got {exc_info.value.rc}" + + +@pytest.mark.topology(KnownTopology.Shadow) +def test_groupadd__no_gshadow(shadow: Shadow): + """ + :title: Group creation succeeds when /etc/gshadow does not exist + :setup: + 1. Remove /etc/gshadow file + 2. Set FORCE_SHADOW=no in /etc/login.defs + :steps: + 1. Create group + 2. Check group entry + 3. Check that /etc/gshadow file is not present + :expectedresults: + 1. Group is created + 2. Group entry is found + 3. /etc/gshadow file is not found + :customerscenario: False + """ + shadow.fs.rm("/etc/gshadow") + + shadow.login_defs["FORCE_SHADOW"] = "no" + + shadow.groupadd("tgroup") + + group_entry = shadow.tools.getent.group("tgroup") + assert group_entry is not None, "Group should be found" + assert group_entry.name == "tgroup", "Incorrect groupname" + + gshadow_file = shadow.fs.exists("/etc/gshadow") + assert not gshadow_file, "/etc/gshadow file should not be found" + + +@pytest.mark.topology(KnownTopology.Shadow) +def test_groupadd__non_unique_requires_gid(shadow: Shadow): + """ + :title: Groupadd command fails when non-unique option is used without GID + :setup: + 1. None required + :steps: + 1. Attempt to create group + 2. Verify that groupadd command fails + 3. Check group and gshadow entries + :expectedresults: + 1. Group is not created + 2. groupadd command fails with error (invalid usage) + 3. No group or gshadow entries are found + :customerscenario: False + """ + with pytest.raises(ProcessError) as exc_info: + shadow.groupadd("-o tgroup") + + assert exc_info.value.rc == 2, f"Expected return code 2 (invalid usage), got {exc_info.value.rc}" + + group_entry = shadow.tools.getent.group("tgroup") + assert group_entry is None, "Group should not be found" + + if shadow.host.features["gshadow"]: + gshadow_entry = shadow.tools.getent.gshadow("tgroup") + assert gshadow_entry is None, "Group should not be found" + + +@pytest.mark.topology(KnownTopology.Shadow) +def test_groupadd__invalid_option(shadow: Shadow): + """ + :title: Group creation fails with invalid option + :setup: + 1. None required + :steps: + 1. Attempt to create group + 2. Verify that groupadd command fails + 3. Check group and gshadow entries + :expectedresults: + 1. Group is not created + 2. groupadd command fails with error (invalid usage) + 3. No group or gshadow entries are found + :customerscenario: False + """ + with pytest.raises(ProcessError) as exc_info: + shadow.groupadd("-invalid tgroup") + + assert exc_info.value.rc == 2, f"Expected return code 2 (invalid usage), got {exc_info.value.rc}" + + group_entry = shadow.tools.getent.group("tgroup") + assert group_entry is None, "Group should not be found" + + if shadow.host.features["gshadow"]: + gshadow_entry = shadow.tools.getent.gshadow("tgroup") + assert gshadow_entry is None, "Group should not be found"