"""Passwort-Hashing mit Salz + Pfeffer sowie Session-/Auth-Helfer. Das Passwort wird wie folgt gehasht: hash = pbkdf2_hmac('sha256', (klartext + salz + pfeffer), iterationen) - Salz: zufälliger Wert pro Benutzer (in DB gespeichert). - Pfeffer: globaler geheimer Wert aus der Umgebung (NICHT in der DB). """ import hashlib import hmac import os from base64 import b64encode, b64decode from functools import wraps from flask import session, redirect, url_for, flash from config import Config def _salt_bytes_to_str(raw: bytes) -> str: return b64encode(raw).decode("ascii") def _salt_str_to_bytes(salt: str) -> bytes: return b64decode(salt.encode("ascii")) def hash_password(password: str) -> tuple[str, str]: """Erzeugt (salt, password_hash) für ein neues Passwort.""" salt_raw = os.urandom(16) salt = _salt_bytes_to_str(salt_raw) digest = hashlib.pbkdf2_hmac( "sha256", (password + Config.PEPPER).encode("utf-8"), salt_raw, Config.HASH_ITERATIONS, ) return salt, digest.hex() def verify_password(password: str, salt: str, password_hash: str) -> bool: """Prüft ein Passwort gegen salt + password_hash.""" salt_raw = _salt_str_to_bytes(salt) digest = hashlib.pbkdf2_hmac( "sha256", (password + Config.PEPPER).encode("utf-8"), salt_raw, Config.HASH_ITERATIONS, ) return hmac.compare_digest(digest.hex(), password_hash) def login_required(view): """Dekorator: nur eingeloggte Benutzer.""" @wraps(view) def wrapped(*args, **kwargs): if "user_id" not in session: flash("Bitte melde dich zuerst an.", "warning") return redirect(url_for("auth.login")) return view(*args, **kwargs) return wrapped def admin_required(view): """Dekorator: nur Admins.""" @wraps(view) def wrapped(*args, **kwargs): if "user_id" not in session: flash("Bitte melde dich zuerst an.", "warning") return redirect(url_for("auth.login")) if not session.get("is_admin"): flash("Keine Berechtigung für diesen Bereich.", "danger") return redirect(url_for("journal.index")) return view(*args, **kwargs) return wrapped