Compare commits

..

No commits in common. "6f4958ba89b9d7b3160272a6c037aa6d4ee89352" and "63ad19eb128d1b5f5aee8c48503c8bf5e21e9f0f" have entirely different histories.

2 changed files with 2 additions and 67 deletions

15
app.py
View file

@ -22,7 +22,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Docker tab caches stale container IDs, so the footer is the reliable way
# to confirm a `docker compose up -d --build` actually took effect.
# Both are env-overridable if the deploy pipeline ever injects them.
APP_VERSION = os.environ.get("APP_VERSION", "1.6.1")
APP_VERSION = os.environ.get("APP_VERSION", "1.6.0")
APP_VERSION_DATE = os.environ.get("APP_VERSION_DATE", "2026-08-27")
# Must point at a path that survives `docker compose up --build` — the
@ -45,19 +45,6 @@ def save_settings(data):
with open(SETTINGS_FILE, "w") as f:
json.dump(existing, f, indent=2)
def delete_setting(key):
"""Remove a key outright. save_settings() only ever merges, so it can't
take something away — used to retire the plaintext shared password once
real accounts exist. Returns True if the key was there."""
settings = load_settings()
if key not in settings:
return False
del settings[key]
os.makedirs(os.path.dirname(SETTINGS_FILE) or ".", exist_ok=True)
with open(SETTINGS_FILE, "w") as f:
json.dump(settings, f, indent=2)
return True
app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY") or secrets.token_hex(32)

View file

@ -12,17 +12,10 @@ Run it inside the container, where Werkzeug and the real settings.json are:
docker exec -it arr-summary python3 manage_users.py add <username>
docker exec -it arr-summary python3 manage_users.py passwd <username>
docker exec -it arr-summary python3 manage_users.py remove <username>
docker exec -it arr-summary python3 manage_users.py retire-shared-password
The first `add` switches the dashboard from its single shared password to
per-user logins. Until then the old LOGIN_PASSWORD keeps working, so you
can't lock yourself out by deploying this.
Once accounts exist and you've confirmed they sign in,
`retire-shared-password` deletes the old plaintext login_password from
settings.json. It refuses to run while no accounts exist, because with
nothing configured at all the dashboard treats that as dev mode and lets
anyone in without a password.
"""
import sys
@ -31,10 +24,7 @@ import getpass
from app import (
SETTINGS_FILE,
PASSWORD_HASH_METHOD,
LOGIN_PASSWORD,
save_settings,
delete_setting,
load_settings,
load_users,
generate_password_hash,
)
@ -132,49 +122,7 @@ def cmd_remove(args):
return 0
def cmd_retire_shared_password(_args):
"""Delete the plaintext login_password from settings.json.
Guarded, because removing it while nothing else is configured makes
get_active_password() empty — and the login route reads "no credentials
at all" as dev mode and lets anyone straight in.
"""
users = load_users()
if not users:
print("Refusing: no accounts exist yet, so the shared password is still",
file=sys.stderr)
print("the only way in. Run `add <username>` first and check you can",
file=sys.stderr)
print("sign in with it, then run this again.", file=sys.stderr)
return 1
if "login_password" not in load_settings():
print("Nothing to do — settings.json has no login_password.")
if LOGIN_PASSWORD:
print()
print("Note: LOGIN_PASSWORD is still set in the environment. It's")
print("ignored while accounts exist, but clear it from .env too if")
print("you want the old shared password gone entirely.")
return 0
delete_setting("login_password")
print(f"Removed the plaintext login_password from {SETTINGS_FILE}.")
print(f"{len(users)} account(s) remain in use: {', '.join(sorted(users))}")
if LOGIN_PASSWORD:
print()
print("LOGIN_PASSWORD is still set in the environment. It's ignored while")
print("accounts exist, but clear it from .env and restart to be rid of it.")
print("Don't clear it before you've confirmed the accounts work.")
return 0
COMMANDS = {
"list": cmd_list,
"add": cmd_add,
"passwd": cmd_passwd,
"remove": cmd_remove,
"retire-shared-password": cmd_retire_shared_password,
}
COMMANDS = {"list": cmd_list, "add": cmd_add, "passwd": cmd_passwd, "remove": cmd_remove}
def main(argv):