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,376 +0,0 @@
1
- """
2
- Unified Security Middleware for mcp_security_framework integration.
3
-
4
- This module provides a single middleware that replaces AuthMiddleware, RateLimitMiddleware,
5
- MTLSMiddleware, and RolesMiddleware with a unified security solution.
6
- """
7
-
8
- import json
9
- import logging
10
- from typing import Callable, Awaitable, Dict, Any, Optional
11
-
12
- from fastapi import Request, Response
13
- from starlette.responses import JSONResponse
14
-
15
- from mcp_proxy_adapter.core.security_factory import SecurityFactory
16
- from mcp_proxy_adapter.core.logging import logger
17
- from .base import BaseMiddleware
18
-
19
-
20
- class SecurityMiddleware(BaseMiddleware):
21
- """
22
- Unified security middleware based on mcp_security_framework.
23
-
24
- Replaces AuthMiddleware, RateLimitMiddleware, MTLSMiddleware, and RolesMiddleware
25
- with a single, comprehensive security solution.
26
- """
27
-
28
- def __init__(self, app, config: Dict[str, Any]):
29
- """
30
- Initialize unified security middleware.
31
-
32
- Args:
33
- app: FastAPI application
34
- config: mcp_proxy_adapter configuration dictionary
35
- """
36
- super().__init__(app)
37
- self.config = config
38
- self.security_config = config.get("security", {})
39
-
40
- # Create security adapter
41
- self.security_adapter = SecurityFactory.create_security_adapter(config)
42
-
43
- # Public paths that don't require security validation
44
- self.public_paths = [
45
- "/docs",
46
- "/redoc",
47
- "/openapi.json",
48
- "/health",
49
- "/favicon.ico"
50
- ]
51
-
52
- # Add custom public paths from config
53
- custom_public_paths = self.security_config.get("public_paths", [])
54
- self.public_paths.extend(custom_public_paths)
55
-
56
- logger.info(f"Security middleware initialized with {len(self.public_paths)} public paths")
57
-
58
- async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
59
- """
60
- Process request and handle security validation.
61
-
62
- Args:
63
- request: Request object
64
- call_next: Next handler
65
-
66
- Returns:
67
- Response object
68
- """
69
- try:
70
- # Process request before calling the main handler
71
- await self.before_request(request)
72
-
73
- # Call the next middleware or main handler
74
- return await call_next(request)
75
-
76
- except SecurityValidationError as e:
77
- # Handle security validation errors
78
- return await self.handle_error(request, e)
79
- except Exception as e:
80
- # Handle other errors
81
- logger.error(f"Unexpected error in security middleware: {e}")
82
- return await self.handle_error(request, e)
83
-
84
- async def before_request(self, request: Request) -> None:
85
- """
86
- Process request before calling the main handler.
87
-
88
- Args:
89
- request: FastAPI request object
90
- """
91
- # Check if security is enabled
92
- if not self.security_config.get("enabled", True):
93
- logger.debug("Security middleware disabled, skipping validation")
94
- return
95
-
96
- # Check if path is public
97
- path = request.url.path
98
- if self._is_public_path(path):
99
- logger.debug(f"Public path accessed: {path}")
100
- return
101
-
102
- try:
103
- # Prepare request data for validation
104
- request_data = await self._prepare_request_data_async(request)
105
-
106
- # Validate request through security framework
107
- logger.debug(f"Validating request data: {request_data}")
108
- validation_result = self.security_adapter.validate_request(request_data)
109
- logger.debug(f"Validation result: {validation_result}")
110
-
111
- if not validation_result.get("is_valid", False):
112
- error_message = validation_result.get("error_message", "Security validation failed")
113
- error_code = validation_result.get("error_code", -32000)
114
- raise SecurityValidationError(error_message, error_code)
115
-
116
- # Store validation results in request state
117
- request.state.security_result = validation_result
118
- request.state.user_roles = validation_result.get("roles", [])
119
- request.state.user_id = validation_result.get("user_id")
120
- request.state.security_validated = True
121
-
122
- logger.debug(f"Security validation successful for {request.state.user_id} "
123
- f"with roles: {request.state.user_roles}")
124
-
125
- except SecurityValidationError as e:
126
- # Re-raise security validation errors
127
- raise
128
- except Exception as e:
129
- logger.error(f"Unexpected error in security validation: {e}")
130
- raise SecurityValidationError(f"Internal security error: {str(e)}", -32603)
131
-
132
- async def _prepare_request_data_async(self, request: Request) -> Dict[str, Any]:
133
- """
134
- Prepare request data for security validation (async version).
135
-
136
- Args:
137
- request: FastAPI request object
138
-
139
- Returns:
140
- Dictionary with request data for validation
141
- """
142
- # Extract basic request information
143
- request_data = {
144
- "method": request.method,
145
- "path": request.url.path,
146
- "headers": dict(request.headers),
147
- "query_params": dict(request.query_params),
148
- "client_ip": self._get_client_ip(request),
149
- "body": {}
150
- }
151
-
152
- # Extract request body for POST/PUT/PATCH requests
153
- if request.method in ["POST", "PUT", "PATCH"]:
154
- try:
155
- body = await request.body()
156
- if body:
157
- try:
158
- request_data["body"] = json.loads(body.decode("utf-8"))
159
- except json.JSONDecodeError:
160
- # If not JSON, store as string
161
- request_data["body"] = body.decode("utf-8", errors="ignore")
162
- except Exception as e:
163
- logger.warning(f"Failed to extract request body: {e}")
164
-
165
- return request_data
166
-
167
- def _prepare_request_data(self, request: Request) -> Dict[str, Any]:
168
- """
169
- Prepare request data for security validation (sync version).
170
-
171
- Args:
172
- request: FastAPI request object
173
-
174
- Returns:
175
- Dictionary with request data for validation
176
- """
177
- # Extract basic request information
178
- request_data = {
179
- "method": request.method,
180
- "path": request.url.path,
181
- "headers": dict(request.headers),
182
- "query_params": dict(request.query_params),
183
- "client_ip": self._get_client_ip(request),
184
- "body": {}
185
- }
186
-
187
- return request_data
188
-
189
- def _get_client_ip(self, request: Request) -> str:
190
- """
191
- Get client IP address from request.
192
-
193
- Args:
194
- request: FastAPI request object
195
-
196
- Returns:
197
- Client IP address string
198
- """
199
- # Check for forwarded headers first
200
- forwarded_for = request.headers.get("X-Forwarded-For")
201
- if forwarded_for:
202
- return forwarded_for.split(",")[0].strip()
203
-
204
- # Check for real IP header
205
- real_ip = request.headers.get("X-Real-IP")
206
- if real_ip:
207
- return real_ip
208
-
209
- # Fallback to client host
210
- if request.client:
211
- return request.client.host
212
-
213
- return "unknown"
214
-
215
- def _is_public_path(self, path: str) -> bool:
216
- """
217
- Check if the path is public (doesn't require security validation).
218
-
219
- Args:
220
- path: Request path
221
-
222
- Returns:
223
- True if path is public, False otherwise
224
- """
225
- return any(path.startswith(public_path) for public_path in self.public_paths)
226
-
227
- async def handle_error(self, request: Request, exception: Exception) -> Response:
228
- """
229
- Handle security validation errors.
230
-
231
- Args:
232
- request: FastAPI request object
233
- exception: Exception that occurred
234
-
235
- Returns:
236
- Error response
237
- """
238
- if isinstance(exception, SecurityValidationError):
239
- status_code = self._get_status_code_for_error(exception.error_code)
240
- error_code = exception.error_code
241
- error_message = exception.message
242
- else:
243
- status_code = 500
244
- error_code = -32603
245
- error_message = "Internal server error"
246
-
247
- logger.warning(f"Security validation failed: {error_message} | "
248
- f"Path: {request.url.path} | Code: {error_code}")
249
-
250
- return JSONResponse(
251
- status_code=status_code,
252
- content={
253
- "jsonrpc": "2.0",
254
- "error": {
255
- "code": error_code,
256
- "message": error_message,
257
- "data": {
258
- "validation_type": "security",
259
- "path": request.url.path,
260
- "method": request.method,
261
- "client_ip": self._get_client_ip(request)
262
- }
263
- },
264
- "id": None
265
- }
266
- )
267
-
268
- def _get_status_code_for_error(self, error_code: int) -> int:
269
- """
270
- Map security error codes to HTTP status codes.
271
-
272
- Args:
273
- error_code: Security error code
274
-
275
- Returns:
276
- HTTP status code
277
- """
278
- error_code_mapping = {
279
- -32000: 401, # Authentication failed
280
- -32001: 401, # Authentication disabled
281
- -32002: 500, # Invalid configuration
282
- -32003: 401, # Certificate validation failed
283
- -32004: 401, # Token validation failed
284
- -32005: 401, # MTLS validation failed
285
- -32006: 401, # SSL validation failed
286
- -32007: 403, # Role validation failed
287
- -32008: 401, # Certificate expired
288
- -32009: 401, # Certificate not found
289
- -32010: 401, # Token expired
290
- -32011: 401, # Token not found
291
- -32603: 500, # Internal error
292
- }
293
-
294
- return error_code_mapping.get(error_code, 500)
295
-
296
- def get_user_roles(self, request: Request) -> list:
297
- """
298
- Get user roles from request state.
299
-
300
- Args:
301
- request: FastAPI request object
302
-
303
- Returns:
304
- List of user roles
305
- """
306
- return getattr(request.state, 'user_roles', [])
307
-
308
- def get_user_id(self, request: Request) -> Optional[str]:
309
- """
310
- Get user ID from request state.
311
-
312
- Args:
313
- request: FastAPI request object
314
-
315
- Returns:
316
- User ID or None
317
- """
318
- return getattr(request.state, 'user_id', None)
319
-
320
- def is_security_validated(self, request: Request) -> bool:
321
- """
322
- Check if security validation passed for the request.
323
-
324
- Args:
325
- request: FastAPI request object
326
-
327
- Returns:
328
- True if security validation passed
329
- """
330
- return getattr(request.state, 'security_validated', False)
331
-
332
- def has_role(self, request: Request, required_role: str) -> bool:
333
- """
334
- Check if user has required role.
335
-
336
- Args:
337
- request: FastAPI request object
338
- required_role: Required role to check
339
-
340
- Returns:
341
- True if user has required role
342
- """
343
- user_roles = self.get_user_roles(request)
344
- return required_role in user_roles or "*" in user_roles
345
-
346
- def has_any_role(self, request: Request, required_roles: list) -> bool:
347
- """
348
- Check if user has any of the required roles.
349
-
350
- Args:
351
- request: FastAPI request object
352
- required_roles: List of required roles
353
-
354
- Returns:
355
- True if user has any of the required roles
356
- """
357
- user_roles = self.get_user_roles(request)
358
- return any(role in user_roles for role in required_roles) or "*" in user_roles
359
-
360
-
361
- class SecurityValidationError(Exception):
362
- """
363
- Exception raised when security validation fails.
364
- """
365
-
366
- def __init__(self, message: str, error_code: int):
367
- """
368
- Initialize security validation error.
369
-
370
- Args:
371
- message: Error message
372
- error_code: JSON-RPC error code
373
- """
374
- super().__init__(message)
375
- self.message = message
376
- self.error_code = error_code
@@ -1,261 +0,0 @@
1
- """
2
- Token Authentication Middleware
3
-
4
- This module provides middleware for token-based authentication using JWT and API tokens.
5
- Supports extraction of tokens from headers and validation using AuthValidator.
6
-
7
- Author: MCP Proxy Adapter Team
8
- Version: 1.0.0
9
- """
10
-
11
- import json
12
- import logging
13
- from typing import Dict, Any, Optional, List
14
- from pathlib import Path
15
-
16
- from fastapi import Request, HTTPException
17
- from starlette.middleware.base import BaseHTTPMiddleware
18
- from starlette.responses import JSONResponse
19
-
20
- from ...core.auth_validator import AuthValidator, AuthValidationResult
21
- from ...core.logging import logger
22
-
23
-
24
- class TokenAuthMiddleware(BaseHTTPMiddleware):
25
- """
26
- Token authentication middleware.
27
-
28
- Validates JWT and API tokens from request headers.
29
- Integrates with AuthValidator for token validation.
30
- """
31
-
32
- def __init__(self, app, token_config: Dict[str, Any]):
33
- """
34
- Initialize token authentication middleware.
35
-
36
- Args:
37
- app: FastAPI application
38
- token_config: Token configuration dictionary
39
- """
40
- super().__init__(app)
41
- self.token_config = token_config
42
- self.auth_validator = AuthValidator()
43
- self.logger = logging.getLogger(__name__)
44
-
45
- # Load configuration
46
- self.enabled = token_config.get("enabled", False)
47
- self.header_name = token_config.get("header_name", "Authorization")
48
- self.token_prefix = token_config.get("token_prefix", "Bearer")
49
- self.tokens_file = token_config.get("tokens_file", "tokens.json")
50
- self.token_expiry = token_config.get("token_expiry", 3600)
51
- self.jwt_secret = token_config.get("jwt_secret", "")
52
- self.jwt_algorithm = token_config.get("jwt_algorithm", "HS256")
53
-
54
- # Load tokens if file exists
55
- self.tokens = self._load_tokens()
56
-
57
- self.logger.info(f"TokenAuthMiddleware initialized. Enabled: {self.enabled}")
58
-
59
- async def dispatch(self, request: Request, call_next):
60
- """
61
- Process request through token authentication middleware.
62
-
63
- Args:
64
- request: FastAPI request object
65
- call_next: Next middleware or endpoint
66
-
67
- Returns:
68
- Response from next middleware or endpoint
69
- """
70
- if not self.enabled:
71
- return await call_next(request)
72
-
73
- try:
74
- # Extract token from header
75
- auth_header = request.headers.get(self.header_name)
76
- if not auth_header:
77
- return self._create_auth_error("Authorization header required", 401)
78
-
79
- # Validate token
80
- is_valid = self._validate_token(auth_header)
81
- if not is_valid:
82
- return self._create_auth_error("Invalid or expired token", 401)
83
-
84
- # Continue to next middleware/endpoint
85
- return await call_next(request)
86
-
87
- except Exception as e:
88
- self.logger.error(f"Token authentication error: {e}")
89
- return self._create_auth_error("Token authentication failed", 500)
90
-
91
- def _validate_token(self, auth_header: str) -> bool:
92
- """
93
- Validate token from authorization header.
94
-
95
- Args:
96
- auth_header: Authorization header value
97
-
98
- Returns:
99
- True if token is valid, False otherwise
100
- """
101
- try:
102
- # Extract token from header
103
- if not auth_header.startswith(f"{self.token_prefix} "):
104
- return False
105
-
106
- token = auth_header[len(f"{self.token_prefix} "):].strip()
107
- if not token:
108
- return False
109
-
110
- # Determine token type and validate
111
- if self._is_jwt_token(token):
112
- return self._validate_jwt_token(token)
113
- else:
114
- return self._validate_api_token(token)
115
-
116
- except Exception as e:
117
- self.logger.error(f"Token validation error: {e}")
118
- return False
119
-
120
- def _is_jwt_token(self, token: str) -> bool:
121
- """
122
- Check if token is JWT format.
123
-
124
- Args:
125
- token: Token string
126
-
127
- Returns:
128
- True if token appears to be JWT, False otherwise
129
- """
130
- # Basic JWT format check (header.payload.signature)
131
- parts = token.split('.')
132
- return len(parts) == 3
133
-
134
- def _validate_jwt_token(self, token: str) -> bool:
135
- """
136
- Validate JWT token.
137
-
138
- Args:
139
- token: JWT token string
140
-
141
- Returns:
142
- True if JWT token is valid, False otherwise
143
- """
144
- try:
145
- # Use AuthValidator for JWT validation
146
- result = self.auth_validator.validate_token(token, "jwt")
147
- return result.is_valid
148
-
149
- except Exception as e:
150
- self.logger.error(f"JWT validation error: {e}")
151
- return False
152
-
153
- def _validate_api_token(self, token: str) -> bool:
154
- """
155
- Validate API token.
156
-
157
- Args:
158
- token: API token string
159
-
160
- Returns:
161
- True if API token is valid, False otherwise
162
- """
163
- try:
164
- # Check if token exists in loaded tokens
165
- if token in self.tokens:
166
- token_data = self.tokens[token]
167
-
168
- # Check if token is active
169
- if not token_data.get("active", True):
170
- return False
171
-
172
- # Check if token has expired
173
- if "expires_at" in token_data:
174
- import time
175
- if time.time() > token_data["expires_at"]:
176
- return False
177
-
178
- return True
179
-
180
- # Use AuthValidator for API token validation
181
- result = self.auth_validator.validate_token(token, "api")
182
- return result.is_valid
183
-
184
- except Exception as e:
185
- self.logger.error(f"API token validation error: {e}")
186
- return False
187
-
188
- def _load_tokens(self) -> Dict[str, Any]:
189
- """
190
- Load tokens from configuration file.
191
-
192
- Returns:
193
- Dictionary of tokens and their metadata
194
- """
195
- try:
196
- if not self.tokens_file or not Path(self.tokens_file).exists():
197
- return {}
198
-
199
- with open(self.tokens_file, 'r', encoding='utf-8') as f:
200
- tokens_data = json.load(f)
201
-
202
- self.logger.info(f"Loaded {len(tokens_data)} tokens from {self.tokens_file}")
203
- return tokens_data
204
-
205
- except Exception as e:
206
- self.logger.error(f"Failed to load tokens from {self.tokens_file}: {e}")
207
- return {}
208
-
209
- def _create_auth_error(self, message: str, status_code: int) -> JSONResponse:
210
- """
211
- Create authentication error response.
212
-
213
- Args:
214
- message: Error message
215
- status_code: HTTP status code
216
-
217
- Returns:
218
- JSONResponse with error details
219
- """
220
- error_data = {
221
- "error": {
222
- "code": -32004, # Token validation failed
223
- "message": message,
224
- "type": "token_authentication_error"
225
- }
226
- }
227
-
228
- return JSONResponse(
229
- status_code=status_code,
230
- content=error_data
231
- )
232
-
233
- def get_roles_from_token(self, auth_header: str) -> List[str]:
234
- """
235
- Extract roles from token.
236
-
237
- Args:
238
- auth_header: Authorization header value
239
-
240
- Returns:
241
- List of roles extracted from token
242
- """
243
- try:
244
- if not auth_header.startswith(f"{self.token_prefix} "):
245
- return []
246
-
247
- token = auth_header[len(f"{self.token_prefix} "):].strip()
248
- if not token:
249
- return []
250
-
251
- # Use AuthValidator to extract roles
252
- if self._is_jwt_token(token):
253
- result = self.auth_validator.validate_token(token, "jwt")
254
- else:
255
- result = self.auth_validator.validate_token(token, "api")
256
-
257
- return result.roles if result.is_valid else []
258
-
259
- except Exception as e:
260
- self.logger.error(f"Failed to extract roles from token: {e}")
261
- return []
@@ -1,7 +0,0 @@
1
- """
2
- MCP Proxy Adapter Examples
3
-
4
- This package contains examples and templates for using the MCP Proxy Adapter framework.
5
- """
6
-
7
- __version__ = "1.0.0"
@@ -1,60 +0,0 @@
1
- # Basic Server Example
2
-
3
- A minimal example of MCP Proxy Adapter server without additional commands.
4
-
5
- ## Features
6
-
7
- This example demonstrates:
8
- - Basic server setup
9
- - Built-in commands only (help, health)
10
- - Default configuration
11
- - No custom commands
12
-
13
- ## Available Commands
14
-
15
- - `help` - Get information about available commands
16
- - `health` - Get server health status
17
-
18
- ## Usage
19
-
20
- ### Run the server
21
-
22
- ```bash
23
- python server.py
24
- ```
25
-
26
- ### Test the server
27
-
28
- ```bash
29
- # Get help
30
- curl -X POST http://localhost:8000/cmd \
31
- -H "Content-Type: application/json" \
32
- -d '{"command": "help"}'
33
-
34
- # Get health status
35
- curl -X POST http://localhost:8000/cmd \
36
- -H "Content-Type: application/json" \
37
- -d '{"command": "health"}'
38
- ```
39
-
40
- ## API Endpoints
41
-
42
- - `POST /cmd` - Execute commands
43
- - `GET /health` - Health check
44
- - `GET /commands` - List available commands
45
- - `GET /docs` - API documentation
46
-
47
- ## Configuration
48
-
49
- The server uses default configuration. You can customize it by:
50
-
51
- 1. Creating a `config.json` file
52
- 2. Setting environment variables
53
- 3. Passing configuration to `create_app()`
54
-
55
- ## Notes
56
-
57
- - This is the simplest possible setup
58
- - No custom commands are registered
59
- - Uses framework defaults
60
- - Good starting point for understanding the framework