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,372 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Certificate creation utilities for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
# Import mcp_security_framework
|
|
14
|
+
try:
|
|
15
|
+
from mcp_security_framework.core.cert_manager import (
|
|
16
|
+
CertificateManager,
|
|
17
|
+
CertificateConfig,
|
|
18
|
+
CAConfig,
|
|
19
|
+
ClientCertConfig,
|
|
20
|
+
ServerCertConfig,
|
|
21
|
+
)
|
|
22
|
+
SECURITY_FRAMEWORK_AVAILABLE = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
SECURITY_FRAMEWORK_AVAILABLE = False
|
|
25
|
+
# Fallback to cryptography if mcp_security_framework is not available
|
|
26
|
+
from cryptography import x509
|
|
27
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
28
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
29
|
+
from cryptography.x509.oid import NameOID
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CertificateCreator:
|
|
35
|
+
"""Creator for various types of certificates."""
|
|
36
|
+
|
|
37
|
+
# Default certificate validity period (1 year)
|
|
38
|
+
DEFAULT_VALIDITY_DAYS = 365
|
|
39
|
+
|
|
40
|
+
# Default key size
|
|
41
|
+
DEFAULT_KEY_SIZE = 2048
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def create_ca_certificate(
|
|
45
|
+
common_name: str,
|
|
46
|
+
output_dir: str,
|
|
47
|
+
validity_days: int = DEFAULT_VALIDITY_DAYS,
|
|
48
|
+
key_size: int = DEFAULT_KEY_SIZE,
|
|
49
|
+
) -> Dict[str, str]:
|
|
50
|
+
"""
|
|
51
|
+
Create a CA certificate and private key using mcp_security_framework.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
common_name: Common name for the CA certificate
|
|
55
|
+
output_dir: Directory to save certificate and key files
|
|
56
|
+
validity_days: Certificate validity period in days
|
|
57
|
+
key_size: RSA key size in bits
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dictionary with paths to created files
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If parameters are invalid
|
|
64
|
+
OSError: If files cannot be created
|
|
65
|
+
"""
|
|
66
|
+
if not SECURITY_FRAMEWORK_AVAILABLE:
|
|
67
|
+
logger.warning("mcp_security_framework not available, using fallback method")
|
|
68
|
+
return CertificateCreator._create_ca_certificate_fallback(
|
|
69
|
+
common_name, output_dir, validity_days, key_size
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
# Validate parameters
|
|
74
|
+
if not common_name or not common_name.strip():
|
|
75
|
+
raise ValueError("Common name cannot be empty")
|
|
76
|
+
|
|
77
|
+
if validity_days <= 0:
|
|
78
|
+
raise ValueError("Validity days must be positive")
|
|
79
|
+
|
|
80
|
+
if key_size < 1024:
|
|
81
|
+
raise ValueError("Key size must be at least 1024 bits")
|
|
82
|
+
|
|
83
|
+
# Create output directory if it doesn't exist
|
|
84
|
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
# Configure CA using mcp_security_framework
|
|
87
|
+
ca_config = CAConfig(
|
|
88
|
+
common_name=common_name,
|
|
89
|
+
organization="MCP Proxy Adapter CA",
|
|
90
|
+
organizational_unit="Certificate Authority",
|
|
91
|
+
country="US",
|
|
92
|
+
state="Default State",
|
|
93
|
+
locality="Default City",
|
|
94
|
+
validity_days=validity_days,
|
|
95
|
+
key_size=key_size,
|
|
96
|
+
key_type="RSA",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Create certificate manager
|
|
100
|
+
cert_config = CertificateConfig(
|
|
101
|
+
output_dir=output_dir,
|
|
102
|
+
ca_cert_path=str(Path(output_dir) / f"{common_name}.crt"),
|
|
103
|
+
ca_key_path=str(Path(output_dir) / f"{common_name}.key"),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
cert_manager = CertificateManager(cert_config)
|
|
107
|
+
|
|
108
|
+
# Generate CA certificate
|
|
109
|
+
ca_pair = cert_manager.create_ca_certificate(ca_config)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
"cert_path": str(ca_pair.cert_path),
|
|
113
|
+
"key_path": str(ca_pair.key_path),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.error(f"Failed to create CA certificate: {e}")
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def _create_ca_certificate_fallback(
|
|
122
|
+
common_name: str, output_dir: str, validity_days: int, key_size: int
|
|
123
|
+
) -> Dict[str, str]:
|
|
124
|
+
"""Fallback CA certificate creation using cryptography."""
|
|
125
|
+
try:
|
|
126
|
+
# Create output directory if it doesn't exist
|
|
127
|
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
128
|
+
|
|
129
|
+
# Generate private key
|
|
130
|
+
private_key = rsa.generate_private_key(
|
|
131
|
+
public_exponent=65537,
|
|
132
|
+
key_size=key_size,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Create certificate
|
|
136
|
+
subject = issuer = x509.Name([
|
|
137
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
|
138
|
+
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Default State"),
|
|
139
|
+
x509.NameAttribute(NameOID.LOCALITY_NAME, "Default City"),
|
|
140
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MCP Proxy Adapter CA"),
|
|
141
|
+
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Certificate Authority"),
|
|
142
|
+
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
|
|
143
|
+
])
|
|
144
|
+
|
|
145
|
+
cert = x509.CertificateBuilder().subject_name(
|
|
146
|
+
subject
|
|
147
|
+
).issuer_name(
|
|
148
|
+
issuer
|
|
149
|
+
).public_key(
|
|
150
|
+
private_key.public_key()
|
|
151
|
+
).serial_number(
|
|
152
|
+
x509.random_serial_number()
|
|
153
|
+
).not_valid_before(
|
|
154
|
+
datetime.now(timezone.utc)
|
|
155
|
+
).not_valid_after(
|
|
156
|
+
datetime.now(timezone.utc) + timedelta(days=validity_days)
|
|
157
|
+
).add_extension(
|
|
158
|
+
x509.BasicConstraints(ca=True, path_length=None),
|
|
159
|
+
critical=True,
|
|
160
|
+
).add_extension(
|
|
161
|
+
x509.KeyUsage(
|
|
162
|
+
key_cert_sign=True,
|
|
163
|
+
crl_sign=True,
|
|
164
|
+
digital_signature=True,
|
|
165
|
+
key_encipherment=False,
|
|
166
|
+
data_encipherment=False,
|
|
167
|
+
key_agreement=False,
|
|
168
|
+
encipher_only=False,
|
|
169
|
+
decipher_only=False,
|
|
170
|
+
content_commitment=False,
|
|
171
|
+
),
|
|
172
|
+
critical=True,
|
|
173
|
+
).sign(private_key, hashes.SHA256())
|
|
174
|
+
|
|
175
|
+
# Save certificate and key
|
|
176
|
+
cert_path = Path(output_dir) / f"{common_name}.crt"
|
|
177
|
+
key_path = Path(output_dir) / f"{common_name}.key"
|
|
178
|
+
|
|
179
|
+
with open(cert_path, "wb") as f:
|
|
180
|
+
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
|
181
|
+
|
|
182
|
+
with open(key_path, "wb") as f:
|
|
183
|
+
f.write(private_key.private_bytes(
|
|
184
|
+
encoding=serialization.Encoding.PEM,
|
|
185
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
186
|
+
encryption_algorithm=serialization.NoEncryption()
|
|
187
|
+
))
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
"cert_path": str(cert_path),
|
|
191
|
+
"key_path": str(key_path),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(f"Failed to create CA certificate (fallback): {e}")
|
|
196
|
+
raise
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def create_server_certificate(
|
|
200
|
+
common_name: str,
|
|
201
|
+
output_dir: str,
|
|
202
|
+
ca_cert_path: str,
|
|
203
|
+
ca_key_path: str,
|
|
204
|
+
validity_days: int = DEFAULT_VALIDITY_DAYS,
|
|
205
|
+
key_size: int = DEFAULT_KEY_SIZE,
|
|
206
|
+
san_dns: Optional[List[str]] = None,
|
|
207
|
+
san_ip: Optional[List[str]] = None,
|
|
208
|
+
) -> Dict[str, str]:
|
|
209
|
+
"""
|
|
210
|
+
Create a server certificate signed by CA.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
common_name: Common name for the server certificate
|
|
214
|
+
output_dir: Directory to save certificate and key files
|
|
215
|
+
ca_cert_path: Path to CA certificate
|
|
216
|
+
ca_key_path: Path to CA private key
|
|
217
|
+
validity_days: Certificate validity period in days
|
|
218
|
+
key_size: RSA key size in bits
|
|
219
|
+
san_dns: List of DNS names for SAN extension
|
|
220
|
+
san_ip: List of IP addresses for SAN extension
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Dictionary with paths to created files
|
|
224
|
+
"""
|
|
225
|
+
if not SECURITY_FRAMEWORK_AVAILABLE:
|
|
226
|
+
logger.warning("mcp_security_framework not available, using fallback method")
|
|
227
|
+
return CertificateCreator._create_server_certificate_fallback(
|
|
228
|
+
common_name, output_dir, ca_cert_path, ca_key_path,
|
|
229
|
+
validity_days, key_size, san_dns, san_ip
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
# Validate parameters
|
|
234
|
+
if not common_name or not common_name.strip():
|
|
235
|
+
raise ValueError("Common name cannot be empty")
|
|
236
|
+
|
|
237
|
+
if not Path(ca_cert_path).exists():
|
|
238
|
+
raise FileNotFoundError(f"CA certificate not found: {ca_cert_path}")
|
|
239
|
+
|
|
240
|
+
if not Path(ca_key_path).exists():
|
|
241
|
+
raise FileNotFoundError(f"CA key not found: {ca_key_path}")
|
|
242
|
+
|
|
243
|
+
# Create output directory if it doesn't exist
|
|
244
|
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
245
|
+
|
|
246
|
+
# Configure server certificate using mcp_security_framework
|
|
247
|
+
server_config = ServerCertConfig(
|
|
248
|
+
common_name=common_name,
|
|
249
|
+
organization="MCP Proxy Adapter",
|
|
250
|
+
organizational_unit="Server",
|
|
251
|
+
country="US",
|
|
252
|
+
state="Default State",
|
|
253
|
+
locality="Default City",
|
|
254
|
+
validity_days=validity_days,
|
|
255
|
+
key_size=key_size,
|
|
256
|
+
key_type="RSA",
|
|
257
|
+
san_dns=san_dns or [],
|
|
258
|
+
san_ip=san_ip or [],
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Create certificate manager
|
|
262
|
+
cert_config = CertificateConfig(
|
|
263
|
+
output_dir=output_dir,
|
|
264
|
+
ca_cert_path=ca_cert_path,
|
|
265
|
+
ca_key_path=ca_key_path,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
cert_manager = CertificateManager(cert_config)
|
|
269
|
+
|
|
270
|
+
# Generate server certificate
|
|
271
|
+
server_pair = cert_manager.create_server_certificate(server_config)
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
"cert_path": str(server_pair.cert_path),
|
|
275
|
+
"key_path": str(server_pair.key_path),
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
except Exception as e:
|
|
279
|
+
logger.error(f"Failed to create server certificate: {e}")
|
|
280
|
+
raise
|
|
281
|
+
|
|
282
|
+
@staticmethod
|
|
283
|
+
def _create_server_certificate_fallback(
|
|
284
|
+
common_name: str,
|
|
285
|
+
output_dir: str,
|
|
286
|
+
ca_cert_path: str,
|
|
287
|
+
ca_key_path: str,
|
|
288
|
+
validity_days: int,
|
|
289
|
+
key_size: int,
|
|
290
|
+
san_dns: Optional[List[str]],
|
|
291
|
+
san_ip: Optional[List[str]],
|
|
292
|
+
) -> Dict[str, str]:
|
|
293
|
+
"""Fallback server certificate creation using cryptography."""
|
|
294
|
+
try:
|
|
295
|
+
# Create output directory if it doesn't exist
|
|
296
|
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
297
|
+
|
|
298
|
+
# Load CA certificate and key
|
|
299
|
+
with open(ca_cert_path, "rb") as f:
|
|
300
|
+
ca_cert = x509.load_pem_x509_certificate(f.read())
|
|
301
|
+
|
|
302
|
+
with open(ca_key_path, "rb") as f:
|
|
303
|
+
ca_key = serialization.load_pem_private_key(f.read(), password=None)
|
|
304
|
+
|
|
305
|
+
# Generate server private key
|
|
306
|
+
private_key = rsa.generate_private_key(
|
|
307
|
+
public_exponent=65537,
|
|
308
|
+
key_size=key_size,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Create certificate
|
|
312
|
+
subject = x509.Name([
|
|
313
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
|
314
|
+
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Default State"),
|
|
315
|
+
x509.NameAttribute(NameOID.LOCALITY_NAME, "Default City"),
|
|
316
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MCP Proxy Adapter"),
|
|
317
|
+
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Server"),
|
|
318
|
+
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
|
|
319
|
+
])
|
|
320
|
+
|
|
321
|
+
# Build certificate
|
|
322
|
+
cert_builder = x509.CertificateBuilder().subject_name(
|
|
323
|
+
subject
|
|
324
|
+
).issuer_name(
|
|
325
|
+
ca_cert.subject
|
|
326
|
+
).public_key(
|
|
327
|
+
private_key.public_key()
|
|
328
|
+
).serial_number(
|
|
329
|
+
x509.random_serial_number()
|
|
330
|
+
).not_valid_before(
|
|
331
|
+
datetime.now(timezone.utc)
|
|
332
|
+
).not_valid_after(
|
|
333
|
+
datetime.now(timezone.utc) + timedelta(days=validity_days)
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Add SAN extension if provided
|
|
337
|
+
if san_dns or san_ip:
|
|
338
|
+
san_list = []
|
|
339
|
+
if san_dns:
|
|
340
|
+
san_list.extend([x509.DNSName(name) for name in san_dns])
|
|
341
|
+
if san_ip:
|
|
342
|
+
san_list.extend([x509.IPAddress(ip) for ip in san_ip])
|
|
343
|
+
|
|
344
|
+
cert_builder = cert_builder.add_extension(
|
|
345
|
+
x509.SubjectAlternativeName(san_list),
|
|
346
|
+
critical=False,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
cert = cert_builder.sign(ca_key, hashes.SHA256())
|
|
350
|
+
|
|
351
|
+
# Save certificate and key
|
|
352
|
+
cert_path = Path(output_dir) / f"{common_name}_server.crt"
|
|
353
|
+
key_path = Path(output_dir) / f"{common_name}_server.key"
|
|
354
|
+
|
|
355
|
+
with open(cert_path, "wb") as f:
|
|
356
|
+
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
|
357
|
+
|
|
358
|
+
with open(key_path, "wb") as f:
|
|
359
|
+
f.write(private_key.private_bytes(
|
|
360
|
+
encoding=serialization.Encoding.PEM,
|
|
361
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
362
|
+
encryption_algorithm=serialization.NoEncryption()
|
|
363
|
+
))
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
"cert_path": str(cert_path),
|
|
367
|
+
"key_path": str(key_path),
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
except Exception as e:
|
|
371
|
+
logger.error(f"Failed to create server certificate (fallback): {e}")
|
|
372
|
+
raise
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Certificate information extraction utilities for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import List
|
|
10
|
+
|
|
11
|
+
# Import mcp_security_framework
|
|
12
|
+
try:
|
|
13
|
+
from mcp_security_framework.utils.cert_utils import (
|
|
14
|
+
parse_certificate,
|
|
15
|
+
extract_roles_from_certificate,
|
|
16
|
+
extract_permissions_from_certificate,
|
|
17
|
+
)
|
|
18
|
+
SECURITY_FRAMEWORK_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
SECURITY_FRAMEWORK_AVAILABLE = False
|
|
21
|
+
# Fallback to cryptography if mcp_security_framework is not available
|
|
22
|
+
from cryptography import x509
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CertificateExtractor:
|
|
28
|
+
"""Extractor for certificate information."""
|
|
29
|
+
|
|
30
|
+
# Custom OID for roles (same as in RoleUtils)
|
|
31
|
+
ROLE_EXTENSION_OID = "1.3.6.1.4.1.99999.1"
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def extract_roles_from_certificate(cert_path: str) -> List[str]:
|
|
35
|
+
"""
|
|
36
|
+
Extract roles from certificate.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
cert_path: Path to certificate file
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of roles found in certificate
|
|
43
|
+
"""
|
|
44
|
+
if not SECURITY_FRAMEWORK_AVAILABLE:
|
|
45
|
+
logger.warning("mcp_security_framework not available, using fallback method")
|
|
46
|
+
return CertificateExtractor._extract_roles_from_certificate_fallback(cert_path)
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
return extract_roles_from_certificate(cert_path)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error(f"Failed to extract roles from certificate: {e}")
|
|
52
|
+
return []
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def _extract_roles_from_certificate_fallback(cert_path: str) -> List[str]:
|
|
56
|
+
"""Fallback role extraction using cryptography."""
|
|
57
|
+
try:
|
|
58
|
+
with open(cert_path, "rb") as f:
|
|
59
|
+
cert = x509.load_pem_x509_certificate(f.read())
|
|
60
|
+
|
|
61
|
+
# Look for custom role extension
|
|
62
|
+
try:
|
|
63
|
+
role_extension = cert.extensions.get_extension_for_oid(
|
|
64
|
+
x509.ObjectIdentifier(CertificateExtractor.ROLE_EXTENSION_OID)
|
|
65
|
+
)
|
|
66
|
+
if role_extension:
|
|
67
|
+
# Parse roles from extension value
|
|
68
|
+
roles_str = role_extension.value.value.decode('utf-8')
|
|
69
|
+
return [role.strip() for role in roles_str.split(',') if role.strip()]
|
|
70
|
+
except x509.ExtensionNotFound:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
# Fallback: look for roles in subject alternative name
|
|
74
|
+
try:
|
|
75
|
+
san_extension = cert.extensions.get_extension_for_oid(
|
|
76
|
+
x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
|
|
77
|
+
)
|
|
78
|
+
if san_extension:
|
|
79
|
+
roles = []
|
|
80
|
+
for name in san_extension.value:
|
|
81
|
+
if isinstance(name, x509.DNSName):
|
|
82
|
+
# Check if this looks like a role (e.g., role:admin)
|
|
83
|
+
if name.value.startswith('role:'):
|
|
84
|
+
roles.append(name.value[5:]) # Remove 'role:' prefix
|
|
85
|
+
return roles
|
|
86
|
+
except x509.ExtensionNotFound:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error(f"Failed to extract roles from certificate (fallback): {e}")
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def extract_roles_from_certificate_object(cert) -> List[str]:
|
|
97
|
+
"""
|
|
98
|
+
Extract roles from certificate object.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
cert: Certificate object
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
List of roles found in certificate
|
|
105
|
+
"""
|
|
106
|
+
try:
|
|
107
|
+
# Look for custom role extension
|
|
108
|
+
try:
|
|
109
|
+
role_extension = cert.extensions.get_extension_for_oid(
|
|
110
|
+
x509.ObjectIdentifier(CertificateExtractor.ROLE_EXTENSION_OID)
|
|
111
|
+
)
|
|
112
|
+
if role_extension:
|
|
113
|
+
# Parse roles from extension value
|
|
114
|
+
roles_str = role_extension.value.value.decode('utf-8')
|
|
115
|
+
return [role.strip() for role in roles_str.split(',') if role.strip()]
|
|
116
|
+
except x509.ExtensionNotFound:
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
# Fallback: look for roles in subject alternative name
|
|
120
|
+
try:
|
|
121
|
+
san_extension = cert.extensions.get_extension_for_oid(
|
|
122
|
+
x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
|
|
123
|
+
)
|
|
124
|
+
if san_extension:
|
|
125
|
+
roles = []
|
|
126
|
+
for name in san_extension.value:
|
|
127
|
+
if isinstance(name, x509.DNSName):
|
|
128
|
+
# Check if this looks like a role (e.g., role:admin)
|
|
129
|
+
if name.value.startswith('role:'):
|
|
130
|
+
roles.append(name.value[5:]) # Remove 'role:' prefix
|
|
131
|
+
return roles
|
|
132
|
+
except x509.ExtensionNotFound:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.error(f"Failed to extract roles from certificate object: {e}")
|
|
139
|
+
return []
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def extract_permissions_from_certificate(cert_path: str) -> List[str]:
|
|
143
|
+
"""
|
|
144
|
+
Extract permissions from certificate.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
cert_path: Path to certificate file
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
List of permissions found in certificate
|
|
151
|
+
"""
|
|
152
|
+
if not SECURITY_FRAMEWORK_AVAILABLE:
|
|
153
|
+
logger.warning("mcp_security_framework not available, using fallback method")
|
|
154
|
+
return CertificateExtractor._extract_permissions_from_certificate_fallback(cert_path)
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
return extract_permissions_from_certificate(cert_path)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.error(f"Failed to extract permissions from certificate: {e}")
|
|
160
|
+
return []
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def _extract_permissions_from_certificate_fallback(cert_path: str) -> List[str]:
|
|
164
|
+
"""Fallback permission extraction using cryptography."""
|
|
165
|
+
try:
|
|
166
|
+
with open(cert_path, "rb") as f:
|
|
167
|
+
cert = x509.load_pem_x509_certificate(f.read())
|
|
168
|
+
|
|
169
|
+
# Look for custom permission extension
|
|
170
|
+
try:
|
|
171
|
+
permission_extension = cert.extensions.get_extension_for_oid(
|
|
172
|
+
x509.ObjectIdentifier("1.3.6.1.4.1.99999.2") # Custom OID for permissions
|
|
173
|
+
)
|
|
174
|
+
if permission_extension:
|
|
175
|
+
# Parse permissions from extension value
|
|
176
|
+
permissions_str = permission_extension.value.value.decode('utf-8')
|
|
177
|
+
return [perm.strip() for perm in permissions_str.split(',') if perm.strip()]
|
|
178
|
+
except x509.ExtensionNotFound:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
return []
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error(f"Failed to extract permissions from certificate (fallback): {e}")
|
|
185
|
+
return []
|