From badab99242af4facb564565a7f74bddefa0a9412 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:01:54 +0000 Subject: [PATCH] Fix chpasswd path on NixOS, add password toggle/hints/validation in change-password form Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/de03873d-5cdb-4929-bd4a-4d306916b525 Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- app/sovran_systemsos_web/server.py | 15 ++++- .../static/css/security.css | 65 +++++++++++++++++++ .../static/js/service-detail.js | 42 +++++++++++- 3 files changed, 119 insertions(+), 3 deletions(-) diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index f626b70..b6b620b 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -9,6 +9,7 @@ import json import os import pwd import re +import shutil import socket import subprocess import time @@ -2978,10 +2979,22 @@ async def api_change_password(req: ChangePasswordRequest): if len(req.new_password) < 8: raise HTTPException(status_code=400, detail="Password must be at least 8 characters long.") + # Locate chpasswd binary (NixOS puts it in the Nix store, not /usr/bin) + chpasswd_bin = ( + shutil.which("chpasswd") + or ("/run/current-system/sw/bin/chpasswd" + if os.path.isfile("/run/current-system/sw/bin/chpasswd") else None) + ) + if chpasswd_bin is None: + raise HTTPException( + status_code=500, + detail="chpasswd binary not found. Cannot update system password.", + ) + # Update /etc/shadow via chpasswd try: result = subprocess.run( - ["chpasswd"], + [chpasswd_bin], input=f"free:{req.new_password}", capture_output=True, text=True, diff --git a/app/sovran_systemsos_web/static/css/security.css b/app/sovran_systemsos_web/static/css/security.css index 6201ccd..7871552 100644 --- a/app/sovran_systemsos_web/static/css/security.css +++ b/app/sovran_systemsos_web/static/css/security.css @@ -40,3 +40,68 @@ .security-inline-link:hover { background-color: rgba(180, 100, 0, 0.22); } + +/* ── System change-password form extras ──────────────────────────── */ + +.sys-chpw-header { + margin-bottom: 14px; +} + +.sys-chpw-title { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 4px; +} + +.sys-chpw-desc { + font-size: 0.82rem; + color: var(--text-secondary); + line-height: 1.5; +} + +.pw-input-wrap { + position: relative; + display: flex; + align-items: center; +} + +.pw-input-wrap .matrix-form-input { + padding-right: 2.4rem; + width: 100%; +} + +.pw-toggle-btn { + position: absolute; + right: 6px; + background: none; + border: none; + cursor: pointer; + font-size: 1rem; + padding: 2px 4px; + line-height: 1; + color: var(--text-secondary); + opacity: 0.75; + transition: opacity 0.15s; +} + +.pw-toggle-btn:hover { + opacity: 1; +} + +.pw-hint { + font-size: 0.76rem; + color: var(--text-secondary); + margin-top: 4px; +} + +.pw-credentials-note { + font-size: 0.78rem; + color: #c97a00; + background-color: rgba(180, 100, 0, 0.10); + border-left: 2px solid #c97a00; + border-radius: 4px; + padding: 7px 10px; + margin-bottom: 12px; + line-height: 1.5; +} diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js index 9ab3592..d53572f 100644 --- a/app/sovran_systemsos_web/static/js/service-detail.js +++ b/app/sovran_systemsos_web/static/js/service-detail.js @@ -547,10 +547,22 @@ function openMatrixChangePasswordModal(unit, name, icon) { function openSystemChangePasswordModal(unit, name, icon) { if (!$credsBody) return; $credsBody.innerHTML = + '
' + + '
🔑 Change \'free\' Account Password
' + + '
This updates the system login password for the free user account on this device.
' + + '
' + '
' + - '
' + + '
' + + '' + + '' + + '
' + + '
Password must be at least 8 characters.
' + '
' + - '
' + + '
' + + '' + + '' + + '
' + + '
⚠ After changing, your updated password will appear in the System Passwords credentials tile. Make sure to remember it.
' + '
' + '' + '' + @@ -561,6 +573,20 @@ function openSystemChangePasswordModal(unit, name, icon) { openServiceDetailModal(unit, name, icon); }); + document.getElementById("sys-chpw-new-toggle").addEventListener("click", function() { + var inp = document.getElementById("sys-chpw-new"); + var isHidden = inp.type === "password"; + inp.type = isHidden ? "text" : "password"; + this.textContent = isHidden ? "👁‍🗨" : "👁"; + }); + + document.getElementById("sys-chpw-confirm-toggle").addEventListener("click", function() { + var inp = document.getElementById("sys-chpw-confirm"); + var isHidden = inp.type === "password"; + inp.type = isHidden ? "text" : "password"; + this.textContent = isHidden ? "👁‍🗨" : "👁"; + }); + document.getElementById("sys-chpw-submit-btn").addEventListener("click", async function() { var submitBtn = document.getElementById("sys-chpw-submit-btn"); var resultEl = document.getElementById("sys-chpw-result"); @@ -573,6 +599,18 @@ function openSystemChangePasswordModal(unit, name, icon) { return; } + if (newPassword.length < 8) { + resultEl.className = "matrix-form-result error"; + resultEl.textContent = "Password must be at least 8 characters."; + return; + } + + if (newPassword !== confirmPassword) { + resultEl.className = "matrix-form-result error"; + resultEl.textContent = "Passwords do not match."; + return; + } + submitBtn.disabled = true; submitBtn.textContent = "Changing…"; resultEl.className = "matrix-form-result";