🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
import hashlib, secrets, functools, os
from datetime import datetime, timedelta
from flask import request, redirect, session
def hash_password(password: str) -> str:
"""Bezpieczne hashowanie: PBKDF2-SHA256 z solą."""
salt = secrets.token_hex(32)
dk = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 600000)
return f"pbkdf2:sha256:600000${salt}${dk.hex()}"
def verify_password(password: str, hash_: str) -> bool:
if hash_.startswith("pbkdf2:sha256:"):
# Format: pbkdf2:sha256:600000$SALT$DK_HEX
try:
meta, salt, stored = hash_.split("$", 2)
_, _, iters_str = meta.split(":", 2)
iters = int(iters_str)
dk = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), iters)
return secrets.compare_digest(dk.hex(), stored)
except Exception:
return False
# Stary format (SHA256 plain) – automatyczna migracja przy logowaniu
old = hashlib.sha256(password.encode()).hexdigest()
if secrets.compare_digest(old, hash_):
# Migruj do nowego formatu (tylko jeśli hash w DB to stary format)
try:
from database import get_db
db = get_db()
new_hash = hash_password(password)
db.execute("UPDATE users SET password_hash=? WHERE password_hash=?", (new_hash, hash_))
db.commit()
db.close()
except Exception:
pass
return True
return False
def login_required(f):
@functools.wraps(f)
def wrap(*a, **kw):
if not session.get("username"):
return redirect("/login?next=" + request.path)
return f(*a, **kw)
return wrap
def build_auth_required(f):
@functools.wraps(f)
def wrap(*a, **kw):
from config.settings import BUILD_PASSWORD
if request.cookies.get("build_auth") != BUILD_PASSWORD:
return redirect("/build/login")
return f(*a, **kw)
return wrap
def can_edit_repo(username, repo_name):
if not username:
return False
from database import get_db
db = get_db()
row = db.execute("SELECT role FROM repo_permissions WHERE repo_name=? AND username=?",
(repo_name, username)).fetchone()
db.close()
return row is not None
def get_section():
"""Wykrywa aktywną sekcję na podstawie domeny."""
host = request.host.split(":")[0]
from config.settings import DOMAINS
return DOMAINS.get(host, "main")