99 lines
3.0 KiB
Python
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())
|