Major changes across backend, frontend, infrastructure: - Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud) - Admin panel: user management, pricing, settings - Glossary system with CSV import/export - Subscription and tier quota management - Security hardening (rate limiting, API key auth, path traversal fixes) - Docker compose for dev, prod, and IONOS deployment - Alembic migrations for new tables - Frontend: dashboard, pricing page, landing page, i18n (en/fr) - Test suite and verification scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
Story 3.3: Admin - Révocation API Key (Any User)
Status: done
Story
En tant qu'Admin, Je veux révoquer la clé API de n'importe quel utilisateur, de sorte que je puisse gérer la sécurité et prévenir les abus.
Acceptance Criteria
- Endpoint Admin DELETE:
DELETE /api/v1/admin/api-keys/{key_id}révoque la clé API spécifiée. (FR31) - Accès Admin Requis: Seul un administrateur authentifié peut accéder à cet endpoint.
- Révocation Universelle: L'admin peut révoquer N'IMPORTE quelle clé, peu importe le propriétaire.
- Révocation Immédiate: La clé est marquée
is_active=Falseet les requêtes suivantes avec cette clé retournent 401 avec le codeAPI_KEY_REVOKED. - Réponse Confirmée: Retourne 200 avec confirmation de révocation dans le format
{data: {...}, meta: {}}. - Audit Logging: L'action est journalisée avec
admin_idetreason(paramètre optionnel). - Clé Introuvable: Si la clé n'existe pas, retourne 404 avec
API_KEY_NOT_FOUND. - Paramètre Raison: L'admin peut optionnellement fournir une raison pour la révocation (body parameter).
Tasks / Subtasks
-
Task 1: Implémenter l'Endpoint Admin DELETE (AC: #1, #2, #3, #4, #5, #8)
- 1.1 Ajouter la route
DELETE /api/v1/admin/api-keys/{key_id}dansmain.py - 1.2 Utiliser la dépendance
require_adminexistante pour l'authentification - 1.3 Ajouter paramètre optionnel
reasondans le body de la requête - 1.4 Rechercher la clé par
iduniquement (PAS de filtre user_id) - 1.5 Si trouvée, définir
is_active=Falseet sauvegarder - 1.6 Retourner 200 avec confirmation
{data: {id, revoked: true, revoked_at, reason?}, meta: {}}
- 1.1 Ajouter la route
-
Task 2: Implémenter l'Audit Logging (AC: #6)
- 2.1 Logger l'action avec structlog ou logging standard
- 2.2 Inclure: timestamp, admin identifier, key_id, key owner user_id, reason
- 2.3 Niveau de log: INFO pour révocation réussie
-
Task 3: Gérer les Cas d'Erreur (AC: #7)
- 3.1 Retourner 404 si clé non trouvée
- 3.2 Retourner 401 si non authentifié admin (déjà géré par require_admin)
- 3.3 Vérifier que la clé existe avant de tenter la révocation
-
Task 4: Ajouter les Tests (AC: Tous)
- 4.1 Test révocation réussie par admin
- 4.2 Test révocation avec raison fournie
- 4.3 Test révocation échoue sans authentification admin (401)
- 4.4 Test révocation de clé inexistante (404)
- 4.5 Test clé révoquée ne peut plus authentifier (vérifier via get_user_by_api_key)
- 4.6 Test vérifier que l'audit logging fonctionne
Dev Notes
Infrastructure Existante (Ne pas réimplémenter)
Fonction Admin Auth (main.py lignes 215-236):
async def require_admin(authorization: Optional[str] = Header(None)) -> bool:
"""Dependency to require admin authentication"""
# Vérifie le token admin (stocké en mémoire ou via JWT)
# Retourne True si valide, sinon lève HTTPException
Modèle ApiKey (database/models.py):
class ApiKey(Base):
__tablename__ = "api_keys"
id = Column(String(36), primary_key=True)
user_id = Column(String(36), ForeignKey("users.id")) # Propriétaire de la clé
name = Column(String(100))
key_hash = Column(String(255))
key_prefix = Column(String(10))
is_active = Column(Boolean, default=True) # ⭐ Champ à modifier pour révocation
# ... autres champs
Pattern Admin Routes (main.py):
- Toutes les routes admin sont directement dans
main.pyavec préfixe/api/v1/admin/ - Utilisent
is_admin: bool = Depends(require_admin)comme dépendance
Différences Critiques avec Story 3.2
| Aspect | Story 3.2 (User) | Story 3.3 (Admin) |
|---|---|---|
| Endpoint | DELETE /api/v1/api-keys/{key_id} |
DELETE /api/v1/admin/api-keys/{key_id} |
| Auth | _require_pro_user (Pro tier) |
require_admin (Admin) |
| Filtre Query | user_id == user.id (propriété) |
Aucun (accès universel) |
| Logging | Non requis | Obligatoire avec admin_id + reason |
| Raison | Non applicable | Paramètre optionnel |
Patterns à Suivre
Format de Réponse Succès:
{
"data": {
"id": "abc123",
"revoked": true,
"revoked_at": "2024-01-15T10:30:00Z",
"owner_user_id": "user-uuid-here",
"reason": "Violation des conditions d'utilisation"
},
"meta": {}
}
Format de Réponse Erreur:
{
"error": "API_KEY_NOT_FOUND",
"message": "Clé API non trouvée"
}
Pattern de Query Admin (SANS filtre user_id):
# Admin peut révoquer N'IMPORTE quelle clé
api_key = session.query(ApiKey).filter(
ApiKey.id == key_id
).first()
Structure de Fichiers
main.py # MODIFIER - Ajouter DELETE /api/v1/admin/api-keys/{key_id}
routes/api_key_routes.py # Existant - Story 3.2 (ne pas modifier)
services/auth_service.py # Existant - get_user_by_api_key vérifie déjà is_active
database/models.py # Existant - ApiKey model
tests/test_story_3_3_admin_api_key_revocation.py # CRÉER
Project Structure Notes
- Les routes admin sont centralisées dans
main.py(pas de fichier routes/admin_routes.py) - Pattern établi par les autres endpoints admin existants
- Le logging utilise
structlogou le moduleloggingstandard de Python
Références
- [Source: _bmad-output/planning-artifacts/epics.md#Story 3.3]
- [Source: _bmad-output/planning-artifacts/architecture.md#Admin Dashboard]
- [Source: _bmad-output/planning-artifacts/architecture.md#API Response Formats]
- [Source: main.py#require_admin (lignes 215-236)]
- [Source: routes/api_key_routes.py#revoke_api_key (lignes 257-308)]
Intelligence de la Story Précédente (3.2)
Ce qui a été implémenté
- Endpoint DELETE pour révocation utilisateur à
DELETE /api/v1/api-keys/{key_id} - Soft delete avec
is_active=False - Fonction
get_user_by_api_keydansservices/auth_service.pyvérifieis_active - 18 tests complets dans
tests/test_story_3_2_api_key_revocation.py
Patterns Établis à Réutiliser
from datetime import datetime, timezone
from fastapi.responses import JSONResponse
# Soft delete pattern
api_key.is_active = False
session.commit()
# Response format
return JSONResponse(
status_code=200,
content={
"data": {
"id": api_key.id,
"revoked": True,
"revoked_at": datetime.now(timezone.utc).isoformat(),
},
"meta": {},
},
)
Points d'Attention Identifiés
- NE PAS utiliser
HTTPExceptionavecdetailstring - UtiliserJSONResponsestructuré - TOUJOURS snake_case dans les réponses JSON
- VÉRIFIER que
get_user_by_api_keyvérifie déjàis_active(✅ confirmé)
Intelligence Git (Commits Récents)
Derniers commits pertinents:
dfd45d9: Admin login endpoint - patterns d'authentification admin80318a8: Admin dashboard - structure des routes admin
Patterns identifiés:
- Routes admin directement dans
main.py - Dépendance
require_adminpour l'auth - Tests avec
pytestdanstests/
Contexte Métier
Epic 3: API & Automation (Pro)
Cette story est la troisième de l'Epic 3 qui permet aux utilisateurs Pro d'automatiser les traductions:
API Keys - Génération(Story 3.1 ✅)API Keys - Révocation User(Story 3.2 ✅)- API Keys - Révocation Admin (cette story)
- Authentification X-API-Key (Story 3.4 - backlog)
- API Versioning (Story 3.5 - backlog)
- Documentation OpenAPI (Story 3.6 - backlog)
- Webhooks (Stories 3.7-3.8 - backlog)
- Glossaires (Stories 3.9-3.10 - backlog)
- Custom Prompts (Stories 3.11-3.12 - backlog)
Valeur Business
La révocation admin est critique pour:
- Gérer les abus (surutilisation, comportement suspect)
- Répondre aux demandes de support (compte compromis)
- Appliquer les conditions d'utilisation
- Contrôle de sécurité centralisé
Dépendances
- Story 3.1 (prérequis): Génération de clés API ✅
- Story 3.2 (prérequis): Révocation utilisateur ✅
- Story 3.4 (impact): L'authentification API vérifie
is_active✅ (déjà implémenté)
Guardrails Développeur
❌ À NE PAS FAIRE
- NE PAS supprimer physiquement la clé de la DB (soft delete avec
is_active=False) - NE PAS filtrer par
user_iddans la query (admin a accès universel) - NE PAS utiliser
HTTPExceptionavecdetailstring (utiliser JSONResponse structuré) - NE PAS oublier l'audit logging (obligatoire pour cette story)
- NE PAS utiliser camelCase dans les réponses JSON (toujours snake_case)
- NE PAS créer un fichier
routes/admin_routes.py- les routes admin restent dansmain.py
✅ À FAIRE
- TOUJOURS utiliser
require_admindependency pour l'authentification - TOUJOURS soft delete (
is_active=False) - TOUJOURS logger l'action avec admin_id, key_id, owner_user_id, et reason
- TOUJOURS retourner 404 si clé non trouvée
- TOUJOURS suivre le format de réponse
{data: {...}, meta: {...}} - TOUJOURS écrire des tests pour tous les cas
- INCLURE le
owner_user_iddans la réponse pour traçabilité
Dev Agent Record
Agent Model Used
Claude 3.5 Sonnet (claude-3-5-sonnet)
Debug Log References
Aucun problème rencontré lors de l'implémentation.
Completion Notes List
- ✅ Analyse exhaustive du contexte terminée - guide complet créé pour le développeur
- ✅ Patterns de la Story 3.2 réutilisables identifiés
- ✅ Code existant analysé (main.py, routes/api_key_routes.py)
- ✅ Différences critiques avec Story 3.2 documentées
- ✅ Endpoint DELETE /api/v1/admin/api-keys/{key_id} implémenté dans main.py
- ✅ AdminRevokeApiKeyRequest model ajouté pour le body parameter optionnel
reason - ✅ Soft delete avec is_active=False et revoked_at timestamp
- ✅ Audit logging avec logger.info incluant admin_id, key_id, owner_user_id, reason, timestamp
- ✅ 15 tests créés et tous passent (tests/test_story_3_3_admin_api_key_revocation.py)
- ✅ Migration alembic 003_add_revoked_at_to_api_keys.py appliquée
- ✅ Tests de régression passent (Story 3.2 et admin tier change)
File List
main.py- MODIFIÉ - Ajout endpoint DELETE /api/v1/admin/api-keys/{key_id}, AdminRevokeApiKeyRequest model, require_admin retourne admin_idtests/test_story_3_3_admin_api_key_revocation.py- CRÉÉ - 15 tests couvrant tous les AC
Change Log
- 2026-02-22: Story créée avec contexte complet (patterns Story 3.2, code suggéré, différences critiques)
- 2026-02-22: Implémentation terminée - Endpoint DELETE, audit logging, tests
- 2026-02-22: Code review - Fix dead code (lignes dupliquées supprimées), fix AC6 (admin_id ajouté au log), fix tests (vérification admin_id ajoutée)
Checklist de Validation
Avant de marquer cette story comme terminée, vérifier:
DELETE /api/v1/admin/api-keys/{key_id}retourne 200 avec confirmation- La clé est marquée
is_active=False(soft delete) - L'admin peut révoquer n'importe quelle clé (pas de filtre user_id)
- L'action est journalisée avec admin_id, key_id, owner_user_id, reason
- Le paramètre
reasonoptionnel fonctionne correctement - Les utilisateurs non-admin reçoivent 401
- Les clés inexistantes retournent 404
- Tous les tests passent (15/15)
- La clé révoquée ne peut plus authentifier (vérifier via test)