feat: add show-admin-password and reset-admin-password make targets

make show-admin-password — prints the admin password from the initial
  setup file if the API hasn't consumed it yet; otherwise prompts to reset
make reset-admin-password — generates a strong random password, updates
  auth_users.json directly, writes it back to the setup file, and prints
  it prominently so it's easy to copy

Also enhances reset_admin_password.py with --show, --generate flags and
a clear banner output, and adds both targets to make help.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 03:17:38 -04:00
parent 7d2979b8af
commit ec9ceec7a7
2 changed files with 97 additions and 11 deletions
+86 -11
View File
@@ -1,27 +1,102 @@
#!/usr/bin/env python3
"""Reset admin password directly in auth_users.json — for test environments only."""
"""
Admin password management utility.
Usage:
reset_admin_password.py --generate # generate a random password and set it
reset_admin_password.py <new_password> # set a specific password
"""
import sys
import os
import json
import secrets
import string
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'api'))
ROOT = os.path.join(os.path.dirname(__file__), '..')
INIT_PW_FILE = os.path.normpath(os.path.join(ROOT, 'data', 'api', '.admin_initial_password'))
def main():
if len(sys.argv) != 2:
print("Usage: reset_admin_password.py <new_password>", file=sys.stderr)
sys.exit(1)
new_password = sys.argv[1]
def _generate_password(length: int = 20) -> str:
alphabet = string.ascii_letters + string.digits + '!@#$%^&*'
while True:
pw = ''.join(secrets.choice(alphabet) for _ in range(length))
# Ensure at least one of each character class required by the validator
if (any(c.isupper() for c in pw)
and any(c.islower() for c in pw)
and any(c.isdigit() for c in pw)
and any(c in '!@#$%^&*' for c in pw)):
return pw
def _set_password(new_password: str) -> None:
from auth_manager import AuthManager
data_dir = os.path.join(os.path.dirname(__file__), '..', 'data', 'api')
data_dir = os.path.normpath(os.path.join(ROOT, 'data', 'api'))
os.makedirs(data_dir, exist_ok=True)
mgr = AuthManager(data_dir=data_dir, config_dir='/tmp')
if mgr.set_password_admin('admin', new_password):
print(f"[OK] Admin password reset successfully.")
print('[OK] Admin password updated in auth_users.json')
else:
print("[WARN] Admin user not found — creating admin user.")
print('[WARN] Admin user not found — creating it now')
mgr.create_user('admin', new_password, 'admin')
print(f"[OK] Admin user created with provided password.")
print('[OK] Admin user created')
def _print_banner(password: str) -> None:
border = '=' * 60
print(border)
print(' ADMIN PASSWORD')
print(border)
print(f' Username : admin')
print(f' Password : {password}')
print(border)
print(' Save this password — it will NOT be shown again.')
print(border)
def main() -> None:
if len(sys.argv) < 2:
print(__doc__, file=sys.stderr)
sys.exit(1)
arg = sys.argv[1]
if arg == '--show':
# Show the initial password file if the API hasn't consumed it yet
if os.path.exists(INIT_PW_FILE):
pw = open(INIT_PW_FILE).read().strip()
_print_banner(pw)
print()
print(f' (file: {INIT_PW_FILE})')
print(' The API will delete this file on first start.')
else:
print('Initial password file not found.')
print('The API has already consumed it, or setup has not been run.')
print()
print('To set a new password run:')
print(' make reset-admin-password')
return
if arg == '--generate':
password = _generate_password()
else:
password = arg
if len(password) < 10:
print('Error: password must be at least 10 characters', file=sys.stderr)
sys.exit(1)
_set_password(password)
# Also update the initial password file so show-admin-password works
os.makedirs(os.path.dirname(INIT_PW_FILE), exist_ok=True)
with open(INIT_PW_FILE, 'w') as f:
f.write(password)
_print_banner(password)
print(f'\n Also saved to: {INIT_PW_FILE}')
print(' Restart the API container for the change to take effect:')
print(' docker restart cell-api')
if __name__ == '__main__':