diff --git a/backend/routers/coffres.py b/backend/routers/coffres.py index c6b0bd3..34d0ab4 100644 --- a/backend/routers/coffres.py +++ b/backend/routers/coffres.py @@ -57,6 +57,7 @@ from backend.coffre_store import ( SecretExpireError, ValidationError, compter_octrois_actifs, + est_expire, compter_secrets, create_coffre, create_consommateur, @@ -495,6 +496,50 @@ async def api_distribuer(request: Request, body: dict = Body(...)): } +@router.get("/api/coffres/distribuables") +async def api_distribuables(request: Request): + """Secrets distribuables pour un consommateur (issue #43). + + Authentification par jeton consommateur (la même que + /distribuer), jamais par session utilisateur : c'est le + serveur agent qui appelle, pour alimenter la liste déroulante + de ses fournisseurs LLM. Retour = métadonnées seules des + secrets pour lesquels ce consommateur a un octroi ACTIF + (coffre actif, secret non expiré, octroi ni révoqué ni + expiré, y compris les octrois coffre-wide). Un consommateur + sans octroi reçoit une liste vide — jamais d'erreur + révélatrice, jamais de valeur. + """ + _exiger_actives() + auth = request.headers.get("Authorization", "") + jeton = auth[7:] if auth.startswith("Bearer ") else "" + consommateur = verifier_jeton_consommateur(jeton) + if consommateur is None: + raise HTTPException(status_code=401, + detail="consommateur_invalide") + if consommateur["statut"] != "actif": + raise HTTPException(status_code=403, + detail="consommateur_revoque") + sortie = [] + for coffre in list_coffres(): + if coffre["statut"] != "actif": + continue + for secret in list_secrets(coffre["id"]): + if est_expire(secret.get("expiration")): + continue + statut, _octroi = statut_octroi_consommateur( + consommateur["id"], coffre["id"], secret["id"]) + if statut != "ok": + continue + sortie.append({ + "reference": f"coffre:{coffre['slug']}/{secret['nom']}", + "coffre": coffre["slug"], + "nom": secret["nom"], + "version": secret["version_courante"], + }) + return sortie + + # --------------------------------------------------------------------------- # Un coffre, ses secrets, ses octrois, son journal # ---------------------------------------------------------------------------