modified: web/app.py

modified:   web/blueprints/auth.py
	modified:   web/blueprints/group_admin.py
	modified:   web/blueprints/panel.py
	modified:   web/blueprints/site_admin.py
	modified:   web/config.py
	new file:   web/templates/404.html
	modified:   web/templates/admin/base.html
	modified:   web/templates/admin/group_edit.html
	modified:   web/templates/admin/group_members.html
	modified:   web/templates/admin/groups.html
	modified:   web/templates/admin/user_edit.html
	modified:   web/templates/admin/users.html
	modified:   web/templates/auth/admin_login.html
	modified:   web/templates/auth/login.html
	modified:   web/templates/base.html
	modified:   web/templates/group_admin/base.html
	modified:   web/templates/group_admin/database.html
	modified:   web/templates/group_admin/member_edit.html
	modified:   web/templates/group_admin/members.html
	modified:   web/templates/login.html
	modified:   web/templates/panel/dashboard.html
This commit is contained in:
simon
2026-04-13 09:55:50 +02:00
parent 486aa2ff18
commit 935dc3f909
22 changed files with 260 additions and 50 deletions

View File

@@ -3,8 +3,9 @@ MCLogger Flask Web-Panel
Multi-Tenant mit Gruppen, Rollen & verschlüsselten DB-Zugangsdaten.
Coolify-kompatibel: alle Einstellungen via ENV.
"""
import secrets
from datetime import datetime
from flask import Flask, session
from flask import Flask, abort, render_template, request, session, url_for
from config import Config
from panel_db import init_databases, get_user_groups
@@ -17,6 +18,13 @@ from blueprints.panel import panel
def create_app() -> Flask:
app = Flask(__name__)
app.secret_key = Config.SECRET_KEY
app.config.update(
SESSION_COOKIE_HTTPONLY=Config.SESSION_COOKIE_HTTPONLY,
SESSION_COOKIE_SAMESITE=Config.SESSION_COOKIE_SAMESITE,
SESSION_COOKIE_SECURE=Config.SESSION_COOKIE_SECURE,
)
Config.validate_security()
# Blueprints registrieren
app.register_blueprint(auth)
@@ -32,6 +40,68 @@ def create_app() -> Flask:
# ── Template-Filter ───────────────────────────────────────
def _get_or_create_csrf_token() -> str:
token = session.get("_csrf_token")
if not token:
token = secrets.token_urlsafe(32)
session["_csrf_token"] = token
return token
@app.before_request
def enforce_csrf():
if request.method not in {"POST", "PUT", "PATCH", "DELETE"}:
return
session_token = session.get("_csrf_token")
request_token = request.form.get("_csrf_token") or request.headers.get("X-CSRF-Token")
if not session_token or not request_token or session_token != request_token:
abort(400)
@app.after_request
def set_security_headers(resp):
resp.headers.setdefault("X-Content-Type-Options", "nosniff")
resp.headers.setdefault("X-Frame-Options", "DENY")
resp.headers.setdefault("Referrer-Policy", "strict-origin-when-cross-origin")
resp.headers.setdefault("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' https://cdn.jsdelivr.net; connect-src 'self'; frame-ancestors 'none';")
return resp
@app.errorhandler(400)
def bad_request(_):
return "Bad request", 400
@app.errorhandler(404)
def not_found(_):
uid = session.get("user_id")
is_site_admin = bool(session.get("is_site_admin"))
role = session.get("role")
links = []
if not uid:
links = [
{"label": "Login", "href": url_for("auth.login"), "btn": "btn-success"},
{"label": "Site Admin Login", "href": url_for("auth.admin_login"), "btn": "btn-outline-danger"},
]
elif is_site_admin and not session.get("group_id"):
links = [
{"label": "Site Admin Dashboard", "href": url_for("site_admin.dashboard"), "btn": "btn-danger"},
]
else:
links.append({"label": "Panel Dashboard", "href": url_for("panel.dashboard"), "btn": "btn-success"})
if is_site_admin:
links.append({"label": "Site Admin", "href": url_for("site_admin.dashboard"), "btn": "btn-outline-danger"})
if role == "admin" and not is_site_admin:
links.append({"label": "Group Admin", "href": url_for("group_admin.dashboard"), "btn": "btn-outline-warning"})
return render_template(
"404.html",
requested_path=request.path,
request_method=request.method,
links=links,
is_logged_in=bool(uid),
is_site_admin=is_site_admin,
role=role,
), 404
@app.template_filter("fmt_duration")
def fmt_duration(seconds):
if seconds is None:
@@ -64,6 +134,7 @@ def create_app() -> Flask:
"app_version": "2.0.0",
"author": "SimolZimol",
"user_groups": groups,
"csrf_token": _get_or_create_csrf_token,
}
return app