"""Recovery CLI. Everyday password changes happen in the UI. This exists for the one case the UI cannot help with — a forgotten password — and for the initial handover. docker exec -it parts python -m app.admin set-password docker exec parts python -m app.admin show-status """ import getpass import sys from . import auth, db def set_password(argv): password = argv[0] if argv else None if password is None: password = getpass.getpass("New password: ") if password != getpass.getpass("Repeat: "): print("Passwords did not match.", file=sys.stderr) return 1 problem = auth.password_problem(password) if problem: print(problem, file=sys.stderr) return 1 with db.session() as conn: auth.set_password(conn, password) print("Password set. All existing sessions have been signed out.") return 0 def clear_password(_argv): """Fall back to the PARTS_PASSWORD environment variable again.""" with db.session() as conn: conn.execute("DELETE FROM settings WHERE key = ?", (auth.PASSWORD_KEY,)) auth.bump_epoch(conn) print("Stored password cleared; PARTS_PASSWORD from the environment is live again.") print("All existing sessions have been signed out.") return 0 def show_status(_argv): with db.session() as conn: stored = auth.stored_hash(conn) print("password source :", "database (set in the app)" if stored else "PARTS_PASSWORD env var") print("session epoch :", auth.current_epoch(conn)) print("auth enabled :", auth.auth_enabled()) return 0 COMMANDS = {"set-password": set_password, "clear-password": clear_password, "show-status": show_status} def main(argv=None): argv = list(sys.argv[1:] if argv is None else argv) if not argv or argv[0] not in COMMANDS: print("usage: python -m app.admin {" + "|".join(COMMANDS) + "}", file=sys.stderr) return 2 db.init() return COMMANDS[argv[0]](argv[1:]) if __name__ == "__main__": raise SystemExit(main())