mcp-proxy-adapter 2.0.1__py3-none-any.whl ā 6.9.50__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.
Potentially problematic release.
This version of mcp-proxy-adapter might be problematic. Click here for more details.
- mcp_proxy_adapter/__init__.py +47 -0
- mcp_proxy_adapter/__main__.py +13 -0
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +66 -0
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +400 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_context.py +356 -0
- mcp_proxy_adapter/api/core/registration_manager.py +307 -0
- mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +181 -0
- mcp_proxy_adapter/api/middleware/__init__.py +21 -0
- mcp_proxy_adapter/api/middleware/base.py +54 -0
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
- mcp_proxy_adapter/api/middleware/factory.py +147 -0
- mcp_proxy_adapter/api/middleware/logging.py +31 -0
- mcp_proxy_adapter/api/middleware/performance.py +51 -0
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +270 -0
- mcp_proxy_adapter/api/tool_integration.py +131 -0
- mcp_proxy_adapter/api/tools.py +163 -0
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +105 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +94 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +132 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +338 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
- mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
- mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
- mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
- mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
- mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
- mcp_proxy_adapter/client/proxy.py +123 -0
- mcp_proxy_adapter/commands/__init__.py +66 -0
- mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
- mcp_proxy_adapter/commands/base.py +389 -0
- mcp_proxy_adapter/commands/builtin_commands.py +30 -0
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +97 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
- mcp_proxy_adapter/commands/command_registry.py +298 -0
- mcp_proxy_adapter/commands/config_command.py +102 -0
- mcp_proxy_adapter/commands/dependency_container.py +40 -0
- mcp_proxy_adapter/commands/dependency_manager.py +143 -0
- mcp_proxy_adapter/commands/echo_command.py +48 -0
- mcp_proxy_adapter/commands/health_command.py +142 -0
- mcp_proxy_adapter/commands/help_command.py +175 -0
- mcp_proxy_adapter/commands/hooks.py +172 -0
- mcp_proxy_adapter/commands/key_management_command.py +484 -0
- mcp_proxy_adapter/commands/load_command.py +123 -0
- mcp_proxy_adapter/commands/plugins_command.py +246 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +76 -0
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +136 -0
- mcp_proxy_adapter/commands/result.py +157 -0
- mcp_proxy_adapter/commands/role_test_command.py +99 -0
- mcp_proxy_adapter/commands/roles_management_command.py +502 -0
- mcp_proxy_adapter/commands/security_command.py +472 -0
- mcp_proxy_adapter/commands/settings_command.py +113 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
- mcp_proxy_adapter/commands/token_management_command.py +500 -0
- mcp_proxy_adapter/commands/transport_management_command.py +129 -0
- mcp_proxy_adapter/commands/unload_command.py +92 -0
- mcp_proxy_adapter/config.py +32 -0
- mcp_proxy_adapter/core/__init__.py +8 -0
- mcp_proxy_adapter/core/app_factory.py +560 -0
- mcp_proxy_adapter/core/app_runner.py +318 -0
- mcp_proxy_adapter/core/auth_validator.py +508 -0
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +481 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
- mcp_proxy_adapter/core/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/client.py +608 -0
- mcp_proxy_adapter/core/client_manager.py +271 -0
- mcp_proxy_adapter/core/client_security.py +411 -0
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +237 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +204 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +476 -0
- mcp_proxy_adapter/core/config_converter.py +252 -0
- mcp_proxy_adapter/core/config_validator.py +211 -0
- mcp_proxy_adapter/core/crl_utils.py +362 -0
- mcp_proxy_adapter/core/errors.py +276 -0
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +250 -0
- mcp_proxy_adapter/core/mtls_asgi.py +140 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/mtls_proxy.py +229 -0
- mcp_proxy_adapter/core/mtls_server.py +154 -0
- mcp_proxy_adapter/core/protocol_manager.py +232 -0
- mcp_proxy_adapter/core/proxy/__init__.py +19 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +184 -0
- mcp_proxy_adapter/core/proxy_registration.py +80 -0
- mcp_proxy_adapter/core/role_utils.py +103 -0
- mcp_proxy_adapter/core/security_adapter.py +343 -0
- mcp_proxy_adapter/core/security_factory.py +96 -0
- mcp_proxy_adapter/core/security_integration.py +342 -0
- mcp_proxy_adapter/core/server_adapter.py +251 -0
- mcp_proxy_adapter/core/server_engine.py +217 -0
- mcp_proxy_adapter/core/settings.py +260 -0
- mcp_proxy_adapter/core/signal_handler.py +107 -0
- mcp_proxy_adapter/core/ssl_utils.py +161 -0
- mcp_proxy_adapter/core/transport_manager.py +153 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
- mcp_proxy_adapter/core/utils.py +101 -0
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +219 -0
- mcp_proxy_adapter/core/validation/file_validator.py +131 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +205 -0
- mcp_proxy_adapter/core/validation/security_validator.py +140 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +58 -0
- mcp_proxy_adapter/examples/__init__.py +16 -0
- 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 +52 -0
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
- mcp_proxy_adapter/examples/check_config.py +413 -0
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/config_builder.py +234 -0
- mcp_proxy_adapter/examples/config_cli.py +282 -0
- mcp_proxy_adapter/examples/create_test_configs.py +174 -0
- mcp_proxy_adapter/examples/debug_request_state.py +130 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
- mcp_proxy_adapter/examples/demo_client.py +287 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
- mcp_proxy_adapter/examples/full_application/main.py +311 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -0
- mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
- mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
- mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
- mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
- mcp_proxy_adapter/examples/generate_config.py +502 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +208 -0
- mcp_proxy_adapter/examples/run_example.py +77 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +72 -0
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
- mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
- mcp_proxy_adapter/examples/test_config.py +205 -0
- mcp_proxy_adapter/examples/test_config_builder.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +308 -0
- mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
- mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
- mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
- mcp_proxy_adapter/examples/universal_client.py +674 -0
- mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +311 -0
- mcp_proxy_adapter/openapi.py +375 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/schemas/roles.json +37 -0
- mcp_proxy_adapter/schemas/roles_schema.json +162 -0
- mcp_proxy_adapter/version.py +5 -0
- mcp_proxy_adapter-6.9.50.dist-info/METADATA +1088 -0
- mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
- {mcp_proxy_adapter-2.0.1.dist-info ā mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
- mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
- mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
- adapters/__init__.py +0 -16
- analyzers/__init__.py +0 -14
- analyzers/docstring_analyzer.py +0 -199
- analyzers/type_analyzer.py +0 -151
- cli/__init__.py +0 -12
- cli/__main__.py +0 -79
- cli/command_runner.py +0 -233
- dispatchers/__init__.py +0 -14
- dispatchers/base_dispatcher.py +0 -85
- dispatchers/json_rpc_dispatcher.py +0 -198
- generators/__init__.py +0 -14
- generators/endpoint_generator.py +0 -172
- generators/openapi_generator.py +0 -254
- generators/rest_api_generator.py +0 -207
- mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
- mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
- mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
- mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
- openapi_schema/__init__.py +0 -38
- openapi_schema/command_registry.py +0 -312
- openapi_schema/rest_schema.py +0 -510
- openapi_schema/rpc_generator.py +0 -307
- openapi_schema/rpc_schema.py +0 -416
- validators/__init__.py +0 -14
- validators/base_validator.py +0 -23
- validators/docstring_validator.py +0 -75
- validators/metadata_validator.py +0 -76
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Application Runner for MCP Proxy Adapter
|
|
6
|
+
|
|
7
|
+
This module provides the ApplicationRunner class for running applications
|
|
8
|
+
with full configuration validation and error handling.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import socket
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Dict, List
|
|
15
|
+
|
|
16
|
+
from fastapi import FastAPI
|
|
17
|
+
|
|
18
|
+
from mcp_proxy_adapter.core.logging import get_logger
|
|
19
|
+
from mcp_proxy_adapter.core.signal_handler import (
|
|
20
|
+
setup_signal_handling,
|
|
21
|
+
is_shutdown_requested,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = get_logger("app_runner")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ApplicationRunner:
|
|
28
|
+
"""
|
|
29
|
+
Class for running applications with configuration validation.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, app: FastAPI, config: Dict[str, Any]):
|
|
33
|
+
"""
|
|
34
|
+
Initialize ApplicationRunner.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
app: FastAPI application instance
|
|
38
|
+
config: Application configuration dictionary
|
|
39
|
+
"""
|
|
40
|
+
self.app = app
|
|
41
|
+
self.config = config
|
|
42
|
+
self.errors: List[str] = []
|
|
43
|
+
|
|
44
|
+
def validate_configuration(self) -> List[str]:
|
|
45
|
+
"""
|
|
46
|
+
Validates configuration and returns list of errors.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List of validation error messages
|
|
50
|
+
"""
|
|
51
|
+
self.errors = []
|
|
52
|
+
|
|
53
|
+
# Validate server configuration
|
|
54
|
+
self._validate_server_config()
|
|
55
|
+
|
|
56
|
+
# Validate SSL configuration
|
|
57
|
+
self._validate_ssl_config()
|
|
58
|
+
|
|
59
|
+
# Validate security configuration
|
|
60
|
+
self._validate_security_config()
|
|
61
|
+
|
|
62
|
+
# Validate file paths
|
|
63
|
+
self._validate_file_paths()
|
|
64
|
+
|
|
65
|
+
# Validate port availability
|
|
66
|
+
self._validate_port_availability()
|
|
67
|
+
|
|
68
|
+
# Validate configuration compatibility
|
|
69
|
+
self._validate_compatibility()
|
|
70
|
+
|
|
71
|
+
return self.errors
|
|
72
|
+
|
|
73
|
+
def _validate_server_config(self) -> None:
|
|
74
|
+
"""Validate server configuration."""
|
|
75
|
+
server_config = self.config.get("server", {})
|
|
76
|
+
|
|
77
|
+
if not server_config:
|
|
78
|
+
self.errors.append("Server configuration is missing")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
host = server_config.get("host")
|
|
82
|
+
port = server_config.get("port")
|
|
83
|
+
|
|
84
|
+
if not host:
|
|
85
|
+
self.errors.append("Server host is not specified")
|
|
86
|
+
|
|
87
|
+
if not port:
|
|
88
|
+
self.errors.append("Server port is not specified")
|
|
89
|
+
elif not isinstance(port, int) or port < 1 or port > 65535:
|
|
90
|
+
self.errors.append(f"Invalid server port: {port}")
|
|
91
|
+
|
|
92
|
+
def _validate_ssl_config(self) -> None:
|
|
93
|
+
"""Validate SSL configuration based on protocol."""
|
|
94
|
+
server_config = self.config.get("server", {})
|
|
95
|
+
protocol = server_config.get("protocol", "http")
|
|
96
|
+
|
|
97
|
+
# SSL is automatically enabled for https and mtls protocols
|
|
98
|
+
if protocol in ("https", "mtls"):
|
|
99
|
+
ssl_config = self.config.get("ssl", {})
|
|
100
|
+
cert_file = ssl_config.get("cert_file")
|
|
101
|
+
key_file = ssl_config.get("key_file")
|
|
102
|
+
|
|
103
|
+
# Only validate if certificates are specified
|
|
104
|
+
if cert_file and key_file:
|
|
105
|
+
if not Path(cert_file).exists():
|
|
106
|
+
self.errors.append(f"Certificate file not found: {cert_file}")
|
|
107
|
+
|
|
108
|
+
if not Path(key_file).exists():
|
|
109
|
+
self.errors.append(f"Private key file not found: {key_file}")
|
|
110
|
+
|
|
111
|
+
# Validate mTLS configuration
|
|
112
|
+
if protocol == "mtls" or ssl_config.get("verify_client", False):
|
|
113
|
+
ca_cert = ssl_config.get("ca_cert")
|
|
114
|
+
if not ca_cert:
|
|
115
|
+
self.errors.append(
|
|
116
|
+
f"{protocol.upper()} requires CA certificate to be specified"
|
|
117
|
+
)
|
|
118
|
+
elif not Path(ca_cert).exists():
|
|
119
|
+
self.errors.append(f"CA certificate file not found: {ca_cert}")
|
|
120
|
+
|
|
121
|
+
def _validate_security_config(self) -> None:
|
|
122
|
+
"""Validate security configuration."""
|
|
123
|
+
security_config = self.config.get("security", {})
|
|
124
|
+
|
|
125
|
+
if security_config.get("enabled", False):
|
|
126
|
+
auth_config = security_config.get("auth", {})
|
|
127
|
+
permissions_config = security_config.get("permissions", {})
|
|
128
|
+
|
|
129
|
+
# Validate authentication configuration
|
|
130
|
+
if auth_config.get("enabled", False):
|
|
131
|
+
methods = auth_config.get("methods", [])
|
|
132
|
+
if not methods:
|
|
133
|
+
self.errors.append(
|
|
134
|
+
"Authentication enabled but no methods specified"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Validate API key configuration
|
|
138
|
+
if "api_key" in methods:
|
|
139
|
+
# Check if roles file exists for API key auth
|
|
140
|
+
if permissions_config.get("enabled", False):
|
|
141
|
+
roles_file = permissions_config.get("roles_file")
|
|
142
|
+
if not roles_file:
|
|
143
|
+
self.errors.append(
|
|
144
|
+
"Permissions enabled but roles file not specified"
|
|
145
|
+
)
|
|
146
|
+
elif not Path(roles_file).exists():
|
|
147
|
+
self.errors.append(f"Roles file not found: {roles_file}")
|
|
148
|
+
|
|
149
|
+
# Validate certificate configuration
|
|
150
|
+
if "certificate" in methods:
|
|
151
|
+
server_config = self.config.get("server", {})
|
|
152
|
+
protocol = server_config.get("protocol", "http")
|
|
153
|
+
if protocol not in ("https", "mtls"):
|
|
154
|
+
self.errors.append(
|
|
155
|
+
"Certificate authentication requires https or mtls protocol"
|
|
156
|
+
)
|
|
157
|
+
ssl_config = self.config.get("ssl", {})
|
|
158
|
+
if not ssl_config.get("verify_client", False):
|
|
159
|
+
self.errors.append(
|
|
160
|
+
"Certificate authentication requires client verification to be enabled"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def _validate_file_paths(self) -> None:
|
|
164
|
+
"""Validate all file paths in configuration."""
|
|
165
|
+
# Check SSL certificate files based on protocol
|
|
166
|
+
server_config = self.config.get("server", {})
|
|
167
|
+
protocol = server_config.get("protocol", "http")
|
|
168
|
+
|
|
169
|
+
if protocol in ("https", "mtls"):
|
|
170
|
+
ssl_config = self.config.get("ssl", {})
|
|
171
|
+
cert_file = ssl_config.get("cert_file")
|
|
172
|
+
key_file = ssl_config.get("key_file")
|
|
173
|
+
ca_cert = ssl_config.get("ca_cert")
|
|
174
|
+
|
|
175
|
+
# Only validate if certificates are specified
|
|
176
|
+
if cert_file and not Path(cert_file).is_file():
|
|
177
|
+
self.errors.append(
|
|
178
|
+
f"Certificate file is not a regular file: {cert_file}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if key_file and not Path(key_file).is_file():
|
|
182
|
+
self.errors.append(
|
|
183
|
+
f"Private key file is not a regular file: {key_file}"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if ca_cert and not Path(ca_cert).is_file():
|
|
187
|
+
self.errors.append(
|
|
188
|
+
f"CA certificate file is not a regular file: {ca_cert}"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Check roles file
|
|
192
|
+
security_config = self.config.get("security", {})
|
|
193
|
+
permissions_config = security_config.get("permissions", {})
|
|
194
|
+
if permissions_config.get("enabled", False):
|
|
195
|
+
roles_file = permissions_config.get("roles_file")
|
|
196
|
+
if roles_file and not Path(roles_file).is_file():
|
|
197
|
+
self.errors.append(f"Roles file is not a regular file: {roles_file}")
|
|
198
|
+
|
|
199
|
+
def _validate_port_availability(self) -> None:
|
|
200
|
+
"""Validate that the configured port is available."""
|
|
201
|
+
server_config = self.config.get("server", {})
|
|
202
|
+
port = server_config.get("port")
|
|
203
|
+
|
|
204
|
+
if port:
|
|
205
|
+
try:
|
|
206
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
207
|
+
s.bind(("127.0.0.1", port))
|
|
208
|
+
except OSError:
|
|
209
|
+
self.errors.append(f"Port {port} is already in use")
|
|
210
|
+
|
|
211
|
+
def _validate_compatibility(self) -> None:
|
|
212
|
+
"""Validate configuration compatibility."""
|
|
213
|
+
server_config = self.config.get("server", {})
|
|
214
|
+
protocol = server_config.get("protocol", "http")
|
|
215
|
+
security_config = self.config.get("security", {})
|
|
216
|
+
protocols_config = self.config.get("protocols", {})
|
|
217
|
+
|
|
218
|
+
# Check protocol compatibility
|
|
219
|
+
if protocol in ("https", "mtls"):
|
|
220
|
+
allowed_protocols = protocols_config.get("allowed_protocols", [])
|
|
221
|
+
if allowed_protocols and protocol not in allowed_protocols:
|
|
222
|
+
self.errors.append(
|
|
223
|
+
f"Protocol {protocol} is not in allowed protocols: {allowed_protocols}"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Check security and protocol compatibility
|
|
227
|
+
if security_config.get("enabled", False):
|
|
228
|
+
auth_config = security_config.get("auth", {})
|
|
229
|
+
if auth_config.get("enabled", False):
|
|
230
|
+
methods = auth_config.get("methods", [])
|
|
231
|
+
if "certificate" in methods and protocol not in ("https", "mtls"):
|
|
232
|
+
self.errors.append(
|
|
233
|
+
"Certificate authentication requires https or mtls protocol"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def setup_hooks(self) -> None:
|
|
237
|
+
"""
|
|
238
|
+
Setup application hooks.
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
# Add startup event
|
|
242
|
+
@self.app.on_event("startup")
|
|
243
|
+
def startup():
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
# Add shutdown event
|
|
247
|
+
@self.app.on_event("shutdown")
|
|
248
|
+
def shutdown():
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
def run(self) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Run application with full validation.
|
|
254
|
+
"""
|
|
255
|
+
# Validate configuration
|
|
256
|
+
errors = self.validate_configuration()
|
|
257
|
+
|
|
258
|
+
if errors:
|
|
259
|
+
print("ERROR: Configuration validation failed:", file=sys.stderr)
|
|
260
|
+
for error in errors:
|
|
261
|
+
print(f" - {error}", file=sys.stderr)
|
|
262
|
+
sys.exit(1)
|
|
263
|
+
|
|
264
|
+
# Setup signal handling for graceful shutdown
|
|
265
|
+
def shutdown_callback():
|
|
266
|
+
"""Callback for graceful shutdown."""
|
|
267
|
+
logger.info("Graceful shutdown requested")
|
|
268
|
+
|
|
269
|
+
setup_signal_handling(shutdown_callback)
|
|
270
|
+
print("š§ Signal handling configured for graceful shutdown")
|
|
271
|
+
|
|
272
|
+
# Setup hooks
|
|
273
|
+
self.setup_hooks()
|
|
274
|
+
|
|
275
|
+
# Get server configuration
|
|
276
|
+
server_config = self.config.get("server", {})
|
|
277
|
+
host = server_config.get("host", "127.0.0.1")
|
|
278
|
+
port = server_config.get("port", 8000)
|
|
279
|
+
|
|
280
|
+
# Prepare server configuration for hypercorn
|
|
281
|
+
server_kwargs = {"host": host, "port": port, "log_level": "info"}
|
|
282
|
+
|
|
283
|
+
# Add SSL configuration based on protocol
|
|
284
|
+
protocol = server_config.get("protocol", "http")
|
|
285
|
+
if protocol in ("https", "mtls"):
|
|
286
|
+
ssl_config = self.config.get("ssl", {})
|
|
287
|
+
cert_file = ssl_config.get("cert_file")
|
|
288
|
+
key_file = ssl_config.get("key_file")
|
|
289
|
+
|
|
290
|
+
# Only add SSL config if certificates are specified
|
|
291
|
+
if cert_file and key_file:
|
|
292
|
+
server_kwargs["certfile"] = cert_file
|
|
293
|
+
server_kwargs["keyfile"] = key_file
|
|
294
|
+
|
|
295
|
+
# Add mTLS configuration
|
|
296
|
+
if protocol == "mtls" or ssl_config.get("verify_client", False):
|
|
297
|
+
ca_cert = ssl_config.get("ca_cert")
|
|
298
|
+
if ca_cert:
|
|
299
|
+
server_kwargs["ca_certs"] = ca_cert
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
import hypercorn.asyncio
|
|
303
|
+
import asyncio
|
|
304
|
+
|
|
305
|
+
print(f"š Starting server on {host}:{port}")
|
|
306
|
+
print("š Use Ctrl+C or send SIGTERM for graceful shutdown")
|
|
307
|
+
print("=" * 60)
|
|
308
|
+
|
|
309
|
+
# Run with hypercorn
|
|
310
|
+
asyncio.run(hypercorn.asyncio.serve(self.app, **server_kwargs))
|
|
311
|
+
|
|
312
|
+
except KeyboardInterrupt:
|
|
313
|
+
print("\nš Server stopped by user (Ctrl+C)")
|
|
314
|
+
if is_shutdown_requested():
|
|
315
|
+
print("ā
Graceful shutdown completed")
|
|
316
|
+
except Exception as e:
|
|
317
|
+
print(f"\nā Failed to start server: {e}", file=sys.stderr)
|
|
318
|
+
sys.exit(1)
|