| """RMI Backend β Core Middleware.""" |
|
|
| import json |
| import os |
| import uuid |
|
|
| from fastapi import Request |
| from fastapi.responses import JSONResponse |
|
|
| |
| |
| |
| CACHEABLE_TOOLS = {"token_price", "token_metadata", "wallet_tokens", "entity_intel"} |
|
|
| |
| |
| |
| MAX_PAYLOAD_SIZE = 1_048_576 |
|
|
|
|
| async def cache_middleware(request: Request, call_next): |
| """Check Redis cache before executing tool. Store result after.""" |
| path = request.url.path |
| if not path.startswith("/api/v1/x402-tools/"): |
| return await call_next(request) |
| if request.method != "POST": |
| return await call_next(request) |
|
|
| tool = path.rstrip("/").split("/")[-1] |
| if tool not in CACHEABLE_TOOLS: |
| return await call_next(request) |
|
|
| try: |
| body = await request.body() |
| params = json.loads(body) if body else {} |
| except Exception: |
| return await call_next(request) |
|
|
| try: |
| from app.routers.x402_advanced_tools import get_cached, set_cached |
|
|
| cached = get_cached(tool, params) |
| if cached: |
| return JSONResponse(content=cached, headers={"X-Cache": "HIT", "X-Cache-TTL": "60"}) |
| except Exception: |
| pass |
|
|
| response = await call_next(request) |
| if response.status_code == 200: |
| try: |
| resp_body = b"" |
| async for chunk in response.body_iterator: |
| resp_body += chunk |
| result = json.loads(resp_body) |
| set_cached(tool, params, result) |
| return JSONResponse( |
| content=result, |
| status_code=response.status_code, |
| headers={**dict(response.headers), "X-Cache": "MISS"}, |
| ) |
| except Exception: |
| pass |
| return response |
|
|
|
|
| async def emergency_lockdown_middleware(request: Request, call_next): |
| """Check for emergency lockdown status. Block non-admin routes if active.""" |
| if request.url.path in ["/health", "/api/v1/admin/emergency-lockdown", "/api/v1/admin/emergency-status"]: |
| return await call_next(request) |
|
|
| try: |
| import redis.asyncio as redis_lib |
|
|
| r = redis_lib.Redis( |
| host=os.getenv("REDIS_HOST", "localhost"), |
| port=int(os.getenv("REDIS_PORT", "6379")), |
| password=os.getenv("REDIS_PASSWORD", ""), |
| decode_responses=True, |
| ) |
| is_locked = await r.exists("rmi:emergency_lockdown") |
| if is_locked: |
| auth_header = request.headers.get("Authorization", "") |
| session_token = request.headers.get("X-Admin-Session", "") |
| if not auth_header and not session_token: |
| return JSONResponse( |
| status_code=503, |
| content={"error": "Service Unavailable", "detail": "System is in emergency lockdown."}, |
| ) |
| except Exception: |
| pass |
| return await call_next(request) |
|
|
|
|
| async def hsts_middleware(request: Request, call_next): |
| """Force HTTPS and prevent protocol downgrade attacks.""" |
| response = await call_next(request) |
| response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload" |
| return response |
|
|
|
|
| async def request_id_middleware(request: Request, call_next): |
| """Generate unique request ID for log correlation.""" |
| request_id = request.headers.get("X-Request-ID") or str(uuid.uuid4()) |
| response = await call_next(request) |
| response.headers["X-Request-ID"] = request_id |
| return response |
|
|
|
|
| async def payload_size_limit_middleware(request: Request, call_next): |
| """Enforce 1MB payload limit on non-upload routes.""" |
| content_length = request.headers.get("content-length") |
| if content_length and int(content_length) > MAX_PAYLOAD_SIZE: |
| if not request.url.path.startswith("/api/v1/admin/backend/upload/"): |
| return JSONResponse( |
| status_code=413, |
| content={"error": "Payload Too Large", "detail": "Maximum payload size is 1MB"}, |
| ) |
| return await call_next(request) |
|
|
|
|
| async def secure_cookie_middleware(request: Request, call_next): |
| """Enforce secure cookie flags for admin sessions.""" |
| response = await call_next(request) |
| if "set-cookie" in response.headers: |
| cookie_val = response.headers["set-cookie"] |
| if "HttpOnly" not in cookie_val: |
| cookie_val += "; HttpOnly" |
| if "Secure" not in cookie_val: |
| cookie_val += "; Secure" |
| if "SameSite=Strict" not in cookie_val and "SameSite=Lax" not in cookie_val: |
| cookie_val += "; SameSite=Strict" |
| response.headers["set-cookie"] = cookie_val |
| return response |
|
|