Files
brunoandClaude Sonnet 4.6 87443535ff feat: implement core platform layers (P5-P8)
Implement complete agent platform with four major layers:

P5 - Engine Layer (LangGraph):
- AgentState with JSON serialization validation
- 5 async nodes (ask, plan, act, verify, report) with error boundaries
- Template resolution system for {{user_input}} and {{steps.xxx}}
- StateGraph with conditional edges and interrupt_after for approval flow
- 17/17 tests passing, 90% coverage

P6 - LLM Layer:
- ClaudeClient with Decimal-based cost calculation (ROUND_HALF_UP)
- Custom exceptions (ClaudeAPIError, ClaudeTimeoutError, ClaudeRateLimitError)
- PromptBuilder with cache_control support
- ModelRouter with sensitivity-based routing (local vs Claude)
- 18/18 tests passing, 90% coverage

P7 - Audit Layer (Three-tier):
- Sanitizer: mask phone/ID card/API keys, contains_sensitive, mask_dict
- Encryptor: AES-256-GCM with keyring support, 12-byte IV
- AuditLogger: L1 (DB), L2 (OTel), L3 (Encrypted payloads)
- 24/24 tests passing, 86% coverage

P8 - API Layer (FastAPI):
- Lifespan management with 8-step startup sequence
- Middleware: TracingMiddleware, RateLimitMiddleware (Redis), CORS
- Auth: JWT decode with python-jose, RBAC with permission matrix
- Routes: health, chat (SSE), approve, trace
- 13/13 tests passing, 91% coverage

All gate checks passing:
- pytest with 85%+ coverage
- mypy type checking
- ruff style checking

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-02-26 22:00:23 +08:00

70 lines
1.8 KiB
Python

"""
JWT authentication middleware.
"""
from __future__ import annotations
import logging
from typing import Any
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from api.contracts import UserContext
from core.config import get_settings
logger = logging.getLogger(__name__)
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> UserContext:
"""
Decode JWT token and extract user context.
Args:
credentials: HTTP Bearer credentials
Returns:
UserContext with user info
Raises:
HTTPException: 401 if token invalid
"""
token = credentials.credentials
settings = get_settings()
try:
# Decode JWT
payload: dict[str, Any] = jwt.decode(
token,
settings.JWT_SECRET_KEY,
algorithms=[settings.JWT_ALGORITHM],
)
# Extract user context
user_context = UserContext(
user_id=payload["sub"],
tenant_id=payload.get("tenant_id", "default"),
role=payload.get("role", "user"),
data_scope=payload.get("data_scope", "tenant"),
token_exp=payload.get("exp", 0),
)
logger.debug(f"Authenticated user: {user_context.user_id}")
return user_context
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired",
)
except JWTError as e:
logger.warning(f"Invalid JWT token: {e}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
)