""" Tenant Middleware - ServiceManagerWeb Middleware para manejo de multi-tenancy """ from fastapi import Request, HTTPException, status from starlette.middleware.base import BaseHTTPMiddleware from starlette.responses import Response import structlog logger = structlog.get_logger(__name__) class TenantMiddleware(BaseHTTPMiddleware): """ Middleware para extraer y validar información del tenant. Extrae el tenant_id del header X-Tenant-ID y lo almacena en el estado de la request para uso posterior. """ # Rutas que no requieren tenant EXCLUDED_PATHS = { "/health", "/", "/api/v1/auth/login", "/v1/auth/login", "/docs", "/api/v1/docs", "/v1/docs", "/openapi.json", "/api/v1/openapi.json", "/v1/openapi.json", "/redoc", "/api/v1/redoc", "/v1/redoc" } async def dispatch(self, request: Request, call_next) -> Response: """Process request and add tenant information.""" # Skip tenant validation for excluded paths if request.url.path in self.EXCLUDED_PATHS or request.url.path.startswith("/docs"): return await call_next(request) # Extract tenant from header tenant_id = request.headers.get("X-Tenant-ID") tenant_slug = request.headers.get("X-Tenant-Slug") # For now, we'll be more permissive in development # In production, tenant should be strictly required if not tenant_id and not tenant_slug: logger.warning( "Request without tenant information", path=request.url.path, method=request.method ) # For now, continue without tenant for development # raise HTTPException( # status_code=status.HTTP_400_BAD_REQUEST, # detail="Tenant information required (X-Tenant-ID or X-Tenant-Slug header)" # ) # Store tenant info in request state request.state.tenant_id = tenant_id request.state.tenant_slug = tenant_slug # TODO: Validate tenant exists and is active # This would involve a database query which we'll implement later logger.debug( "Tenant middleware processed", tenant_id=tenant_id, tenant_slug=tenant_slug, path=request.url.path ) return await call_next(request)