From ff1632dcda3c5f0f280fc52db9ea63cde5c9a299 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:44:57 +0000 Subject: [PATCH] Fix Change Passwords button: add API endpoint, system password modal, fix security banner link Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/bf43bea9-9f93-4f7b-b6fd-c76714e7f25b Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- app/sovran_systemsos_web/server.py | 63 +++++++++++++++++ .../static/js/features.js | 2 +- .../static/js/service-detail.js | 68 +++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index a7fb835..f626b70 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -2952,6 +2952,69 @@ async def api_security_status(): return {"status": status, "warning": warning} +# ── System password change ──────────────────────────────────────── + +FREE_PASSWORD_FILE = "/var/lib/secrets/free-password" + + +class ChangePasswordRequest(BaseModel): + new_password: str + confirm_password: str + + +@app.post("/api/change-password") +async def api_change_password(req: ChangePasswordRequest): + """Change the system 'free' user password. + + Updates /etc/shadow via chpasswd and writes the new password to + /var/lib/secrets/free-password so the Hub credentials view stays in sync. + Also clears the legacy security-status and security-warning files so the + security banner disappears after a successful change. + """ + if not req.new_password: + raise HTTPException(status_code=400, detail="New password must not be empty.") + if req.new_password != req.confirm_password: + raise HTTPException(status_code=400, detail="Passwords do not match.") + if len(req.new_password) < 8: + raise HTTPException(status_code=400, detail="Password must be at least 8 characters long.") + + # Update /etc/shadow via chpasswd + try: + result = subprocess.run( + ["chpasswd"], + input=f"free:{req.new_password}", + capture_output=True, + text=True, + ) + if result.returncode != 0: + detail = (result.stderr or result.stdout).strip() or "chpasswd failed." + raise HTTPException(status_code=500, detail=detail) + except HTTPException: + raise + except Exception as exc: + raise HTTPException(status_code=500, detail=f"Failed to update system password: {exc}") + + # Write new password to secrets file so Hub credentials stay in sync + try: + os.makedirs(os.path.dirname(FREE_PASSWORD_FILE), exist_ok=True) + with open(FREE_PASSWORD_FILE, "w") as f: + f.write(req.new_password) + os.chmod(FREE_PASSWORD_FILE, 0o600) + except Exception as exc: + raise HTTPException(status_code=500, detail=f"Failed to write secrets file: {exc}") + + # Clear legacy security status so the warning banner is removed + for path in (SECURITY_STATUS_FILE, SECURITY_WARNING_FILE): + try: + os.remove(path) + except FileNotFoundError: + pass + except Exception: + pass # Non-fatal; don't block a successful password change + + return {"ok": True} + + # ── Matrix user management ──────────────────────────────────────── MATRIX_USERS_FILE = "/var/lib/secrets/matrix-users" diff --git a/app/sovran_systemsos_web/static/js/features.js b/app/sovran_systemsos_web/static/js/features.js index eaeee3c..831ceff 100644 --- a/app/sovran_systemsos_web/static/js/features.js +++ b/app/sovran_systemsos_web/static/js/features.js @@ -606,7 +606,7 @@ function renderAutolaunchToggle(enabled) { '
'; } diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js index 9ed9eef..9ab3592 100644 --- a/app/sovran_systemsos_web/static/js/service-detail.js +++ b/app/sovran_systemsos_web/static/js/service-detail.js @@ -280,6 +280,10 @@ async function openServiceDetailModal(unit, name, icon) { '' + '' + '' : "") + + (unit === "root-password-setup.service" ? + '