modified: web/app.py

modified:   web/blueprints/auth.py
	modified:   web/blueprints/site_admin.py
	modified:   web/config.py
	modified:   web/panel_db.py
	modified:   web/templates/admin/audit_log.html
	modified:   web/templates/admin/dashboard.html
	new file:   web/templates/auth/consent.html
This commit is contained in:
simon
2026-04-15 11:05:21 +02:00
parent 179a0e1042
commit bdf83bd275
8 changed files with 333 additions and 2 deletions

View File

@@ -5,12 +5,75 @@ Getrennte Login-Seiten für Site-Admins und normale Nutzer/Gruppen-Admins.
import json
from datetime import datetime
from flask import Blueprint, render_template, request, redirect, url_for, session, flash
from panel_db import accept_group_invite, check_login, get_invite_by_token, get_user_groups, log_audit_event
from panel_db import (
accept_group_invite, check_login, get_invite_by_token, get_user_groups,
log_audit_event, get_user_consent_version, set_user_consent,
)
from config import Config
from limiter import limiter
auth = Blueprint("auth", __name__)
# ── DSGVO-Einwilligungs-Check ─────────────────────────────────
# Routen, die ohne Zustimmung erreichbar sein müssen:
_CONSENT_EXEMPT = frozenset({
"auth.consent", "auth.logout", "auth.login", "auth.admin_login",
"auth.accept_invite", "privacy_policy", "static",
})
@auth.before_app_request
def require_consent():
"""Leitet angemeldete Nutzer auf die Zustimmungsseite, solange sie der
aktuellen Datenschutzerklärung noch nicht zugestimmt haben."""
if request.endpoint in _CONSENT_EXEMPT:
return
user_id = session.get("user_id")
if not user_id:
return
# Site-Admins sind ebenfalls einwilligungspflichtig
if session.get("needs_consent"):
return redirect(url_for("auth.consent"))
@auth.route("/consent", methods=["GET", "POST"])
def consent():
user_id = session.get("user_id")
if not user_id:
return redirect(url_for("auth.login"))
if request.method == "POST":
action = request.form.get("action")
if action == "accept":
set_user_consent(user_id, Config.PRIVACY_POLICY_VERSION)
log_audit_event(
user_id, session.get("username"), "consent.given",
details={"policy_version": Config.PRIVACY_POLICY_VERSION},
ip_address=request.remote_addr,
)
session.pop("needs_consent", None)
# Nach Zustimmung weiterleiten
if session.get("is_site_admin"):
return redirect(url_for("site_admin.dashboard"))
return redirect(url_for("panel.dashboard"))
else:
# Ablehnen → ausloggen
log_audit_event(
user_id, session.get("username"), "consent.declined",
details={"policy_version": Config.PRIVACY_POLICY_VERSION},
ip_address=request.remote_addr,
)
session.clear()
flash("You must accept the Privacy Policy to use this service.", "warning")
return redirect(url_for("auth.login"))
return render_template(
"auth/consent.html",
policy_version=Config.PRIVACY_POLICY_VERSION,
)
@auth.route("/login", methods=["GET", "POST"])
@limiter.limit("15 per minute", methods=["POST"])
def login():
@@ -34,6 +97,10 @@ def login():
entity_type="user", entity_id=user["id"],
ip_address=request.remote_addr,
)
# DSGVO: Zustimmung prüfen
if get_user_consent_version(user["id"]) != Config.PRIVACY_POLICY_VERSION:
session["needs_consent"] = True
return redirect(url_for("auth.consent"))
return redirect(url_for("panel.dashboard"))
else:
log_audit_event(
@@ -65,6 +132,10 @@ def admin_login():
entity_type="user", entity_id=user["id"],
ip_address=request.remote_addr,
)
# DSGVO: Zustimmung prüfen
if get_user_consent_version(user["id"]) != Config.PRIVACY_POLICY_VERSION:
session["needs_consent"] = True
return redirect(url_for("auth.consent"))
return redirect(url_for("site_admin.dashboard"))
elif user:
log_audit_event(