import argparse import getpass import os import secrets import sys from pathlib import Path from .crypto import SecretBox, generate_master_key from .db import Database from .fileconfig import MASTER_KEY_FILE, SESSION_SECRET_FILE, load_file_config, load_master_key from .users import ROLE_ADMIN, UserError, UserStore CLI_ACTOR = "cli" SECRET_FILE_MODE = 0o600 CONFIG_DIR_MODE = 0o700 SESSION_SECRET_BYTES = 48 def _write_secret_file(path: Path, content: str) -> bool: if path.exists(): return False path.parent.mkdir(parents=True, exist_ok=True, mode=CONFIG_DIR_MODE) descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, SECRET_FILE_MODE) with os.fdopen(descriptor, "w") as handle: handle.write(content) return True def _user_store() -> UserStore: config = load_file_config() box = SecretBox(load_master_key(config.config_dir / MASTER_KEY_FILE)) return UserStore(Database(config.db_path), box) def _ask_password() -> str: password = getpass.getpass("Passwort: ") if password != getpass.getpass("Passwort wiederholen: "): raise UserError("Passwörter stimmen nicht überein") return password def cmd_gen_keys(args: argparse.Namespace) -> int: config = load_file_config() files = { MASTER_KEY_FILE: generate_master_key(), SESSION_SECRET_FILE: secrets.token_urlsafe(SESSION_SECRET_BYTES), } for name, content in files.items(): created = _write_secret_file(config.config_dir / name, content) print(f"{name}: {'angelegt' if created else 'existiert bereits, unverändert'}") return 0 def cmd_create_admin(args: argparse.Namespace) -> int: username = args.username or input("Benutzername: ").strip() try: _user_store().create(username, _ask_password(), ROLE_ADMIN, actor=CLI_ACTOR) except UserError as exc: print(f"Fehler: {exc}", file=sys.stderr) return 1 print(f"Admin '{username}' angelegt") return 0 def cmd_set_password(args: argparse.Namespace) -> int: users = _user_store() user = users.get_by_name(args.username) if user is None: print("Fehler: Benutzer nicht gefunden", file=sys.stderr) return 1 try: users.set_password(user.id, _ask_password(), actor=CLI_ACTOR) except UserError as exc: print(f"Fehler: {exc}", file=sys.stderr) return 1 print(f"Passwort für '{args.username}' gesetzt") return 0 def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(prog="checkbot") commands = parser.add_subparsers(dest="command", required=True) commands.add_parser("gen-keys") create = commands.add_parser("create-admin") create.add_argument("username", nargs="?") reset = commands.add_parser("set-password") reset.add_argument("username") args = parser.parse_args(argv) handlers = { "gen-keys": cmd_gen_keys, "create-admin": cmd_create_admin, "set-password": cmd_set_password, } return handlers[args.command](args) if __name__ == "__main__": sys.exit(main())