From d65da4e1de1a544438d98d0adb6f8cfecac6488b Mon Sep 17 00:00:00 2001 From: aborah-sudo Date: Mon, 4 May 2026 14:15:43 +0530 Subject: [PATCH 1/4] Tests: Add passlib to requirements.txt for proper crypt hash generation Added passlib dependency for generating valid SHA-512 crypt hashes --- tests/system/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/system/requirements.txt b/tests/system/requirements.txt index e526a92bee..a1d922d8ed 100644 --- a/tests/system/requirements.txt +++ b/tests/system/requirements.txt @@ -1,5 +1,6 @@ flaky jc +passlib pytest git+https://github.com/next-actions/pytest-mh@1.0.21 git+https://github.com/next-actions/pytest-ticket From 06718ff7f35c60f634d1dc23eca21e2838737398 Mon Sep 17 00:00:00 2001 From: aborah-sudo Date: Fri, 8 May 2026 13:57:59 +0530 Subject: [PATCH 2/4] Tests: Rename user who is member of a group This is the transformation to Python of the test located in `tests/usertools/01/10_usermod_rename_user_in_group.test` which checks that `usermod` can rename user who is member of a group --- tests/system/tests/test_usermod.py | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/system/tests/test_usermod.py b/tests/system/tests/test_usermod.py index 8378ef1137..e4f42d2a5a 100644 --- a/tests/system/tests/test_usermod.py +++ b/tests/system/tests/test_usermod.py @@ -171,3 +171,38 @@ def test_usermod__set_expire_date_with_empty_date(shadow: Shadow, expiration_dat assert result is not None, "User should be found" assert result.name == "tuser1", "Incorrect username" assert result.expiration_date is None, "Expiration date should be empty" + + +@pytest.mark.topology(KnownTopology.Shadow) +def test_usermod__rename_user_in_group(shadow: Shadow): + """ + :title: Rename user who is member of a group + :setup: + 1. Create group + 2. Create user with additional group membership + 3. Rename user + :steps: + 1. Check passwd entry + 2. Check group entry for user's primary group + 3. Check group entry for secondary group membership + :expectedresults: + 1. Passwd entry for renamed user exists + 2. User's primary group still exists with old name + 3. User is member of group with new name + :customerscenario: False + """ + shadow.groupadd("tgroup") + shadow.useradd("-G tgroup tuser1") + shadow.usermod("-l tuser2 tuser1") + + passwd_entry = shadow.tools.getent.passwd("tuser2") + assert passwd_entry is not None, "User should be found" + assert passwd_entry.name == "tuser2", "Incorrect username" + + group_entry = shadow.tools.getent.group("tuser1") + assert group_entry is not None, "Primary group should still exist" + assert group_entry.name == "tuser1", "Primary group should keep old name" + + tgroup_group = shadow.tools.getent.group("tgroup") + assert tgroup_group is not None, "tgroup group should exist" + assert "tuser2" in tgroup_group.members, "User should be in tgroup group with new name" From 44a190861f8b4382e23e134028c5972d04d61ecd Mon Sep 17 00:00:00 2001 From: aborah-sudo Date: Fri, 8 May 2026 14:01:53 +0530 Subject: [PATCH 3/4] Tests: Change user password This is the transformation to Python of the test located in `tests/usertools/01/11_usermod_change_password.test` which checks that `usermod` can change user password --- tests/system/tests/test_usermod.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/system/tests/test_usermod.py b/tests/system/tests/test_usermod.py index e4f42d2a5a..7a8d33e3b3 100644 --- a/tests/system/tests/test_usermod.py +++ b/tests/system/tests/test_usermod.py @@ -5,6 +5,7 @@ from __future__ import annotations import pytest +from passlib.hash import sha512_crypt from pytest_mh.conn import ProcessError from framework.roles.shadow import Shadow @@ -206,3 +207,28 @@ def test_usermod__rename_user_in_group(shadow: Shadow): tgroup_group = shadow.tools.getent.group("tgroup") assert tgroup_group is not None, "tgroup group should exist" assert "tuser2" in tgroup_group.members, "User should be in tgroup group with new name" + + +@pytest.mark.topology(KnownTopology.Shadow) +def test_usermod__change_password(shadow: Shadow): + """ + :title: Change user password + :setup: + 1. Create user + 2. Change password using usermod -p + :steps: + 1. Check shadow entry has new password + :expectedresults: + 1. Password is updated in shadow file + :customerscenario: False + """ + shadow.useradd("tuser1") + + password = "Secret123" + password_hash = sha512_crypt.hash(password) + shadow.usermod(f"-p '{password_hash}' tuser1") + + shadow_entry = shadow.tools.getent.shadow("tuser1") + assert shadow_entry is not None, "User should be found" + assert shadow_entry.password is not None, "Password should not be None" + assert shadow_entry.password.startswith("$6$"), "Password should be SHA-512 crypt hash" From ed06979088cc636f608f560052d3c261d01a46b3 Mon Sep 17 00:00:00 2001 From: aborah-sudo Date: Fri, 8 May 2026 14:04:17 +0530 Subject: [PATCH 4/4] Tests: Unlock user password This is the transformation to Python of the test located in `tests/usertools/01/11_usermod_lock_password.test` which checks that `usermod` can lock user password --- tests/system/tests/test_usermod.py | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/system/tests/test_usermod.py b/tests/system/tests/test_usermod.py index 7a8d33e3b3..fd5511cb70 100644 --- a/tests/system/tests/test_usermod.py +++ b/tests/system/tests/test_usermod.py @@ -232,3 +232,43 @@ def test_usermod__change_password(shadow: Shadow): assert shadow_entry is not None, "User should be found" assert shadow_entry.password is not None, "Password should not be None" assert shadow_entry.password.startswith("$6$"), "Password should be SHA-512 crypt hash" + + +@pytest.mark.topology(KnownTopology.Shadow) +def test_usermod__unlock_password(shadow: Shadow): + """ + :title: Unlock user password + :setup: + 1. Create user + 2. Set password + :steps: + 1. Lock password with usermod -L + 2. Check shadow entry has locked password + 3. Unlock password with usermod -U + 4. Check shadow entry has unlocked password + :expectedresults: + 1. Password is locked + 2. Password has ! prefix when locked + 3. Password is unlocked + 4. Password no longer has ! prefix + :customerscenario: False + """ + shadow.useradd("tuser1") + + password = "Secret123" + password_hash = sha512_crypt.hash(password) + + shadow.usermod(f"-p '{password_hash}' tuser1") + shadow.usermod("-L tuser1") + + shadow_entry = shadow.tools.getent.shadow("tuser1") + assert shadow_entry is not None, "User should be found" + assert shadow_entry.password is not None, "Password should not be None" + assert shadow_entry.password.startswith("!"), "Password should be locked with ! prefix" + assert shadow_entry.password == f"!{password_hash}", "Password should have ! prefix when locked" + + shadow.usermod("-U tuser1") + + shadow_entry = shadow.tools.getent.shadow("tuser1") + assert shadow_entry is not None, "User should be found" + assert shadow_entry.password == password_hash, "Password should be unlocked"