Erste Produktivversion
This commit is contained in:
78
backend/app/bootstrap_admin.py
Normal file
78
backend/app/bootstrap_admin.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Anlegen des ersten Administratorkontos beim Start.
|
||||
|
||||
Bewusst eng gefasst: Das Konto entsteht nur, wenn die Datenbank noch
|
||||
*gar keinen* Nutzer enthält. Damit kann ein vergessener Eintrag in der
|
||||
.env kein bestehendes Konto überschreiben und auch kein zweites
|
||||
Admin-Konto nachschieben.
|
||||
|
||||
Das angelegte Konto trägt `must_change_password`. Bis zum Wechsel sind
|
||||
alle Routen gesperrt, die an `verified_user` hängen - also alles außer
|
||||
"eigenes Profil lesen" und "Passwort ändern". Der Wert in der .env ist
|
||||
nach der ersten Anmeldung damit wertlos.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.config import settings
|
||||
from app.models import User
|
||||
from app.security import hash_password, normalize_email, utcnow
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MIN_LENGTH = 12
|
||||
|
||||
|
||||
def ensure_initial_admin(db: Session) -> None:
|
||||
password = settings.initial_admin_password
|
||||
user_count = db.scalar(select(func.count()).select_from(User)) or 0
|
||||
email = normalize_email(settings.admin_email)
|
||||
|
||||
if user_count > 0:
|
||||
if password:
|
||||
log.warning(
|
||||
"ADMIN_INITIAL_PASSWORD ist gesetzt, es existieren aber bereits "
|
||||
"%d Konten - der Wert wird ignoriert. Entferne ihn aus der .env, "
|
||||
"damit kein Passwort unnötig in der Umgebung steht.",
|
||||
user_count,
|
||||
)
|
||||
return
|
||||
|
||||
if not password:
|
||||
log.info(
|
||||
"Keine Konten vorhanden und kein ADMIN_INITIAL_PASSWORD gesetzt. "
|
||||
"Erstes Konto über POST /api/auth/register anlegen (%s wird dabei "
|
||||
"automatisch Administrator).",
|
||||
email,
|
||||
)
|
||||
return
|
||||
|
||||
if len(password) < MIN_LENGTH:
|
||||
raise RuntimeError(
|
||||
f"ADMIN_INITIAL_PASSWORD ist zu kurz (mindestens {MIN_LENGTH} Zeichen)."
|
||||
)
|
||||
|
||||
admin = User(
|
||||
email=email,
|
||||
display_name="Administrator",
|
||||
password_hash=hash_password(password),
|
||||
# Die Adresse gilt als bestätigt: Wer die .env schreiben kann,
|
||||
# hat ohnehin vollen Zugriff. Ein Verifikationsumweg brächte hier
|
||||
# keinen Sicherheitsgewinn, nur eine Abhängigkeit vom Mailversand
|
||||
# bei der Ersteinrichtung.
|
||||
verified_at=utcnow(),
|
||||
is_admin=True,
|
||||
must_change_password=True,
|
||||
)
|
||||
db.add(admin)
|
||||
db.flush()
|
||||
|
||||
log.warning(
|
||||
"Administratorkonto %s aus ADMIN_INITIAL_PASSWORD angelegt. "
|
||||
"Das Passwort MUSS bei der ersten Anmeldung geändert werden; bis dahin "
|
||||
"sind alle übrigen Funktionen gesperrt. Danach den Wert aus der .env "
|
||||
"entfernen.",
|
||||
email,
|
||||
)
|
||||
Reference in New Issue
Block a user