"""Credentials for node integrations. Values go in and are never handed back out — the API only ever lists names. """ from typing import Any from fastapi import APIRouter, Depends, HTTPException from fastapi.concurrency import run_in_threadpool from pydantic import BaseModel from fluksio.api.deps import get_current_active_superuser from fluksio.flow.secrets import SecretNotFound, get_secrets from fluksio.models import Message # Superuser, not merely signed in. The names alone say what this installation # talks to, and `PUT /{name}` takes any name — so an ordinary account could # overwrite the credential a flow authenticates with. `/search` already gates # secret names this way and said so; this router was the half that did not. router = APIRouter( prefix="/secrets", tags=["secrets"], dependencies=[Depends(get_current_active_superuser)], ) class SecretNames(BaseModel): data: list[str] count: int class SecretValue(BaseModel): value: str @router.get("/", response_model=SecretNames) def read_secrets() -> Any: """List the names of stored secrets.""" names = get_secrets().list() return SecretNames(data=names, count=len(names)) @router.put("/{name}", response_model=Message) async def save_secret(name: str, body: SecretValue) -> Any: """Store a secret under a name that nodes can reference.""" await run_in_threadpool(get_secrets().set, name, body.value) return Message(message=f"Saved secret '{name}'") @router.delete("/{name}", response_model=Message) async def delete_secret(name: str) -> Any: """Delete a secret.""" try: await run_in_threadpool(get_secrets().delete, name) except SecretNotFound: raise HTTPException(status_code=404, detail=f"No secret named '{name}'") return Message(message=f"Deleted secret '{name}'")