mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/api/app.py +174 -80
- mcp_proxy_adapter/api/handlers.py +16 -5
- mcp_proxy_adapter/api/middleware/__init__.py +9 -4
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/factory.py +36 -12
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
- mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +7 -1
- mcp_proxy_adapter/commands/base.py +7 -4
- mcp_proxy_adapter/commands/builtin_commands.py +8 -2
- mcp_proxy_adapter/commands/command_registry.py +8 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
- mcp_proxy_adapter/commands/token_management_command.py +1 -1
- mcp_proxy_adapter/config.py +81 -21
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/logging.py +8 -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 +139 -8
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +299 -47
- mcp_proxy_adapter/core/security_adapter.py +12 -15
- mcp_proxy_adapter/core/security_integration.py +285 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
- mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_config_generator.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +21 -10
- mcp_proxy_adapter/utils/config_generator.py +727 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
- mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
- mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
- mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
- mcp_proxy_adapter/api/middleware/security.py +0 -376
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
- mcp_proxy_adapter/examples/__init__.py +0 -7
- 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 -70
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
- mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
- mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -114
- 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 -566
- 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/auto_commands/test_command.py +0 -105
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
- 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/full_help_response.json +0 -1
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
- 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/loadable_commands/test_ignored.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
- mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
- 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/examples/simple_custom_commands/README.md +0 -149
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/schemas/roles_schema.json +0 -162
- 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 -270
- mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,285 @@
|
|
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
|
+
security_section = self.config.get("security", {})
|
76
|
+
|
77
|
+
# Create SSL config
|
78
|
+
ssl_config = SSLConfig(
|
79
|
+
enabled=security_section.get("ssl", {}).get("enabled", False),
|
80
|
+
cert_file=security_section.get("ssl", {}).get("cert_file"),
|
81
|
+
key_file=security_section.get("ssl", {}).get("key_file"),
|
82
|
+
ca_cert_file=security_section.get("ssl", {}).get("ca_cert_file"),
|
83
|
+
client_cert_file=security_section.get("ssl", {}).get("client_cert_file"),
|
84
|
+
client_key_file=security_section.get("ssl", {}).get("client_key_file"),
|
85
|
+
verify_mode=security_section.get("ssl", {}).get("verify_mode", "CERT_REQUIRED"),
|
86
|
+
min_tls_version=security_section.get("ssl", {}).get("min_tls_version", "TLSv1.2"),
|
87
|
+
check_hostname=security_section.get("ssl", {}).get("check_hostname", True),
|
88
|
+
check_expiry=security_section.get("ssl", {}).get("check_expiry", True),
|
89
|
+
expiry_warning_days=security_section.get("ssl", {}).get("expiry_warning_days", 30)
|
90
|
+
)
|
91
|
+
|
92
|
+
# Create auth config
|
93
|
+
auth_config = AuthConfig(
|
94
|
+
enabled=security_section.get("auth", {}).get("enabled", True),
|
95
|
+
methods=security_section.get("auth", {}).get("methods", ["api_key"]),
|
96
|
+
api_keys=security_section.get("auth", {}).get("api_keys", {}),
|
97
|
+
user_roles=security_section.get("auth", {}).get("user_roles", {}),
|
98
|
+
jwt_secret=security_section.get("auth", {}).get("jwt_secret"),
|
99
|
+
jwt_algorithm=security_section.get("auth", {}).get("jwt_algorithm", "HS256"),
|
100
|
+
jwt_expiry_hours=security_section.get("auth", {}).get("jwt_expiry_hours", 24),
|
101
|
+
certificate_auth=security_section.get("auth", {}).get("certificate_auth", False),
|
102
|
+
public_paths=security_section.get("auth", {}).get("public_paths", [])
|
103
|
+
)
|
104
|
+
|
105
|
+
# Create permission config - handle null values properly
|
106
|
+
permissions_section = security_section.get("permissions", {})
|
107
|
+
roles_file = permissions_section.get("roles_file")
|
108
|
+
|
109
|
+
# If roles_file is None or empty string, don't pass it to avoid framework errors
|
110
|
+
if roles_file is None or roles_file == "":
|
111
|
+
logger.warning("roles_file is None or empty, permissions will use default configuration")
|
112
|
+
roles_file = None
|
113
|
+
|
114
|
+
permission_config = PermissionConfig(
|
115
|
+
enabled=permissions_section.get("enabled", True),
|
116
|
+
roles_file=roles_file,
|
117
|
+
default_role=permissions_section.get("default_role", "guest"),
|
118
|
+
admin_role=permissions_section.get("admin_role", "admin"),
|
119
|
+
role_hierarchy=permissions_section.get("role_hierarchy", {}),
|
120
|
+
permission_cache_enabled=permissions_section.get("permission_cache_enabled", True),
|
121
|
+
permission_cache_ttl=permissions_section.get("permission_cache_ttl", 300),
|
122
|
+
wildcard_permissions=permissions_section.get("wildcard_permissions", False),
|
123
|
+
strict_mode=permissions_section.get("strict_mode", True),
|
124
|
+
roles=permissions_section.get("roles")
|
125
|
+
)
|
126
|
+
|
127
|
+
# Create rate limit config
|
128
|
+
rate_limit_config = RateLimitConfig(
|
129
|
+
enabled=security_section.get("rate_limit", {}).get("enabled", True),
|
130
|
+
default_requests_per_minute=security_section.get("rate_limit", {}).get("default_requests_per_minute", 60),
|
131
|
+
default_requests_per_hour=security_section.get("rate_limit", {}).get("default_requests_per_hour", 1000),
|
132
|
+
burst_limit=security_section.get("rate_limit", {}).get("burst_limit", 2),
|
133
|
+
window_size_seconds=security_section.get("rate_limit", {}).get("window_size_seconds", 60),
|
134
|
+
storage_backend=security_section.get("rate_limit", {}).get("storage_backend", "memory"),
|
135
|
+
exempt_paths=security_section.get("rate_limit", {}).get("exempt_paths", []),
|
136
|
+
exempt_roles=security_section.get("rate_limit", {}).get("exempt_roles", [])
|
137
|
+
)
|
138
|
+
|
139
|
+
# Create certificate config
|
140
|
+
certificate_config = CertificateConfig(
|
141
|
+
enabled=security_section.get("certificates", {}).get("enabled", False),
|
142
|
+
ca_cert_path=security_section.get("certificates", {}).get("ca_cert_path"),
|
143
|
+
ca_key_path=security_section.get("certificates", {}).get("ca_key_path"),
|
144
|
+
cert_storage_path=security_section.get("certificates", {}).get("cert_storage_path", "./certs"),
|
145
|
+
key_storage_path=security_section.get("certificates", {}).get("key_storage_path", "./keys"),
|
146
|
+
default_validity_days=security_section.get("certificates", {}).get("default_validity_days", 365),
|
147
|
+
key_size=security_section.get("certificates", {}).get("key_size", 2048),
|
148
|
+
hash_algorithm=security_section.get("certificates", {}).get("hash_algorithm", "sha256")
|
149
|
+
)
|
150
|
+
|
151
|
+
# Create logging config
|
152
|
+
logging_config = LoggingConfig(
|
153
|
+
enabled=security_section.get("logging", {}).get("enabled", True),
|
154
|
+
level=security_section.get("logging", {}).get("level", "INFO"),
|
155
|
+
format=security_section.get("logging", {}).get("format"),
|
156
|
+
console_output=security_section.get("logging", {}).get("console_output", True),
|
157
|
+
file_path=security_section.get("logging", {}).get("file_path")
|
158
|
+
)
|
159
|
+
|
160
|
+
# Create main security config
|
161
|
+
return SecurityConfig(
|
162
|
+
ssl=ssl_config,
|
163
|
+
auth=auth_config,
|
164
|
+
permissions=permission_config,
|
165
|
+
rate_limit=rate_limit_config,
|
166
|
+
certificates=certificate_config,
|
167
|
+
logging=logging_config,
|
168
|
+
debug=security_section.get("debug", False),
|
169
|
+
environment=security_section.get("environment", "dev"),
|
170
|
+
version=security_section.get("version", "1.0.0")
|
171
|
+
)
|
172
|
+
|
173
|
+
# Authentication methods - direct calls to AuthManager
|
174
|
+
async def authenticate_api_key(self, api_key: str) -> AuthResult:
|
175
|
+
"""Authenticate using API key."""
|
176
|
+
return await self.auth_manager.authenticate_api_key(api_key)
|
177
|
+
|
178
|
+
async def authenticate_jwt(self, token: str) -> AuthResult:
|
179
|
+
"""Authenticate using JWT token."""
|
180
|
+
return await self.auth_manager.authenticate_jwt(token)
|
181
|
+
|
182
|
+
async def authenticate_certificate(self, cert_data: bytes) -> AuthResult:
|
183
|
+
"""Authenticate using certificate."""
|
184
|
+
return await self.auth_manager.authenticate_certificate(cert_data)
|
185
|
+
|
186
|
+
async def validate_request(self, request_data: Dict[str, Any]) -> ValidationResult:
|
187
|
+
"""Validate request using security manager."""
|
188
|
+
return await self.security_manager.validate_request(request_data)
|
189
|
+
|
190
|
+
# Certificate methods - direct calls to CertificateManager
|
191
|
+
async def create_ca_certificate(self, common_name: str, **kwargs) -> CertificatePair:
|
192
|
+
"""Create CA certificate."""
|
193
|
+
return await self.certificate_manager.create_ca_certificate(common_name, **kwargs)
|
194
|
+
|
195
|
+
async def create_client_certificate(self, common_name: str, **kwargs) -> CertificatePair:
|
196
|
+
"""Create client certificate."""
|
197
|
+
return await self.certificate_manager.create_client_certificate(common_name, **kwargs)
|
198
|
+
|
199
|
+
async def create_server_certificate(self, common_name: str, **kwargs) -> CertificatePair:
|
200
|
+
"""Create server certificate."""
|
201
|
+
return await self.certificate_manager.create_server_certificate(common_name, **kwargs)
|
202
|
+
|
203
|
+
async def validate_certificate(self, cert_path: str) -> bool:
|
204
|
+
"""Validate certificate."""
|
205
|
+
return await self.certificate_manager.validate_certificate(cert_path)
|
206
|
+
|
207
|
+
async def extract_roles_from_certificate(self, cert_path: str) -> List[str]:
|
208
|
+
"""Extract roles from certificate."""
|
209
|
+
return await self.certificate_manager.extract_roles_from_certificate(cert_path)
|
210
|
+
|
211
|
+
async def revoke_certificate(self, cert_path: str) -> bool:
|
212
|
+
"""Revoke certificate."""
|
213
|
+
return await self.certificate_manager.revoke_certificate(cert_path)
|
214
|
+
|
215
|
+
# Permission methods - direct calls to PermissionManager
|
216
|
+
async def check_permission(self, user_id: str, permission: str) -> bool:
|
217
|
+
"""Check user permission."""
|
218
|
+
return await self.permission_manager.check_permission(user_id, permission)
|
219
|
+
|
220
|
+
async def get_user_roles(self, user_id: str) -> List[str]:
|
221
|
+
"""Get user roles."""
|
222
|
+
return await self.permission_manager.get_user_roles(user_id)
|
223
|
+
|
224
|
+
async def add_user_role(self, user_id: str, role: str) -> bool:
|
225
|
+
"""Add role to user."""
|
226
|
+
return await self.permission_manager.add_user_role(user_id, role)
|
227
|
+
|
228
|
+
async def remove_user_role(self, user_id: str, role: str) -> bool:
|
229
|
+
"""Remove role from user."""
|
230
|
+
return await self.permission_manager.remove_user_role(user_id, role)
|
231
|
+
|
232
|
+
# Rate limiting methods - direct calls to RateLimiter
|
233
|
+
async def check_rate_limit(self, identifier: str, limit_type: str = "per_minute") -> bool:
|
234
|
+
"""Check rate limit."""
|
235
|
+
return await self.rate_limiter.check_rate_limit(identifier, limit_type)
|
236
|
+
|
237
|
+
async def increment_rate_limit(self, identifier: str) -> None:
|
238
|
+
"""Increment rate limit counter."""
|
239
|
+
await self.rate_limiter.increment_rate_limit(identifier)
|
240
|
+
|
241
|
+
async def get_rate_limit_info(self, identifier: str) -> Dict[str, Any]:
|
242
|
+
"""Get rate limit information."""
|
243
|
+
return await self.rate_limiter.get_rate_limit_info(identifier)
|
244
|
+
|
245
|
+
# Middleware creation - direct use of framework middleware
|
246
|
+
def create_fastapi_middleware(self, app) -> FastAPISecurityMiddleware:
|
247
|
+
"""Create FastAPI security middleware."""
|
248
|
+
return FastAPISecurityMiddleware(app, self.security_config)
|
249
|
+
|
250
|
+
# Utility methods
|
251
|
+
def is_security_enabled(self) -> bool:
|
252
|
+
"""Check if security is enabled."""
|
253
|
+
return self.security_config.auth.enabled or self.security_config.ssl.enabled
|
254
|
+
|
255
|
+
def get_public_paths(self) -> List[str]:
|
256
|
+
"""Get public paths that bypass authentication."""
|
257
|
+
return self.security_config.auth.public_paths
|
258
|
+
|
259
|
+
def get_security_config(self) -> SecurityConfig:
|
260
|
+
"""Get security configuration."""
|
261
|
+
return self.security_config
|
262
|
+
|
263
|
+
|
264
|
+
# Factory function for easy integration
|
265
|
+
def create_security_integration(config: Dict[str, Any]) -> SecurityIntegration:
|
266
|
+
"""
|
267
|
+
Create security integration instance.
|
268
|
+
|
269
|
+
Args:
|
270
|
+
config: Configuration dictionary
|
271
|
+
|
272
|
+
Returns:
|
273
|
+
SecurityIntegration instance
|
274
|
+
|
275
|
+
Raises:
|
276
|
+
RuntimeError: If security integration cannot be created
|
277
|
+
"""
|
278
|
+
try:
|
279
|
+
return SecurityIntegration(config)
|
280
|
+
except ImportError as e:
|
281
|
+
logger.error(f"mcp_security_framework not available: {e}")
|
282
|
+
raise RuntimeError("Security framework is required but not available") from e
|
283
|
+
except Exception as e:
|
284
|
+
logger.error(f"Failed to create security integration: {e}")
|
285
|
+
raise RuntimeError(f"Security integration failed: {e}") from e
|
@@ -0,0 +1,345 @@
|
|
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 (uvicorn, hypercorn, etc.)
|
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 == "uvicorn":
|
49
|
+
return ServerConfigAdapter._convert_to_uvicorn_ssl(ssl_config)
|
50
|
+
elif target_engine == "hypercorn":
|
51
|
+
return ServerConfigAdapter._convert_to_hypercorn_ssl(ssl_config)
|
52
|
+
else:
|
53
|
+
logger.warning(f"No SSL conversion available for engine: {target_engine}")
|
54
|
+
return {}
|
55
|
+
|
56
|
+
@staticmethod
|
57
|
+
def _convert_to_uvicorn_ssl(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
|
58
|
+
"""Convert SSL configuration to uvicorn format."""
|
59
|
+
uvicorn_ssl = {}
|
60
|
+
|
61
|
+
# Map SSL parameters
|
62
|
+
if ssl_config.get("cert_file"):
|
63
|
+
uvicorn_ssl["ssl_certfile"] = ssl_config["cert_file"]
|
64
|
+
if ssl_config.get("key_file"):
|
65
|
+
uvicorn_ssl["ssl_keyfile"] = ssl_config["key_file"]
|
66
|
+
if ssl_config.get("ca_cert"):
|
67
|
+
uvicorn_ssl["ssl_ca_certs"] = ssl_config["ca_cert"]
|
68
|
+
|
69
|
+
# Map verification mode
|
70
|
+
if ssl_config.get("verify_client", False):
|
71
|
+
import ssl
|
72
|
+
uvicorn_ssl["ssl_cert_reqs"] = ssl.CERT_REQUIRED
|
73
|
+
|
74
|
+
logger.debug(f"Converted SSL config to uvicorn: {uvicorn_ssl}")
|
75
|
+
return uvicorn_ssl
|
76
|
+
|
77
|
+
@staticmethod
|
78
|
+
def _convert_to_hypercorn_ssl(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
|
79
|
+
"""Convert SSL configuration to hypercorn format."""
|
80
|
+
hypercorn_ssl = {}
|
81
|
+
|
82
|
+
# Map SSL parameters
|
83
|
+
if ssl_config.get("cert_file"):
|
84
|
+
hypercorn_ssl["certfile"] = ssl_config["cert_file"]
|
85
|
+
if ssl_config.get("key_file"):
|
86
|
+
hypercorn_ssl["keyfile"] = ssl_config["key_file"]
|
87
|
+
if ssl_config.get("ca_cert"):
|
88
|
+
hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
|
89
|
+
|
90
|
+
# Map verification mode
|
91
|
+
if ssl_config.get("verify_client", False):
|
92
|
+
hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
|
93
|
+
|
94
|
+
logger.debug(f"Converted SSL config to hypercorn: {hypercorn_ssl}")
|
95
|
+
return hypercorn_ssl
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
def get_optimal_engine_for_config(config: Dict[str, Any]) -> Optional[str]:
|
99
|
+
"""
|
100
|
+
Determine the optimal server engine for a given configuration.
|
101
|
+
|
102
|
+
Args:
|
103
|
+
config: Server configuration
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
Name of the optimal engine or None if no suitable engine found
|
107
|
+
"""
|
108
|
+
# Check if mTLS is required
|
109
|
+
ssl_config = config.get("ssl", {})
|
110
|
+
if not ssl_config:
|
111
|
+
# Try to get SSL config from security section
|
112
|
+
ssl_config = config.get("security", {}).get("ssl", {})
|
113
|
+
|
114
|
+
# Prefer hypercorn for all SSL/TLS scenarios due to better mTLS support
|
115
|
+
if ssl_config.get("enabled", False):
|
116
|
+
engine = ServerEngineFactory.get_engine("hypercorn")
|
117
|
+
if engine:
|
118
|
+
logger.info("Selected hypercorn for SSL/TLS support (better mTLS capabilities)")
|
119
|
+
return engine.get_name()
|
120
|
+
else:
|
121
|
+
logger.warning("SSL enabled but hypercorn not available")
|
122
|
+
|
123
|
+
# For mTLS client verification, hypercorn is required
|
124
|
+
if ssl_config.get("verify_client", False) or ssl_config.get("client_cert_required", False):
|
125
|
+
engine = ServerEngineFactory.get_engine_with_feature("mtls_client_certs")
|
126
|
+
if engine:
|
127
|
+
logger.info(f"Selected {engine.get_name()} for mTLS support")
|
128
|
+
return engine.get_name()
|
129
|
+
else:
|
130
|
+
logger.warning("mTLS required but no suitable engine available")
|
131
|
+
return None
|
132
|
+
|
133
|
+
# Default to hypercorn for better async support, fallback to uvicorn
|
134
|
+
engine = ServerEngineFactory.get_engine("hypercorn")
|
135
|
+
if engine:
|
136
|
+
logger.info("Selected hypercorn as default engine (better async support)")
|
137
|
+
return engine.get_name()
|
138
|
+
|
139
|
+
engine = ServerEngineFactory.get_engine("uvicorn")
|
140
|
+
if engine:
|
141
|
+
logger.info("Selected uvicorn as fallback engine")
|
142
|
+
return "uvicorn"
|
143
|
+
|
144
|
+
return None
|
145
|
+
|
146
|
+
@staticmethod
|
147
|
+
def validate_engine_compatibility(
|
148
|
+
config: Dict[str, Any],
|
149
|
+
engine_name: str
|
150
|
+
) -> bool:
|
151
|
+
"""
|
152
|
+
Validate if a configuration is compatible with a specific engine.
|
153
|
+
|
154
|
+
Args:
|
155
|
+
config: Server configuration
|
156
|
+
engine_name: Name of the server engine
|
157
|
+
|
158
|
+
Returns:
|
159
|
+
True if compatible, False otherwise
|
160
|
+
"""
|
161
|
+
engine = ServerEngineFactory.get_engine(engine_name)
|
162
|
+
if not engine:
|
163
|
+
logger.error(f"Unknown engine: {engine_name}")
|
164
|
+
return False
|
165
|
+
|
166
|
+
# Check SSL requirements
|
167
|
+
ssl_config = config.get("ssl", {})
|
168
|
+
if not ssl_config:
|
169
|
+
# Try to get SSL config from security section
|
170
|
+
ssl_config = config.get("security", {}).get("ssl", {})
|
171
|
+
|
172
|
+
if ssl_config.get("verify_client", False):
|
173
|
+
if not engine.get_supported_features().get("mtls_client_certs", False):
|
174
|
+
logger.error(f"Engine {engine_name} doesn't support mTLS client certificates")
|
175
|
+
return False
|
176
|
+
|
177
|
+
# Validate engine-specific configuration
|
178
|
+
return engine.validate_config(config)
|
179
|
+
|
180
|
+
@staticmethod
|
181
|
+
def get_engine_capabilities(engine_name: str) -> Dict[str, Any]:
|
182
|
+
"""
|
183
|
+
Get capabilities of a specific server engine.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
engine_name: Name of the server engine
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
Dictionary of engine capabilities
|
190
|
+
"""
|
191
|
+
engine = ServerEngineFactory.get_engine(engine_name)
|
192
|
+
if not engine:
|
193
|
+
return {}
|
194
|
+
|
195
|
+
return {
|
196
|
+
"name": engine.get_name(),
|
197
|
+
"features": engine.get_supported_features(),
|
198
|
+
"config_schema": engine.get_config_schema()
|
199
|
+
}
|
200
|
+
|
201
|
+
|
202
|
+
class UnifiedServerRunner:
|
203
|
+
"""
|
204
|
+
Unified server runner that abstracts the choice of server engine.
|
205
|
+
|
206
|
+
This class provides a unified interface for running servers regardless
|
207
|
+
of the underlying engine, automatically selecting the best engine
|
208
|
+
for the given configuration.
|
209
|
+
"""
|
210
|
+
|
211
|
+
def __init__(self, default_engine: str = "uvicorn"):
|
212
|
+
"""
|
213
|
+
Initialize the unified server runner.
|
214
|
+
|
215
|
+
Args:
|
216
|
+
default_engine: Default engine to use if no specific requirements
|
217
|
+
"""
|
218
|
+
self.default_engine = default_engine
|
219
|
+
self.available_engines = ServerEngineFactory.get_available_engines()
|
220
|
+
|
221
|
+
logger.info(f"Available engines: {list(self.available_engines.keys())}")
|
222
|
+
logger.info(f"Default engine: {default_engine}")
|
223
|
+
|
224
|
+
def run_server(
|
225
|
+
self,
|
226
|
+
app: Any,
|
227
|
+
config: Dict[str, Any],
|
228
|
+
engine_name: Optional[str] = None
|
229
|
+
) -> None:
|
230
|
+
"""
|
231
|
+
Run server with the specified or optimal engine.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
app: ASGI application
|
235
|
+
config: Server configuration
|
236
|
+
engine_name: Specific engine to use (optional)
|
237
|
+
"""
|
238
|
+
# Determine which engine to use
|
239
|
+
if engine_name:
|
240
|
+
selected_engine = engine_name
|
241
|
+
logger.info(f"Using specified engine: {selected_engine}")
|
242
|
+
else:
|
243
|
+
selected_engine = ServerConfigAdapter.get_optimal_engine_for_config(config)
|
244
|
+
if not selected_engine:
|
245
|
+
selected_engine = self.default_engine
|
246
|
+
logger.info(f"Using default engine: {selected_engine}")
|
247
|
+
|
248
|
+
# Validate compatibility
|
249
|
+
if not ServerConfigAdapter.validate_engine_compatibility(config, selected_engine):
|
250
|
+
raise ValueError(f"Configuration not compatible with engine: {selected_engine}")
|
251
|
+
|
252
|
+
# Get engine instance
|
253
|
+
engine = ServerEngineFactory.get_engine(selected_engine)
|
254
|
+
if not engine:
|
255
|
+
raise ValueError(f"Engine not available: {selected_engine}")
|
256
|
+
|
257
|
+
# Convert configuration if needed
|
258
|
+
converted_config = self._prepare_config_for_engine(config, selected_engine)
|
259
|
+
|
260
|
+
# Run server
|
261
|
+
logger.info(f"Starting server with {selected_engine} engine")
|
262
|
+
engine.run_server(app, converted_config)
|
263
|
+
|
264
|
+
def _prepare_config_for_engine(
|
265
|
+
self,
|
266
|
+
config: Dict[str, Any],
|
267
|
+
engine_name: str
|
268
|
+
) -> Dict[str, Any]:
|
269
|
+
logger.info(f"🔍 Debug: _prepare_config_for_engine called with config keys: {list(config.keys())}")
|
270
|
+
logger.info(f"🔍 Debug: SSL config in input: {config.get('ssl', 'NOT_FOUND')}")
|
271
|
+
"""
|
272
|
+
Prepare configuration for a specific engine.
|
273
|
+
|
274
|
+
Args:
|
275
|
+
config: Original configuration
|
276
|
+
engine_name: Target engine name
|
277
|
+
|
278
|
+
Returns:
|
279
|
+
Engine-specific configuration
|
280
|
+
"""
|
281
|
+
# Start with basic config
|
282
|
+
engine_config = {
|
283
|
+
"host": config.get("host", "127.0.0.1"),
|
284
|
+
"port": config.get("port", 8000),
|
285
|
+
"log_level": config.get("log_level", "info"),
|
286
|
+
"reload": config.get("reload", False)
|
287
|
+
}
|
288
|
+
|
289
|
+
# Add SSL configuration if present
|
290
|
+
# First check for direct SSL parameters (from app_factory.py)
|
291
|
+
if "certfile" in config or "keyfile" in config or "ca_certs" in config or "verify_mode" in config:
|
292
|
+
logger.info(f"🔍 DEBUG: Direct SSL parameters found in config")
|
293
|
+
if "certfile" in config:
|
294
|
+
engine_config["certfile"] = config["certfile"]
|
295
|
+
if "keyfile" in config:
|
296
|
+
engine_config["keyfile"] = config["keyfile"]
|
297
|
+
if "ca_certs" in config:
|
298
|
+
engine_config["ca_certs"] = config["ca_certs"]
|
299
|
+
if "verify_mode" in config:
|
300
|
+
engine_config["verify_mode"] = config["verify_mode"]
|
301
|
+
else:
|
302
|
+
# Try to get SSL config from ssl section
|
303
|
+
ssl_config = config.get("ssl", {})
|
304
|
+
if not ssl_config:
|
305
|
+
# Try to get SSL config from security section
|
306
|
+
ssl_config = config.get("security", {}).get("ssl", {})
|
307
|
+
|
308
|
+
if ssl_config:
|
309
|
+
converted_ssl = ServerConfigAdapter.convert_ssl_config_for_engine(
|
310
|
+
ssl_config, engine_name
|
311
|
+
)
|
312
|
+
engine_config.update(converted_ssl)
|
313
|
+
|
314
|
+
# Add engine-specific configuration
|
315
|
+
if "workers" in config:
|
316
|
+
engine_config["workers"] = config["workers"]
|
317
|
+
|
318
|
+
return engine_config
|
319
|
+
|
320
|
+
def get_engine_info(self, engine_name: str) -> Dict[str, Any]:
|
321
|
+
"""
|
322
|
+
Get information about a specific engine.
|
323
|
+
|
324
|
+
Args:
|
325
|
+
engine_name: Name of the engine
|
326
|
+
|
327
|
+
Returns:
|
328
|
+
Engine information dictionary
|
329
|
+
"""
|
330
|
+
return ServerConfigAdapter.get_engine_capabilities(engine_name)
|
331
|
+
|
332
|
+
def list_available_engines(self) -> Dict[str, Dict[str, Any]]:
|
333
|
+
"""
|
334
|
+
List all available engines with their capabilities.
|
335
|
+
|
336
|
+
Returns:
|
337
|
+
Dictionary mapping engine names to their capabilities
|
338
|
+
"""
|
339
|
+
engines_info = {}
|
340
|
+
for name, engine in self.available_engines.items():
|
341
|
+
engines_info[name] = {
|
342
|
+
"features": engine.get_supported_features(),
|
343
|
+
"config_schema": engine.get_config_schema()
|
344
|
+
}
|
345
|
+
return engines_info
|