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)