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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +209 -79
  3. mcp_proxy_adapter/api/handlers.py +16 -5
  4. mcp_proxy_adapter/api/middleware/__init__.py +14 -9
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  7. mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
  8. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  9. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  10. mcp_proxy_adapter/commands/__init__.py +7 -1
  11. mcp_proxy_adapter/commands/base.py +7 -4
  12. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  13. mcp_proxy_adapter/commands/command_registry.py +8 -0
  14. mcp_proxy_adapter/commands/echo_command.py +81 -0
  15. mcp_proxy_adapter/commands/health_command.py +1 -1
  16. mcp_proxy_adapter/commands/help_command.py +21 -14
  17. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  18. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  19. mcp_proxy_adapter/commands/security_command.py +488 -0
  20. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  21. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  22. mcp_proxy_adapter/config.py +323 -40
  23. mcp_proxy_adapter/core/app_factory.py +410 -0
  24. mcp_proxy_adapter/core/app_runner.py +272 -0
  25. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  26. mcp_proxy_adapter/core/client.py +574 -0
  27. mcp_proxy_adapter/core/client_manager.py +284 -0
  28. mcp_proxy_adapter/core/client_security.py +384 -0
  29. mcp_proxy_adapter/core/logging.py +8 -3
  30. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  31. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  32. mcp_proxy_adapter/core/protocol_manager.py +169 -10
  33. mcp_proxy_adapter/core/proxy_client.py +602 -0
  34. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  35. mcp_proxy_adapter/core/security_adapter.py +12 -15
  36. mcp_proxy_adapter/core/security_integration.py +286 -0
  37. mcp_proxy_adapter/core/server_adapter.py +282 -0
  38. mcp_proxy_adapter/core/server_engine.py +270 -0
  39. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  40. mcp_proxy_adapter/core/transport_manager.py +5 -5
  41. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  42. mcp_proxy_adapter/examples/__init__.py +13 -4
  43. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  44. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  45. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  46. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  47. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  48. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  49. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  50. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  51. mcp_proxy_adapter/examples/demo_client.py +275 -0
  52. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  53. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  54. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  55. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  56. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  57. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  58. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  59. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  60. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  61. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  62. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  63. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  64. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  65. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  66. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  67. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  68. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  69. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  70. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  71. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  72. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  73. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  74. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  75. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  76. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  77. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  78. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  79. mcp_proxy_adapter/examples/run_example.py +59 -0
  80. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  81. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  82. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  83. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  84. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  85. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  86. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  87. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  88. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  89. mcp_proxy_adapter/examples/test_config.py +148 -0
  90. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  91. mcp_proxy_adapter/examples/test_examples.py +281 -0
  92. mcp_proxy_adapter/examples/universal_client.py +620 -0
  93. mcp_proxy_adapter/main.py +66 -148
  94. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  95. mcp_proxy_adapter/version.py +5 -2
  96. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  97. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  98. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  99. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  100. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  101. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  102. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  103. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  104. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  105. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  106. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  107. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  108. mcp_proxy_adapter/api/middleware/security.py +0 -376
  109. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  110. mcp_proxy_adapter/examples/README.md +0 -124
  111. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  112. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  113. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  114. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  115. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  116. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  117. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  118. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  119. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  120. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  121. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  122. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  123. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  124. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  125. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  126. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  127. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  128. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  129. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  130. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  131. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  132. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  133. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  134. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  135. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  136. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  137. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  138. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  139. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  140. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  141. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  142. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  143. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  144. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  145. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  146. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  147. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  148. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  149. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  150. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  153. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  154. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  155. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  156. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  157. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  158. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  159. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  160. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  161. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  162. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  163. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  164. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  166. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  167. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  168. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  169. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  170. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  171. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  172. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  173. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  174. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  175. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  176. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  177. mcp_proxy_adapter/tests/__init__.py +0 -0
  178. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  180. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  181. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  182. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  183. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  184. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  185. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  186. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  187. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  188. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  189. mcp_proxy_adapter/tests/conftest.py +0 -131
  190. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  191. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  192. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  193. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  194. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  195. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  196. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  197. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  198. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  199. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  200. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  201. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  202. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  203. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  204. mcp_proxy_adapter/tests/test_config.py +0 -127
  205. mcp_proxy_adapter/tests/test_utils.py +0 -65
  206. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  207. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  208. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  209. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  210. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  211. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,286 @@
1
+ """
2
+ Direct Security Framework Integration
3
+
4
+ This module provides direct integration with mcp_security_framework,
5
+ replacing all project security methods with framework calls.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ from typing import Dict, Any, Optional, List
13
+ from pathlib import Path
14
+
15
+ # Direct imports from framework
16
+ try:
17
+ from mcp_security_framework import (
18
+ SecurityManager, AuthManager, CertificateManager,
19
+ PermissionManager, RateLimiter
20
+ )
21
+ from mcp_security_framework.schemas.config import (
22
+ SecurityConfig, AuthConfig, SSLConfig, PermissionConfig,
23
+ RateLimitConfig, CertificateConfig, LoggingConfig
24
+ )
25
+ from mcp_security_framework.schemas.models import (
26
+ AuthResult, ValidationResult, CertificateInfo, CertificatePair
27
+ )
28
+ from mcp_security_framework.middleware.fastapi_middleware import FastAPISecurityMiddleware
29
+ SECURITY_FRAMEWORK_AVAILABLE = True
30
+ except ImportError:
31
+ SECURITY_FRAMEWORK_AVAILABLE = False
32
+ SecurityManager = None
33
+ SecurityConfig = None
34
+ AuthManager = None
35
+ CertificateManager = None
36
+ PermissionManager = None
37
+ RateLimiter = None
38
+ FastAPISecurityMiddleware = None
39
+
40
+ from mcp_proxy_adapter.core.logging import logger
41
+
42
+
43
+ class SecurityIntegration:
44
+ """
45
+ Direct integration with mcp_security_framework.
46
+
47
+ This class replaces all project security methods with direct calls
48
+ to the security framework components.
49
+ """
50
+
51
+ def __init__(self, config: Dict[str, Any]):
52
+ """
53
+ Initialize security integration.
54
+
55
+ Args:
56
+ config: Configuration dictionary
57
+ """
58
+ if not SECURITY_FRAMEWORK_AVAILABLE:
59
+ raise ImportError("mcp_security_framework is not available")
60
+
61
+ self.config = config
62
+ self.security_config = self._create_security_config()
63
+
64
+ # Initialize framework components
65
+ self.security_manager = SecurityManager(self.security_config)
66
+ self.permission_manager = PermissionManager(self.security_config.permissions)
67
+ self.auth_manager = AuthManager(self.security_config.auth, self.permission_manager)
68
+ self.certificate_manager = CertificateManager(self.security_config.certificates)
69
+ self.rate_limiter = RateLimiter(self.security_config.rate_limit)
70
+
71
+ logger.info("Security integration initialized with mcp_security_framework")
72
+
73
+ def _create_security_config(self) -> SecurityConfig:
74
+ """Create SecurityConfig from project configuration."""
75
+ # self.config is already the security section passed from unified_security.py
76
+ security_section = self.config
77
+
78
+ # Create SSL config
79
+ ssl_config = SSLConfig(
80
+ enabled=security_section.get("ssl", {}).get("enabled", False),
81
+ cert_file=security_section.get("ssl", {}).get("cert_file"),
82
+ key_file=security_section.get("ssl", {}).get("key_file"),
83
+ ca_cert_file=security_section.get("ssl", {}).get("ca_cert_file"),
84
+ client_cert_file=security_section.get("ssl", {}).get("client_cert_file"),
85
+ client_key_file=security_section.get("ssl", {}).get("client_key_file"),
86
+ verify_mode=security_section.get("ssl", {}).get("verify_mode", "CERT_REQUIRED"),
87
+ min_tls_version=security_section.get("ssl", {}).get("min_tls_version", "TLSv1.2"),
88
+ check_hostname=security_section.get("ssl", {}).get("check_hostname", True),
89
+ check_expiry=security_section.get("ssl", {}).get("check_expiry", True),
90
+ expiry_warning_days=security_section.get("ssl", {}).get("expiry_warning_days", 30)
91
+ )
92
+
93
+ # Create auth config
94
+ auth_config = AuthConfig(
95
+ enabled=security_section.get("auth", {}).get("enabled", True),
96
+ methods=security_section.get("auth", {}).get("methods", ["api_key"]),
97
+ api_keys=security_section.get("auth", {}).get("api_keys", {}),
98
+ user_roles=security_section.get("auth", {}).get("user_roles", {}),
99
+ jwt_secret=security_section.get("auth", {}).get("jwt_secret"),
100
+ jwt_algorithm=security_section.get("auth", {}).get("jwt_algorithm", "HS256"),
101
+ jwt_expiry_hours=security_section.get("auth", {}).get("jwt_expiry_hours", 24),
102
+ certificate_auth=security_section.get("auth", {}).get("certificate_auth", False),
103
+ public_paths=security_section.get("auth", {}).get("public_paths", [])
104
+ )
105
+
106
+ # Create permission config - handle null values properly
107
+ permissions_section = security_section.get("permissions", {})
108
+ roles_file = permissions_section.get("roles_file")
109
+
110
+ # If roles_file is None or empty string, don't pass it to avoid framework errors
111
+ if roles_file is None or roles_file == "":
112
+ logger.warning("roles_file is None or empty, permissions will use default configuration")
113
+ roles_file = None
114
+
115
+ permission_config = PermissionConfig(
116
+ enabled=permissions_section.get("enabled", True),
117
+ roles_file=roles_file,
118
+ default_role=permissions_section.get("default_role", "guest"),
119
+ admin_role=permissions_section.get("admin_role", "admin"),
120
+ role_hierarchy=permissions_section.get("role_hierarchy", {}),
121
+ permission_cache_enabled=permissions_section.get("permission_cache_enabled", True),
122
+ permission_cache_ttl=permissions_section.get("permission_cache_ttl", 300),
123
+ wildcard_permissions=permissions_section.get("wildcard_permissions", False),
124
+ strict_mode=permissions_section.get("strict_mode", True),
125
+ roles=permissions_section.get("roles")
126
+ )
127
+
128
+ # Create rate limit config
129
+ rate_limit_config = RateLimitConfig(
130
+ enabled=security_section.get("rate_limit", {}).get("enabled", True),
131
+ default_requests_per_minute=security_section.get("rate_limit", {}).get("default_requests_per_minute", 60),
132
+ default_requests_per_hour=security_section.get("rate_limit", {}).get("default_requests_per_hour", 1000),
133
+ burst_limit=security_section.get("rate_limit", {}).get("burst_limit", 2),
134
+ window_size_seconds=security_section.get("rate_limit", {}).get("window_size_seconds", 60),
135
+ storage_backend=security_section.get("rate_limit", {}).get("storage_backend", "memory"),
136
+ exempt_paths=security_section.get("rate_limit", {}).get("exempt_paths", []),
137
+ exempt_roles=security_section.get("rate_limit", {}).get("exempt_roles", [])
138
+ )
139
+
140
+ # Create certificate config
141
+ certificate_config = CertificateConfig(
142
+ enabled=security_section.get("certificates", {}).get("enabled", False),
143
+ ca_cert_path=security_section.get("certificates", {}).get("ca_cert_path"),
144
+ ca_key_path=security_section.get("certificates", {}).get("ca_key_path"),
145
+ cert_storage_path=security_section.get("certificates", {}).get("cert_storage_path", "./certs"),
146
+ key_storage_path=security_section.get("certificates", {}).get("key_storage_path", "./keys"),
147
+ default_validity_days=security_section.get("certificates", {}).get("default_validity_days", 365),
148
+ key_size=security_section.get("certificates", {}).get("key_size", 2048),
149
+ hash_algorithm=security_section.get("certificates", {}).get("hash_algorithm", "sha256")
150
+ )
151
+
152
+ # Create logging config
153
+ logging_config = LoggingConfig(
154
+ enabled=security_section.get("logging", {}).get("enabled", True),
155
+ level=security_section.get("logging", {}).get("level", "INFO"),
156
+ format=security_section.get("logging", {}).get("format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"),
157
+ console_output=security_section.get("logging", {}).get("console_output", True),
158
+ file_path=security_section.get("logging", {}).get("file_path")
159
+ )
160
+
161
+ # Create main security config
162
+ return SecurityConfig(
163
+ ssl=ssl_config,
164
+ auth=auth_config,
165
+ permissions=permission_config,
166
+ rate_limit=rate_limit_config,
167
+ certificates=certificate_config,
168
+ logging=logging_config,
169
+ debug=security_section.get("debug", False),
170
+ environment=security_section.get("environment", "dev"),
171
+ version=security_section.get("version", "1.0.0")
172
+ )
173
+
174
+ # Authentication methods - direct calls to AuthManager
175
+ async def authenticate_api_key(self, api_key: str) -> AuthResult:
176
+ """Authenticate using API key."""
177
+ return await self.auth_manager.authenticate_api_key(api_key)
178
+
179
+ async def authenticate_jwt(self, token: str) -> AuthResult:
180
+ """Authenticate using JWT token."""
181
+ return await self.auth_manager.authenticate_jwt(token)
182
+
183
+ async def authenticate_certificate(self, cert_data: bytes) -> AuthResult:
184
+ """Authenticate using certificate."""
185
+ return await self.auth_manager.authenticate_certificate(cert_data)
186
+
187
+ async def validate_request(self, request_data: Dict[str, Any]) -> ValidationResult:
188
+ """Validate request using security manager."""
189
+ return await self.security_manager.validate_request(request_data)
190
+
191
+ # Certificate methods - direct calls to CertificateManager
192
+ async def create_ca_certificate(self, common_name: str, **kwargs) -> CertificatePair:
193
+ """Create CA certificate."""
194
+ return await self.certificate_manager.create_ca_certificate(common_name, **kwargs)
195
+
196
+ async def create_client_certificate(self, common_name: str, **kwargs) -> CertificatePair:
197
+ """Create client certificate."""
198
+ return await self.certificate_manager.create_client_certificate(common_name, **kwargs)
199
+
200
+ async def create_server_certificate(self, common_name: str, **kwargs) -> CertificatePair:
201
+ """Create server certificate."""
202
+ return await self.certificate_manager.create_server_certificate(common_name, **kwargs)
203
+
204
+ async def validate_certificate(self, cert_path: str) -> bool:
205
+ """Validate certificate."""
206
+ return await self.certificate_manager.validate_certificate(cert_path)
207
+
208
+ async def extract_roles_from_certificate(self, cert_path: str) -> List[str]:
209
+ """Extract roles from certificate."""
210
+ return await self.certificate_manager.extract_roles_from_certificate(cert_path)
211
+
212
+ async def revoke_certificate(self, cert_path: str) -> bool:
213
+ """Revoke certificate."""
214
+ return await self.certificate_manager.revoke_certificate(cert_path)
215
+
216
+ # Permission methods - direct calls to PermissionManager
217
+ async def check_permission(self, user_id: str, permission: str) -> bool:
218
+ """Check user permission."""
219
+ return await self.permission_manager.check_permission(user_id, permission)
220
+
221
+ async def get_user_roles(self, user_id: str) -> List[str]:
222
+ """Get user roles."""
223
+ return await self.permission_manager.get_user_roles(user_id)
224
+
225
+ async def add_user_role(self, user_id: str, role: str) -> bool:
226
+ """Add role to user."""
227
+ return await self.permission_manager.add_user_role(user_id, role)
228
+
229
+ async def remove_user_role(self, user_id: str, role: str) -> bool:
230
+ """Remove role from user."""
231
+ return await self.permission_manager.remove_user_role(user_id, role)
232
+
233
+ # Rate limiting methods - direct calls to RateLimiter
234
+ async def check_rate_limit(self, identifier: str, limit_type: str = "per_minute") -> bool:
235
+ """Check rate limit."""
236
+ return await self.rate_limiter.check_rate_limit(identifier, limit_type)
237
+
238
+ async def increment_rate_limit(self, identifier: str) -> None:
239
+ """Increment rate limit counter."""
240
+ await self.rate_limiter.increment_rate_limit(identifier)
241
+
242
+ async def get_rate_limit_info(self, identifier: str) -> Dict[str, Any]:
243
+ """Get rate limit information."""
244
+ return await self.rate_limiter.get_rate_limit_info(identifier)
245
+
246
+ # Middleware creation - direct use of framework middleware
247
+ def create_fastapi_middleware(self, app) -> FastAPISecurityMiddleware:
248
+ """Create FastAPI security middleware."""
249
+ return FastAPISecurityMiddleware(app, self.security_config)
250
+
251
+ # Utility methods
252
+ def is_security_enabled(self) -> bool:
253
+ """Check if security is enabled."""
254
+ return self.security_config.auth.enabled or self.security_config.ssl.enabled
255
+
256
+ def get_public_paths(self) -> List[str]:
257
+ """Get public paths that bypass authentication."""
258
+ return self.security_config.auth.public_paths
259
+
260
+ def get_security_config(self) -> SecurityConfig:
261
+ """Get security configuration."""
262
+ return self.security_config
263
+
264
+
265
+ # Factory function for easy integration
266
+ def create_security_integration(config: Dict[str, Any]) -> SecurityIntegration:
267
+ """
268
+ Create security integration instance.
269
+
270
+ Args:
271
+ config: Configuration dictionary
272
+
273
+ Returns:
274
+ SecurityIntegration instance
275
+
276
+ Raises:
277
+ RuntimeError: If security integration cannot be created
278
+ """
279
+ try:
280
+ return SecurityIntegration(config)
281
+ except ImportError as e:
282
+ logger.error(f"mcp_security_framework not available: {e}")
283
+ raise RuntimeError("Security framework is required but not available") from e
284
+ except Exception as e:
285
+ logger.error(f"Failed to create security integration: {e}")
286
+ raise RuntimeError(f"Security integration failed: {e}") from e
@@ -0,0 +1,282 @@
1
+ """
2
+ Server Configuration Adapter
3
+
4
+ This module provides adapters for converting configuration between different
5
+ server engines and handling SSL configuration mapping.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ from typing import Dict, Any, Optional
13
+ from pathlib import Path
14
+
15
+ from .server_engine import ServerEngineFactory, ServerEngine
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ServerConfigAdapter:
21
+ """
22
+ Adapter for converting server configurations between different engines.
23
+
24
+ This class handles the mapping of configuration parameters between
25
+ different server engines and provides unified configuration management.
26
+ """
27
+
28
+ @staticmethod
29
+ def convert_ssl_config_for_engine(
30
+ ssl_config: Dict[str, Any],
31
+ target_engine: str
32
+ ) -> Dict[str, Any]:
33
+ """
34
+ Convert SSL configuration for a specific server engine.
35
+
36
+ Args:
37
+ ssl_config: Source SSL configuration
38
+ target_engine: Target engine name (hypercorn)
39
+
40
+ Returns:
41
+ Converted SSL configuration for the target engine
42
+ """
43
+ engine = ServerEngineFactory.get_engine(target_engine)
44
+ if not engine:
45
+ logger.error(f"Unknown server engine: {target_engine}")
46
+ return {}
47
+
48
+ if target_engine == "hypercorn":
49
+ return ServerConfigAdapter._convert_to_hypercorn_ssl(ssl_config)
50
+ else:
51
+ logger.warning(f"No SSL conversion available for engine: {target_engine}")
52
+ return {}
53
+
54
+ @staticmethod
55
+ def _convert_to_hypercorn_ssl(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
56
+ """Convert SSL configuration to hypercorn format."""
57
+ hypercorn_ssl = {}
58
+
59
+ # Map SSL parameters
60
+ if ssl_config.get("cert_file"):
61
+ hypercorn_ssl["certfile"] = ssl_config["cert_file"]
62
+ if ssl_config.get("key_file"):
63
+ hypercorn_ssl["keyfile"] = ssl_config["key_file"]
64
+ if ssl_config.get("ca_cert"):
65
+ hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
66
+
67
+ # Map verification mode
68
+ if ssl_config.get("verify_client", False):
69
+ hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
70
+
71
+ logger.debug(f"Converted SSL config to hypercorn: {hypercorn_ssl}")
72
+ return hypercorn_ssl
73
+
74
+ @staticmethod
75
+ def get_optimal_engine_for_config(config: Dict[str, Any]) -> Optional[str]:
76
+ """
77
+ Determine the optimal server engine for a given configuration.
78
+
79
+ Currently only hypercorn is supported.
80
+
81
+ Args:
82
+ config: Server configuration
83
+
84
+ Returns:
85
+ Optimal engine name (currently always "hypercorn")
86
+ """
87
+ # Currently only hypercorn is supported
88
+ return "hypercorn"
89
+
90
+ @staticmethod
91
+ def validate_engine_compatibility(
92
+ config: Dict[str, Any],
93
+ engine_name: str
94
+ ) -> bool:
95
+ """
96
+ Validate if a configuration is compatible with a specific engine.
97
+
98
+ Args:
99
+ config: Server configuration
100
+ engine_name: Name of the server engine
101
+
102
+ Returns:
103
+ True if compatible, False otherwise
104
+ """
105
+ engine = ServerEngineFactory.get_engine(engine_name)
106
+ if not engine:
107
+ logger.error(f"Unknown engine: {engine_name}")
108
+ return False
109
+
110
+ # Check SSL requirements
111
+ ssl_config = config.get("ssl", {})
112
+ if not ssl_config:
113
+ # Try to get SSL config from security section
114
+ ssl_config = config.get("security", {}).get("ssl", {})
115
+
116
+ if ssl_config.get("verify_client", False):
117
+ if not engine.get_supported_features().get("mtls_client_certs", False):
118
+ logger.error(f"Engine {engine_name} doesn't support mTLS client certificates")
119
+ return False
120
+
121
+ # Validate engine-specific configuration
122
+ return engine.validate_config(config)
123
+
124
+ @staticmethod
125
+ def get_engine_capabilities(engine_name: str) -> Dict[str, Any]:
126
+ """
127
+ Get capabilities of a specific server engine.
128
+
129
+ Args:
130
+ engine_name: Name of the server engine
131
+
132
+ Returns:
133
+ Dictionary of engine capabilities
134
+ """
135
+ engine = ServerEngineFactory.get_engine(engine_name)
136
+ if not engine:
137
+ return {}
138
+
139
+ return {
140
+ "name": engine.get_name(),
141
+ "features": engine.get_supported_features(),
142
+ "config_schema": engine.get_config_schema()
143
+ }
144
+
145
+
146
+ class UnifiedServerRunner:
147
+ """
148
+ Unified server runner that uses hypercorn as the default engine.
149
+
150
+ This class provides a unified interface for running servers using hypercorn
151
+ as the underlying engine.
152
+ """
153
+
154
+ def __init__(self, default_engine: str = "hypercorn"):
155
+ """
156
+ Initialize the unified server runner.
157
+
158
+ Args:
159
+ default_engine: Default engine to use (currently only hypercorn is supported)
160
+ """
161
+ self.default_engine = default_engine
162
+ self.available_engines = ServerEngineFactory.get_available_engines()
163
+
164
+ logger.info(f"Available engines: {list(self.available_engines.keys())}")
165
+ logger.info(f"Default engine: {default_engine}")
166
+
167
+ def run_server(
168
+ self,
169
+ app: Any,
170
+ config: Dict[str, Any],
171
+ engine_name: Optional[str] = None
172
+ ) -> None:
173
+ """
174
+ Run server with hypercorn engine.
175
+
176
+ Args:
177
+ app: ASGI application
178
+ config: Server configuration
179
+ engine_name: Engine to use (currently only hypercorn is supported)
180
+ """
181
+ # Use hypercorn as the only supported engine
182
+ selected_engine = "hypercorn"
183
+ logger.info(f"Using hypercorn engine")
184
+
185
+ # Validate compatibility
186
+ if not ServerConfigAdapter.validate_engine_compatibility(config, selected_engine):
187
+ raise ValueError(f"Configuration not compatible with engine: {selected_engine}")
188
+
189
+ # Get engine instance
190
+ engine = ServerEngineFactory.get_engine(selected_engine)
191
+ if not engine:
192
+ raise ValueError(f"Engine not available: {selected_engine}")
193
+
194
+ # Convert configuration if needed
195
+ converted_config = self._prepare_config_for_engine(config, selected_engine)
196
+
197
+ # Run server
198
+ logger.info(f"Starting server with {selected_engine} engine")
199
+ engine.run_server(app, converted_config)
200
+
201
+ def _prepare_config_for_engine(
202
+ self,
203
+ config: Dict[str, Any],
204
+ engine_name: str
205
+ ) -> Dict[str, Any]:
206
+ logger.info(f"🔍 Debug: _prepare_config_for_engine called with config keys: {list(config.keys())}")
207
+ logger.info(f"🔍 Debug: SSL config in input: {config.get('ssl', 'NOT_FOUND')}")
208
+ """
209
+ Prepare configuration for a specific engine.
210
+
211
+ Args:
212
+ config: Original configuration
213
+ engine_name: Target engine name
214
+
215
+ Returns:
216
+ Engine-specific configuration
217
+ """
218
+ # Start with basic config
219
+ engine_config = {
220
+ "host": config.get("host", "127.0.0.1"),
221
+ "port": config.get("port", 8000),
222
+ "log_level": config.get("log_level", "info"),
223
+ "reload": config.get("reload", False)
224
+ }
225
+
226
+ # Add SSL configuration if present
227
+ # First check for direct SSL parameters (from app_factory.py)
228
+ if "certfile" in config or "keyfile" in config or "ca_certs" in config or "verify_mode" in config:
229
+ logger.info(f"🔍 DEBUG: Direct SSL parameters found in config")
230
+ if "certfile" in config:
231
+ engine_config["certfile"] = config["certfile"]
232
+ if "keyfile" in config:
233
+ engine_config["keyfile"] = config["keyfile"]
234
+ if "ca_certs" in config:
235
+ engine_config["ca_certs"] = config["ca_certs"]
236
+ if "verify_mode" in config:
237
+ engine_config["verify_mode"] = config["verify_mode"]
238
+ else:
239
+ # Try to get SSL config from ssl section
240
+ ssl_config = config.get("ssl", {})
241
+ if not ssl_config:
242
+ # Try to get SSL config from security section
243
+ ssl_config = config.get("security", {}).get("ssl", {})
244
+
245
+ if ssl_config:
246
+ converted_ssl = ServerConfigAdapter.convert_ssl_config_for_engine(
247
+ ssl_config, engine_name
248
+ )
249
+ engine_config.update(converted_ssl)
250
+
251
+ # Add engine-specific configuration
252
+ if "workers" in config:
253
+ engine_config["workers"] = config["workers"]
254
+
255
+ return engine_config
256
+
257
+ def get_engine_info(self, engine_name: str) -> Dict[str, Any]:
258
+ """
259
+ Get information about a specific engine.
260
+
261
+ Args:
262
+ engine_name: Name of the engine
263
+
264
+ Returns:
265
+ Engine information dictionary
266
+ """
267
+ return ServerConfigAdapter.get_engine_capabilities(engine_name)
268
+
269
+ def list_available_engines(self) -> Dict[str, Dict[str, Any]]:
270
+ """
271
+ List all available engines with their capabilities.
272
+
273
+ Returns:
274
+ Dictionary mapping engine names to their capabilities
275
+ """
276
+ engines_info = {}
277
+ for name, engine in self.available_engines.items():
278
+ engines_info[name] = {
279
+ "features": engine.get_supported_features(),
280
+ "config_schema": engine.get_config_schema()
281
+ }
282
+ return engines_info