From dffc360790bf8240b09e04b8183ce943b75e2b66 Mon Sep 17 00:00:00 2001
From: Bjoern Runden
Date: Wed, 17 Jun 2026 20:03:09 +0200
Subject: [PATCH] user management drin.
---
app/__init__.py | 58 ++++++++++++++++++++++++++++++-------------------
app/auth.py | 2 +-
app/routes.py | 40 +++++++++++++++++++++++++++++-----
3 files changed, 72 insertions(+), 28 deletions(-)
diff --git a/app/__init__.py b/app/__init__.py
index 3a3c47f..a52cba2 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -49,18 +49,29 @@ def _register_cli(app):
db.create_all()
# ------------------------------------------------------------------
- # PLATZHALTER – nach Zitadel-Setup durch echte sub-Werte ersetzen.
- # Anleitung: ZITADEL-SETUP.md, Schritt 6
+ # Zitadel-User-IDs (sub-Werte) eintragen – Anleitung: ZITADEL-SETUP.md Schritt 6
+ # Beliebig viele Einträge pro Rolle möglich.
+ # Format: (zitadel_sub, email)
# ------------------------------------------------------------------
- ADMIN_ID = "ZITADEL_SUB_ADMIN"
- MITARBEITER_ID = "ZITADEL_SUB_MITARBEITER"
- KUNDE_ID = "ZITADEL_SUB_KUNDE"
+ ADMINS = [
+ ("377858996305921031", "test@oidctest.local"), # Test Admin
+ ("361726819499508906", "br@runden.de"), # Björn Runden
+ ]
+ MITARBEITER = [
+ ("377859593088271367", "testmuellerma@oidctest.local"), # Test Müller mitarbeiter
+ ("377859271703921671", "staff2@oidctest.local"), # test2 staff
+ ("377859141412062215", "teststaff@oidctest.local"), # test Mitarbeiter
+ ]
+ KUNDEN = [
+ ("377859426305967111", "testmueller@oidctest.local"), # oidctest kund1 Müller
+ ]
- db.session.add_all([
- User(zitadel_user_id=ADMIN_ID, email="admin@test.local", global_role="Admin"),
- User(zitadel_user_id=MITARBEITER_ID, email="ma@test.local", global_role="Mitarbeiter"),
- User(zitadel_user_id=KUNDE_ID, email="kunde@test.local", global_role="Kunde"),
- ])
+ for sub, email in ADMINS:
+ db.session.add(User(zitadel_user_id=sub, email=email, global_role="Admin"))
+ for sub, email in MITARBEITER:
+ db.session.add(User(zitadel_user_id=sub, email=email, global_role="Mitarbeiter"))
+ for sub, email in KUNDEN:
+ db.session.add(User(zitadel_user_id=sub, email=email, global_role="Kunde"))
firma = Client(name="Firma Müller")
db.session.add(firma)
@@ -72,25 +83,28 @@ def _register_cli(app):
db.session.add_all([p1, p2, p3])
db.session.flush()
- # Mitarbeiter → Projekte 1 + 2 (nicht 3 → Failure-Test-Button)
- db.session.add_all([
- ProjectMitarbeiterMapping(user_id=MITARBEITER_ID, project_id=p1.id),
- ProjectMitarbeiterMapping(user_id=MITARBEITER_ID, project_id=p2.id),
- ])
+ # Alle Mitarbeiter → Projekte 1 + 2 (nicht 3 → Failure-Test-Button)
+ for sub, _ in MITARBEITER:
+ db.session.add(ProjectMitarbeiterMapping(user_id=sub, project_id=p1.id))
+ db.session.add(ProjectMitarbeiterMapping(user_id=sub, project_id=p2.id))
- # Kunde → Projekt 1 (nicht 2 + 3 → Failure-Test-Button)
- db.session.add(ProjectKundenMapping(user_id=KUNDE_ID, project_id=p1.id))
+ # Alle Kunden → Projekt 1 (nicht 2 + 3 → Failure-Test-Button)
+ for sub, _ in KUNDEN:
+ db.session.add(ProjectKundenMapping(user_id=sub, project_id=p1.id))
+ # Beispielnotizen werden dem ersten Mitarbeiter zugeordnet
+ ma_sub = MITARBEITER[0][0]
db.session.add_all([
Note(text="Erstkontakt aufgenommen.", project_id=p1.id,
- user_id=MITARBEITER_ID, created_at=datetime.utcnow()),
+ user_id=ma_sub, created_at=datetime.utcnow()),
Note(text="Angebot versendet.", project_id=p1.id,
- user_id=MITARBEITER_ID, created_at=datetime.utcnow()),
+ user_id=ma_sub, created_at=datetime.utcnow()),
Note(text="Folgetermin vereinbart.", project_id=p2.id,
- user_id=MITARBEITER_ID, created_at=datetime.utcnow()),
+ user_id=ma_sub, created_at=datetime.utcnow()),
])
db.session.commit()
click.echo("Datenbank initialisiert und mit Testdaten befüllt.")
- click.echo(f" → Platzhalter-IDs aktiv: {ADMIN_ID}, {MITARBEITER_ID}, {KUNDE_ID}")
- click.echo(" → Echte IDs aus ZITADEL-SETUP.md Schritt 6 eintragen, dann erneut seeden.")
+ click.echo(f" Admins : {[s for s, _ in ADMINS]}")
+ click.echo(f" Mitarbeiter : {[s for s, _ in MITARBEITER]}")
+ click.echo(f" Kunden : {[s for s, _ in KUNDEN]}")
diff --git a/app/auth.py b/app/auth.py
index 819ac66..ab36ebd 100644
--- a/app/auth.py
+++ b/app/auth.py
@@ -4,7 +4,7 @@ OIDC-Authentifizierung via Authlib und Zitadel.
Konfiguriert den OAuth2-Client und stellt ``login_required`` sowie
``role_required`` als Route-Guard-Dekoratoren bereit.
-:author:
+:author:
:date: 2026-06-17
:version: 0.1
"""
diff --git a/app/routes.py b/app/routes.py
index dc47e9c..a1b4e50 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -49,13 +49,9 @@ def auth_callback():
# Zitadel-Roles-Claim: {"Admin": {"": ""}, ...}
roles_claim = userinfo.get("urn:zitadel:iam:org:project:roles", {})
- logger.info(
- "OIDC Login – sub: %s | email: %s | roles: %s | full_userinfo: %s",
- sub, email, list(roles_claim.keys()), dict(userinfo),
- )
-
if not roles_claim:
logger.warning("Kein Roles-Claim im Token für sub=%s – Zugriff verweigert.", sub)
+ _log_login(sub, email, list(roles_claim.keys()), app_role=None, projects=[])
return redirect(url_for("main.error"))
role = next(iter(roles_claim.keys()))
@@ -69,12 +65,46 @@ def auth_callback():
db.session.add(user)
db.session.commit()
+ # Projekt-Zuordnungen für den Log ermitteln
+ if role == "Mitarbeiter":
+ mappings = ProjectMitarbeiterMapping.query.filter_by(user_id=sub).all()
+ projects = Project.query.filter(
+ Project.id.in_({m.project_id for m in mappings})
+ ).all()
+ elif role == "Kunde":
+ mappings = ProjectKundenMapping.query.filter_by(user_id=sub).all()
+ projects = Project.query.filter(
+ Project.id.in_({m.project_id for m in mappings})
+ ).all()
+ else:
+ projects = Project.query.all() # Admin sieht alles
+
+ _log_login(sub, email, list(roles_claim.keys()), app_role=role, projects=projects)
+
session["user"] = {"sub": sub, "email": email, "role": role}
session["id_token"] = token.get("id_token")
return redirect(url_for("main.index"))
+def _log_login(sub, email, zitadel_roles, app_role, projects):
+ project_list = ", ".join(f"[{p.id}] {p.name}" for p in projects) or "–"
+ logger.info(
+ "\n"
+ "╔══ LOGIN ══════════════════════════════════\n"
+ "║ sub : %s\n"
+ "║ email : %s\n"
+ "║ Zitadel-Rol. : %s\n"
+ "║ App-Rolle : %s\n"
+ "║ Projekte : %s\n"
+ "╚═══════════════════════════════════════════",
+ sub, email,
+ ", ".join(zitadel_roles) or "–",
+ app_role or "KEIN ZUGRIFF",
+ project_list,
+ )
+
+
@main.route("/logout")
def logout():
id_token = session.pop("id_token", None)