test_service_password.py #1

  • //
  • guest/
  • russell_jackson/
  • sdp/
  • test/
  • test_service_password.py
  • View
  • Commits
  • Open Download .zip Download (5 KB)
#!/usr/bin/env python3
"""Service account initialization keeps reset policy and checks both mutations."""
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest

ROOT = Path(__file__).resolve().parents[1]
HELPER = ROOT / "Server/Unix/p4/common/bin/sdp_service_user.sh"
BASH = os.environ.get("SDP_TEST_BASH", shutil.which("bash"))


class ServicePassword(unittest.TestCase):
    def run_case(self, policy="1", failure=""):
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            stub = root / "p4"
            stub.write_text("#!" + sys.executable + r'''
import json,os,sys
from pathlib import Path
args=sys.argv[1:]
if args[:2] == ['-ztag','configure']:
    if os.environ['FAILURE']=='query': sys.exit(9)
    print('... Value '+os.environ['POLICY'])
else:
    data=sys.stdin.read()
    with Path(os.environ['TRACE']).open('a') as out: out.write(json.dumps({'argv':args,'input':data})+'\n')
    phase='self' if args[:1]==['-u'] else 'initial'
    if os.environ['FAILURE']==phase: sys.exit(7)
    print('Password updated.')
''')
            stub.chmod(0o755)
            env = dict(os.environ, P4BIN=str(stub), TRACE=str(root / "trace"), POLICY=policy, FAILURE=failure)
            result = subprocess.run([BASH, "-c", 'source "$1"; sdp_initialize_service_password svc_fixture "$2"', "test", str(HELPER), "Final Password * 2026!"], env=env, capture_output=True, text=True)
            trace = [json.loads(x) for x in (root / "trace").read_text().splitlines()] if (root / "trace").exists() else []
            self.assertNotIn("Final Password", result.stdout + result.stderr)
            return result, trace

    def test_non_expiring_policy_preserves_direct_initialization(self):
        result, calls = self.run_case("0")
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertEqual(calls, [{"argv": ["passwd", "svc_fixture"], "input": "Final Password * 2026!\n" * 2}])

    def test_expiring_policy_completes_own_reset_without_password_arguments(self):
        result, calls = self.run_case()
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertEqual([r["argv"] for r in calls], [["passwd", "svc_fixture"], ["-u", "svc_fixture", "passwd"]])
        initial = calls[0]["input"].splitlines()
        self.assertEqual(initial[0], initial[1])
        self.assertRegex(initial[0], r"^SdpInit[0-9a-f]{48}!$")
        self.assertEqual(calls[1]["input"].splitlines(), [initial[0], "Final Password * 2026!", "Final Password * 2026!"])

    def test_query_failure_changes_no_password(self):
        result, calls = self.run_case(failure="query")
        self.assertEqual(result.returncode, 9)
        self.assertEqual(calls, [])

    def test_invalid_policy_changes_no_password(self):
        result, calls = self.run_case("unknown")
        self.assertNotEqual(result.returncode, 0)
        self.assertEqual(calls, [])

    def test_initial_failure_prevents_self_change(self):
        result, calls = self.run_case(failure="initial")
        self.assertEqual(result.returncode, 7)
        self.assertEqual(len(calls), 1)

    def test_self_change_failure_is_not_success(self):
        result, calls = self.run_case(failure="self")
        self.assertEqual(result.returncode, 7)
        self.assertEqual(len(calls), 2)

    def test_installer_service_password_path_uses_checked_helper(self):
        # Execute the actual branch with an encrypted input and a failed helper;
        # this catches an unchecked password failure in configure_new_server.
        source = (ROOT / "Server/Unix/setup/configure_new_server.sh").read_text()
        start = source.index('      source "$P4CBIN/sdp_service_user.sh"')
        end = source.index('      unset ServicePassword', start) + len('      unset ServicePassword')
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            (root / "sdp_service_user.sh").write_text('sdp_initialize_service_password() { return 7; }\n')
            password = root / "password"
            password.write_text("installer fixture password")
            env = dict(os.environ, P4CBIN=str(root), P4SERVICEUSER="svc_fixture", CleartextPasswordFile=str(password), EncryptedPasswordFile=str(root / "absent"))
            result = subprocess.run([BASH, "-c", 'ErrorCount=0; errmsg(){ ErrorCount=$((ErrorCount+1)); }; bail(){ exit 99; };\n' + source[start:end] + '\nexit "$ErrorCount"'], env=env, capture_output=True, text=True)
            self.assertEqual(result.returncode, 1)
            self.assertNotIn("installer fixture password", result.stdout + result.stderr)


if __name__ == "__main__":
    unittest.main()
# Change User Description Committed
#1 34004 Russell C. Jackson (Rusty) Improve SDP backup/recovery, Maintenance, logging and cloud operation.

Add owned checkpoint locks, recovery-aware retention, preserved database switches,
bounded cleanup and coordinated AWS/Azure snapshots. Fix service-user password
initialization, edge staging, standby promotion state parsing, proxy cache setup,
typed depot verification and historical-server configuration/error handling.
Remove unused OTP programs; keep Windows removed, server.locks cleanup and the
journal-count condition unchanged. Add no global shell strict modes.

Update operational documentation and add reproducible mkdirs.sh/mkdirs.cfg
container assurance. Validate Ubuntu 26.04 and UBI 8.10/9.8/10.2 across both Python
runtimes: 44 stages; 428 offline tests per runtime/OS, zero offline skips;
19 deployed-archive comparisons per combination. Three six-node topology runs
pass all 30 recovery/failure scenarios. P4D 2023.1 through 2026.1 on UBI 8/9
pass all 14 cells and 98 scenarios. Static, documentation and provenance checks
pass. Containers do not certify every fleet kernel, patch build or integration.

Tested source manifest SHA-256:
e1a23e17b8ced8c1148d9a250589977c6f6e98606bf2a81dc3ff5d7c909e3695
Deployed comparison archive SHA-256:
1bd23217356ee3808e040d39aea5be9519bda7c8f2b4924656a70aff8ad7293a