File size: 5,426 Bytes
bde2f3a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | """RMI Backend β Core Middleware."""
import json
import os
import uuid
from fastapi import Request
from fastapi.responses import JSONResponse
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Rate-limit config
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
CACHEABLE_TOOLS = {"token_price", "token_metadata", "wallet_tokens", "entity_intel"}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Payload size limit
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
MAX_PAYLOAD_SIZE = 1_048_576 # 1MB for standard JSON APIs
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
|