auth.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. """api.v1.auth — API Key management endpoints."""
  2. from __future__ import annotations
  3. import json
  4. import secrets
  5. from fastapi import APIRouter, Depends, HTTPException
  6. from pydantic import BaseModel, Field
  7. from typing import Optional
  8. from agentpaas.api.deps import get_tenant, get_database
  9. from agentpaas.api.middleware.auth import TenantContext, hash_key
  10. from agentpaas.db.models import Database, gen_id, now_utc
  11. router = APIRouter(prefix="/auth", tags=["auth"])
  12. class CreateKeyRequest(BaseModel):
  13. name: str = "default"
  14. scopes: list = Field(default_factory=lambda: ["agents:read", "agents:execute"])
  15. rate_limit: int = 60
  16. expires_at: Optional[str] = None
  17. @router.post("/keys", status_code=201)
  18. async def create_api_key(req: CreateKeyRequest, tenant: TenantContext = Depends(get_tenant), db: Database = Depends(get_database)):
  19. raw_key = f"ap_{secrets.token_hex(16)}"
  20. key_id = gen_id("key_")
  21. db.execute(
  22. "INSERT INTO api_keys (id, tenant_id, user_id, key_hash, key_prefix, name, scopes, rate_limit, expires_at, status, created_at) "
  23. "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?)",
  24. (key_id, tenant.tenant_id, tenant.user_id, hash_key(raw_key), raw_key[:8],
  25. req.name, json.dumps(req.scopes), req.rate_limit, req.expires_at, now_utc())
  26. )
  27. db.commit()
  28. return {"key_id": key_id, "api_key": raw_key, "warning": "Store this key securely. It will not be shown again."}
  29. @router.get("/keys")
  30. async def list_keys(tenant: TenantContext = Depends(get_tenant), db: Database = Depends(get_database)):
  31. keys = db.fetchall("SELECT id, key_prefix, name, scopes, rate_limit, status, last_used_at, created_at FROM api_keys WHERE tenant_id = ?", (tenant.tenant_id,))
  32. for k in keys:
  33. k["scopes"] = json.loads(k.get("scopes", "[]"))
  34. return {"keys": keys}
  35. @router.delete("/keys/{key_id}")
  36. async def revoke_key(key_id: str, tenant: TenantContext = Depends(get_tenant), db: Database = Depends(get_database)):
  37. db.execute("UPDATE api_keys SET status='revoked' WHERE id = ? AND tenant_id = ?", (key_id, tenant.tenant_id))
  38. db.commit()
  39. return {"message": "Key revoked"}