🔒 Repository is read-only – file editing is disabled.

PaganLinux/pagan-web-v2/blueprints/profile.py main

102 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
import hashlib, secrets, json
from flask import Blueprint, render_template, request, redirect, session, flash
from database import get_db
from auth import hash_password, verify_password
import totp as totp_mod

profile_bp = Blueprint("profile", __name__)

def _verify_2fa_action(user, code):
    """Weryfikuje kod 2FA dla wrażliwych akcji."""
    if not user["totp_enabled"]:
        return True
    return bool(code) and totp_mod.verify_totp(user["totp_secret"] or "", code)

@profile_bp.route("/profile", methods=["GET", "POST"])
def profile():
    if not session.get("username"):
        return redirect("/login?next=/profile")

    db = get_db()
    user = db.execute("SELECT * FROM users WHERE username=?", (session["username"],)).fetchone()

    if request.method == "POST":
        action = request.form.get("action", "")

        if action == "password":
            current = request.form.get("current_password", "")
            new = request.form.get("new_password", "")
            if not verify_password(current, user["password_hash"]):
                flash("Nieprawidłowe obecne hasło", "error")
            elif len(new) < 5:
                flash("Nowe hasło za krótkie (min. 5)", "error")
            elif user["totp_enabled"] and not _verify_2fa_action(user, request.form.get("2fa_code", "")):
                flash("❌ Wymagany kod 2FA do zmiany hasła", "error")
            else:
                db.execute("UPDATE users SET password_hash=? WHERE username=?",
                           (hash_password(new), session["username"]))
                db.commit()
                flash("✅ Hasło zmienione", "ok")

        elif action == "email":
            email = request.form.get("email", "").strip()
            if "@" not in email:
                flash("Niepoprawny email", "error")
            else:
                try:
                    db.execute("UPDATE users SET email=? WHERE username=?", (email, session["username"]))
                    db.commit()
                    session["email"] = email
                    flash("✅ Email zaktualizowany", "ok")
                except Exception:
                    flash("Email już zajęty", "error")

        elif action == "token":
            if user["totp_enabled"] and not _verify_2fa_action(user, request.form.get("2fa_code", "")):
                flash("❌ Wymagany kod 2FA do generowania tokena", "error")
            else:
                token = f"pag_pat_{secrets.token_hex(20)}"
                db.execute("UPDATE users SET git_token=? WHERE username=?", (token, session["username"]))
                db.commit()
                flash(f"Token: {token}", "ok")
                return render_template("profile/index.html", user=user, generated_token=token,
                                       totp_enabled=bool(user["totp_enabled"]))

        # ── 2FA ──
        elif action == "setup_2fa":
            secret = totp_mod.generate_secret()
            db.execute("UPDATE users SET totp_secret=? WHERE username=?", (secret, session["username"]))
            db.commit()
            user = dict(user)
            user["totp_secret"] = secret
            flash("Zeskanuj kod QR lub wpisz klucz ręcznie w aplikacji 2FA (Google Authenticator, Authy).", "ok")
            return render_template("profile/index.html", user=user, setup_2fa=True, generated_token=None)

        elif action == "verify_2fa":
            code = request.form.get("code", "").strip()
            if totp_mod.verify_totp(user["totp_secret"] or "", code):
                codes = totp_mod.generate_recovery_codes()
                hashed = totp_mod.hash_recovery_codes(codes)
                db.execute("UPDATE users SET totp_enabled=1, recovery_codes=? WHERE username=?",
                           (json.dumps(hashed), session["username"]))
                db.commit()
                flash(f"✅ 2FA włączone! Zapisz kody zapasowe:\n" + "\n".join(codes), "ok")
            else:
                flash("❌ Nieprawidłowy kod. Spróbuj ponownie.", "error")
            return redirect("/profile")

        elif action == "disable_2fa":
            pwd = request.form.get("current_password", "")
            if not verify_password(pwd, user["password_hash"]):
                flash("Nieprawidłowe hasło", "error")
            else:
                db.execute("UPDATE users SET totp_enabled=0, totp_secret=NULL, recovery_codes=NULL WHERE username=?",
                           (session["username"],))
                db.commit()
                flash("2FA wyłączone", "ok")
            return redirect("/profile")

    db.close()
    totp_enabled = bool(user["totp_enabled"]) if "totp_enabled" in user.keys() else False
    return render_template("profile/index.html", user=user, setup_2fa=False, generated_token=None,
                           totp_enabled=totp_enabled)