Files
A-Team-Security-Infra-Agent…/backend/routes/users.py
nogueiraguh 1135e9d6a9 refactor: decompose monolith — app.py 10,600→143 lines, 30+ modules
Fase 0 complete: extract all business logic, auth, database, and 176
endpoints from monolithic app.py into dedicated modules.

Structure:
- app.py: FastAPI hub (CORS, routers, startup/shutdown)
- models.py: 13 Pydantic request models
- utils.py: shared utilities (embed status, upload validation, process registries)
- config.py: constants, env vars, model catalogs
- auth/: crypto, jwt_auth, oidc, rate_limit
- database/: db(), init_db(), schema DDL
- services/: genai, compliance, chat, terraform, embeddings, cis_reports, mcp
- routes/: 13 APIRouter modules (auth, users, oci_config, oci_explorer,
  genai, mcp, adb, embeddings, reports, chat, terraform, settings, cis_engine)

Also: README updated with OCIR auth instructions for manual docker run.

86 tests passing.
2026-04-06 15:20:10 -03:00

65 lines
3.0 KiB
Python

"""User routes — list, me, update, delete, timezone."""
from fastapi import APIRouter, HTTPException, Depends
from database import db
from auth.jwt_auth import current_user, require, _audit
from models import UserUpdateReq
router = APIRouter()
# ── Users ─────────────────────────────────────────────────────────────────────
@router.get("/api/users")
async def list_users(u=Depends(require("admin"))):
with db() as c: rows = c.execute("SELECT id,first_name,last_name,username,email,role,mfa_enabled,timezone,is_active,created_at,last_login,auth_provider FROM users").fetchall()
return [dict(r) for r in rows]
@router.get("/api/users/me")
async def me(u=Depends(current_user)):
fields = ("id","first_name","last_name","username","email","role","mfa_enabled","timezone","auth_provider","must_change_password")
r = {k: u.get(k, "local" if k == "auth_provider" else None) for k in fields}
r["must_change_password"] = bool(r.get("must_change_password", 0))
return r
@router.put("/api/users/{uid}")
async def update_user(uid: str, req: UserUpdateReq, adm=Depends(require("admin"))):
sets, vals = [], []
if req.email is not None: sets.append("email=?"); vals.append(req.email)
if req.role is not None:
if req.role not in ("admin","user","viewer"): raise HTTPException(400, "Role inválida")
sets.append("role=?"); vals.append(req.role)
if req.is_active is not None: sets.append("is_active=?"); vals.append(int(req.is_active))
if req.timezone is not None: sets.append("timezone=?"); vals.append(req.timezone)
if sets:
vals.append(uid)
with db() as c: c.execute(f"UPDATE users SET {','.join(sets)} WHERE id=?", vals)
_audit(adm["id"], adm["username"], "update_user", uid)
return {"ok": True}
@router.delete("/api/users/{uid}")
async def del_user(uid: str, adm=Depends(require("admin"))):
if uid == adm["id"]: raise HTTPException(400, "Não pode desativar a si mesmo")
with db() as c: c.execute("UPDATE users SET is_active=0 WHERE id=?", (uid,))
_audit(adm["id"], adm["username"], "deactivate_user", uid)
return {"ok": True}
@router.put("/api/users/me/timezone")
async def set_user_timezone(body: dict, u=Depends(current_user)):
"""Set the current user's timezone."""
tz = body.get("timezone", "").strip()
if not tz:
raise HTTPException(400, "timezone is required")
try:
import zoneinfo
zoneinfo.ZoneInfo(tz)
except Exception:
raise HTTPException(400, f"Invalid timezone: {tz}")
with db() as c:
c.execute("UPDATE users SET timezone=? WHERE id=?", (tz, u["id"]))
_audit(u["id"], u["username"], "set_timezone", tz)
return {"ok": True, "timezone": tz}
@router.get("/api/users/me/timezone")
async def get_user_timezone(u=Depends(current_user)):
"""Get the current user's timezone."""
return {"timezone": u.get("timezone", "America/Sao_Paulo")}