🔒 Repository is read-only – file editing is disabled.
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)