mcp-proxy-adapter 6.1.1__py3-none-any.whl → 6.2.0__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.
Files changed (145) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +18 -7
  3. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  4. mcp_proxy_adapter/core/app_factory.py +87 -3
  5. mcp_proxy_adapter/core/app_runner.py +272 -0
  6. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  7. mcp_proxy_adapter/core/client.py +574 -0
  8. mcp_proxy_adapter/core/client_manager.py +284 -0
  9. mcp_proxy_adapter/core/server_adapter.py +17 -80
  10. mcp_proxy_adapter/core/server_engine.py +5 -99
  11. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  12. mcp_proxy_adapter/core/transport_manager.py +5 -5
  13. mcp_proxy_adapter/examples/__init__.py +16 -0
  14. mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
  15. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  16. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  17. mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
  18. mcp_proxy_adapter/examples/commands/__init__.py +5 -1
  19. mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
  20. mcp_proxy_adapter/examples/debug_request_state.py +4 -36
  21. mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
  22. mcp_proxy_adapter/examples/demo_client.py +0 -66
  23. mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
  24. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  25. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
  26. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
  27. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  28. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
  29. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
  30. mcp_proxy_adapter/examples/full_application/main.py +65 -44
  31. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  32. mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
  33. mcp_proxy_adapter/examples/generate_certificates.py +0 -15
  34. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  35. mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
  36. mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
  37. mcp_proxy_adapter/examples/run_example.py +1 -23
  38. mcp_proxy_adapter/examples/run_security_tests.py +2 -60
  39. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
  40. mcp_proxy_adapter/examples/security_test_client.py +18 -123
  41. mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
  42. mcp_proxy_adapter/examples/test_config.py +148 -0
  43. mcp_proxy_adapter/examples/test_config_generator.py +1 -25
  44. mcp_proxy_adapter/examples/test_examples.py +4 -67
  45. mcp_proxy_adapter/examples/universal_client.py +154 -162
  46. mcp_proxy_adapter/main.py +51 -161
  47. mcp_proxy_adapter/version.py +1 -1
  48. mcp_proxy_adapter-6.2.0.dist-info/METADATA +687 -0
  49. mcp_proxy_adapter-6.2.0.dist-info/RECORD +122 -0
  50. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +0 -285
  51. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +0 -285
  52. mcp_proxy_adapter/examples/README.md +0 -257
  53. mcp_proxy_adapter/examples/README_EN.md +0 -258
  54. mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
  55. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
  56. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
  57. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -43
  58. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +0 -36
  59. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -29
  60. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +0 -34
  61. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
  62. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +0 -35
  63. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
  64. mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
  65. mcp_proxy_adapter/examples/cert_config.json +0 -9
  66. mcp_proxy_adapter/examples/certs/admin.crt +0 -32
  67. mcp_proxy_adapter/examples/certs/admin.key +0 -52
  68. mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
  69. mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
  70. mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
  71. mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
  72. mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
  73. mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
  74. mcp_proxy_adapter/examples/certs/client.crt +0 -32
  75. mcp_proxy_adapter/examples/certs/client.key +0 -52
  76. mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
  77. mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
  78. mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
  79. mcp_proxy_adapter/examples/certs/client_user.key +0 -52
  80. mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
  81. mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
  82. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
  83. mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
  84. mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
  85. mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
  86. mcp_proxy_adapter/examples/certs/readonly.key +0 -52
  87. mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
  88. mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
  89. mcp_proxy_adapter/examples/certs/server.crt +0 -32
  90. mcp_proxy_adapter/examples/certs/server.key +0 -52
  91. mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
  92. mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
  93. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
  94. mcp_proxy_adapter/examples/certs/user.crt +0 -32
  95. mcp_proxy_adapter/examples/certs/user.key +0 -52
  96. mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
  97. mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
  98. mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
  99. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
  100. mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
  101. mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
  102. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
  103. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
  104. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
  105. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
  106. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
  107. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
  108. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
  109. mcp_proxy_adapter/examples/full_application/roles.json +0 -21
  110. mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
  111. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
  112. mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
  128. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
  129. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
  130. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
  131. mcp_proxy_adapter/examples/roles.json +0 -38
  132. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
  133. mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
  134. mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
  135. mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
  136. mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
  137. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
  138. mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
  139. mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
  140. mcp_proxy_adapter-6.1.1.dist-info/METADATA +0 -205
  141. mcp_proxy_adapter-6.1.1.dist-info/RECORD +0 -197
  142. {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/WHEEL +0 -0
  143. {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/entry_points.txt +0 -0
  144. {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/licenses/LICENSE +0 -0
  145. {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/top_level.txt +0 -0
@@ -1,453 +1,284 @@
1
1
  """
2
2
  SSL Setup Command
3
3
 
4
- This module provides commands for SSL configuration and management including
5
- setup, status checking, testing, and configuration management.
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
6
 
7
- Author: MCP Proxy Adapter Team
8
- Version: 1.0.0
7
+ Command for SSL/TLS configuration and certificate management.
9
8
  """
10
9
 
11
10
  import logging
12
- import os
13
11
  import ssl
14
- from typing import Dict, List, Optional, Any
15
- from pathlib import Path
12
+ from typing import Dict, Any, Optional
13
+
14
+ # Import mcp_security_framework
15
+ try:
16
+ from mcp_security_framework.core.ssl_manager import SSLManager
17
+ from mcp_security_framework.schemas.config import SSLConfig
18
+ from mcp_security_framework.utils.cert_utils import validate_certificate_chain
19
+ SECURITY_FRAMEWORK_AVAILABLE = True
20
+ except ImportError:
21
+ SECURITY_FRAMEWORK_AVAILABLE = False
16
22
 
17
23
  from .base import Command
18
- from .result import CommandResult, SuccessResult, ErrorResult
19
- from ..core.certificate_utils import CertificateUtils
20
- from ..core.auth_validator import AuthValidator
24
+ from .result import SuccessResult, ErrorResult
25
+ from ..config import Config
21
26
 
22
27
  logger = logging.getLogger(__name__)
23
28
 
24
29
 
25
- class SSLSetupResult:
26
- """
27
- Result class for SSL setup operations.
28
-
29
- Contains SSL configuration status and details.
30
- """
31
-
32
- def __init__(self, ssl_enabled: bool, cert_path: Optional[str] = None,
33
- key_path: Optional[str] = None, config: Optional[Dict] = None,
34
- status: str = "unknown", error: Optional[str] = None):
35
- """
36
- Initialize SSL setup result.
37
-
38
- Args:
39
- ssl_enabled: Whether SSL is enabled
40
- cert_path: Path to certificate file
41
- key_path: Path to private key file
42
- config: SSL configuration
43
- status: SSL status (enabled, disabled, error)
44
- error: Error message if any
45
- """
46
- self.ssl_enabled = ssl_enabled
47
- self.cert_path = cert_path
48
- self.key_path = key_path
49
- self.config = config or {}
50
- self.status = status
51
- self.error = error
52
-
53
- def to_dict(self) -> Dict[str, Any]:
54
- """
55
- Convert to dictionary format.
56
-
57
- Returns:
58
- Dictionary representation
59
- """
60
- return {
61
- "ssl_enabled": self.ssl_enabled,
62
- "cert_path": self.cert_path,
63
- "key_path": self.key_path,
64
- "config": self.config,
65
- "status": self.status,
66
- "error": self.error
67
- }
68
-
69
- def get_schema(self) -> Dict[str, Any]:
70
- """
71
- Get JSON schema for this result.
72
-
73
- Returns:
74
- JSON schema dictionary
75
- """
76
- return {
77
- "type": "object",
78
- "properties": {
79
- "ssl_enabled": {"type": "boolean", "description": "Whether SSL is enabled"},
80
- "cert_path": {"type": "string", "description": "Path to certificate file"},
81
- "key_path": {"type": "string", "description": "Path to private key file"},
82
- "config": {"type": "object", "description": "SSL configuration"},
83
- "status": {"type": "string", "enum": ["enabled", "disabled", "error"],
84
- "description": "SSL status"},
85
- "error": {"type": "string", "description": "Error message if any"}
86
- },
87
- "required": ["ssl_enabled", "status"]
88
- }
89
-
90
-
91
30
  class SSLSetupCommand(Command):
92
31
  """
93
- Command for SSL setup and configuration.
32
+ SSL Setup Command
94
33
 
95
- Provides methods for SSL setup, status checking, testing, and configuration.
34
+ Handles SSL/TLS configuration and certificate management.
96
35
  """
97
36
 
98
37
  # Command metadata
99
38
  name = "ssl_setup"
100
39
  version = "1.0.0"
101
- descr = "SSL setup and configuration management"
40
+ descr = "Configure SSL/TLS settings and manage certificates"
102
41
  category = "security"
103
42
  author = "MCP Proxy Adapter Team"
104
43
  email = "team@mcp-proxy-adapter.com"
105
44
  source_url = "https://github.com/mcp-proxy-adapter"
106
- result_class = SSLSetupResult
45
+ result_class = SuccessResult
107
46
 
108
47
  def __init__(self):
109
- """Initialize SSL setup command."""
48
+ """Initialize SSL Setup Command."""
110
49
  super().__init__()
111
- self.certificate_utils = CertificateUtils()
112
- self.auth_validator = AuthValidator()
113
50
 
114
- async def execute(self, **kwargs) -> CommandResult:
51
+ async def execute(self, **kwargs) -> SuccessResult | ErrorResult:
115
52
  """
116
53
  Execute SSL setup command.
117
54
 
118
55
  Args:
119
- **kwargs: Command parameters including:
120
- - action: Action to perform (ssl_setup, ssl_status, ssl_test, ssl_config)
121
- - ssl_config: SSL configuration for ssl_setup action
122
- - cert_file: Certificate file path for ssl_test action
123
- - key_file: Key file path for ssl_test action
124
- - action: Action for ssl_config (get, set, update, reset)
125
- - config_data: Configuration data for ssl_config action
56
+ params: Command parameters including:
57
+ - action: Operation to perform (get, set, update, reset, test)
58
+ - config_data: Configuration data for set/update actions
59
+ - cert_file: Certificate file path for testing
60
+ - key_file: Private key file path for testing
126
61
 
127
62
  Returns:
128
- CommandResult with SSL operation status
129
- """
130
- action = kwargs.get("action", "ssl_status")
131
-
132
- if action == "ssl_setup":
133
- ssl_config = kwargs.get("ssl_config", {})
134
- return await self.ssl_setup(ssl_config)
135
- elif action == "ssl_status":
136
- return await self.ssl_status()
137
- elif action == "ssl_test":
138
- cert_file = kwargs.get("cert_file")
139
- key_file = kwargs.get("key_file")
140
- return await self.ssl_test(cert_file, key_file)
141
- elif action == "ssl_config":
142
- config_action = kwargs.get("config_action", "get")
143
- config_data = kwargs.get("config_data", {})
144
- return await self.ssl_config(config_action, config_data)
145
- else:
146
- return ErrorResult(
147
- message=f"Unknown action: {action}. Supported actions: ssl_setup, ssl_status, ssl_test, ssl_config"
148
- )
149
-
150
- async def ssl_setup(self, ssl_config: Dict[str, Any]) -> CommandResult:
151
- """
152
- Setup SSL configuration.
153
-
154
- Args:
155
- ssl_config: SSL configuration dictionary containing:
156
- - enabled: Whether to enable SSL
157
- - cert_file: Path to certificate file
158
- - key_file: Path to private key file
159
- - ca_file: Path to CA certificate file (optional)
160
- - verify_mode: SSL verification mode (optional)
161
- - cipher_suites: List of allowed cipher suites (optional)
162
-
163
- Returns:
164
- CommandResult with SSL setup status
63
+ SuccessResult or ErrorResult
165
64
  """
166
65
  try:
167
- logger.info("Setting up SSL configuration")
66
+ action = kwargs.get("action", "get")
168
67
 
169
- # Validate SSL configuration
170
- if not isinstance(ssl_config, dict):
171
- return ErrorResult(
172
- message="SSL configuration must be a dictionary"
68
+ if action == "get":
69
+ return await self._get_ssl_config()
70
+ elif action == "set":
71
+ return await self._set_ssl_config(kwargs.get("config_data", {}))
72
+ elif action == "update":
73
+ return await self._update_ssl_config(kwargs.get("config_data", {}))
74
+ elif action == "reset":
75
+ return await self._reset_ssl_config()
76
+ elif action == "test":
77
+ return await self._test_ssl_config(
78
+ kwargs.get("cert_file"),
79
+ kwargs.get("key_file")
173
80
  )
174
-
175
- enabled = ssl_config.get("enabled", False)
176
- if not enabled:
177
- result = SSLSetupResult(
178
- ssl_enabled=False,
179
- status="disabled",
180
- config=ssl_config
81
+ else:
82
+ return ErrorResult(
83
+ message=f"Unknown action: {action}. Supported actions: get, set, update, reset, test"
181
84
  )
182
- return SuccessResult(data=result.to_dict())
85
+
86
+ except Exception as e:
87
+ logger.error(f"SSL setup command failed: {e}")
88
+ return ErrorResult(
89
+ message=f"SSL setup command failed: {str(e)}"
90
+ )
91
+
92
+ async def _get_ssl_config(self) -> SuccessResult | ErrorResult:
93
+ """Get current SSL configuration."""
94
+ try:
95
+ config = Config()
96
+ ssl_config = config.get("ssl", {})
183
97
 
184
- # Validate required files
185
- cert_file = ssl_config.get("cert_file")
186
- key_file = ssl_config.get("key_file")
98
+ # Add framework information
99
+ ssl_config["framework_available"] = SECURITY_FRAMEWORK_AVAILABLE
187
100
 
188
- if not cert_file or not key_file:
189
- return ErrorResult(
190
- message="Certificate and key files are required when SSL is enabled"
191
- )
101
+ return SuccessResult(
102
+ data={"ssl_config": ssl_config}
103
+ )
192
104
 
193
- # Check if files exist
194
- if not os.path.exists(cert_file):
195
- return ErrorResult(
196
- message=f"Certificate file not found: {cert_file}"
197
- )
105
+ except Exception as e:
106
+ logger.error(f"Failed to get SSL config: {e}")
107
+ return ErrorResult(
108
+ message=f"Failed to get SSL config: {str(e)}"
109
+ )
198
110
 
199
- if not os.path.exists(key_file):
111
+ async def _set_ssl_config(self, config_data: Dict[str, Any]) -> SuccessResult | ErrorResult:
112
+ """Set SSL configuration."""
113
+ try:
114
+ if not isinstance(config_data, dict):
200
115
  return ErrorResult(
201
- message=f"Private key file not found: {key_file}"
116
+ message="Configuration data must be a dictionary"
202
117
  )
203
118
 
204
- # Validate certificate and key
205
- cert_validation = self.auth_validator.validate_certificate(cert_file)
206
- if not cert_validation.is_valid:
207
- return ErrorResult(
208
- message=f"Certificate validation failed: {cert_validation.error_message}"
209
- )
119
+ # Validate configuration if mcp_security_framework is available
120
+ if SECURITY_FRAMEWORK_AVAILABLE:
121
+ try:
122
+ ssl_config = SSLConfig(**config_data)
123
+ config_data = ssl_config.dict()
124
+ except Exception as e:
125
+ return ErrorResult(
126
+ message=f"Invalid SSL configuration: {str(e)}"
127
+ )
210
128
 
211
- # Test SSL configuration
212
- test_result = await self._test_ssl_config(cert_file, key_file)
213
- if not test_result["success"]:
214
- return ErrorResult(
215
- message=f"SSL configuration test failed: {test_result['error']}"
216
- )
129
+ # Update configuration
130
+ config = Config()
131
+ config.update_config({"ssl": config_data})
217
132
 
218
- result = SSLSetupResult(
219
- ssl_enabled=True,
220
- cert_path=cert_file,
221
- key_path=key_file,
222
- config=ssl_config,
223
- status="enabled"
133
+ return SuccessResult(
134
+ data={"message": "SSL configuration updated", "ssl_config": config_data}
224
135
  )
225
136
 
226
- logger.info("SSL configuration setup completed successfully")
227
- return SuccessResult(data=result.to_dict())
228
-
229
137
  except Exception as e:
230
- logger.error(f"SSL setup failed: {e}")
138
+ logger.error(f"Failed to set SSL config: {e}")
231
139
  return ErrorResult(
232
- message=f"SSL setup failed: {str(e)}"
140
+ message=f"Failed to set SSL config: {str(e)}"
233
141
  )
234
142
 
235
- async def ssl_status(self) -> CommandResult:
236
- """
237
- Get current SSL status.
238
-
239
- Returns:
240
- CommandResult with SSL status information
241
- """
143
+ async def _update_ssl_config(self, config_data: Dict[str, Any]) -> SuccessResult | ErrorResult:
144
+ """Update SSL configuration."""
242
145
  try:
243
- logger.info("Checking SSL status")
146
+ if not isinstance(config_data, dict):
147
+ return ErrorResult(
148
+ message="Configuration data must be a dictionary"
149
+ )
244
150
 
245
- # Check if SSL is configured in the application
246
- from ...config import Config
247
151
  config = Config()
152
+ current_config = config.get("ssl", {})
248
153
 
249
- ssl_config = config.get("ssl", {})
250
- enabled = ssl_config.get("enabled", False)
154
+ # Update with new data
155
+ current_config.update(config_data)
251
156
 
252
- if not enabled:
253
- result = SSLSetupResult(
254
- ssl_enabled=False,
255
- status="disabled",
256
- config=ssl_config
257
- )
258
- return SuccessResult(data=result.to_dict())
157
+ # Validate configuration if mcp_security_framework is available
158
+ if SECURITY_FRAMEWORK_AVAILABLE:
159
+ try:
160
+ ssl_config = SSLConfig(**current_config)
161
+ current_config = ssl_config.dict()
162
+ except Exception as e:
163
+ return ErrorResult(
164
+ message=f"Invalid SSL configuration: {str(e)}"
165
+ )
259
166
 
260
- cert_file = ssl_config.get("cert_file")
261
- key_file = ssl_config.get("key_file")
167
+ # Update configuration
168
+ config.update_config({"ssl": current_config})
262
169
 
263
- # Check file existence
264
- cert_exists = os.path.exists(cert_file) if cert_file else False
265
- key_exists = os.path.exists(key_file) if key_file else False
170
+ return SuccessResult(
171
+ data={"message": "SSL configuration updated", "ssl_config": current_config}
172
+ )
266
173
 
267
- if not cert_exists or not key_exists:
268
- result = SSLSetupResult(
269
- ssl_enabled=False,
270
- status="error",
271
- cert_path=cert_file,
272
- key_path=key_file,
273
- config=ssl_config,
274
- error="Certificate or key file not found"
275
- )
276
- return SuccessResult(data=result.to_dict())
174
+ except Exception as e:
175
+ logger.error(f"Failed to update SSL config: {e}")
176
+ return ErrorResult(
177
+ message=f"Failed to update SSL config: {str(e)}"
178
+ )
179
+
180
+ async def _reset_ssl_config(self) -> SuccessResult | ErrorResult:
181
+ """Reset SSL configuration to defaults."""
182
+ try:
183
+ default_config = {
184
+ "enabled": False,
185
+ "cert_file": None,
186
+ "key_file": None,
187
+ "ca_file": None,
188
+ "verify_mode": "CERT_REQUIRED",
189
+ "cipher_suites": [],
190
+ "framework_available": SECURITY_FRAMEWORK_AVAILABLE
191
+ }
277
192
 
278
- # Validate certificate
279
- cert_validation = self.auth_validator.validate_certificate(cert_file)
280
- if not cert_validation.is_valid:
281
- result = SSLSetupResult(
282
- ssl_enabled=False,
283
- status="error",
284
- cert_path=cert_file,
285
- key_path=key_file,
286
- config=ssl_config,
287
- error=f"Certificate validation failed: {cert_validation.error_message}"
288
- )
289
- return SuccessResult(data=result.to_dict())
193
+ config = Config()
194
+ config.update_config({"ssl": default_config})
290
195
 
291
- result = SSLSetupResult(
292
- ssl_enabled=True,
293
- cert_path=cert_file,
294
- key_path=key_file,
295
- config=ssl_config,
296
- status="enabled"
196
+ return SuccessResult(
197
+ data={"message": "SSL configuration reset to defaults", "ssl_config": default_config}
297
198
  )
298
199
 
299
- logger.info("SSL status check completed")
300
- return SuccessResult(data=result.to_dict())
301
-
302
200
  except Exception as e:
303
- logger.error(f"SSL status check failed: {e}")
201
+ logger.error(f"Failed to reset SSL config: {e}")
304
202
  return ErrorResult(
305
- message=f"SSL status check failed: {str(e)}"
203
+ message=f"Failed to reset SSL config: {str(e)}"
306
204
  )
307
205
 
308
- async def ssl_test(self, cert_file: str, key_file: str) -> CommandResult:
206
+ async def _test_ssl_config(self, cert_file: Optional[str], key_file: Optional[str]) -> SuccessResult | ErrorResult:
309
207
  """
310
- Test SSL configuration with certificate and key files.
208
+ Test SSL configuration.
311
209
 
312
210
  Args:
313
211
  cert_file: Path to certificate file
314
212
  key_file: Path to private key file
315
213
 
316
214
  Returns:
317
- CommandResult with test results
215
+ SuccessResult or ErrorResult with test results
318
216
  """
319
217
  try:
320
- logger.info(f"Testing SSL configuration with cert: {cert_file}, key: {key_file}")
321
-
322
- # Validate parameters
323
218
  if not cert_file or not key_file:
324
219
  return ErrorResult(
325
- message="Certificate and key file paths are required"
326
- )
327
-
328
- # Check file existence
329
- if not os.path.exists(cert_file):
330
- return ErrorResult(
331
- message=f"Certificate file not found: {cert_file}"
220
+ message="Both cert_file and key_file are required for testing"
332
221
  )
333
222
 
334
- if not os.path.exists(key_file):
335
- return ErrorResult(
336
- message=f"Private key file not found: {key_file}"
337
- )
338
-
339
- # Test SSL configuration
340
- test_result = await self._test_ssl_config(cert_file, key_file)
341
-
342
- if test_result["success"]:
343
- logger.info("SSL configuration test passed")
344
- return SuccessResult(
345
- data={
346
- "test_passed": True,
347
- "cert_file": cert_file,
348
- "key_file": key_file,
349
- "details": test_result["details"]
350
- }
351
- )
223
+ if SECURITY_FRAMEWORK_AVAILABLE:
224
+ return await self._test_ssl_config_with_framework(cert_file, key_file)
352
225
  else:
353
- logger.error(f"SSL configuration test failed: {test_result['error']}")
354
- return ErrorResult(
355
- message=test_result["error"]
356
- )
226
+ return await self._test_ssl_config_fallback(cert_file, key_file)
357
227
 
358
228
  except Exception as e:
359
- logger.error(f"SSL test failed: {e}")
229
+ logger.error(f"Failed to test SSL config: {e}")
360
230
  return ErrorResult(
361
- message=f"SSL test failed: {str(e)}"
231
+ message=f"Failed to test SSL config: {str(e)}"
362
232
  )
363
233
 
364
- async def ssl_config(self, action: str, config_data: Dict[str, Any]) -> CommandResult:
365
- """
366
- Manage SSL configuration.
367
-
368
- Args:
369
- action: Action to perform (get, set, update, reset)
370
- config_data: Configuration data for the action
371
-
372
- Returns:
373
- CommandResult with configuration operation result
374
- """
234
+ async def _test_ssl_config_with_framework(self, cert_file: str, key_file: str) -> SuccessResult | ErrorResult:
235
+ """Test SSL configuration using mcp_security_framework."""
375
236
  try:
376
- logger.info(f"Performing SSL config action: {action}")
377
-
378
- from ...config import Config
379
- config = Config()
237
+ # Create SSL manager
238
+ ssl_config = SSLConfig(
239
+ cert_file=cert_file,
240
+ key_file=key_file,
241
+ enabled=True
242
+ )
380
243
 
381
- if action == "get":
382
- ssl_config = config.get("ssl", {})
383
- return SuccessResult(
384
- data={"ssl_config": ssl_config}
385
- )
244
+ ssl_manager = SSLManager(ssl_config)
386
245
 
387
- elif action == "set":
388
- # Validate configuration
389
- if not isinstance(config_data, dict):
390
- return ErrorResult(
391
- message="Configuration data must be a dictionary"
392
- )
393
-
394
- # Update configuration
395
- config.update_config({"ssl": config_data})
396
- return SuccessResult(
397
- data={"message": "SSL configuration updated", "ssl_config": config_data}
398
- )
246
+ # Test SSL context creation
247
+ context = ssl_manager.create_server_ssl_context()
399
248
 
400
- elif action == "update":
401
- # Get current configuration
402
- current_config = config.get("ssl", {})
403
-
404
- # Update with new data
405
- current_config.update(config_data)
406
-
407
- # Update configuration
408
- config.update_config({"ssl": current_config})
409
- return SuccessResult(
410
- data={"message": "SSL configuration updated", "ssl_config": current_config}
411
- )
249
+ details = {
250
+ "framework": "mcp_security_framework",
251
+ "certificate_loaded": True,
252
+ "private_key_loaded": True,
253
+ "context_created": True,
254
+ "cert_file": cert_file,
255
+ "key_file": key_file
256
+ }
412
257
 
413
- elif action == "reset":
414
- # Reset to default configuration
415
- default_config = {
416
- "enabled": False,
417
- "cert_file": None,
418
- "key_file": None,
419
- "ca_file": None,
420
- "verify_mode": "CERT_REQUIRED",
421
- "cipher_suites": []
258
+ return SuccessResult(
259
+ data={
260
+ "success": True,
261
+ "details": details
422
262
  }
423
-
424
- config.update_config({"ssl": default_config})
425
- return SuccessResult(
426
- data={"message": "SSL configuration reset to defaults", "ssl_config": default_config}
427
- )
428
-
429
- else:
430
- return ErrorResult(
431
- message=f"Unknown action: {action}. Supported actions: get, set, update, reset"
432
- )
263
+ )
433
264
 
434
265
  except Exception as e:
435
- logger.error(f"SSL config action failed: {e}")
436
266
  return ErrorResult(
437
- message=f"SSL config action failed: {str(e)}"
267
+ message=f"SSL test failed: {str(e)}",
268
+ data={
269
+ "success": False,
270
+ "error": str(e),
271
+ "details": {
272
+ "framework": "mcp_security_framework",
273
+ "certificate_loaded": False,
274
+ "private_key_loaded": False,
275
+ "context_created": False
276
+ }
277
+ }
438
278
  )
439
279
 
440
- async def _test_ssl_config(self, cert_file: str, key_file: str) -> Dict[str, Any]:
441
- """
442
- Test SSL configuration internally.
443
-
444
- Args:
445
- cert_file: Path to certificate file
446
- key_file: Path to private key file
447
-
448
- Returns:
449
- Dictionary with test results
450
- """
280
+ async def _test_ssl_config_fallback(self, cert_file: str, key_file: str) -> SuccessResult | ErrorResult:
281
+ """Test SSL configuration using fallback method."""
451
282
  try:
452
283
  # Create SSL context
453
284
  context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
@@ -459,25 +290,77 @@ class SSLSetupCommand(Command):
459
290
 
460
291
  # Test basic SSL functionality
461
292
  details = {
293
+ "framework": "fallback (ssl module)",
462
294
  "ssl_version": ssl.OPENSSL_VERSION,
463
295
  "certificate_loaded": True,
464
296
  "private_key_loaded": True,
465
- "context_created": True
297
+ "context_created": True,
298
+ "cert_file": cert_file,
299
+ "key_file": key_file
466
300
  }
467
301
 
468
- return {
302
+ return SuccessResult(
303
+ data={
469
304
  "success": True,
470
305
  "details": details
471
306
  }
307
+ )
472
308
 
473
309
  except Exception as e:
474
- return {
310
+ return ErrorResult(
311
+ message=f"SSL test failed: {str(e)}",
312
+ data={
475
313
  "success": False,
476
314
  "error": str(e),
477
315
  "details": {
316
+ "framework": "fallback (ssl module)",
478
317
  "ssl_version": ssl.OPENSSL_VERSION,
479
318
  "certificate_loaded": False,
480
319
  "private_key_loaded": False,
481
320
  "context_created": False
482
321
  }
322
+ }
323
+ )
324
+
325
+ def to_dict(self) -> Dict[str, Any]:
326
+ """Convert command to dictionary."""
327
+ return {
328
+ "name": self.name,
329
+ "description": self.description,
330
+ "version": self.version,
331
+ "framework_available": SECURITY_FRAMEWORK_AVAILABLE
332
+ }
333
+
334
+ def get_schema(self) -> Dict[str, Any]:
335
+ """Get command schema."""
336
+ return {
337
+ "type": "object",
338
+ "properties": {
339
+ "action": {
340
+ "type": "string",
341
+ "enum": ["get", "set", "update", "reset", "test"],
342
+ "description": "Action to perform"
343
+ },
344
+ "config_data": {
345
+ "type": "object",
346
+ "description": "SSL configuration data",
347
+ "properties": {
348
+ "enabled": {"type": "boolean"},
349
+ "cert_file": {"type": "string"},
350
+ "key_file": {"type": "string"},
351
+ "ca_file": {"type": "string"},
352
+ "verify_mode": {"type": "string"},
353
+ "cipher_suites": {"type": "array", "items": {"type": "string"}}
354
+ }
355
+ },
356
+ "cert_file": {
357
+ "type": "string",
358
+ "description": "Certificate file path for testing"
359
+ },
360
+ "key_file": {
361
+ "type": "string",
362
+ "description": "Private key file path for testing"
363
+ }
364
+ },
365
+ "required": ["action"]
483
366
  }