mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +32 -0
- mcp_proxy_adapter/api/app.py +290 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +38 -32
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +8 -1
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +394 -14
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +1045 -0
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +385 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +234 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/__init__.py +13 -4
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +93 -0
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/README.md +0 -124
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,385 @@
|
|
1
|
+
"""
|
2
|
+
Protocol management module for MCP Proxy Adapter.
|
3
|
+
|
4
|
+
This module provides functionality for managing and validating protocol configurations,
|
5
|
+
including HTTP, HTTPS, and MTLS protocols with their respective ports.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import ssl
|
9
|
+
from typing import Dict, List, Optional, Tuple, Union
|
10
|
+
from urllib.parse import urlparse
|
11
|
+
|
12
|
+
from mcp_proxy_adapter.config import config
|
13
|
+
from mcp_proxy_adapter.core.logging import logger
|
14
|
+
|
15
|
+
|
16
|
+
class ProtocolManager:
|
17
|
+
"""
|
18
|
+
Manages protocol configurations and validates protocol access.
|
19
|
+
|
20
|
+
This class handles the validation of allowed protocols and their associated ports,
|
21
|
+
ensuring that only configured protocols are accessible.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(self, app_config: Optional[Dict] = None):
|
25
|
+
"""
|
26
|
+
Initialize the protocol manager.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
app_config: Application configuration dictionary (optional)
|
30
|
+
"""
|
31
|
+
self.app_config = app_config
|
32
|
+
self._load_config()
|
33
|
+
|
34
|
+
def _load_config(self):
|
35
|
+
"""Load protocol configuration from config."""
|
36
|
+
# Use provided config or fallback to global config; normalize types
|
37
|
+
current_config = self.app_config if self.app_config is not None else config.get_all()
|
38
|
+
logger.debug(f"ProtocolManager._load_config - current_config type: {type(current_config)}")
|
39
|
+
|
40
|
+
if not hasattr(current_config, 'get'):
|
41
|
+
# Not a dict-like config, fallback to global
|
42
|
+
logger.debug(f"ProtocolManager._load_config - current_config is not dict-like, falling back to global config")
|
43
|
+
current_config = config.get_all()
|
44
|
+
|
45
|
+
logger.debug(f"ProtocolManager._load_config - final current_config type: {type(current_config)}")
|
46
|
+
if hasattr(current_config, 'get'):
|
47
|
+
logger.debug(f"ProtocolManager._load_config - current_config keys: {list(current_config.keys()) if hasattr(current_config, 'keys') else 'no keys'}")
|
48
|
+
|
49
|
+
# Get protocols configuration
|
50
|
+
logger.debug(f"ProtocolManager._load_config - before getting protocols")
|
51
|
+
try:
|
52
|
+
self.protocols_config = current_config.get("protocols", {})
|
53
|
+
logger.debug(f"ProtocolManager._load_config - protocols_config type: {type(self.protocols_config)}")
|
54
|
+
if hasattr(self.protocols_config, 'get'):
|
55
|
+
logger.debug(f"ProtocolManager._load_config - protocols_config is dict-like")
|
56
|
+
else:
|
57
|
+
logger.debug(f"ProtocolManager._load_config - protocols_config is NOT dict-like: {repr(self.protocols_config)}")
|
58
|
+
except Exception as e:
|
59
|
+
logger.debug(f"ProtocolManager._load_config - ERROR getting protocols: {e}")
|
60
|
+
self.protocols_config = {}
|
61
|
+
|
62
|
+
self.enabled = self.protocols_config.get("enabled", True) if hasattr(self.protocols_config, 'get') else True
|
63
|
+
|
64
|
+
# Get SSL configuration to determine allowed protocols
|
65
|
+
ssl_enabled = self._is_ssl_enabled(current_config)
|
66
|
+
|
67
|
+
# Set allowed protocols based on SSL configuration
|
68
|
+
if ssl_enabled:
|
69
|
+
# If SSL is enabled, allow both HTTP and HTTPS
|
70
|
+
self.allowed_protocols = self.protocols_config.get("allowed_protocols", ["http", "https"])
|
71
|
+
# Ensure HTTPS is in allowed protocols if SSL is enabled
|
72
|
+
if "https" not in self.allowed_protocols:
|
73
|
+
self.allowed_protocols.append("https")
|
74
|
+
else:
|
75
|
+
# If SSL is disabled, only allow HTTP
|
76
|
+
self.allowed_protocols = self.protocols_config.get("allowed_protocols", ["http"])
|
77
|
+
# Remove HTTPS from allowed protocols if SSL is disabled
|
78
|
+
if "https" in self.allowed_protocols:
|
79
|
+
self.allowed_protocols.remove("https")
|
80
|
+
|
81
|
+
logger.debug(f"Protocol manager loaded config: enabled={self.enabled}, allowed_protocols={self.allowed_protocols}, ssl_enabled={ssl_enabled}")
|
82
|
+
|
83
|
+
def _is_ssl_enabled(self, current_config: Dict) -> bool:
|
84
|
+
"""
|
85
|
+
Check if SSL is enabled in configuration.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
current_config: Current configuration dictionary
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
True if SSL is enabled, False otherwise
|
92
|
+
"""
|
93
|
+
# Try security framework SSL config first
|
94
|
+
security_config = current_config.get("security", {})
|
95
|
+
ssl_config = security_config.get("ssl", {})
|
96
|
+
|
97
|
+
if ssl_config.get("enabled", False):
|
98
|
+
logger.debug("SSL enabled via security.ssl configuration")
|
99
|
+
return True
|
100
|
+
|
101
|
+
# Fallback to legacy SSL config
|
102
|
+
legacy_ssl_config = current_config.get("ssl", {})
|
103
|
+
if legacy_ssl_config.get("enabled", False):
|
104
|
+
logger.debug("SSL enabled via legacy ssl configuration")
|
105
|
+
return True
|
106
|
+
|
107
|
+
logger.debug("SSL is disabled in configuration")
|
108
|
+
return False
|
109
|
+
|
110
|
+
def update_config(self, new_config: Dict):
|
111
|
+
"""
|
112
|
+
Update configuration and reload protocol settings.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
new_config: New configuration dictionary
|
116
|
+
"""
|
117
|
+
self.app_config = new_config
|
118
|
+
self._load_config()
|
119
|
+
logger.info(f"Protocol manager configuration updated: allowed_protocols={self.allowed_protocols}")
|
120
|
+
|
121
|
+
def reload_config(self):
|
122
|
+
"""Reload protocol configuration from global config."""
|
123
|
+
self._load_config()
|
124
|
+
|
125
|
+
def is_protocol_allowed(self, protocol: str) -> bool:
|
126
|
+
"""
|
127
|
+
Check if a protocol is allowed based on configuration.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
protocol: Protocol name (http, https, mtls)
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
True if protocol is allowed, False otherwise
|
134
|
+
"""
|
135
|
+
if not self.enabled:
|
136
|
+
logger.debug("Protocol management is disabled, allowing all protocols")
|
137
|
+
return True
|
138
|
+
|
139
|
+
protocol_lower = protocol.lower()
|
140
|
+
is_allowed = protocol_lower in self.allowed_protocols
|
141
|
+
|
142
|
+
logger.debug(f"Protocol '{protocol}' allowed: {is_allowed}")
|
143
|
+
return is_allowed
|
144
|
+
|
145
|
+
def get_protocol_port(self, protocol: str) -> Optional[int]:
|
146
|
+
"""
|
147
|
+
Get the configured port for a specific protocol.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
protocol: Protocol name (http, https, mtls)
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
Port number if configured, None otherwise
|
154
|
+
"""
|
155
|
+
protocol_lower = protocol.lower()
|
156
|
+
protocol_config = self.protocols_config.get(protocol_lower, {})
|
157
|
+
|
158
|
+
if not protocol_config.get("enabled", False):
|
159
|
+
logger.debug(f"Protocol '{protocol}' is not enabled")
|
160
|
+
return None
|
161
|
+
|
162
|
+
port = protocol_config.get("port")
|
163
|
+
logger.debug(f"Protocol '{protocol}' port: {port}")
|
164
|
+
return port
|
165
|
+
|
166
|
+
def get_allowed_protocols(self) -> List[str]:
|
167
|
+
"""
|
168
|
+
Get list of all allowed protocols.
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
List of allowed protocol names
|
172
|
+
"""
|
173
|
+
return self.allowed_protocols.copy()
|
174
|
+
|
175
|
+
def get_protocol_config(self, protocol: str) -> Dict:
|
176
|
+
"""
|
177
|
+
Get full configuration for a specific protocol.
|
178
|
+
|
179
|
+
Args:
|
180
|
+
protocol: Protocol name (http, https, mtls)
|
181
|
+
|
182
|
+
Returns:
|
183
|
+
Protocol configuration dictionary
|
184
|
+
"""
|
185
|
+
protocol_lower = protocol.lower()
|
186
|
+
cfg = self.protocols_config.get(protocol_lower, {})
|
187
|
+
# Ensure dict type
|
188
|
+
if isinstance(cfg, dict):
|
189
|
+
try:
|
190
|
+
return cfg.copy()
|
191
|
+
except Exception:
|
192
|
+
return {}
|
193
|
+
return {}
|
194
|
+
|
195
|
+
def validate_url_protocol(self, url: str) -> Tuple[bool, Optional[str]]:
|
196
|
+
"""
|
197
|
+
Validate if the URL protocol is allowed.
|
198
|
+
|
199
|
+
Args:
|
200
|
+
url: URL to validate
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
Tuple of (is_allowed, error_message)
|
204
|
+
"""
|
205
|
+
try:
|
206
|
+
parsed = urlparse(url)
|
207
|
+
protocol = parsed.scheme.lower()
|
208
|
+
|
209
|
+
if not protocol:
|
210
|
+
return False, "No protocol specified in URL"
|
211
|
+
|
212
|
+
if not self.is_protocol_allowed(protocol):
|
213
|
+
return False, f"Protocol '{protocol}' is not allowed. Allowed protocols: {self.allowed_protocols}"
|
214
|
+
|
215
|
+
return True, None
|
216
|
+
|
217
|
+
except Exception as e:
|
218
|
+
return False, f"Invalid URL format: {str(e)}"
|
219
|
+
|
220
|
+
def get_ssl_context_for_protocol(self, protocol: str) -> Optional[ssl.SSLContext]:
|
221
|
+
"""
|
222
|
+
Get SSL context for HTTPS or MTLS protocol.
|
223
|
+
|
224
|
+
Args:
|
225
|
+
protocol: Protocol name (https, mtls)
|
226
|
+
|
227
|
+
Returns:
|
228
|
+
SSL context if protocol requires SSL, None otherwise
|
229
|
+
"""
|
230
|
+
if protocol.lower() not in ["https", "mtls"]:
|
231
|
+
return None
|
232
|
+
|
233
|
+
# Use provided config or fallback to global config
|
234
|
+
current_config = self.app_config if self.app_config is not None else config.get_all()
|
235
|
+
|
236
|
+
# Get SSL configuration
|
237
|
+
ssl_config = self._get_ssl_config(current_config)
|
238
|
+
|
239
|
+
if not ssl_config.get("enabled", False):
|
240
|
+
logger.warning(f"SSL required for protocol '{protocol}' but SSL is disabled")
|
241
|
+
return None
|
242
|
+
|
243
|
+
cert_file = ssl_config.get("cert_file")
|
244
|
+
key_file = ssl_config.get("key_file")
|
245
|
+
|
246
|
+
if not cert_file or not key_file:
|
247
|
+
logger.warning(f"SSL required for protocol '{protocol}' but certificate files not configured")
|
248
|
+
return None
|
249
|
+
|
250
|
+
try:
|
251
|
+
from mcp_proxy_adapter.core.ssl_utils import SSLUtils
|
252
|
+
|
253
|
+
ssl_context = SSLUtils.create_ssl_context(
|
254
|
+
cert_file=cert_file,
|
255
|
+
key_file=key_file,
|
256
|
+
ca_cert=ssl_config.get("ca_cert"),
|
257
|
+
verify_client=protocol.lower() == "mtls" or ssl_config.get("verify_client", False),
|
258
|
+
cipher_suites=ssl_config.get("cipher_suites", []),
|
259
|
+
min_tls_version=ssl_config.get("min_tls_version", "1.2"),
|
260
|
+
max_tls_version=ssl_config.get("max_tls_version", "1.3")
|
261
|
+
)
|
262
|
+
|
263
|
+
logger.info(f"SSL context created for protocol '{protocol}'")
|
264
|
+
return ssl_context
|
265
|
+
|
266
|
+
except Exception as e:
|
267
|
+
logger.error(f"Failed to create SSL context for protocol '{protocol}': {e}")
|
268
|
+
return None
|
269
|
+
|
270
|
+
def _get_ssl_config(self, current_config: Dict) -> Dict:
|
271
|
+
"""
|
272
|
+
Get SSL configuration from config.
|
273
|
+
|
274
|
+
Args:
|
275
|
+
current_config: Current configuration dictionary
|
276
|
+
|
277
|
+
Returns:
|
278
|
+
SSL configuration dictionary
|
279
|
+
"""
|
280
|
+
# Try security framework SSL config first
|
281
|
+
security_config = current_config.get("security", {})
|
282
|
+
ssl_config = security_config.get("ssl", {})
|
283
|
+
|
284
|
+
if ssl_config.get("enabled", False):
|
285
|
+
logger.debug("Using security.ssl configuration")
|
286
|
+
return ssl_config
|
287
|
+
|
288
|
+
# Fallback to legacy SSL config
|
289
|
+
legacy_ssl_config = current_config.get("ssl", {})
|
290
|
+
if legacy_ssl_config.get("enabled", False):
|
291
|
+
logger.debug("Using legacy ssl configuration")
|
292
|
+
return legacy_ssl_config
|
293
|
+
|
294
|
+
# Return empty config if SSL is disabled
|
295
|
+
return {"enabled": False}
|
296
|
+
|
297
|
+
def get_protocol_info(self) -> Dict[str, Dict]:
|
298
|
+
"""
|
299
|
+
Get information about all configured protocols.
|
300
|
+
|
301
|
+
Returns:
|
302
|
+
Dictionary with protocol information
|
303
|
+
"""
|
304
|
+
info = {}
|
305
|
+
|
306
|
+
for protocol in ["http", "https", "mtls"]:
|
307
|
+
protocol_config = self.get_protocol_config(protocol)
|
308
|
+
info[protocol] = {
|
309
|
+
"enabled": protocol_config.get("enabled", False),
|
310
|
+
"allowed": self.is_protocol_allowed(protocol),
|
311
|
+
"port": protocol_config.get("port"),
|
312
|
+
"requires_ssl": protocol in ["https", "mtls"],
|
313
|
+
"ssl_context_available": self.get_ssl_context_for_protocol(protocol) is not None
|
314
|
+
}
|
315
|
+
|
316
|
+
return info
|
317
|
+
|
318
|
+
def validate_protocol_configuration(self) -> List[str]:
|
319
|
+
"""
|
320
|
+
Validate the current protocol configuration.
|
321
|
+
|
322
|
+
Returns:
|
323
|
+
List of validation errors (empty if configuration is valid)
|
324
|
+
"""
|
325
|
+
errors = []
|
326
|
+
|
327
|
+
if not self.enabled:
|
328
|
+
return errors
|
329
|
+
|
330
|
+
# Check if allowed protocols are configured
|
331
|
+
for protocol in self.allowed_protocols:
|
332
|
+
if protocol not in ["http", "https", "mtls"]:
|
333
|
+
errors.append(f"Unknown protocol '{protocol}' in allowed_protocols")
|
334
|
+
continue
|
335
|
+
|
336
|
+
protocol_config = self.get_protocol_config(protocol)
|
337
|
+
|
338
|
+
if not protocol_config.get("enabled", False):
|
339
|
+
errors.append(f"Protocol '{protocol}' is in allowed_protocols but not enabled")
|
340
|
+
continue
|
341
|
+
|
342
|
+
port = protocol_config.get("port")
|
343
|
+
if not port:
|
344
|
+
errors.append(f"Protocol '{protocol}' is enabled but no port configured")
|
345
|
+
continue
|
346
|
+
|
347
|
+
# Check SSL requirements
|
348
|
+
if protocol in ["https", "mtls"]:
|
349
|
+
# Use provided config or fallback to global config
|
350
|
+
current_config = self.app_config if self.app_config is not None else config.get_all()
|
351
|
+
ssl_config = self._get_ssl_config(current_config)
|
352
|
+
|
353
|
+
if not ssl_config.get("enabled", False):
|
354
|
+
errors.append(f"Protocol '{protocol}' requires SSL but SSL is disabled")
|
355
|
+
elif not ssl_config.get("cert_file") or not ssl_config.get("key_file"):
|
356
|
+
errors.append(f"Protocol '{protocol}' requires SSL but certificate files not configured")
|
357
|
+
|
358
|
+
return errors
|
359
|
+
|
360
|
+
|
361
|
+
# Global protocol manager instance - will be updated with config when needed
|
362
|
+
protocol_manager = None
|
363
|
+
|
364
|
+
def get_protocol_manager(app_config: Optional[Dict] = None) -> ProtocolManager:
|
365
|
+
"""
|
366
|
+
Get protocol manager instance with current configuration.
|
367
|
+
|
368
|
+
Args:
|
369
|
+
app_config: Application configuration dictionary (optional)
|
370
|
+
|
371
|
+
Returns:
|
372
|
+
ProtocolManager instance
|
373
|
+
"""
|
374
|
+
global protocol_manager
|
375
|
+
|
376
|
+
# If no app_config provided, use global config
|
377
|
+
if app_config is None:
|
378
|
+
app_config = config.get_all()
|
379
|
+
|
380
|
+
# Create new instance if none exists or config changed
|
381
|
+
if protocol_manager is None or protocol_manager.app_config != app_config:
|
382
|
+
protocol_manager = ProtocolManager(app_config)
|
383
|
+
logger.info("Protocol manager created with new configuration")
|
384
|
+
|
385
|
+
return protocol_manager
|