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
This commit is contained in:
75
auth.py
Normal file
75
auth.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user