Commit inicial - upload de todos os arquivos da pasta

This commit is contained in:
2026-07-24 23:55:21 -03:00
commit 9d4d395881
88 changed files with 9484 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
"""Autenticação: hash de senha (bcrypt), seed do admin, CSRF e guarda de sessão."""
from __future__ import annotations
import secrets
import time
import bcrypt
from starlette.requests import Request
from . import database as db
from .config import get_settings
# --------------------------------------------------------------------------- #
# Senhas
# --------------------------------------------------------------------------- #
def hash_password(password: str) -> str:
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("ascii")
def verify_password(password: str, password_hash: str) -> bool:
try:
return bcrypt.checkpw(password.encode("utf-8"), password_hash.encode("ascii"))
except (ValueError, TypeError):
return False
def seed_admin() -> str | None:
"""Cria o usuário admin na 1ª execução. Retorna uma mensagem de aviso, se houver."""
settings = get_settings()
with db.session() as conn:
if db.count_users(conn) > 0:
return None
password = settings.admin_password
note = None
if not password:
# Sem ADMIN_PASSWORD definido: gera uma senha aleatória e a expõe UMA vez
# no log para o operador. Em produção defina ADMIN_PASSWORD no ambiente.
password = secrets.token_urlsafe(12)
note = (
f"[AVISO] Nenhum ADMIN_PASSWORD definido. Usuário '{settings.admin_username}' "
f"criado com senha temporária: {password} (defina ADMIN_PASSWORD e reinicie)"
)
db.create_user(conn, settings.admin_username, hash_password(password))
return note
def authenticate(username: str, password: str) -> bool:
with db.session() as conn:
row = db.get_user(conn, username)
if row is None:
# Compara mesmo sem usuário para não vazar tempo (mitiga user enumeration).
verify_password(password, hash_password("dummy"))
return False
return verify_password(password, row["password_hash"])
# --------------------------------------------------------------------------- #
# Sessão / guarda
# --------------------------------------------------------------------------- #
def current_user(request: Request) -> str | None:
return request.session.get("user")
def login_session(request: Request, username: str) -> None:
request.session["user"] = username
request.session["logged_at"] = int(time.time())
# rotaciona o token CSRF a cada login
request.session["csrf"] = secrets.token_urlsafe(32)
def logout_session(request: Request) -> None:
request.session.clear()
# --------------------------------------------------------------------------- #
# CSRF
# --------------------------------------------------------------------------- #
def get_csrf_token(request: Request) -> str:
token = request.session.get("csrf")
if not token:
token = secrets.token_urlsafe(32)
request.session["csrf"] = token
return token
def check_csrf(request: Request, submitted: str | None) -> bool:
expected = request.session.get("csrf")
return bool(expected) and bool(submitted) and secrets.compare_digest(expected, submitted)