feat: /setup/new nach erster Registrierung sperren (Ersteinrichtung einmalig)

This commit is contained in:
mscadm 2026-07-13 15:16:46 +00:00
parent fe11241c18
commit 99a9761901
1 changed files with 18 additions and 2 deletions

20
app.py
View File

@ -234,6 +234,15 @@ def _shell(active_path, title, body_html, extra_head=""):
# ── Ersteinrichtung (neuer Mandant) ──────────────────────────────────────────── # ── Ersteinrichtung (neuer Mandant) ────────────────────────────────────────────
@app.route("/setup/new", methods=["GET", "POST"]) @app.route("/setup/new", methods=["GET", "POST"])
def setup_new(): def setup_new():
# Die Ersteinrichtung ist ein einmaliger Vorgang: sobald irgendein
# Mandant existiert, ist die Registrierung neuer Organisationen ueber
# diese Route dauerhaft gesperrt (Nutzer-Vorgabe 2026-07-13: "/setup/new
# darf nur einmal funktionieren"). Weitere Mandanten koennen danach nur
# noch durch einen Administrator ueber die Datenbank/ein internes
# Verfahren angelegt werden, nicht mehr per offenem Self-Service-Formular.
if db.any_tenant_exists():
return redirect(url_for("login", already_setup="1"))
error = None error = None
# Vorbelegung: bei GET die Standardwerte, bei einem fehlgeschlagenen POST # Vorbelegung: bei GET die Standardwerte, bei einem fehlgeschlagenen POST
# die vom Nutzer eingegebenen Werte (ausser Passwoerter -- die bleiben aus # die vom Nutzer eingegebenen Werte (ausser Passwoerter -- die bleiben aus
@ -341,6 +350,8 @@ def login():
notice = None notice = None
if request.args.get("registered"): if request.args.get("registered"):
notice = "Organisation angelegt. Bitte melde dich mit deinem Admin-Konto an." notice = "Organisation angelegt. Bitte melde dich mit deinem Admin-Konto an."
elif request.args.get("already_setup"):
notice = "Die Ersteinrichtung wurde bereits abgeschlossen. Bitte melde dich mit deinem bestehenden Konto an."
if request.method == "POST": if request.method == "POST":
email = request.form.get("email", "").strip().lower() email = request.form.get("email", "").strip().lower()
pw = request.form.get("password", "") pw = request.form.get("password", "")
@ -360,6 +371,11 @@ def login():
error = "E-Mail oder Passwort falsch." error = "E-Mail oder Passwort falsch."
err_html = "<p class='err'>%s</p>" % html.escape(error) if error else "" err_html = "<p class='err'>%s</p>" % html.escape(error) if error else ""
notice_html = "<p class='sz' style='color:var(--ok);margin:0 0 10px'>%s</p>" % html.escape(notice) if notice else "" notice_html = "<p class='sz' style='color:var(--ok);margin:0 0 10px'>%s</p>" % html.escape(notice) if notice else ""
# Der Registrierungs-Link wird nur angezeigt, solange die Ersteinrichtung
# noch nicht abgeschlossen ist -- danach ist /setup/new gesperrt, ein
# Link dorthin waere ein toter Verweis (Nutzer-Vorgabe 2026-07-13).
setup_link_html = ('<div class="sz" style="margin-top:12px">Neue Organisation? '
'<a href="/setup/new">Jetzt einrichten</a></div>') if not db.any_tenant_exists() else ""
return """<!doctype html><html lang="de"><head><meta charset="utf-8"> return """<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Anmelden -- ITSM</title>%s</head><body> <title>Anmelden -- ITSM</title>%s</head><body>
@ -369,8 +385,8 @@ def login():
<input name="email" type="email" placeholder="E-Mail" autofocus> <input name="email" type="email" placeholder="E-Mail" autofocus>
<input name="password" type="password" placeholder="Passwort"> <input name="password" type="password" placeholder="Passwort">
<button class="btn" type="submit">Anmelden</button> <button class="btn" type="submit">Anmelden</button>
<div class="sz" style="margin-top:12px">Neue Organisation? <a href="/setup/new">Jetzt einrichten</a></div> %s
</form></div></body></html>""" % (_CSS_TAG, err_html, notice_html) </form></div></body></html>""" % (_CSS_TAG, err_html, notice_html, setup_link_html)
@app.route("/logout") @app.route("/logout")