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:
73
web/app.py
73
web/app.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user