"""JWT authentication — current_user dependency, require roles, audit/log helpers.""" import uuid import jwt as pyjwt from fastapi import HTTPException, Depends from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from config import APP_SECRET, JWT_ALG from database import db security = HTTPBearer() async def current_user(cred: HTTPAuthorizationCredentials = Depends(security)): try: p = pyjwt.decode(cred.credentials, APP_SECRET, algorithms=[JWT_ALG]) except pyjwt.ExpiredSignatureError: raise HTTPException(401, "Token expirado") except pyjwt.InvalidTokenError: raise HTTPException(401, "Token inválido") with db() as c: u = c.execute("SELECT * FROM users WHERE id=? AND is_active=1", (p["sub"],)).fetchone() s = c.execute("SELECT * FROM sessions WHERE id=? AND is_active=1", (p["sid"],)).fetchone() if not u or not s: raise HTTPException(401, "Sessão inválida") return dict(u) def _user_from_token(token: str): try: p = pyjwt.decode(token, APP_SECRET, algorithms=[JWT_ALG]) except Exception: raise HTTPException(401, "Token inválido") with db() as c: u = c.execute("SELECT * FROM users WHERE id=? AND is_active=1", (p["sub"],)).fetchone() s = c.execute("SELECT * FROM sessions WHERE id=? AND is_active=1", (p["sid"],)).fetchone() if not u or not s: raise HTTPException(401, "Sessão inválida") return dict(u) def require(*roles): async def dep(u=Depends(current_user)): if u["role"] not in roles: raise HTTPException(403, f"Requer role: {', '.join(roles)}") return u return dep def _audit(uid, uname, action, resource=None, details=None, ip=None): with db() as c: c.execute( "INSERT INTO audit_log (id,user_id,username,action,resource,details,ip) VALUES (?,?,?,?,?,?,?)", (str(uuid.uuid4()), uid, uname, action, resource, details, ip)) def _config_log(config_type, config_id, config_name, severity, action, message, uid=None, uname=None): with db() as c: c.execute( "INSERT INTO config_logs (id,config_type,config_id,config_name,severity,action,message,user_id,username) VALUES (?,?,?,?,?,?,?,?,?)", (str(uuid.uuid4()), config_type, config_id, config_name or "", severity, action, str(message)[:2000], uid, uname)) def _chat_log(sid, mid, uid, severity, source, action, message): with db() as c: c.execute( "INSERT INTO chat_logs (id,session_id,message_id,user_id,severity,source,action,message) VALUES (?,?,?,?,?,?,?,?)", (str(uuid.uuid4()), sid, mid, uid, severity, source, action, str(message)[:2000])) # ── Access Control ─────────────────────────────────────────────────────────── _CONFIG_TABLES = { "oci": "oci_configs", "genai": "genai_configs", "adb": "adb_vector_configs", "mcp": "mcp_servers", } def _verify_config_access(config_type: str, config_id: str, user: dict, *, require_owner: bool = False) -> dict: table = _CONFIG_TABLES.get(config_type) if not table: raise HTTPException(400, f"Tipo de config inválido: {config_type}") with db() as c: row = c.execute(f"SELECT * FROM {table} WHERE id=?", (config_id,)).fetchone() if not row: raise HTTPException(404) row = dict(row) if user["role"] == "admin": return row is_owner = row.get("user_id") == user["id"] is_global = row.get("is_global", 0) if not is_owner and not is_global: raise HTTPException(404) if require_owner and not is_owner: raise HTTPException(403, "Apenas o dono pode modificar este recurso") return row def _verify_report_access(report_id: str, user: dict) -> dict: with db() as c: row = c.execute("SELECT * FROM reports WHERE id=? AND user_id=?", (report_id, user["id"])).fetchone() if not row: raise HTTPException(404) return dict(row)