Files
Journal/auth.py
SimolZimol 0e7efe4b74 new file: .env.example
new file:   .gitignore
	new file:   Dockerfile
	new file:   README.md
	new file:   app.py
	new file:   auth.py
	new file:   config.py
	new file:   db.py
	new file:   db_helpers.py
	new file:   docker-compose.yml
	new file:   requirements.txt
	new file:   routes/__init__.py
	new file:   routes/admin_routes.py
	new file:   routes/auth_routes.py
	new file:   routes/journal_routes.py
	new file:   static/css/style.css
	new file:   static/js/main.js
	new file:   templates/admin/dashboard.html
	new file:   templates/admin/users.html
	new file:   templates/base.html
	new file:   templates/day.html
	new file:   templates/future.html
	new file:   templates/index.html
	new file:   templates/login.html
	new file:   templates/week.html
2026-08-02 20:41:24 +02:00

76 lines
2.2 KiB
Python

"""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