mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. mcp_proxy_adapter/__main__.py +32 -0
  2. mcp_proxy_adapter/api/app.py +290 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +38 -32
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +8 -1
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +394 -14
  41. mcp_proxy_adapter/core/app_factory.py +410 -0
  42. mcp_proxy_adapter/core/app_runner.py +272 -0
  43. mcp_proxy_adapter/core/auth_validator.py +606 -0
  44. mcp_proxy_adapter/core/certificate_utils.py +1045 -0
  45. mcp_proxy_adapter/core/client.py +574 -0
  46. mcp_proxy_adapter/core/client_manager.py +284 -0
  47. mcp_proxy_adapter/core/client_security.py +384 -0
  48. mcp_proxy_adapter/core/config_converter.py +405 -0
  49. mcp_proxy_adapter/core/config_validator.py +218 -0
  50. mcp_proxy_adapter/core/logging.py +19 -3
  51. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  52. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  53. mcp_proxy_adapter/core/protocol_manager.py +385 -0
  54. mcp_proxy_adapter/core/proxy_client.py +602 -0
  55. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  56. mcp_proxy_adapter/core/role_utils.py +426 -0
  57. mcp_proxy_adapter/core/security_adapter.py +370 -0
  58. mcp_proxy_adapter/core/security_factory.py +239 -0
  59. mcp_proxy_adapter/core/security_integration.py +286 -0
  60. mcp_proxy_adapter/core/server_adapter.py +282 -0
  61. mcp_proxy_adapter/core/server_engine.py +270 -0
  62. mcp_proxy_adapter/core/settings.py +1 -0
  63. mcp_proxy_adapter/core/ssl_utils.py +234 -0
  64. mcp_proxy_adapter/core/transport_manager.py +292 -0
  65. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  66. mcp_proxy_adapter/custom_openapi.py +22 -11
  67. mcp_proxy_adapter/examples/__init__.py +13 -4
  68. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  69. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  70. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  71. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  72. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  73. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  74. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  75. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  76. mcp_proxy_adapter/examples/demo_client.py +275 -0
  77. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  78. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  79. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  80. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  81. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  82. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  83. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  84. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  85. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  86. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  87. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  88. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  89. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  90. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  91. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  92. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  93. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  94. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  95. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  96. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  97. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  98. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  99. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  100. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  101. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  102. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  103. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  104. mcp_proxy_adapter/examples/run_example.py +59 -0
  105. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  106. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  107. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  108. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  109. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  110. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  111. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  112. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  113. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  114. mcp_proxy_adapter/examples/test_config.py +148 -0
  115. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  116. mcp_proxy_adapter/examples/test_examples.py +281 -0
  117. mcp_proxy_adapter/examples/universal_client.py +620 -0
  118. mcp_proxy_adapter/main.py +93 -0
  119. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  120. mcp_proxy_adapter/version.py +5 -2
  121. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  122. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  123. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  124. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  125. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  126. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  127. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  128. mcp_proxy_adapter/examples/README.md +0 -124
  129. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  130. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  131. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  132. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  133. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  134. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  135. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  136. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  137. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  138. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  139. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  140. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  141. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  142. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  143. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  144. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  145. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  146. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  147. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  148. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  149. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  150. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  153. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  154. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  155. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  156. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  157. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  158. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  159. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  160. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  161. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  162. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  163. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  164. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  165. mcp_proxy_adapter/tests/__init__.py +0 -0
  166. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  167. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  168. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  169. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  170. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  171. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  172. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  173. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  174. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  175. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  176. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  177. mcp_proxy_adapter/tests/conftest.py +0 -131
  178. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  180. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  181. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  182. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  183. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  184. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  185. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  186. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  187. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  188. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  189. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  190. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  191. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  192. mcp_proxy_adapter/tests/test_config.py +0 -127
  193. mcp_proxy_adapter/tests/test_utils.py +0 -65
  194. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  195. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  196. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  197. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  198. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  199. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  200. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,122 @@
1
+ """
2
+ Transport Middleware Module
3
+
4
+ This module provides middleware for transport validation in the MCP Proxy Adapter.
5
+ """
6
+
7
+ from typing import Callable
8
+ from fastapi import Request, Response
9
+ from fastapi.responses import JSONResponse
10
+ from starlette.middleware.base import BaseHTTPMiddleware
11
+
12
+ from mcp_proxy_adapter.core.transport_manager import transport_manager
13
+ from mcp_proxy_adapter.core.logging import logger
14
+
15
+
16
+ class TransportMiddleware(BaseHTTPMiddleware):
17
+ """Middleware for transport validation."""
18
+
19
+ def __init__(self, app, transport_manager_instance=None):
20
+ """
21
+ Initialize transport middleware.
22
+
23
+ Args:
24
+ app: FastAPI application
25
+ transport_manager_instance: Transport manager instance (optional)
26
+ """
27
+ super().__init__(app)
28
+ self.transport_manager = transport_manager_instance or transport_manager
29
+
30
+ async def dispatch(self, request: Request, call_next: Callable) -> Response:
31
+ """
32
+ Process request through transport middleware.
33
+
34
+ Args:
35
+ request: Incoming request
36
+ call_next: Next middleware/endpoint
37
+
38
+ Returns:
39
+ Response from next middleware/endpoint or error response
40
+ """
41
+ # Determine transport type from request
42
+ transport_type = self._get_request_transport_type(request)
43
+
44
+ # Check if request matches configured transport
45
+ if not self._is_transport_allowed(transport_type):
46
+ configured_type = self.transport_manager.get_transport_type()
47
+ configured_type_str = configured_type.value if configured_type else "not configured"
48
+ logger.warning(f"Transport not allowed: {transport_type} for {request.url}")
49
+ return JSONResponse(
50
+ status_code=403,
51
+ content={
52
+ "error": "Transport not allowed",
53
+ "message": f"Transport '{transport_type}' is not allowed. Configured transport: {configured_type_str}",
54
+ "configured_transport": configured_type_str,
55
+ "request_url": str(request.url)
56
+ }
57
+ )
58
+
59
+ # Add transport info to request state
60
+ request.state.transport_type = transport_type
61
+ request.state.transport_allowed = True
62
+
63
+ response = await call_next(request)
64
+ return response
65
+
66
+ def _get_request_transport_type(self, request: Request) -> str:
67
+ """
68
+ Determine transport type from request.
69
+
70
+ Args:
71
+ request: Incoming request
72
+
73
+ Returns:
74
+ Transport type string
75
+ """
76
+ if request.url.scheme == "https":
77
+ # Check for client certificate for MTLS
78
+ if self._has_client_certificate(request):
79
+ return "mtls"
80
+ return "https"
81
+ return "http"
82
+
83
+ def _has_client_certificate(self, request: Request) -> bool:
84
+ """
85
+ Check if request has client certificate.
86
+
87
+ Args:
88
+ request: Incoming request
89
+
90
+ Returns:
91
+ True if client certificate is present, False otherwise
92
+ """
93
+ # Check for client certificate in request headers or SSL context
94
+ # This is a simplified check - in production, you might need more sophisticated detection
95
+ client_cert_header = request.headers.get("ssl-client-cert")
96
+ if client_cert_header:
97
+ return True
98
+
99
+ # Check if request has SSL client certificate context
100
+ if hasattr(request, "client") and request.client:
101
+ # In a real implementation, you would check the SSL context
102
+ # For now, we'll assume HTTPS with client cert is MTLS
103
+ return self.transport_manager.is_mtls()
104
+
105
+ return False
106
+
107
+ def _is_transport_allowed(self, transport_type: str) -> bool:
108
+ """
109
+ Check if transport type is allowed.
110
+
111
+ Args:
112
+ transport_type: Transport type to check
113
+
114
+ Returns:
115
+ True if transport is allowed, False otherwise
116
+ """
117
+ configured_type = self.transport_manager.get_transport_type()
118
+ if not configured_type:
119
+ logger.error("Transport not configured")
120
+ return False
121
+
122
+ return transport_type == configured_type.value
@@ -0,0 +1,197 @@
1
+ """
2
+ Unified Security Middleware - Direct Framework Integration
3
+
4
+ This middleware now directly uses mcp_security_framework components
5
+ instead of custom implementations.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import time
12
+ import logging
13
+ from typing import Dict, Any, Optional, Callable, Awaitable
14
+ from fastapi import Request, Response
15
+ from starlette.middleware.base import BaseHTTPMiddleware
16
+
17
+ # Direct import from framework
18
+ try:
19
+ from mcp_security_framework.middleware.fastapi_middleware import FastAPISecurityMiddleware
20
+ from mcp_security_framework import SecurityManager
21
+ from mcp_security_framework.schemas.config import SecurityConfig
22
+ SECURITY_FRAMEWORK_AVAILABLE = True
23
+ except ImportError:
24
+ SECURITY_FRAMEWORK_AVAILABLE = False
25
+ FastAPISecurityMiddleware = None
26
+ SecurityManager = None
27
+ SecurityConfig = None
28
+
29
+ from mcp_proxy_adapter.core.logging import logger
30
+ from mcp_proxy_adapter.core.security_integration import create_security_integration
31
+
32
+
33
+ class SecurityValidationError(Exception):
34
+ """Security validation error."""
35
+
36
+ def __init__(self, message: str, error_code: int):
37
+ self.message = message
38
+ self.error_code = error_code
39
+ super().__init__(self.message)
40
+
41
+
42
+ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
43
+ """
44
+ Unified security middleware using mcp_security_framework.
45
+
46
+ This middleware now directly uses the security framework's FastAPI middleware
47
+ and components instead of custom implementations.
48
+ """
49
+
50
+ def __init__(self, app, config: Dict[str, Any]):
51
+ """
52
+ Initialize unified security middleware.
53
+
54
+ Args:
55
+ app: FastAPI application
56
+ config: mcp_proxy_adapter configuration dictionary
57
+ """
58
+ super().__init__(app)
59
+ self.config = config
60
+
61
+ # Create security integration
62
+ try:
63
+ security_config = config.get("security", {})
64
+ self.security_integration = create_security_integration(security_config)
65
+ # Use framework's FastAPI middleware
66
+ self.framework_middleware = self.security_integration.security_manager.create_fastapi_middleware()
67
+ logger.info("Using mcp_security_framework FastAPI middleware")
68
+ # IMPORTANT: Don't replace self.app! This breaks the middleware chain.
69
+ # Instead, store the framework middleware for use in dispatch method.
70
+ logger.info("Framework middleware will be used in dispatch method")
71
+ except Exception as e:
72
+ logger.error(f"Security framework integration failed: {e}")
73
+ # Instead of raising error, log warning and continue without security
74
+ logger.warning("Continuing without security framework - some security features will be disabled")
75
+ self.security_integration = None
76
+ self.framework_middleware = None
77
+ # Keep original app in place when framework middleware is unavailable
78
+ # BaseHTTPMiddleware initialized it via super().__init__(app)
79
+
80
+ logger.info("Unified security middleware initialized")
81
+
82
+ async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
83
+ """
84
+ Process request using framework middleware.
85
+
86
+ Args:
87
+ request: Request object
88
+ call_next: Next handler
89
+
90
+ Returns:
91
+ Response object
92
+ """
93
+ try:
94
+ # Simple built-in API key enforcement if configured
95
+ security_cfg = self.config.get("security", {}) if isinstance(self.config, dict) else {}
96
+ auth_cfg = security_cfg.get("auth", {})
97
+ permissions_cfg = security_cfg.get("permissions", {})
98
+ public_paths = set(auth_cfg.get("public_paths", ["/health", "/docs", "/openapi.json"]))
99
+ # JSON-RPC endpoint must not be public when API key is required
100
+ public_paths.discard("/api/jsonrpc")
101
+ path = request.url.path
102
+ methods = set(auth_cfg.get("methods", []))
103
+ api_keys: Dict[str, str] = auth_cfg.get("api_keys", {}) or {}
104
+
105
+ # Enforce only for non-public paths when api_key method configured
106
+ if security_cfg.get("enabled", False) and ("api_key" in methods) and (path not in public_paths):
107
+ # Accept either X-API-Key or Authorization: Bearer
108
+ token = request.headers.get("X-API-Key")
109
+ if not token:
110
+ authz = request.headers.get("Authorization", "")
111
+ if authz.startswith("Bearer "):
112
+ token = authz[7:]
113
+ if not token or (api_keys and token not in api_keys):
114
+ from fastapi.responses import JSONResponse
115
+ return JSONResponse(status_code=401, content={
116
+ "error": {
117
+ "code": 401,
118
+ "message": "Unauthorized: invalid or missing API key",
119
+ "type": "authentication_error"
120
+ }
121
+ })
122
+
123
+ # Continue with framework middleware or regular flow
124
+ if self.framework_middleware:
125
+ # If framework middleware exists, we need to call it manually
126
+ # This is a workaround since we can't chain ASGI apps in BaseHTTPMiddleware
127
+ logger.debug("Framework middleware exists, continuing with regular call_next")
128
+ return await call_next(request)
129
+ else:
130
+ # No framework middleware, continue normally
131
+ return await call_next(request)
132
+
133
+ except SecurityValidationError as e:
134
+ # Handle security validation errors
135
+ return await self._handle_security_error(request, e)
136
+ except Exception as e:
137
+ # Handle other errors
138
+ logger.error(f"Unexpected error in unified security middleware: {e}")
139
+ return await self._handle_general_error(request, e)
140
+
141
+
142
+
143
+ async def _handle_security_error(self, request: Request, error: SecurityValidationError) -> Response:
144
+ """
145
+ Handle security validation errors.
146
+
147
+ Args:
148
+ request: Request object
149
+ error: Security validation error
150
+
151
+ Returns:
152
+ Error response
153
+ """
154
+ from fastapi.responses import JSONResponse
155
+
156
+ error_response = {
157
+ "error": {
158
+ "code": error.error_code,
159
+ "message": error.message,
160
+ "type": "security_validation_error"
161
+ }
162
+ }
163
+
164
+ logger.warning(f"Security validation failed: {error.message}")
165
+
166
+ return JSONResponse(
167
+ status_code=error.error_code,
168
+ content=error_response
169
+ )
170
+
171
+ async def _handle_general_error(self, request: Request, error: Exception) -> Response:
172
+ """
173
+ Handle general errors.
174
+
175
+ Args:
176
+ request: Request object
177
+ error: General error
178
+
179
+ Returns:
180
+ Error response
181
+ """
182
+ from fastapi.responses import JSONResponse
183
+
184
+ error_response = {
185
+ "error": {
186
+ "code": 500,
187
+ "message": "Internal server error",
188
+ "type": "general_error"
189
+ }
190
+ }
191
+
192
+ logger.error(f"General error in security middleware: {error}")
193
+
194
+ return JSONResponse(
195
+ status_code=500,
196
+ content=error_response
197
+ )
@@ -0,0 +1,158 @@
1
+ """
2
+ User Info Middleware
3
+
4
+ This middleware extracts user information from authentication headers
5
+ and sets it in request.state for use by commands.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ from typing import Dict, Any, Optional, Callable, Awaitable
13
+ from fastapi import Request, Response
14
+ from starlette.middleware.base import BaseHTTPMiddleware
15
+
16
+ from mcp_proxy_adapter.core.logging import logger
17
+
18
+ # Import mcp_security_framework components
19
+ try:
20
+ from mcp_security_framework import AuthManager
21
+ from mcp_security_framework.schemas.config import AuthConfig
22
+ _MCP_SECURITY_AVAILABLE = True
23
+ print("✅ mcp_security_framework available in middleware")
24
+ except ImportError:
25
+ _MCP_SECURITY_AVAILABLE = False
26
+ print("⚠️ mcp_security_framework not available in middleware, using basic auth")
27
+
28
+
29
+ class UserInfoMiddleware(BaseHTTPMiddleware):
30
+ """
31
+ Middleware for setting user information in request.state.
32
+
33
+ This middleware extracts user information from authentication headers
34
+ and sets it in request.state for use by commands.
35
+ """
36
+
37
+ def __init__(self, app, config: Dict[str, Any]):
38
+ """
39
+ Initialize user info middleware.
40
+
41
+ Args:
42
+ app: FastAPI application
43
+ config: Configuration dictionary
44
+ """
45
+ super().__init__(app)
46
+ self.config = config
47
+
48
+ # Initialize AuthManager if available
49
+ self.auth_manager = None
50
+ self._security_available = _MCP_SECURITY_AVAILABLE
51
+
52
+ if self._security_available:
53
+ try:
54
+ # Get API keys configuration
55
+ security_config = config.get("security", {})
56
+ auth_config = security_config.get("auth", {})
57
+
58
+ # Create AuthConfig for mcp_security_framework
59
+ mcp_auth_config = AuthConfig(
60
+ enabled=True,
61
+ methods=["api_key"],
62
+ api_keys=auth_config.get("api_keys", {})
63
+ )
64
+
65
+ self.auth_manager = AuthManager(mcp_auth_config)
66
+ logger.info("✅ User info middleware initialized with mcp_security_framework")
67
+ except Exception as e:
68
+ logger.warning(f"⚠️ Failed to initialize AuthManager: {e}")
69
+ self._security_available = False
70
+
71
+ if not self._security_available:
72
+ # Fallback to basic API key handling
73
+ security_config = config.get("security", {})
74
+ auth_config = security_config.get("auth", {})
75
+ self.api_keys = auth_config.get("api_keys", {})
76
+ logger.info("ℹ️ User info middleware initialized with basic auth")
77
+
78
+ async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
79
+ """
80
+ Process request and set user info in request.state.
81
+
82
+ Args:
83
+ request: Request object
84
+ call_next: Next handler
85
+
86
+ Returns:
87
+ Response object
88
+ """
89
+ # Extract API key from headers
90
+ api_key = request.headers.get("X-API-Key")
91
+
92
+ if api_key:
93
+ if self.auth_manager and self._security_available:
94
+ try:
95
+ # Use mcp_security_framework AuthManager
96
+ auth_result = self.auth_manager.authenticate_api_key(api_key)
97
+
98
+ if auth_result.is_valid:
99
+ # Set user info from AuthManager result
100
+ request.state.user = {
101
+ "id": api_key,
102
+ "role": auth_result.roles[0] if auth_result.roles else "guest",
103
+ "roles": auth_result.roles or ["guest"],
104
+ "permissions": getattr(auth_result, 'permissions', ["read"])
105
+ }
106
+ logger.debug(f"✅ Authenticated user with mcp_security_framework: {request.state.user}")
107
+ else:
108
+ # Authentication failed
109
+ request.state.user = {
110
+ "id": None,
111
+ "role": "guest",
112
+ "roles": ["guest"],
113
+ "permissions": ["read"]
114
+ }
115
+ logger.debug(f"❌ Authentication failed for API key: {api_key[:8]}...")
116
+ except Exception as e:
117
+ logger.warning(f"⚠️ AuthManager error: {e}, falling back to basic auth")
118
+ self._security_available = False
119
+
120
+ if not self._security_available:
121
+ # Fallback to basic API key handling
122
+ if api_key in getattr(self, 'api_keys', {}):
123
+ user_role = self.api_keys[api_key]
124
+
125
+ # Get permissions for this role from roles file if available
126
+ role_permissions = ["read"] # default permissions
127
+ if hasattr(self, 'roles_config') and self.roles_config and user_role in self.roles_config:
128
+ role_permissions = self.roles_config[user_role].get("permissions", ["read"])
129
+
130
+ # Set user info in request.state
131
+ request.state.user = {
132
+ "id": api_key,
133
+ "role": user_role,
134
+ "roles": [user_role],
135
+ "permissions": role_permissions
136
+ }
137
+
138
+ logger.debug(f"✅ Authenticated user with basic auth: {request.state.user}")
139
+ else:
140
+ # API key not found
141
+ request.state.user = {
142
+ "id": None,
143
+ "role": "guest",
144
+ "roles": ["guest"],
145
+ "permissions": ["read"]
146
+ }
147
+ logger.debug(f"❌ API key not found: {api_key[:8]}...")
148
+ else:
149
+ # No API key provided - guest access
150
+ request.state.user = {
151
+ "id": None,
152
+ "role": "guest",
153
+ "roles": ["guest"],
154
+ "permissions": ["read"]
155
+ }
156
+ logger.debug("ℹ️ No API key provided, using guest access")
157
+
158
+ return await call_next(request)
@@ -6,9 +6,15 @@ from mcp_proxy_adapter.commands.base import Command
6
6
  from mcp_proxy_adapter.commands.command_registry import registry, CommandRegistry
7
7
  from mcp_proxy_adapter.commands.dependency_container import container, DependencyContainer
8
8
  from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult, ErrorResult
9
-
10
- # Automatically discover and register commands
11
- registry.discover_commands()
9
+ from mcp_proxy_adapter.commands.auth_validation_command import AuthValidationCommand
10
+ from mcp_proxy_adapter.commands.ssl_setup_command import SSLSetupCommand
11
+ from mcp_proxy_adapter.commands.certificate_management_command import CertificateManagementCommand
12
+ from mcp_proxy_adapter.commands.key_management_command import KeyManagementCommand
13
+ from mcp_proxy_adapter.commands.cert_monitor_command import CertMonitorCommand
14
+ from mcp_proxy_adapter.commands.transport_management_command import TransportManagementCommand
15
+ from mcp_proxy_adapter.commands.role_test_command import RoleTestCommand
16
+ from mcp_proxy_adapter.commands.echo_command import EchoCommand
17
+ from mcp_proxy_adapter.commands.proxy_registration_command import ProxyRegistrationCommand
12
18
 
13
19
  __all__ = [
14
20
  "Command",
@@ -18,5 +24,14 @@ __all__ = [
18
24
  "registry",
19
25
  "CommandRegistry",
20
26
  "container",
21
- "DependencyContainer"
27
+ "DependencyContainer",
28
+ "AuthValidationCommand",
29
+ "SSLSetupCommand",
30
+ "CertificateManagementCommand",
31
+ "KeyManagementCommand",
32
+ "CertMonitorCommand",
33
+ "TransportManagementCommand",
34
+ "RoleTestCommand",
35
+ "EchoCommand",
36
+ "ProxyRegistrationCommand"
22
37
  ]