This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""Programmatic API keys. Only admins may create, list, or revoke keys.
|
||||
|
||||
A key authenticates via the ``X-API-Key`` header and acts as the admin who
|
||||
created it. The raw key is returned exactly once, at creation; afterwards only
|
||||
its prefix and hash are stored.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session as DbSession
|
||||
|
||||
from .. import auth
|
||||
from ..auth import require_admin
|
||||
from ..database import get_db
|
||||
from ..errors import APIError
|
||||
from ..models import Admin, ApiKey
|
||||
from ..schemas import ApiKeyCreate, ApiKeyCreated, ApiKeyOut
|
||||
|
||||
router = APIRouter(prefix="/apikeys", tags=["apikeys"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[ApiKeyOut])
|
||||
def list_api_keys(_: Admin = Depends(require_admin), db: DbSession = Depends(get_db)):
|
||||
return db.execute(select(ApiKey).order_by(ApiKey.id.desc())).scalars().all()
|
||||
|
||||
|
||||
@router.post("", response_model=ApiKeyCreated, status_code=201)
|
||||
def create_api_key(
|
||||
payload: ApiKeyCreate,
|
||||
request: Request,
|
||||
admin: Admin = Depends(require_admin),
|
||||
db: DbSession = Depends(get_db),
|
||||
):
|
||||
raw = auth.generate_api_key()
|
||||
key = ApiKey(
|
||||
name=payload.name,
|
||||
key_prefix=raw[:8],
|
||||
key_hash=auth.hash_api_key(raw),
|
||||
created_by=admin.username,
|
||||
)
|
||||
db.add(key)
|
||||
db.commit()
|
||||
db.refresh(key)
|
||||
auth.record_log(
|
||||
db,
|
||||
username=admin.username,
|
||||
action="create_api_key",
|
||||
detail=f"Created API key '{key.name}' ({key.key_prefix}…)",
|
||||
request=request,
|
||||
)
|
||||
return ApiKeyCreated(**ApiKeyOut.model_validate(key).model_dump(), key=raw)
|
||||
|
||||
|
||||
@router.delete("/{key_id}", status_code=204)
|
||||
def revoke_api_key(
|
||||
key_id: int,
|
||||
request: Request,
|
||||
admin: Admin = Depends(require_admin),
|
||||
db: DbSession = Depends(get_db),
|
||||
):
|
||||
key = db.get(ApiKey, key_id)
|
||||
if key is None:
|
||||
raise APIError(status_code=404, detail=f"API key '{key_id}' not found")
|
||||
key.revoked = True
|
||||
db.commit()
|
||||
auth.record_log(
|
||||
db,
|
||||
username=admin.username,
|
||||
action="revoke_api_key",
|
||||
detail=f"Revoked API key '{key.name}' ({key.key_prefix}…)",
|
||||
request=request,
|
||||
)
|
||||
Reference in New Issue
Block a user