mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.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 (264) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +9 -4
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -1,235 +0,0 @@
1
- """
2
- Auth Middleware Adapter for backward compatibility.
3
-
4
- This module provides an adapter that maintains the same interface as AuthMiddleware
5
- while using the new SecurityMiddleware internally.
6
- """
7
-
8
- import json
9
- from typing import Dict, List, Optional, Callable, Awaitable
10
-
11
- from fastapi import Request, Response
12
- from starlette.responses import JSONResponse
13
-
14
- from mcp_proxy_adapter.core.logging import logger
15
- from .base import BaseMiddleware
16
- from .security import SecurityMiddleware
17
-
18
-
19
- class AuthMiddlewareAdapter(BaseMiddleware):
20
- """
21
- Adapter for AuthMiddleware that uses SecurityMiddleware internally.
22
-
23
- Maintains the same interface as the original AuthMiddleware for backward compatibility.
24
- """
25
-
26
- def __init__(self, app, api_keys: Dict[str, str] = None,
27
- public_paths: List[str] = None, auth_enabled: bool = True):
28
- """
29
- Initialize auth middleware adapter.
30
-
31
- Args:
32
- app: FastAPI application
33
- api_keys: Dictionary with API keys (key: username)
34
- public_paths: List of paths accessible without authentication
35
- auth_enabled: Flag to enable/disable authentication
36
- """
37
- super().__init__(app)
38
-
39
- # Store original parameters for backward compatibility
40
- self.api_keys = api_keys or {}
41
- self.public_paths = public_paths or [
42
- "/docs",
43
- "/redoc",
44
- "/openapi.json",
45
- "/health"
46
- ]
47
- self.auth_enabled = auth_enabled
48
-
49
- # Create internal security middleware
50
- self.security_middleware = self._create_security_middleware()
51
-
52
- logger.info(f"AuthMiddlewareAdapter initialized: auth_enabled={auth_enabled}, "
53
- f"api_keys_count={len(self.api_keys)}, public_paths_count={len(self.public_paths)}")
54
-
55
- def _create_security_middleware(self) -> SecurityMiddleware:
56
- """
57
- Create internal SecurityMiddleware with AuthMiddleware configuration.
58
-
59
- Returns:
60
- SecurityMiddleware instance
61
- """
62
- # Convert AuthMiddleware config to SecurityMiddleware config
63
- security_config = {
64
- "security": {
65
- "enabled": self.auth_enabled,
66
- "auth": {
67
- "enabled": self.auth_enabled,
68
- "methods": ["api_key"],
69
- "api_keys": self.api_keys
70
- },
71
- "ssl": {
72
- "enabled": False
73
- },
74
- "permissions": {
75
- "enabled": False
76
- },
77
- "rate_limit": {
78
- "enabled": False
79
- },
80
- "public_paths": self.public_paths
81
- }
82
- }
83
-
84
- return SecurityMiddleware(self.app, security_config)
85
-
86
- async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
87
- """
88
- Process request using internal SecurityMiddleware with legacy API key handling.
89
-
90
- Args:
91
- request: Request object
92
- call_next: Next handler
93
-
94
- Returns:
95
- Response object
96
- """
97
- # Check if authentication is disabled
98
- if not self.auth_enabled:
99
- logger.debug("Authentication is disabled, skipping authentication check")
100
- return await call_next(request)
101
-
102
- # Check if path is public
103
- path = request.url.path
104
- if self._is_public_path(path):
105
- return await call_next(request)
106
-
107
- # Extract API key from various sources (legacy compatibility)
108
- api_key = self._extract_api_key(request)
109
-
110
- # Check for API key in JSON-RPC request body if not found in headers/query
111
- if not api_key and request.method in ["POST", "PUT", "PATCH"]:
112
- try:
113
- body = await request.body()
114
- if body:
115
- try:
116
- body_json = json.loads(body.decode("utf-8"))
117
- # Look for API key in params of JSON-RPC object
118
- if isinstance(body_json, dict) and "params" in body_json:
119
- api_key = body_json.get("params", {}).get("api_key")
120
- except json.JSONDecodeError:
121
- pass
122
- except Exception:
123
- pass
124
-
125
- if api_key:
126
- # Validate API key
127
- username = self._validate_api_key(api_key)
128
- if username:
129
- # Store username in request state for backward compatibility
130
- request.state.username = username
131
- logger.debug(f"API key authentication successful for {username}")
132
- return await call_next(request)
133
- else:
134
- logger.warning(f"Invalid API key provided | Path: {path}")
135
- return self._create_error_response("Invalid API key", 401)
136
- else:
137
- logger.warning(f"API key not provided | Path: {path}")
138
- return self._create_error_response("API key not provided", 401)
139
-
140
- def _extract_api_key(self, request: Request) -> Optional[str]:
141
- """
142
- Extract API key from request (legacy compatibility).
143
-
144
- Args:
145
- request: Request object
146
-
147
- Returns:
148
- API key or None
149
- """
150
- # Check for API key in header
151
- api_key = request.headers.get("X-API-Key")
152
-
153
- if not api_key:
154
- # Check for API key in query parameters
155
- api_key = request.query_params.get("api_key")
156
-
157
- # Note: Body extraction is handled in dispatch method
158
- # This method only handles headers and query parameters
159
-
160
- return api_key
161
-
162
- def _is_public_path(self, path: str) -> bool:
163
- """
164
- Check if the path is public (doesn't require authentication).
165
-
166
- Args:
167
- path: Request path
168
-
169
- Returns:
170
- True if path is public, False otherwise
171
- """
172
- return any(path.startswith(public_path) for public_path in self.public_paths)
173
-
174
- def _validate_api_key(self, api_key: str) -> Optional[str]:
175
- """
176
- Validate API key and return username.
177
-
178
- Args:
179
- api_key: API key to validate
180
-
181
- Returns:
182
- Username if valid, None otherwise
183
- """
184
- return self.api_keys.get(api_key)
185
-
186
- def _create_error_response(self, message: str, status_code: int) -> JSONResponse:
187
- """
188
- Create error response in AuthMiddleware format.
189
-
190
- Args:
191
- message: Error message
192
- status_code: HTTP status code
193
-
194
- Returns:
195
- JSONResponse with error
196
- """
197
- return JSONResponse(
198
- status_code=status_code,
199
- content={
200
- "jsonrpc": "2.0",
201
- "error": {
202
- "code": -32000 if status_code == 401 else -32603,
203
- "message": message,
204
- "data": {
205
- "auth_type": "api_key",
206
- "status_code": status_code
207
- }
208
- },
209
- "id": None
210
- }
211
- )
212
-
213
- def get_username(self, request: Request) -> Optional[str]:
214
- """
215
- Get username from request state (backward compatibility).
216
-
217
- Args:
218
- request: Request object
219
-
220
- Returns:
221
- Username or None
222
- """
223
- return getattr(request.state, 'username', None)
224
-
225
- def is_authenticated(self, request: Request) -> bool:
226
- """
227
- Check if request is authenticated (backward compatibility).
228
-
229
- Args:
230
- request: Request object
231
-
232
- Returns:
233
- True if authenticated, False otherwise
234
- """
235
- return hasattr(request.state, 'username') and request.state.username is not None
@@ -1,305 +0,0 @@
1
- """
2
- MTLS Middleware Adapter for backward compatibility.
3
-
4
- This module provides an adapter that maintains the same interface as MTLSMiddleware
5
- while using the new SecurityMiddleware internally.
6
- """
7
-
8
- import logging
9
- from typing import Dict, List, Optional, Any, Callable, Awaitable
10
- from cryptography import x509
11
- from cryptography.hazmat.primitives import serialization
12
-
13
- from fastapi import Request, Response
14
- from starlette.responses import JSONResponse
15
-
16
- from mcp_proxy_adapter.core.logging import logger
17
- from mcp_proxy_adapter.core.auth_validator import AuthValidator
18
- from mcp_proxy_adapter.core.role_utils import RoleUtils
19
- from mcp_proxy_adapter.core.certificate_utils import CertificateUtils
20
- from .base import BaseMiddleware
21
- from .security import SecurityMiddleware
22
-
23
-
24
- class MTLSMiddlewareAdapter(BaseMiddleware):
25
- """
26
- Adapter for MTLSMiddleware that uses SecurityMiddleware internally.
27
-
28
- Maintains the same interface as the original MTLSMiddleware for backward compatibility.
29
- """
30
-
31
- def __init__(self, app, mtls_config: Dict[str, Any]):
32
- """
33
- Initialize mTLS middleware adapter.
34
-
35
- Args:
36
- app: FastAPI application
37
- mtls_config: mTLS configuration dictionary
38
- """
39
- super().__init__(app)
40
-
41
- # Store original configuration for backward compatibility
42
- self.mtls_config = mtls_config
43
- self.auth_validator = AuthValidator()
44
- self.role_utils = RoleUtils()
45
- self.certificate_utils = CertificateUtils()
46
-
47
- # Extract configuration
48
- self.enabled = mtls_config.get("enabled", False)
49
- self.ca_cert_path = mtls_config.get("ca_cert")
50
- self.verify_client = mtls_config.get("verify_client", True)
51
- self.client_cert_required = mtls_config.get("client_cert_required", True)
52
- self.allowed_roles = mtls_config.get("allowed_roles", [])
53
- self.require_roles = mtls_config.get("require_roles", False)
54
-
55
- # Create internal security middleware
56
- self.security_middleware = self._create_security_middleware()
57
-
58
- logger.info(f"MTLSMiddlewareAdapter initialized: enabled={self.enabled}, "
59
- f"verify_client={self.verify_client}, "
60
- f"client_cert_required={self.client_cert_required}")
61
-
62
- def _create_security_middleware(self) -> SecurityMiddleware:
63
- """
64
- Create internal SecurityMiddleware with MTLSMiddleware configuration.
65
-
66
- Returns:
67
- SecurityMiddleware instance
68
- """
69
- # Convert MTLSMiddleware config to SecurityMiddleware config
70
- security_config = {
71
- "security": {
72
- "enabled": self.enabled,
73
- "auth": {
74
- "enabled": False
75
- },
76
- "ssl": {
77
- "enabled": self.enabled,
78
- "cert_file": None,
79
- "key_file": None,
80
- "ca_cert": self.ca_cert_path,
81
- "min_tls_version": "TLSv1.2",
82
- "verify_client": self.verify_client,
83
- "client_cert_required": self.client_cert_required
84
- },
85
- "permissions": {
86
- "enabled": self.require_roles,
87
- "roles_file": None,
88
- "default_role": "user",
89
- "deny_by_default": True
90
- },
91
- "rate_limit": {
92
- "enabled": False
93
- }
94
- }
95
- }
96
-
97
- return SecurityMiddleware(self.app, security_config)
98
-
99
- async def before_request(self, request: Request) -> None:
100
- """
101
- Process request before calling the main handler.
102
-
103
- Args:
104
- request: FastAPI request object
105
- """
106
- if not self.enabled:
107
- return
108
-
109
- try:
110
- # Use SecurityMiddleware for validation
111
- await self.security_middleware.before_request(request)
112
-
113
- # Additional MTLS-specific processing
114
- client_cert = self._extract_client_certificate(request)
115
- if client_cert:
116
- # Store certificate and roles in request state for backward compatibility
117
- request.state.client_certificate = client_cert
118
- request.state.client_roles = self._extract_roles_from_certificate(client_cert)
119
- request.state.client_common_name = self._get_common_name(client_cert)
120
-
121
- logger.debug(f"mTLS authentication successful for {request.state.client_common_name} "
122
- f"with roles: {request.state.client_roles}")
123
-
124
- except Exception as e:
125
- logger.error(f"mTLS authentication failed: {e}")
126
- raise
127
-
128
- def _extract_client_certificate(self, request: Request) -> Optional[x509.Certificate]:
129
- """
130
- Extract client certificate from request.
131
-
132
- Args:
133
- request: FastAPI request object
134
-
135
- Returns:
136
- Client certificate or None
137
- """
138
- # Check for certificate in request headers
139
- cert_header = request.headers.get("X-Client-Cert")
140
- if cert_header:
141
- try:
142
- cert_data = cert_header.encode('utf-8')
143
- return x509.load_pem_x509_certificate(cert_data)
144
- except Exception as e:
145
- logger.warning(f"Failed to parse certificate from header: {e}")
146
-
147
- # Check for certificate in request state (from SSL context)
148
- if hasattr(request, 'client') and hasattr(request.client, 'get_extra_info'):
149
- cert = request.client.get_extra_info('ssl_object')
150
- if cert:
151
- return cert
152
-
153
- return None
154
-
155
- def _validate_client_certificate(self, cert: x509.Certificate) -> bool:
156
- """
157
- Validate client certificate.
158
-
159
- Args:
160
- cert: Client certificate
161
-
162
- Returns:
163
- True if valid, False otherwise
164
- """
165
- try:
166
- # Basic validation
167
- if not self.certificate_utils.is_certificate_valid(cert):
168
- return False
169
-
170
- # CA validation if CA cert is provided
171
- if self.ca_cert_path:
172
- return self.certificate_utils.validate_certificate_chain(cert, self.ca_cert_path)
173
-
174
- return True
175
-
176
- except Exception as e:
177
- logger.error(f"Certificate validation failed: {e}")
178
- return False
179
-
180
- def _extract_roles_from_certificate(self, cert: x509.Certificate) -> List[str]:
181
- """
182
- Extract roles from client certificate.
183
-
184
- Args:
185
- cert: Client certificate
186
-
187
- Returns:
188
- List of roles
189
- """
190
- try:
191
- # Extract from subject alternative names
192
- roles = []
193
-
194
- # Check for roles in SAN
195
- san = cert.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
196
- if san:
197
- for name in san.value:
198
- if isinstance(name, x509.DNSName):
199
- if name.value.startswith("role="):
200
- role = name.value.split("=", 1)[1]
201
- roles.append(role)
202
-
203
- # Check for roles in subject
204
- subject = cert.subject
205
- for attr in subject:
206
- if attr.oid.dotted_string == "2.5.4.3": # Common Name
207
- if attr.value.startswith("role="):
208
- role = attr.value.split("=", 1)[1]
209
- roles.append(role)
210
-
211
- # Check allowed roles if specified
212
- if self.allowed_roles:
213
- roles = [role for role in roles if role in self.allowed_roles]
214
-
215
- return roles
216
-
217
- except Exception as e:
218
- logger.error(f"Failed to extract roles from certificate: {e}")
219
- return []
220
-
221
- def _get_common_name(self, cert: x509.Certificate) -> str:
222
- """
223
- Get common name from certificate.
224
-
225
- Args:
226
- cert: Client certificate
227
-
228
- Returns:
229
- Common name
230
- """
231
- try:
232
- subject = cert.subject
233
- for attr in subject:
234
- if attr.oid.dotted_string == "2.5.4.3": # Common Name
235
- return attr.value
236
- return "unknown"
237
- except Exception:
238
- return "unknown"
239
-
240
- def _validate_access(self, roles: List[str]) -> bool:
241
- """
242
- Validate access based on roles.
243
-
244
- Args:
245
- roles: List of client roles
246
-
247
- Returns:
248
- True if access is allowed, False otherwise
249
- """
250
- if not self.require_roles:
251
- return True
252
-
253
- if not roles:
254
- return False
255
-
256
- # Check if any role is allowed
257
- return any(role in self.allowed_roles for role in roles)
258
-
259
- def get_client_certificate(self, request: Request) -> Optional[x509.Certificate]:
260
- """
261
- Get client certificate from request state (backward compatibility).
262
-
263
- Args:
264
- request: Request object
265
-
266
- Returns:
267
- Client certificate or None
268
- """
269
- return getattr(request.state, 'client_certificate', None)
270
-
271
- def get_client_roles(self, request: Request) -> List[str]:
272
- """
273
- Get client roles from request state (backward compatibility).
274
-
275
- Args:
276
- request: Request object
277
-
278
- Returns:
279
- List of client roles
280
- """
281
- return getattr(request.state, 'client_roles', [])
282
-
283
- def get_client_common_name(self, request: Request) -> str:
284
- """
285
- Get client common name from request state (backward compatibility).
286
-
287
- Args:
288
- request: Request object
289
-
290
- Returns:
291
- Client common name
292
- """
293
- return getattr(request.state, 'client_common_name', 'unknown')
294
-
295
- def is_mtls_authenticated(self, request: Request) -> bool:
296
- """
297
- Check if request is mTLS authenticated (backward compatibility).
298
-
299
- Args:
300
- request: Request object
301
-
302
- Returns:
303
- True if mTLS authenticated, False otherwise
304
- """
305
- return hasattr(request.state, 'client_certificate') and request.state.client_certificate is not None