second commit

This commit is contained in:
2026-08-19 22:33:19 +02:00
parent 411812e954
commit 199f306993
107 changed files with 5984 additions and 0 deletions

56
scripts/create_admin.py Normal file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Legt einmalig den ersten Admin-User an (interaktive Passwortabfrage).
Aufruf (nach Deployment, siehe ansible/roles/sqlite_init):
<venv>/bin/python scripts/create_admin.py --username admin
"""
from __future__ import annotations
import argparse
import asyncio
import getpass
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from app.db import close_db, get_db, init_db # noqa: E402
from app.security.passwords import hash_password # noqa: E402
async def main(username: str) -> None:
await init_db()
conn = get_db()
cursor = await conn.execute("SELECT 1 FROM users WHERE username = ?", (username,))
if await cursor.fetchone() is not None:
print(f"Benutzer '{username}' existiert bereits.", file=sys.stderr)
await close_db()
sys.exit(1)
password = getpass.getpass("Initiales Passwort (min. 12 Zeichen): ")
if len(password) < 12:
print("Passwort zu kurz.", file=sys.stderr)
await close_db()
sys.exit(1)
confirm = getpass.getpass("Passwort wiederholen: ")
if password != confirm:
print("Passwoerter stimmen nicht ueberein.", file=sys.stderr)
await close_db()
sys.exit(1)
await conn.execute(
"INSERT INTO users (username, password_hash, is_admin, must_change_password) "
"VALUES (?, ?, 1, 1)",
(username, hash_password(password)),
)
await conn.commit()
await close_db()
print(f"Admin-User '{username}' angelegt. TOTP-Einrichtung erfolgt beim ersten Login.")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--username", required=True)
args = parser.parse_args()
asyncio.run(main(args.username))

View File

@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Laedt xterm.js und guacamole-common-js lokal herunter (npm) und kopiert die
# fertigen Dist-Dateien nach static/js/vendor/ bzw. static/js/vendor/.
#
# Bewusst KEIN CDN-Bezug zur Laufzeit im Browser (Hardening-Konzept 4.1/6.6) --
# der Download passiert einmalig zur Build-/Deployzeit, im Betrieb werden die
# Assets ausschliesslich lokal vom Jumphost selbst ausgeliefert.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
VENDOR_DIR="$PROJECT_ROOT/static/js/vendor"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
mkdir -p "$VENDOR_DIR"
echo "==> Installiere xterm.js + Addon 'fit' + guacamole-common-js nach $TMP_DIR"
cd "$TMP_DIR"
npm init -y >/dev/null
npm install --no-audit --no-fund \
xterm@5 \
xterm-addon-fit@0.8 \
guacamole-common-js@1.5
echo "==> Kopiere Dist-Dateien nach $VENDOR_DIR"
cp node_modules/xterm/lib/xterm.js "$VENDOR_DIR/xterm.js"
cp node_modules/xterm/css/xterm.css "$VENDOR_DIR/xterm.css"
cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js "$VENDOR_DIR/xterm-addon-fit.js"
# guacamole-common-js liefert nur ESM/CJS-Builds aus, keinen fertigen
# Browser-Global-Build. Fuer die Einbindung per klassischem <script>-Tag
# (kein CDN, kein <script type=module> noetig) wird die CJS-Variante
# genommen und das abschliessende `module.exports = Guacamole;` durch eine
# Zuweisung auf window ersetzt.
cp node_modules/guacamole-common-js/dist/cjs/guacamole-common.js "$VENDOR_DIR/guacamole-common.js"
sed -i '$ d' "$VENDOR_DIR/guacamole-common.js" # letzte Zeile (module.exports=...) entfernen
echo "window.Guacamole = Guacamole;" >> "$VENDOR_DIR/guacamole-common.js"
echo "==> Fertig. Assets liegen unter $VENDOR_DIR"