Files

99 lines
3.0 KiB
Python

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())