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,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Main configuration validator for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, List, Any, Optional
|
|
12
|
+
|
|
13
|
+
from .file_validator import FileValidator
|
|
14
|
+
from .security_validator import SecurityValidator
|
|
15
|
+
from .protocol_validator import ProtocolValidator
|
|
16
|
+
from .validation_result import ValidationResult
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ConfigValidator:
|
|
22
|
+
"""
|
|
23
|
+
Comprehensive configuration validator for MCP Proxy Adapter.
|
|
24
|
+
|
|
25
|
+
Validates:
|
|
26
|
+
- Required sections and keys
|
|
27
|
+
- File existence for referenced files
|
|
28
|
+
- Feature flag dependencies
|
|
29
|
+
- Protocol-specific requirements
|
|
30
|
+
- Security configuration consistency
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, config_path: Optional[str] = None):
|
|
34
|
+
"""
|
|
35
|
+
Initialize configuration validator.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
config_path: Path to configuration file (optional)
|
|
39
|
+
"""
|
|
40
|
+
self.config_path = config_path
|
|
41
|
+
self.config_data: Dict[str, Any] = {}
|
|
42
|
+
self.validation_results: List[ValidationResult] = []
|
|
43
|
+
|
|
44
|
+
def load_config(self, config_path: Optional[str] = None) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Load configuration from file.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
config_path: Path to configuration file
|
|
50
|
+
"""
|
|
51
|
+
if config_path:
|
|
52
|
+
self.config_path = config_path
|
|
53
|
+
|
|
54
|
+
if not self.config_path:
|
|
55
|
+
raise ValueError("No configuration path provided")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
59
|
+
self.config_data = json.load(f)
|
|
60
|
+
logger.info(f"Configuration loaded from {self.config_path}")
|
|
61
|
+
except FileNotFoundError:
|
|
62
|
+
raise FileNotFoundError(f"Configuration file not found: {self.config_path}")
|
|
63
|
+
except json.JSONDecodeError as e:
|
|
64
|
+
raise ValueError(f"Invalid JSON in configuration file: {e}")
|
|
65
|
+
except Exception as e:
|
|
66
|
+
raise RuntimeError(f"Error loading configuration: {e}")
|
|
67
|
+
|
|
68
|
+
def validate_config(self, config_data: Optional[Dict[str, Any]] = None) -> List[ValidationResult]:
|
|
69
|
+
"""
|
|
70
|
+
Validate configuration data.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
config_data: Configuration data to validate (optional)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List of validation results
|
|
77
|
+
"""
|
|
78
|
+
if config_data is not None:
|
|
79
|
+
self.config_data = config_data
|
|
80
|
+
|
|
81
|
+
if not self.config_data:
|
|
82
|
+
raise ValueError("No configuration data to validate")
|
|
83
|
+
|
|
84
|
+
self.validation_results = []
|
|
85
|
+
|
|
86
|
+
# Initialize validators
|
|
87
|
+
file_validator = FileValidator(self.config_data)
|
|
88
|
+
security_validator = SecurityValidator(self.config_data)
|
|
89
|
+
protocol_validator = ProtocolValidator(self.config_data)
|
|
90
|
+
|
|
91
|
+
# Run all validations
|
|
92
|
+
protocol_validator._validate_server_section()
|
|
93
|
+
protocol_validator._validate_feature_flags()
|
|
94
|
+
protocol = self.config_data.get("server", {}).get("protocol", "http")
|
|
95
|
+
if protocol == "https":
|
|
96
|
+
protocol_validator._validate_https_requirements()
|
|
97
|
+
elif protocol == "mtls":
|
|
98
|
+
protocol_validator._validate_mtls_requirements()
|
|
99
|
+
self.validation_results.extend(protocol_validator.validation_results)
|
|
100
|
+
|
|
101
|
+
self.validation_results.extend(file_validator.validate_file_existence())
|
|
102
|
+
self.validation_results.extend(security_validator.validate_security_consistency())
|
|
103
|
+
self.validation_results.extend(security_validator.validate_ssl_configuration())
|
|
104
|
+
self.validation_results.extend(security_validator.validate_roles_configuration())
|
|
105
|
+
self.validation_results.extend(security_validator.validate_proxy_registration())
|
|
106
|
+
|
|
107
|
+
# Additional validations
|
|
108
|
+
self._validate_unknown_fields()
|
|
109
|
+
self._validate_uuid_format()
|
|
110
|
+
|
|
111
|
+
return self.validation_results
|
|
112
|
+
|
|
113
|
+
def validate_all(self, config_data: Optional[Dict[str, Any]] = None) -> List[ValidationResult]:
|
|
114
|
+
"""
|
|
115
|
+
Validate all aspects of the configuration.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
config_data: Configuration data to validate (optional)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
List of validation results
|
|
122
|
+
"""
|
|
123
|
+
return self.validate_config(config_data)
|
|
124
|
+
|
|
125
|
+
def _validate_unknown_fields(self) -> None:
|
|
126
|
+
"""Validate for unknown configuration fields."""
|
|
127
|
+
known_sections = {
|
|
128
|
+
"server", "protocols", "security", "ssl", "auth", "roles",
|
|
129
|
+
"logging", "commands", "proxy_registration", "transport"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for section in self.config_data.keys():
|
|
133
|
+
if section not in known_sections:
|
|
134
|
+
self.validation_results.append(ValidationResult(
|
|
135
|
+
level="warning",
|
|
136
|
+
message=f"Unknown configuration section: {section}",
|
|
137
|
+
section=section,
|
|
138
|
+
suggestion="Check if this section is needed or if it's a typo"
|
|
139
|
+
))
|
|
140
|
+
|
|
141
|
+
def _validate_uuid_format(self) -> None:
|
|
142
|
+
"""Validate UUID format in configuration."""
|
|
143
|
+
uuid_fields = ["server.server_id", "proxy_registration.server_id"]
|
|
144
|
+
|
|
145
|
+
for field in uuid_fields:
|
|
146
|
+
value = self._get_nested_value_safe(field)
|
|
147
|
+
if value and not self._is_valid_uuid4(str(value)):
|
|
148
|
+
self.validation_results.append(ValidationResult(
|
|
149
|
+
level="warning",
|
|
150
|
+
message=f"Invalid UUID format in {field}: {value}",
|
|
151
|
+
section=field.split(".")[0],
|
|
152
|
+
key=field.split(".")[1],
|
|
153
|
+
suggestion="Use a valid UUID4 format"
|
|
154
|
+
))
|
|
155
|
+
|
|
156
|
+
def _is_valid_uuid4(self, uuid_str: str) -> bool:
|
|
157
|
+
"""Check if string is a valid UUID4."""
|
|
158
|
+
import re
|
|
159
|
+
uuid_pattern = r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
160
|
+
return bool(re.match(uuid_pattern, uuid_str, re.IGNORECASE))
|
|
161
|
+
|
|
162
|
+
def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
|
|
163
|
+
"""Safely get a nested value from configuration."""
|
|
164
|
+
keys = key.split('.')
|
|
165
|
+
value = self.config_data
|
|
166
|
+
|
|
167
|
+
for k in keys:
|
|
168
|
+
if isinstance(value, dict) and k in value:
|
|
169
|
+
value = value[k]
|
|
170
|
+
else:
|
|
171
|
+
return default
|
|
172
|
+
|
|
173
|
+
return value
|
|
174
|
+
|
|
175
|
+
def get_validation_summary(self) -> Dict[str, Any]:
|
|
176
|
+
"""
|
|
177
|
+
Get a summary of validation results.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Dictionary with validation summary
|
|
181
|
+
"""
|
|
182
|
+
error_count = sum(1 for r in self.validation_results if r.level == "error")
|
|
183
|
+
warning_count = sum(1 for r in self.validation_results if r.level == "warning")
|
|
184
|
+
info_count = sum(1 for r in self.validation_results if r.level == "info")
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
"total_issues": len(self.validation_results),
|
|
188
|
+
"errors": error_count,
|
|
189
|
+
"warnings": warning_count,
|
|
190
|
+
"info": info_count,
|
|
191
|
+
"is_valid": error_count == 0
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def print_validation_report(self) -> None:
|
|
195
|
+
"""Print a formatted validation report."""
|
|
196
|
+
summary = self.get_validation_summary()
|
|
197
|
+
|
|
198
|
+
print(f"\\n📋 Configuration Validation Report")
|
|
199
|
+
print(f"{'=' * 40}")
|
|
200
|
+
print(f"Total issues: {summary['total_issues']}")
|
|
201
|
+
print(f"Errors: {summary['errors']}")
|
|
202
|
+
print(f"Warnings: {summary['warnings']}")
|
|
203
|
+
print(f"Info: {summary['info']}")
|
|
204
|
+
print(f"Valid: {'✅ Yes' if summary['is_valid'] else '❌ No'}")
|
|
205
|
+
|
|
206
|
+
if self.validation_results:
|
|
207
|
+
print(f"\\n📝 Issues:")
|
|
208
|
+
for i, result in enumerate(self.validation_results, 1):
|
|
209
|
+
level_icon = {"error": "❌", "warning": "⚠️", "info": "ℹ️"}[result.level]
|
|
210
|
+
print(f"{i:2d}. {level_icon} {result.message}")
|
|
211
|
+
if result.section:
|
|
212
|
+
print(f" Section: {result.section}")
|
|
213
|
+
if result.key:
|
|
214
|
+
print(f" Key: {result.key}")
|
|
215
|
+
if result.suggestion:
|
|
216
|
+
print(f" Suggestion: {result.suggestion}")
|
|
217
|
+
print()
|
|
218
|
+
|
|
219
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
File validation utilities for MCP Proxy Adapter configuration validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import ssl
|
|
10
|
+
from typing import Dict, Any, List
|
|
11
|
+
|
|
12
|
+
from .validation_result import ValidationResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FileValidator:
|
|
16
|
+
"""Validator for file-related configuration settings."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, config_data: Dict[str, Any]):
|
|
19
|
+
self.config_data = config_data
|
|
20
|
+
self.validation_results: List[ValidationResult] = []
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
|
|
27
|
+
"""Safely get a nested value from configuration."""
|
|
28
|
+
keys = key.split('.')
|
|
29
|
+
value = self.config_data
|
|
30
|
+
|
|
31
|
+
for k in keys:
|
|
32
|
+
if isinstance(value, dict) and k in value:
|
|
33
|
+
value = value[k]
|
|
34
|
+
else:
|
|
35
|
+
return default
|
|
36
|
+
|
|
37
|
+
return value
|
|
38
|
+
|
|
39
|
+
def _has_nested_key(self, key: str) -> bool:
|
|
40
|
+
"""Check if a nested key exists in configuration."""
|
|
41
|
+
keys = key.split('.')
|
|
42
|
+
value = self.config_data
|
|
43
|
+
|
|
44
|
+
for k in keys:
|
|
45
|
+
if isinstance(value, dict) and k in value:
|
|
46
|
+
value = value[k]
|
|
47
|
+
else:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
def _is_file_required_for_enabled_features(self, file_key: str) -> bool:
|
|
53
|
+
"""Check if a file is required based on enabled features."""
|
|
54
|
+
# SSL files are required if SSL is enabled
|
|
55
|
+
if file_key.startswith("ssl.") or file_key.startswith("transport.ssl."):
|
|
56
|
+
return self._get_nested_value_safe("ssl.enabled", False)
|
|
57
|
+
|
|
58
|
+
# Proxy registration files are required if proxy registration is enabled
|
|
59
|
+
if file_key.startswith("proxy_registration."):
|
|
60
|
+
return self._get_nested_value_safe("proxy_registration.enabled", False)
|
|
61
|
+
|
|
62
|
+
# Log directory is required if logging is enabled
|
|
63
|
+
if file_key == "logging.log_dir":
|
|
64
|
+
return self._get_nested_value_safe("logging.enabled", True)
|
|
65
|
+
|
|
66
|
+
# Command directories are required if commands are enabled
|
|
67
|
+
if file_key.startswith("commands."):
|
|
68
|
+
return self._get_nested_value_safe("commands.enabled", True)
|
|
69
|
+
|
|
70
|
+
# Security files are required if security is enabled
|
|
71
|
+
if file_key.startswith("security."):
|
|
72
|
+
return self._get_nested_value_safe("security.enabled", False)
|
|
73
|
+
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
def validate_file_existence(self) -> List[ValidationResult]:
|
|
77
|
+
"""
|
|
78
|
+
Validate that referenced files exist.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
List of validation results
|
|
82
|
+
"""
|
|
83
|
+
self.validation_results = []
|
|
84
|
+
|
|
85
|
+
# Check SSL certificate files if SSL is enabled
|
|
86
|
+
if self._get_nested_value_safe("ssl.enabled", False):
|
|
87
|
+
cert_file = self._get_nested_value_safe("ssl.cert_file")
|
|
88
|
+
key_file = self._get_nested_value_safe("ssl.key_file")
|
|
89
|
+
|
|
90
|
+
if cert_file and not os.path.exists(cert_file):
|
|
91
|
+
self.validation_results.append(ValidationResult(
|
|
92
|
+
level="error",
|
|
93
|
+
message=f"SSL certificate file not found: {cert_file}",
|
|
94
|
+
section="ssl",
|
|
95
|
+
key="cert_file",
|
|
96
|
+
suggestion=f"Create or fix path to certificate file"
|
|
97
|
+
))
|
|
98
|
+
|
|
99
|
+
if key_file and not os.path.exists(key_file):
|
|
100
|
+
self.validation_results.append(ValidationResult(
|
|
101
|
+
level="error",
|
|
102
|
+
message=f"SSL key file not found: {key_file}",
|
|
103
|
+
section="ssl",
|
|
104
|
+
key="key_file",
|
|
105
|
+
suggestion=f"Create or fix path to key file"
|
|
106
|
+
))
|
|
107
|
+
|
|
108
|
+
# Check proxy registration certificate files if proxy registration is enabled
|
|
109
|
+
if self._get_nested_value_safe("proxy_registration.enabled", False):
|
|
110
|
+
proxy_cert = self._get_nested_value_safe("proxy_registration.certificate.cert_file")
|
|
111
|
+
proxy_key = self._get_nested_value_safe("proxy_registration.certificate.key_file")
|
|
112
|
+
|
|
113
|
+
if proxy_cert and not os.path.exists(proxy_cert):
|
|
114
|
+
self.validation_results.append(ValidationResult(
|
|
115
|
+
level="warning",
|
|
116
|
+
message=f"Proxy registration certificate file not found: {proxy_cert}",
|
|
117
|
+
section="proxy_registration.certificate",
|
|
118
|
+
key="cert_file",
|
|
119
|
+
suggestion=f"Create or fix path to certificate file"
|
|
120
|
+
))
|
|
121
|
+
|
|
122
|
+
if proxy_key and not os.path.exists(proxy_key):
|
|
123
|
+
self.validation_results.append(ValidationResult(
|
|
124
|
+
level="warning",
|
|
125
|
+
message=f"Proxy registration key file not found: {proxy_key}",
|
|
126
|
+
section="proxy_registration.certificate",
|
|
127
|
+
key="key_file",
|
|
128
|
+
suggestion=f"Create or fix path to key file"
|
|
129
|
+
))
|
|
130
|
+
|
|
131
|
+
return self.validation_results
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Protocol validation utilities for MCP Proxy Adapter configuration validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import Dict, List, Any
|
|
10
|
+
|
|
11
|
+
from .validation_result import ValidationResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProtocolValidator:
|
|
15
|
+
"""Validator for protocol-related configuration settings."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, config_data: Dict[str, Any]):
|
|
18
|
+
self.config_data = config_data
|
|
19
|
+
self.validation_results: List[ValidationResult] = []
|
|
20
|
+
|
|
21
|
+
def _validate_https_requirements(self) -> None:
|
|
22
|
+
"""Validate HTTPS-specific requirements."""
|
|
23
|
+
# Check server section for certificates (SimpleConfig format)
|
|
24
|
+
server_config = self._get_nested_value_safe("server", {})
|
|
25
|
+
|
|
26
|
+
# Check for required SSL files in server section
|
|
27
|
+
if not server_config.get("cert_file"):
|
|
28
|
+
self.validation_results.append(
|
|
29
|
+
ValidationResult(
|
|
30
|
+
level="error",
|
|
31
|
+
message="HTTPS protocol requires SSL certificate file",
|
|
32
|
+
section="server",
|
|
33
|
+
key="cert_file",
|
|
34
|
+
suggestion="Specify server.cert_file",
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if not server_config.get("key_file"):
|
|
39
|
+
self.validation_results.append(
|
|
40
|
+
ValidationResult(
|
|
41
|
+
level="error",
|
|
42
|
+
message="HTTPS protocol requires SSL key file",
|
|
43
|
+
section="server",
|
|
44
|
+
key="key_file",
|
|
45
|
+
suggestion="Specify server.key_file",
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def _validate_mtls_requirements(self) -> None:
|
|
50
|
+
"""Validate mTLS-specific requirements."""
|
|
51
|
+
# mTLS requires HTTPS
|
|
52
|
+
self._validate_https_requirements()
|
|
53
|
+
|
|
54
|
+
# Check server section for certificates (SimpleConfig format)
|
|
55
|
+
server_config = self._get_nested_value_safe("server", {})
|
|
56
|
+
transport_config = self._get_nested_value_safe("transport", {})
|
|
57
|
+
|
|
58
|
+
# For mTLS server, we need:
|
|
59
|
+
# - Server cert/key (already checked by _validate_https_requirements)
|
|
60
|
+
# - CA cert for verifying client certificates
|
|
61
|
+
# - verify_client enabled
|
|
62
|
+
|
|
63
|
+
# Check for CA certificate (needed for client certificate verification)
|
|
64
|
+
if not server_config.get("ca_cert_file"):
|
|
65
|
+
self.validation_results.append(
|
|
66
|
+
ValidationResult(
|
|
67
|
+
level="error",
|
|
68
|
+
message="mTLS protocol requires CA certificate for client verification",
|
|
69
|
+
section="server",
|
|
70
|
+
key="ca_cert_file",
|
|
71
|
+
suggestion="Specify server.ca_cert_file for client certificate verification",
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Check for client verification
|
|
76
|
+
if not transport_config.get("verify_client", False):
|
|
77
|
+
self.validation_results.append(
|
|
78
|
+
ValidationResult(
|
|
79
|
+
level="warning",
|
|
80
|
+
message="mTLS protocol should have client verification enabled",
|
|
81
|
+
section="transport",
|
|
82
|
+
key="verify_client",
|
|
83
|
+
suggestion="Set transport.verify_client to true",
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Note: client_cert and client_key are NOT required for mTLS server
|
|
88
|
+
# They are only needed for client/registration configuration when connecting TO a proxy
|
|
89
|
+
|
|
90
|
+
def _validate_feature_flags(self) -> None:
|
|
91
|
+
"""Validate feature flags based on protocol."""
|
|
92
|
+
protocol = self._get_nested_value_safe("server.protocol", "http")
|
|
93
|
+
server_config = self._get_nested_value_safe("server", {})
|
|
94
|
+
|
|
95
|
+
# Check if features are compatible with protocol
|
|
96
|
+
if protocol == "http":
|
|
97
|
+
# HTTP doesn't support SSL features
|
|
98
|
+
if server_config.get("cert_file") or server_config.get("key_file"):
|
|
99
|
+
self.validation_results.append(
|
|
100
|
+
ValidationResult(
|
|
101
|
+
level="warning",
|
|
102
|
+
message="SSL certificates are configured but protocol is HTTP. Consider using HTTPS",
|
|
103
|
+
section="server",
|
|
104
|
+
suggestion="Change protocol to https or remove certificate configuration",
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Check transport configuration
|
|
109
|
+
transport_config = self._get_nested_value_safe("transport", {})
|
|
110
|
+
if transport_config:
|
|
111
|
+
verify_client = transport_config.get("verify_client", False)
|
|
112
|
+
if verify_client and protocol == "http":
|
|
113
|
+
self.validation_results.append(
|
|
114
|
+
ValidationResult(
|
|
115
|
+
level="warning",
|
|
116
|
+
message="Client verification is enabled but protocol is HTTP",
|
|
117
|
+
section="transport",
|
|
118
|
+
key="verify_client",
|
|
119
|
+
suggestion="Change protocol to https or mtls, or disable client verification",
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def _validate_server_section(self) -> None:
|
|
124
|
+
"""Validate server section requirements."""
|
|
125
|
+
server_config = self.config_data.get("server", {})
|
|
126
|
+
|
|
127
|
+
# Check required fields
|
|
128
|
+
if "host" not in server_config:
|
|
129
|
+
self.validation_results.append(
|
|
130
|
+
ValidationResult(
|
|
131
|
+
level="error",
|
|
132
|
+
message="Server host is required",
|
|
133
|
+
section="server",
|
|
134
|
+
key="host",
|
|
135
|
+
suggestion="Add host field to server section",
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if "port" not in server_config:
|
|
140
|
+
self.validation_results.append(
|
|
141
|
+
ValidationResult(
|
|
142
|
+
level="error",
|
|
143
|
+
message="Server port is required",
|
|
144
|
+
section="server",
|
|
145
|
+
key="port",
|
|
146
|
+
suggestion="Add port field to server section",
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Validate port number
|
|
151
|
+
port = server_config.get("port")
|
|
152
|
+
if port is not None:
|
|
153
|
+
if not isinstance(port, int) or not (1 <= port <= 65535):
|
|
154
|
+
self.validation_results.append(
|
|
155
|
+
ValidationResult(
|
|
156
|
+
level="error",
|
|
157
|
+
message=f"Invalid port number: {port}. Must be between 1 and 65535",
|
|
158
|
+
section="server",
|
|
159
|
+
key="port",
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Validate host format
|
|
164
|
+
host = server_config.get("host")
|
|
165
|
+
if host is not None:
|
|
166
|
+
if not self._is_valid_host(host):
|
|
167
|
+
self.validation_results.append(
|
|
168
|
+
ValidationResult(
|
|
169
|
+
level="error",
|
|
170
|
+
message=f"Invalid host format: {host}",
|
|
171
|
+
section="server",
|
|
172
|
+
key="host",
|
|
173
|
+
suggestion="Use a valid hostname or IP address",
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _is_valid_host(self, host: str) -> bool:
|
|
178
|
+
"""Check if host has valid format."""
|
|
179
|
+
# Check for localhost
|
|
180
|
+
if host in ["localhost", "127.0.0.1", "::1", "0.0.0.0"]:
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
# Check for IP address
|
|
184
|
+
ip_pattern = r"^(\d{1,3}\.){3}\d{1,3}$"
|
|
185
|
+
if re.match(ip_pattern, host):
|
|
186
|
+
# Validate IP address ranges
|
|
187
|
+
parts = host.split(".")
|
|
188
|
+
return all(0 <= int(part) <= 255 for part in parts)
|
|
189
|
+
|
|
190
|
+
# Check for hostname (basic validation)
|
|
191
|
+
hostname_pattern = r"^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$"
|
|
192
|
+
return bool(re.match(hostname_pattern, host))
|
|
193
|
+
|
|
194
|
+
def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
|
|
195
|
+
"""Safely get a nested value from configuration."""
|
|
196
|
+
keys = key.split(".")
|
|
197
|
+
value = self.config_data
|
|
198
|
+
|
|
199
|
+
for k in keys:
|
|
200
|
+
if isinstance(value, dict) and k in value:
|
|
201
|
+
value = value[k]
|
|
202
|
+
else:
|
|
203
|
+
return default
|
|
204
|
+
|
|
205
|
+
return value
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Security validation utilities for MCP Proxy Adapter configuration validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from typing import Dict, List, Any
|
|
10
|
+
|
|
11
|
+
from .validation_result import ValidationResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SecurityValidator:
|
|
15
|
+
"""Validator for security-related configuration settings."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, config_data: Dict[str, Any]):
|
|
18
|
+
self.config_data = config_data
|
|
19
|
+
self.validation_results: List[ValidationResult] = []
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_nested_value_safe(self, key: str, default: Any = None) -> Any:
|
|
26
|
+
"""Safely get a nested value from configuration."""
|
|
27
|
+
keys = key.split('.')
|
|
28
|
+
value = self.config_data
|
|
29
|
+
|
|
30
|
+
for k in keys:
|
|
31
|
+
if isinstance(value, dict) and k in value:
|
|
32
|
+
value = value[k]
|
|
33
|
+
else:
|
|
34
|
+
return default
|
|
35
|
+
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
def _has_nested_key(self, key: str) -> bool:
|
|
39
|
+
"""Check if a nested key exists in configuration."""
|
|
40
|
+
keys = key.split('.')
|
|
41
|
+
value = self.config_data
|
|
42
|
+
|
|
43
|
+
for k in keys:
|
|
44
|
+
if isinstance(value, dict) and k in value:
|
|
45
|
+
value = value[k]
|
|
46
|
+
else:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
def _is_valid_url(self, url: str) -> bool:
|
|
52
|
+
"""Check if URL has valid format."""
|
|
53
|
+
try:
|
|
54
|
+
from urllib.parse import urlparse
|
|
55
|
+
parsed = urlparse(url)
|
|
56
|
+
return bool(parsed.scheme and parsed.netloc)
|
|
57
|
+
except Exception:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
def validate_security_consistency(self) -> List[ValidationResult]:
|
|
61
|
+
"""
|
|
62
|
+
Validate security configuration consistency.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List of validation results
|
|
66
|
+
"""
|
|
67
|
+
self.validation_results = []
|
|
68
|
+
# Basic consistency checks can be added here
|
|
69
|
+
return self.validation_results
|
|
70
|
+
|
|
71
|
+
def validate_ssl_configuration(self) -> List[ValidationResult]:
|
|
72
|
+
"""
|
|
73
|
+
Validate SSL configuration.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List of validation results
|
|
77
|
+
"""
|
|
78
|
+
self.validation_results = []
|
|
79
|
+
|
|
80
|
+
ssl_config = self._get_nested_value_safe("ssl", {})
|
|
81
|
+
if ssl_config.get("enabled", False):
|
|
82
|
+
if not ssl_config.get("cert_file"):
|
|
83
|
+
self.validation_results.append(ValidationResult(
|
|
84
|
+
level="error",
|
|
85
|
+
message="SSL is enabled but cert_file is not specified",
|
|
86
|
+
section="ssl",
|
|
87
|
+
key="cert_file",
|
|
88
|
+
suggestion="Specify ssl.cert_file"
|
|
89
|
+
))
|
|
90
|
+
if not ssl_config.get("key_file"):
|
|
91
|
+
self.validation_results.append(ValidationResult(
|
|
92
|
+
level="error",
|
|
93
|
+
message="SSL is enabled but key_file is not specified",
|
|
94
|
+
section="ssl",
|
|
95
|
+
key="key_file",
|
|
96
|
+
suggestion="Specify ssl.key_file"
|
|
97
|
+
))
|
|
98
|
+
|
|
99
|
+
return self.validation_results
|
|
100
|
+
|
|
101
|
+
def validate_roles_configuration(self) -> List[ValidationResult]:
|
|
102
|
+
"""
|
|
103
|
+
Validate roles configuration.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
List of validation results
|
|
107
|
+
"""
|
|
108
|
+
self.validation_results = []
|
|
109
|
+
# Roles validation can be added here
|
|
110
|
+
return self.validation_results
|
|
111
|
+
|
|
112
|
+
def validate_proxy_registration(self) -> List[ValidationResult]:
|
|
113
|
+
"""
|
|
114
|
+
Validate proxy registration configuration.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
List of validation results
|
|
118
|
+
"""
|
|
119
|
+
self.validation_results = []
|
|
120
|
+
|
|
121
|
+
proxy_config = self._get_nested_value_safe("proxy_registration", {})
|
|
122
|
+
if proxy_config.get("enabled", False):
|
|
123
|
+
if not proxy_config.get("proxy_url"):
|
|
124
|
+
self.validation_results.append(ValidationResult(
|
|
125
|
+
level="error",
|
|
126
|
+
message="Proxy registration is enabled but proxy_url is not specified",
|
|
127
|
+
section="proxy_registration",
|
|
128
|
+
key="proxy_url",
|
|
129
|
+
suggestion="Specify proxy_registration.proxy_url"
|
|
130
|
+
))
|
|
131
|
+
elif not self._is_valid_url(proxy_config.get("proxy_url", "")):
|
|
132
|
+
self.validation_results.append(ValidationResult(
|
|
133
|
+
level="error",
|
|
134
|
+
message=f"Invalid proxy URL format: {proxy_config.get('proxy_url')}",
|
|
135
|
+
section="proxy_registration",
|
|
136
|
+
key="proxy_url",
|
|
137
|
+
suggestion="Use a valid URL format (e.g., http://host:port)"
|
|
138
|
+
))
|
|
139
|
+
|
|
140
|
+
return self.validation_results
|