mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__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 (253) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +254 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +36 -30
  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 +135 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -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 +7 -0
  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 +483 -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 +159 -2
  41. mcp_proxy_adapter/core/app_factory.py +326 -0
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/client_security.py +384 -0
  45. mcp_proxy_adapter/core/config_converter.py +405 -0
  46. mcp_proxy_adapter/core/config_validator.py +218 -0
  47. mcp_proxy_adapter/core/logging.py +19 -3
  48. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  49. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  50. mcp_proxy_adapter/core/protocol_manager.py +235 -0
  51. mcp_proxy_adapter/core/proxy_client.py +602 -0
  52. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  53. mcp_proxy_adapter/core/role_utils.py +426 -0
  54. mcp_proxy_adapter/core/security_adapter.py +370 -0
  55. mcp_proxy_adapter/core/security_factory.py +239 -0
  56. mcp_proxy_adapter/core/security_integration.py +277 -0
  57. mcp_proxy_adapter/core/server_adapter.py +345 -0
  58. mcp_proxy_adapter/core/server_engine.py +364 -0
  59. mcp_proxy_adapter/core/settings.py +1 -0
  60. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  61. mcp_proxy_adapter/core/transport_manager.py +292 -0
  62. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  63. mcp_proxy_adapter/custom_openapi.py +22 -11
  64. mcp_proxy_adapter/examples/README.md +230 -97
  65. mcp_proxy_adapter/examples/README_EN.md +258 -0
  66. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  67. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  68. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  69. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  70. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  71. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  72. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  73. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  74. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  75. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  76. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  77. mcp_proxy_adapter/examples/cert_config.json +9 -0
  78. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  79. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  80. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  81. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  82. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  83. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  84. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  85. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  86. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  87. mcp_proxy_adapter/examples/certs/client.key +52 -0
  88. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  89. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  90. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  91. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  92. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  93. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  94. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  95. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  96. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  97. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  98. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  99. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  100. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  101. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  102. mcp_proxy_adapter/examples/certs/server.key +52 -0
  103. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  104. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  105. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  106. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  107. mcp_proxy_adapter/examples/certs/user.key +52 -0
  108. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  109. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  110. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  111. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  112. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  113. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  114. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  115. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  116. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  117. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  118. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  119. mcp_proxy_adapter/examples/demo_client.py +341 -0
  120. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  121. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  122. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  123. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  124. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  125. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  126. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  127. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  128. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  129. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  130. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  131. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  132. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  133. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  134. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  135. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  136. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  137. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  138. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  139. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  140. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  141. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  142. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  143. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  144. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  145. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  146. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  147. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  148. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  149. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  150. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  151. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  152. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  153. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  154. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  155. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  156. mcp_proxy_adapter/examples/roles.json +38 -0
  157. mcp_proxy_adapter/examples/run_example.py +81 -0
  158. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  159. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  160. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  161. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  162. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  163. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  164. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  165. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  166. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  167. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  168. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  169. mcp_proxy_adapter/examples/test_examples.py +344 -0
  170. mcp_proxy_adapter/examples/universal_client.py +628 -0
  171. mcp_proxy_adapter/main.py +186 -0
  172. mcp_proxy_adapter/utils/config_generator.py +639 -0
  173. mcp_proxy_adapter/version.py +2 -1
  174. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  175. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  176. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  177. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  178. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  179. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  180. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  181. mcp_proxy_adapter/examples/__init__.py +0 -7
  182. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  183. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  184. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  185. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  186. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  187. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  188. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  189. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  190. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  191. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  192. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  193. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  194. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  195. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  196. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  197. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  198. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  199. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  200. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  201. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  202. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  203. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  204. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  206. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  207. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  208. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  209. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  210. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  211. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  212. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  213. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  214. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  215. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  216. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  217. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  218. mcp_proxy_adapter/tests/__init__.py +0 -0
  219. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  220. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  221. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  222. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  223. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  224. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  225. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  226. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  227. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  228. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  229. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  230. mcp_proxy_adapter/tests/conftest.py +0 -131
  231. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  232. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  233. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  234. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  235. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  236. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  237. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  238. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  239. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  240. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  241. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  242. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  243. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  244. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  245. mcp_proxy_adapter/tests/test_config.py +0 -127
  246. mcp_proxy_adapter/tests/test_utils.py +0 -65
  247. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  249. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  250. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  251. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  252. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  253. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,370 @@
1
+ """
2
+ Security Adapter for mcp_security_framework integration.
3
+
4
+ This module provides an adapter layer between mcp_proxy_adapter and mcp_security_framework,
5
+ handling configuration conversion and request validation.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from typing import Dict, Any, List, Optional
11
+ from pathlib import Path
12
+
13
+ # Import mcp_security_framework components
14
+ try:
15
+ from mcp_security_framework import SecurityManager, SecurityConfig
16
+ from mcp_security_framework.schemas.config import (
17
+ AuthConfig, SSLConfig, PermissionConfig, RateLimitConfig
18
+ )
19
+ # Note: SecurityRequest and SecurityResult are not available in current version
20
+ SECURITY_FRAMEWORK_AVAILABLE = True
21
+ except ImportError:
22
+ # Fallback for when mcp_security_framework is not available
23
+ SECURITY_FRAMEWORK_AVAILABLE = False
24
+ SecurityManager = None
25
+ SecurityConfig = None
26
+ AuthConfig = None
27
+ SSLConfig = None
28
+ PermissionConfig = None
29
+ RateLimitConfig = None
30
+
31
+ from mcp_proxy_adapter.core.logging import logger
32
+
33
+
34
+ class SecurityAdapter:
35
+ """
36
+ Adapter for integrating with mcp_security_framework.
37
+
38
+ Provides methods to convert mcp_proxy_adapter configuration to SecurityConfig
39
+ and handle request validation through the security framework.
40
+ """
41
+
42
+ def __init__(self, config: Dict[str, Any]):
43
+ """
44
+ Initialize security adapter.
45
+
46
+ Args:
47
+ config: mcp_proxy_adapter configuration dictionary
48
+ """
49
+ self.config = config
50
+ self.security_manager = None
51
+
52
+ if SECURITY_FRAMEWORK_AVAILABLE:
53
+ self.security_manager = self._create_security_manager()
54
+ logger.info("Security adapter initialized with mcp_security_framework")
55
+ else:
56
+ logger.warning("mcp_security_framework not available, using fallback mode")
57
+
58
+ def _create_security_manager(self) -> Optional[SecurityManager]:
59
+ """
60
+ Create SecurityManager from mcp_proxy_adapter configuration.
61
+
62
+ Returns:
63
+ SecurityManager instance or None if framework not available
64
+ """
65
+ if not SECURITY_FRAMEWORK_AVAILABLE:
66
+ return None
67
+
68
+ try:
69
+ security_config = self._convert_config()
70
+ return SecurityManager(security_config)
71
+ except Exception as e:
72
+ logger.error(f"Failed to create SecurityManager: {e}")
73
+ return None
74
+
75
+ def _convert_config(self) -> SecurityConfig:
76
+ """
77
+ Convert mcp_proxy_adapter configuration to SecurityConfig.
78
+
79
+ Returns:
80
+ SecurityConfig instance
81
+ """
82
+ # Get security configuration section
83
+ security_config = self.config.get("security", {})
84
+
85
+ # Convert auth configuration
86
+ auth_config = self._convert_auth_config(security_config)
87
+
88
+ # Convert SSL configuration
89
+ ssl_config = self._convert_ssl_config(security_config)
90
+
91
+ # Convert permissions configuration
92
+ permission_config = self._convert_permission_config(security_config)
93
+
94
+ # Convert rate limit configuration
95
+ rate_limit_config = self._convert_rate_limit_config(security_config)
96
+
97
+ return SecurityConfig(
98
+ auth=auth_config,
99
+ ssl=ssl_config,
100
+ permissions=permission_config,
101
+ rate_limit=rate_limit_config
102
+ )
103
+
104
+ def _convert_auth_config(self, security_config: Dict[str, Any]) -> AuthConfig:
105
+ """
106
+ Convert authentication configuration.
107
+
108
+ Args:
109
+ security_config: Security configuration section
110
+
111
+ Returns:
112
+ AuthConfig instance
113
+ """
114
+ auth_config = security_config.get("auth", {})
115
+
116
+ # Get authentication methods
117
+ methods = auth_config.get("methods", ["api_key"])
118
+
119
+ # Get API keys from legacy config if not in security section
120
+ api_keys = auth_config.get("api_keys", {})
121
+ if not api_keys:
122
+ # Try to get from legacy SSL config
123
+ legacy_ssl = self.config.get("ssl", {})
124
+ if "api_keys" in legacy_ssl:
125
+ api_keys = legacy_ssl["api_keys"]
126
+
127
+ return AuthConfig(
128
+ enabled=auth_config.get("enabled", True),
129
+ methods=methods,
130
+ api_keys=api_keys,
131
+ jwt_secret=auth_config.get("jwt_secret", ""),
132
+ jwt_algorithm=auth_config.get("jwt_algorithm", "HS256")
133
+ )
134
+
135
+ def _convert_ssl_config(self, security_config: Dict[str, Any]) -> SSLConfig:
136
+ """
137
+ Convert SSL configuration.
138
+
139
+ Args:
140
+ security_config: Security configuration section
141
+
142
+ Returns:
143
+ SSLConfig instance
144
+ """
145
+ ssl_config = security_config.get("ssl", {})
146
+
147
+ # Fallback to legacy SSL config if not in security section
148
+ if not ssl_config:
149
+ ssl_config = self.config.get("ssl", {})
150
+
151
+ return SSLConfig(
152
+ enabled=ssl_config.get("enabled", False),
153
+ cert_file=ssl_config.get("cert_file"),
154
+ key_file=ssl_config.get("key_file"),
155
+ ca_cert=ssl_config.get("ca_cert"),
156
+ min_tls_version=ssl_config.get("min_tls_version", "TLSv1.2"),
157
+ verify_client=ssl_config.get("verify_client", False),
158
+ client_cert_required=ssl_config.get("client_cert_required", False)
159
+ )
160
+
161
+ def _convert_permission_config(self, security_config: Dict[str, Any]) -> PermissionConfig:
162
+ """
163
+ Convert permissions configuration.
164
+
165
+ Args:
166
+ security_config: Security configuration section
167
+
168
+ Returns:
169
+ PermissionConfig instance
170
+ """
171
+ permission_config = security_config.get("permissions", {})
172
+
173
+ # Fallback to legacy roles config if not in security section
174
+ if not permission_config:
175
+ roles_config = self.config.get("roles", {})
176
+ permission_config = {
177
+ "enabled": roles_config.get("enabled", True),
178
+ "roles_file": roles_config.get("config_file", "roles.json"),
179
+ "default_role": "user"
180
+ }
181
+
182
+ return PermissionConfig(
183
+ enabled=permission_config.get("enabled", True),
184
+ roles_file=permission_config.get("roles_file", "roles.json"),
185
+ default_role=permission_config.get("default_role", "user"),
186
+ deny_by_default=permission_config.get("deny_by_default", True)
187
+ )
188
+
189
+ def _convert_rate_limit_config(self, security_config: Dict[str, Any]) -> RateLimitConfig:
190
+ """
191
+ Convert rate limit configuration.
192
+
193
+ Args:
194
+ security_config: Security configuration section
195
+
196
+ Returns:
197
+ RateLimitConfig instance
198
+ """
199
+ rate_limit_config = security_config.get("rate_limit", {})
200
+
201
+ return RateLimitConfig(
202
+ enabled=rate_limit_config.get("enabled", True),
203
+ requests_per_minute=rate_limit_config.get("requests_per_minute", 60),
204
+ requests_per_hour=rate_limit_config.get("requests_per_hour", 1000),
205
+ burst_limit=rate_limit_config.get("burst_limit", 10),
206
+ by_ip=rate_limit_config.get("by_ip", True),
207
+ by_user=rate_limit_config.get("by_user", True)
208
+ )
209
+
210
+ def validate_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
211
+ """
212
+ Validate request through mcp_security_framework.
213
+
214
+ Args:
215
+ request_data: Request data dictionary
216
+
217
+ Returns:
218
+ Validation result dictionary
219
+ """
220
+ logger.debug(f"Security manager available: {self.security_manager is not None}")
221
+ if not self.security_manager:
222
+ # Fallback validation when framework is not available
223
+ logger.debug("Using fallback validation")
224
+ return self._fallback_validation(request_data)
225
+
226
+ try:
227
+ # Convert request data to SecurityRequest
228
+ security_request = self._create_security_request(request_data)
229
+
230
+ # Validate through security framework
231
+ result = self.security_manager.validate_request(security_request)
232
+
233
+ return result.to_dict()
234
+
235
+ except Exception as e:
236
+ logger.error(f"Security validation failed: {e}")
237
+ return {
238
+ "is_valid": False,
239
+ "error_code": -32603,
240
+ "error_message": f"Security validation error: {str(e)}",
241
+ "roles": [],
242
+ "user_id": None
243
+ }
244
+
245
+ def _create_security_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
246
+ """
247
+ Create request data for security validation.
248
+
249
+ Args:
250
+ request_data: Request data dictionary
251
+
252
+ Returns:
253
+ Request data dictionary for security validation
254
+ """
255
+ return {
256
+ "method": request_data.get("method", "GET"),
257
+ "path": request_data.get("path", "/"),
258
+ "headers": request_data.get("headers", {}),
259
+ "query_params": request_data.get("query_params", {}),
260
+ "client_ip": request_data.get("client_ip", "unknown"),
261
+ "body": request_data.get("body", {})
262
+ }
263
+
264
+ def _fallback_validation(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
265
+ """
266
+ Fallback validation when mcp_security_framework is not available.
267
+
268
+ Args:
269
+ request_data: Request data dictionary
270
+
271
+ Returns:
272
+ Validation result dictionary
273
+ """
274
+ # Simple API key validation as fallback
275
+ headers = request_data.get("headers", {})
276
+ query_params = request_data.get("query_params", {})
277
+ body = request_data.get("body", {})
278
+
279
+ # Check for API key in headers (FastAPI converts headers to lowercase)
280
+ api_key = headers.get("x-api-key") or headers.get("X-API-Key")
281
+ logger.debug(f"API key from headers: {api_key}")
282
+
283
+ # Check for API key in query parameters
284
+ if not api_key:
285
+ api_key = query_params.get("api_key")
286
+ logger.debug(f"API key from query params: {api_key}")
287
+
288
+ # Check for API key in JSON-RPC body
289
+ if not api_key and isinstance(body, dict):
290
+ api_key = body.get("params", {}).get("api_key")
291
+ logger.debug(f"API key from body: {api_key}")
292
+
293
+ # Get API keys from config
294
+ api_keys = self._get_api_keys()
295
+ logger.debug(f"Available API keys: {list(api_keys.keys())}")
296
+
297
+ if api_key and api_key in api_keys:
298
+ return {
299
+ "is_valid": True,
300
+ "error_code": None,
301
+ "error_message": None,
302
+ "roles": ["user"],
303
+ "user_id": api_keys[api_key]
304
+ }
305
+ else:
306
+ return {
307
+ "is_valid": False,
308
+ "error_code": -32000,
309
+ "error_message": "API key not provided or invalid",
310
+ "roles": [],
311
+ "user_id": None
312
+ }
313
+
314
+ def _get_api_keys(self) -> Dict[str, str]:
315
+ """
316
+ Get API keys from configuration.
317
+
318
+ Returns:
319
+ Dictionary mapping API keys to usernames
320
+ """
321
+ # Try security config first
322
+ security_config = self.config.get("security", {})
323
+ auth_config = security_config.get("auth", {})
324
+ api_keys = auth_config.get("api_keys", {})
325
+
326
+ logger.debug(f"Security config: {security_config}")
327
+ logger.debug(f"Auth config: {auth_config}")
328
+ logger.debug(f"API keys from security config: {api_keys}")
329
+
330
+ # Fallback to legacy SSL config
331
+ if not api_keys:
332
+ ssl_config = self.config.get("ssl", {})
333
+ api_keys = ssl_config.get("api_keys", {})
334
+ logger.debug(f"API keys from SSL config: {api_keys}")
335
+
336
+ logger.info(f"Total API keys loaded: {len(api_keys)}")
337
+ return api_keys
338
+
339
+ def create_middleware(self, framework: str = "fastapi"):
340
+ """
341
+ Create framework-specific middleware.
342
+
343
+ Args:
344
+ framework: Framework type (fastapi, flask, etc.)
345
+
346
+ Returns:
347
+ Middleware instance
348
+ """
349
+ if not self.security_manager:
350
+ logger.warning("Cannot create middleware: security framework not available")
351
+ return None
352
+
353
+ try:
354
+ if framework == "fastapi":
355
+ from mcp_security_framework.middleware import create_fastapi_security_middleware
356
+ return create_fastapi_security_middleware(self.security_manager)
357
+ else:
358
+ raise ValueError(f"Unsupported framework: {framework}")
359
+ except Exception as e:
360
+ logger.error(f"Failed to create middleware: {e}")
361
+ return None
362
+
363
+ def is_available(self) -> bool:
364
+ """
365
+ Check if security framework is available.
366
+
367
+ Returns:
368
+ True if framework is available, False otherwise
369
+ """
370
+ return SECURITY_FRAMEWORK_AVAILABLE and self.security_manager is not None
@@ -0,0 +1,239 @@
1
+ """
2
+ Security Factory for creating security components.
3
+
4
+ This module provides factory methods for creating security adapters, managers,
5
+ and middleware components with proper configuration and error handling.
6
+ """
7
+
8
+ import logging
9
+ from typing import Dict, Any, Optional
10
+
11
+ from mcp_proxy_adapter.core.logging import logger
12
+ from .security_adapter import SecurityAdapter
13
+
14
+
15
+ class SecurityFactory:
16
+ """
17
+ Factory for creating security components.
18
+
19
+ Provides static methods to create security adapters, managers,
20
+ and middleware components with proper configuration handling.
21
+ """
22
+
23
+ @staticmethod
24
+ def create_security_adapter(config: Dict[str, Any]) -> SecurityAdapter:
25
+ """
26
+ Create SecurityAdapter from configuration.
27
+
28
+ Args:
29
+ config: mcp_proxy_adapter configuration dictionary
30
+
31
+ Returns:
32
+ SecurityAdapter instance
33
+ """
34
+ try:
35
+ adapter = SecurityAdapter(config)
36
+ logger.info("Security adapter created successfully")
37
+ return adapter
38
+ except Exception as e:
39
+ logger.error(f"Failed to create security adapter: {e}")
40
+ raise
41
+
42
+ @staticmethod
43
+ def create_security_manager(config: Dict[str, Any]):
44
+ """
45
+ Create SecurityManager from configuration.
46
+
47
+ Args:
48
+ config: mcp_proxy_adapter configuration dictionary
49
+
50
+ Returns:
51
+ SecurityManager instance or None if not available
52
+ """
53
+ try:
54
+ adapter = SecurityFactory.create_security_adapter(config)
55
+ return adapter.security_manager
56
+ except Exception as e:
57
+ logger.error(f"Failed to create security manager: {e}")
58
+ return None
59
+
60
+ @staticmethod
61
+ def create_middleware(config: Dict[str, Any], framework: str = "fastapi"):
62
+ """
63
+ Create framework-specific security middleware.
64
+
65
+ Args:
66
+ config: mcp_proxy_adapter configuration dictionary
67
+ framework: Framework type (fastapi, flask, etc.)
68
+
69
+ Returns:
70
+ Middleware instance or None if creation failed
71
+ """
72
+ try:
73
+ adapter = SecurityFactory.create_security_adapter(config)
74
+ middleware = adapter.create_middleware(framework)
75
+
76
+ if middleware:
77
+ logger.info(f"Security middleware created for {framework}")
78
+ else:
79
+ logger.warning(f"Failed to create security middleware for {framework}")
80
+
81
+ return middleware
82
+
83
+ except Exception as e:
84
+ logger.error(f"Failed to create security middleware: {e}")
85
+ return None
86
+
87
+ @staticmethod
88
+ def create_fastapi_middleware(config: Dict[str, Any]):
89
+ """
90
+ Create FastAPI-specific security middleware.
91
+
92
+ Args:
93
+ config: mcp_proxy_adapter configuration dictionary
94
+
95
+ Returns:
96
+ FastAPI middleware instance or None if creation failed
97
+ """
98
+ return SecurityFactory.create_middleware(config, "fastapi")
99
+
100
+ @staticmethod
101
+ def validate_config(config: Dict[str, Any]) -> bool:
102
+ """
103
+ Validate security configuration.
104
+
105
+ Args:
106
+ config: Configuration dictionary to validate
107
+
108
+ Returns:
109
+ True if configuration is valid, False otherwise
110
+ """
111
+ try:
112
+ # Check if security section exists
113
+ security_config = config.get("security", {})
114
+
115
+ # Validate required fields
116
+ if not isinstance(security_config, dict):
117
+ logger.error("Security configuration must be a dictionary")
118
+ return False
119
+
120
+ # Validate auth configuration
121
+ auth_config = security_config.get("auth", {})
122
+ if not isinstance(auth_config, dict):
123
+ logger.error("Auth configuration must be a dictionary")
124
+ return False
125
+
126
+ # Validate SSL configuration
127
+ ssl_config = security_config.get("ssl", {})
128
+ if not isinstance(ssl_config, dict):
129
+ logger.error("SSL configuration must be a dictionary")
130
+ return False
131
+
132
+ # Validate permissions configuration
133
+ permissions_config = security_config.get("permissions", {})
134
+ if not isinstance(permissions_config, dict):
135
+ logger.error("Permissions configuration must be a dictionary")
136
+ return False
137
+
138
+ # Validate rate limit configuration
139
+ rate_limit_config = security_config.get("rate_limit", {})
140
+ if not isinstance(rate_limit_config, dict):
141
+ logger.error("Rate limit configuration must be a dictionary")
142
+ return False
143
+
144
+ logger.info("Security configuration validation passed")
145
+ return True
146
+
147
+ except Exception as e:
148
+ logger.error(f"Configuration validation failed: {e}")
149
+ return False
150
+
151
+ @staticmethod
152
+ def get_default_config() -> Dict[str, Any]:
153
+ """
154
+ Get default security configuration.
155
+
156
+ Returns:
157
+ Default security configuration dictionary
158
+ """
159
+ return {
160
+ "security": {
161
+ "framework": "mcp_security_framework",
162
+ "enabled": True,
163
+ "auth": {
164
+ "enabled": True,
165
+ "methods": ["api_key"],
166
+ "api_keys": {},
167
+ "jwt_secret": "",
168
+ "jwt_algorithm": "HS256"
169
+ },
170
+ "ssl": {
171
+ "enabled": False,
172
+ "cert_file": None,
173
+ "key_file": None,
174
+ "ca_cert": None,
175
+ "min_tls_version": "TLSv1.2",
176
+ "verify_client": False,
177
+ "client_cert_required": False
178
+ },
179
+ "permissions": {
180
+ "enabled": True,
181
+ "roles_file": "roles.json",
182
+ "default_role": "user",
183
+ "deny_by_default": True
184
+ },
185
+ "rate_limit": {
186
+ "enabled": True,
187
+ "requests_per_minute": 60,
188
+ "requests_per_hour": 1000,
189
+ "burst_limit": 10,
190
+ "by_ip": True,
191
+ "by_user": True
192
+ }
193
+ }
194
+ }
195
+
196
+ @staticmethod
197
+ def merge_config(base_config: Dict[str, Any], security_config: Dict[str, Any]) -> Dict[str, Any]:
198
+ """
199
+ Merge security configuration into base configuration.
200
+
201
+ Args:
202
+ base_config: Base configuration dictionary
203
+ security_config: Security configuration to merge
204
+
205
+ Returns:
206
+ Merged configuration dictionary
207
+ """
208
+ try:
209
+ # Create a copy of base config
210
+ merged_config = base_config.copy()
211
+
212
+ # Merge security configuration
213
+ if "security" not in merged_config:
214
+ merged_config["security"] = {}
215
+
216
+ # Deep merge security configuration
217
+ SecurityFactory._deep_merge(merged_config["security"], security_config)
218
+
219
+ logger.info("Security configuration merged successfully")
220
+ return merged_config
221
+
222
+ except Exception as e:
223
+ logger.error(f"Failed to merge security configuration: {e}")
224
+ return base_config
225
+
226
+ @staticmethod
227
+ def _deep_merge(base_dict: Dict[str, Any], update_dict: Dict[str, Any]) -> None:
228
+ """
229
+ Deep merge two dictionaries.
230
+
231
+ Args:
232
+ base_dict: Base dictionary to update
233
+ update_dict: Dictionary with updates
234
+ """
235
+ for key, value in update_dict.items():
236
+ if key in base_dict and isinstance(base_dict[key], dict) and isinstance(value, dict):
237
+ SecurityFactory._deep_merge(base_dict[key], value)
238
+ else:
239
+ base_dict[key] = value