mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
"""
|
|
Shared middleware for inter-service communication validation and security.
|
|
|
|
Provides:
|
|
- Request size limiting (max 5GB)
|
|
- Input validation and sanitization
|
|
- Comprehensive audit logging
|
|
"""
|
|
|
|
import logging
|
|
import time
|
|
from typing import Callable
|
|
from fastapi import FastAPI, Request, HTTPException, status
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
from starlette.responses import Response
|
|
|
|
logger = logging.getLogger("uvicorn.error")
|
|
|
|
# Maximum request size: 5GB
|
|
MAX_REQUEST_SIZE = 5 * 1024 * 1024 * 1024 # 5GB in bytes
|
|
|
|
|
|
class RequestSizeLimitMiddleware(BaseHTTPMiddleware):
|
|
"""Middleware to enforce maximum request size."""
|
|
|
|
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
"""Check request size before processing."""
|
|
# Check Content-Length header if available
|
|
content_length = request.headers.get("content-length")
|
|
if content_length:
|
|
try:
|
|
size = int(content_length)
|
|
if size > MAX_REQUEST_SIZE:
|
|
logger.warning(
|
|
"Request size %d exceeds limit %d from %s %s",
|
|
size,
|
|
MAX_REQUEST_SIZE,
|
|
request.client.host if request.client else "unknown",
|
|
request.url.path,
|
|
)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
|
detail=f"Request size exceeds {MAX_REQUEST_SIZE} bytes limit"
|
|
)
|
|
except ValueError:
|
|
pass
|
|
|
|
return await call_next(request)
|
|
|
|
|
|
class AuditLoggingMiddleware(BaseHTTPMiddleware):
|
|
"""Middleware for comprehensive audit logging of all requests."""
|
|
|
|
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
"""Log request and response details."""
|
|
start_time = time.time()
|
|
|
|
# Log request
|
|
client_ip = request.client.host if request.client else "unknown"
|
|
method = request.method
|
|
path = request.url.path
|
|
|
|
logger.info(
|
|
"REQUEST: %s %s from %s",
|
|
method,
|
|
path,
|
|
client_ip,
|
|
)
|
|
|
|
try:
|
|
response = await call_next(request)
|
|
|
|
# Log response
|
|
duration = time.time() - start_time
|
|
logger.info(
|
|
"RESPONSE: %s %s -> %d in %.2fms",
|
|
method,
|
|
path,
|
|
response.status_code,
|
|
duration * 1000,
|
|
)
|
|
|
|
return response
|
|
|
|
except Exception as e:
|
|
duration = time.time() - start_time
|
|
logger.exception(
|
|
"ERROR: %s %s failed after %.2fms: %s",
|
|
method,
|
|
path,
|
|
duration * 1000,
|
|
e,
|
|
)
|
|
raise
|
|
|
|
|
|
def add_security_middleware(app: FastAPI):
|
|
"""
|
|
Add all security and audit middleware to FastAPI app.
|
|
|
|
Args:
|
|
app: FastAPI application instance
|
|
"""
|
|
# Request size limiting (inner, checked first)
|
|
app.add_middleware(RequestSizeLimitMiddleware)
|
|
|
|
# Audit logging (outer, logs everything)
|
|
app.add_middleware(AuditLoggingMiddleware)
|