aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xarchsetup10
-rw-r--r--tests/installer-steps/test_set_user_password.py56
2 files changed, 65 insertions, 1 deletions
diff --git a/archsetup b/archsetup
index 9eea404..cefd9f2 100755
--- a/archsetup
+++ b/archsetup
@@ -1151,6 +1151,14 @@ EOF
}
### Create User
+set_user_password() {
+ # $1 = username; reads $password. set -e is off (line 21), so a bare
+ # `chpasswd` that failed would silently leave the user with no password and
+ # no log entry. Guard it: a failure aborts loudly instead.
+ echo "$1:$password" | chpasswd 2>> "$logfile" \
+ || error_fatal "setting password for $1" "$?" "set it by hand: passwd $1"
+}
+
create_user() {
display "title" "User Creation"
@@ -1165,7 +1173,7 @@ create_user() {
error_fatal "adding user '$username'" "$?" "run the useradd manually to see why it failed"
display "task" "assigning the password"
- echo "$username:$password" | chpasswd # any text is allowable! be careful!
+ set_user_password "$username"
unset password # clear from memory after use
display "task" "adding to appropriate groups"
diff --git a/tests/installer-steps/test_set_user_password.py b/tests/installer-steps/test_set_user_password.py
new file mode 100644
index 0000000..9f21b3a
--- /dev/null
+++ b/tests/installer-steps/test_set_user_password.py
@@ -0,0 +1,56 @@
+"""Test that the primary user's password is set under a guard.
+
+archsetup runs with `set -e` OFF (line 21), so an unguarded `chpasswd` that
+fails silently leaves the primary user with no password and no log entry -- an
+unloggable account on a fresh install. set_user_password guards the chpasswd
+with error_fatal so a failure aborts loudly instead of passing unnoticed.
+
+Method: sed-extract set_user_password from the real `archsetup`, run it with a
+fake chpasswd (controlled exit) and a fake error_fatal recorder. Assert the
+guard fires on failure and stays quiet on success.
+
+Run from repo root:
+ python3 -m unittest tests.installer-steps.test_set_user_password
+"""
+
+import os
+import subprocess
+import textwrap
+import unittest
+
+REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
+ARCHSETUP = os.path.join(REPO_ROOT, "archsetup")
+
+
+def run_set_password(chpasswd_rc):
+ """Run set_user_password with a fake chpasswd exiting chpasswd_rc."""
+ script = textwrap.dedent(f"""\
+ password="hunter2"
+ logfile=/dev/null
+ display() {{ :; }}
+ chpasswd() {{ cat >/dev/null; return {chpasswd_rc}; }}
+ error_fatal() {{ echo "FATAL: $1"; exit 9; }}
+ source <(sed -n '/^set_user_password() {{/,/^}}/p' "{ARCHSETUP}")
+ set_user_password "alice"
+ echo "REACHED-END"
+ """)
+ return subprocess.run(
+ ["bash", "-c", script], capture_output=True, text=True, timeout=10,
+ )
+
+
+class SetUserPassword(unittest.TestCase):
+ def test_success_does_not_abort(self):
+ r = run_set_password(0)
+ self.assertIn("REACHED-END", r.stdout)
+ self.assertNotIn("FATAL", r.stdout)
+
+ def test_failure_aborts_with_error_fatal(self):
+ r = run_set_password(1)
+ self.assertIn("FATAL: setting password for alice", r.stdout,
+ "a chpasswd failure must abort via error_fatal")
+ self.assertNotIn("REACHED-END", r.stdout)
+
+
+if __name__ == "__main__":
+ unittest.main()