mcp-proxy-adapter 6.2.36__py3-none-any.whl → 6.3.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.
- mcp_proxy_adapter/core/app_factory.py +127 -86
- mcp_proxy_adapter/core/config_validator.py +92 -55
- mcp_proxy_adapter/core/crl_utils.py +89 -76
- mcp_proxy_adapter/core/security_integration.py +110 -77
- mcp_proxy_adapter/main.py +19 -10
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.2.36.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.2.36.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/RECORD +12 -12
- {mcp_proxy_adapter-6.2.36.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.2.36.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.2.36.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.2.36.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/top_level.txt +0 -0
@@ -7,8 +7,8 @@ with invalid or insecure configurations.
|
|
7
7
|
|
8
8
|
import os
|
9
9
|
import json
|
10
|
-
|
11
|
-
from
|
10
|
+
import uuid
|
11
|
+
from typing import Dict, Any, List
|
12
12
|
|
13
13
|
from mcp_proxy_adapter.core.logging import logger
|
14
14
|
|
@@ -16,220 +16,257 @@ from mcp_proxy_adapter.core.logging import logger
|
|
16
16
|
class ConfigValidator:
|
17
17
|
"""
|
18
18
|
Strict configuration validator.
|
19
|
-
|
19
|
+
|
20
20
|
Validates configuration before startup and prevents startup
|
21
21
|
with invalid or insecure configurations.
|
22
22
|
"""
|
23
|
-
|
23
|
+
|
24
24
|
def __init__(self, config: Dict[str, Any]):
|
25
25
|
"""
|
26
26
|
Initialize configuration validator.
|
27
|
-
|
27
|
+
|
28
28
|
Args:
|
29
29
|
config: Configuration dictionary to validate
|
30
30
|
"""
|
31
31
|
self.config = config
|
32
32
|
self.errors: List[str] = []
|
33
33
|
self.warnings: List[str] = []
|
34
|
-
|
34
|
+
|
35
35
|
def validate_all(self) -> bool:
|
36
36
|
"""
|
37
37
|
Validate all configuration sections.
|
38
|
-
|
38
|
+
|
39
39
|
Returns:
|
40
40
|
True if configuration is valid, False otherwise
|
41
41
|
"""
|
42
42
|
self.errors.clear()
|
43
43
|
self.warnings.clear()
|
44
|
-
|
44
|
+
|
45
45
|
# Validate basic structure
|
46
46
|
if not self._validate_basic_structure():
|
47
47
|
return False
|
48
|
-
|
48
|
+
|
49
|
+
# Validate UUID configuration (mandatory)
|
50
|
+
if not self._validate_uuid_config():
|
51
|
+
return False
|
52
|
+
|
49
53
|
# Validate server configuration
|
50
54
|
if not self._validate_server_config():
|
51
55
|
return False
|
52
|
-
|
56
|
+
|
53
57
|
# Validate security configuration
|
54
58
|
if not self._validate_security_config():
|
55
59
|
return False
|
56
|
-
|
60
|
+
|
57
61
|
# Validate commands configuration
|
58
62
|
if not self._validate_commands_config():
|
59
63
|
return False
|
60
|
-
|
64
|
+
|
61
65
|
# Validate SSL configuration
|
62
66
|
if not self._validate_ssl_config():
|
63
67
|
return False
|
64
|
-
|
68
|
+
|
65
69
|
# Validate roles configuration
|
66
70
|
if not self._validate_roles_config():
|
67
71
|
return False
|
68
|
-
|
72
|
+
|
69
73
|
# Log warnings if any
|
70
74
|
if self.warnings:
|
71
75
|
for warning in self.warnings:
|
72
76
|
logger.warning(f"Configuration warning: {warning}")
|
73
|
-
|
77
|
+
|
74
78
|
# Return success only if no errors
|
75
79
|
return len(self.errors) == 0
|
76
|
-
|
80
|
+
|
77
81
|
def _validate_basic_structure(self) -> bool:
|
78
82
|
"""Validate basic configuration structure."""
|
79
83
|
required_sections = ["server", "logging", "commands"]
|
80
|
-
|
84
|
+
|
81
85
|
for section in required_sections:
|
82
86
|
if section not in self.config:
|
83
87
|
self.errors.append(f"Missing required configuration section: {section}")
|
84
|
-
|
88
|
+
|
85
89
|
return len(self.errors) == 0
|
86
|
-
|
90
|
+
|
91
|
+
def _validate_uuid_config(self) -> bool:
|
92
|
+
"""Validate UUID configuration (mandatory parameter)."""
|
93
|
+
# Check if UUID is present in root config
|
94
|
+
service_uuid = self.config.get("uuid")
|
95
|
+
|
96
|
+
if not service_uuid:
|
97
|
+
self.errors.append(
|
98
|
+
"UUID is required in configuration. Add 'uuid' field with a valid UUID4 value."
|
99
|
+
)
|
100
|
+
return False
|
101
|
+
|
102
|
+
# Validate UUID format
|
103
|
+
try:
|
104
|
+
# Try to parse as UUID
|
105
|
+
parsed_uuid = uuid.UUID(service_uuid)
|
106
|
+
|
107
|
+
# Check if it's UUID4 (version 4)
|
108
|
+
if parsed_uuid.version != 4:
|
109
|
+
self.errors.append(
|
110
|
+
f"UUID must be version 4 (UUID4). Current version: {parsed_uuid.version}"
|
111
|
+
)
|
112
|
+
return False
|
113
|
+
|
114
|
+
except (ValueError, TypeError) as e:
|
115
|
+
self.errors.append(f"Invalid UUID format: {service_uuid}. Error: {e}")
|
116
|
+
return False
|
117
|
+
|
118
|
+
return len(self.errors) == 0
|
119
|
+
|
87
120
|
def _validate_server_config(self) -> bool:
|
88
121
|
"""Validate server configuration."""
|
89
122
|
server_config = self.config.get("server", {})
|
90
|
-
|
123
|
+
|
91
124
|
# Validate host
|
92
125
|
host = server_config.get("host")
|
93
126
|
if not host:
|
94
127
|
self.errors.append("Server host is required")
|
95
|
-
|
128
|
+
|
96
129
|
# Validate port
|
97
130
|
port = server_config.get("port")
|
98
131
|
if not isinstance(port, int) or port < 1 or port > 65535:
|
99
132
|
self.errors.append("Server port must be an integer between 1 and 65535")
|
100
|
-
|
133
|
+
|
101
134
|
return len(self.errors) == 0
|
102
|
-
|
135
|
+
|
103
136
|
def _validate_security_config(self) -> bool:
|
104
137
|
"""Validate security configuration."""
|
105
138
|
security_config = self.config.get("security", {})
|
106
|
-
|
139
|
+
|
107
140
|
# Check if security is enabled
|
108
141
|
security_enabled = security_config.get("enabled", True)
|
109
142
|
auth_enabled = self.config.get("auth_enabled", False)
|
110
|
-
|
143
|
+
|
111
144
|
# Validate permissions configuration
|
112
145
|
permissions_config = security_config.get("permissions", {})
|
113
146
|
permissions_enabled = permissions_config.get("enabled", False)
|
114
|
-
|
147
|
+
|
115
148
|
if permissions_enabled:
|
116
149
|
# Permissions require authentication to identify users
|
117
150
|
auth_config = security_config.get("auth", {})
|
118
151
|
if not auth_config.get("enabled", False):
|
119
|
-
self.errors.append(
|
152
|
+
self.errors.append(
|
153
|
+
"Permissions are enabled but authentication is disabled. Permissions require authentication to identify users."
|
154
|
+
)
|
120
155
|
return False
|
121
|
-
|
156
|
+
|
122
157
|
# Check if there are any authentication methods available
|
123
158
|
auth_methods = auth_config.get("methods", [])
|
124
159
|
if not auth_methods:
|
125
|
-
self.errors.append(
|
160
|
+
self.errors.append(
|
161
|
+
"Permissions are enabled but no authentication methods are configured. At least one authentication method is required."
|
162
|
+
)
|
126
163
|
return False
|
127
|
-
|
164
|
+
|
128
165
|
if security_enabled and auth_enabled:
|
129
166
|
# Validate auth configuration
|
130
167
|
auth_config = security_config.get("auth", {})
|
131
168
|
if not auth_config.get("enabled", False):
|
132
169
|
self.errors.append("Security is enabled but auth is disabled")
|
133
170
|
return False
|
134
|
-
|
171
|
+
|
135
172
|
# Validate API keys if auth is enabled
|
136
173
|
if auth_config.get("enabled", False):
|
137
174
|
api_keys = auth_config.get("api_keys", {})
|
138
175
|
if not api_keys:
|
139
176
|
self.errors.append("API keys are required when authentication is enabled")
|
140
177
|
return False
|
141
|
-
|
178
|
+
|
142
179
|
# Validate API key format
|
143
180
|
for key, value in api_keys.items():
|
144
181
|
if not key or not value:
|
145
182
|
self.errors.append("API keys must have non-empty key and value")
|
146
183
|
return False
|
147
|
-
|
184
|
+
|
148
185
|
return len(self.errors) == 0
|
149
|
-
|
186
|
+
|
150
187
|
def _validate_commands_config(self) -> bool:
|
151
188
|
"""Validate commands configuration."""
|
152
189
|
commands_config = self.config.get("commands", {})
|
153
|
-
|
190
|
+
|
154
191
|
# Validate commands directory if auto_discovery is enabled
|
155
192
|
if commands_config.get("auto_discovery", True):
|
156
193
|
commands_dir = commands_config.get("commands_directory", "./commands")
|
157
194
|
if not os.path.exists(commands_dir):
|
158
195
|
self.warnings.append(f"Commands directory does not exist: {commands_dir}")
|
159
|
-
|
196
|
+
|
160
197
|
return True
|
161
|
-
|
198
|
+
|
162
199
|
def _validate_ssl_config(self) -> bool:
|
163
200
|
"""Validate SSL configuration."""
|
164
201
|
ssl_config = self.config.get("ssl", {})
|
165
202
|
ssl_enabled = ssl_config.get("enabled", False)
|
166
|
-
|
203
|
+
|
167
204
|
if ssl_enabled:
|
168
205
|
# Validate certificate files
|
169
206
|
cert_file = ssl_config.get("cert_file")
|
170
207
|
key_file = ssl_config.get("key_file")
|
171
|
-
|
208
|
+
|
172
209
|
if not cert_file or not key_file:
|
173
210
|
self.errors.append("SSL certificate and key files are required when SSL is enabled")
|
174
211
|
return False
|
175
|
-
|
212
|
+
|
176
213
|
if not os.path.exists(cert_file):
|
177
214
|
self.errors.append(f"SSL certificate file not found: {cert_file}")
|
178
|
-
|
215
|
+
|
179
216
|
if not os.path.exists(key_file):
|
180
217
|
self.errors.append(f"SSL private key file not found: {key_file}")
|
181
|
-
|
218
|
+
|
182
219
|
return len(self.errors) == 0
|
183
|
-
|
220
|
+
|
184
221
|
def _validate_roles_config(self) -> bool:
|
185
222
|
"""Validate roles configuration."""
|
186
223
|
roles_config = self.config.get("roles", {})
|
187
224
|
roles_enabled = roles_config.get("enabled", False)
|
188
|
-
|
225
|
+
|
189
226
|
if roles_enabled:
|
190
227
|
config_file = roles_config.get("config_file")
|
191
228
|
if not config_file:
|
192
229
|
self.errors.append("Roles config file is required when roles are enabled")
|
193
230
|
return False
|
194
|
-
|
231
|
+
|
195
232
|
if not os.path.exists(config_file):
|
196
233
|
self.errors.append(f"Roles config file not found: {config_file}")
|
197
234
|
return False
|
198
|
-
|
235
|
+
|
199
236
|
# Validate roles schema file
|
200
237
|
try:
|
201
|
-
with open(config_file,
|
238
|
+
with open(config_file, "r") as f:
|
202
239
|
roles_schema = json.load(f)
|
203
|
-
|
240
|
+
|
204
241
|
if "roles" not in roles_schema:
|
205
242
|
self.errors.append("Roles config file must contain 'roles' section")
|
206
243
|
return False
|
207
|
-
|
244
|
+
|
208
245
|
except (json.JSONDecodeError, IOError) as e:
|
209
246
|
self.errors.append(f"Failed to read roles config file: {e}")
|
210
247
|
return False
|
211
|
-
|
248
|
+
|
212
249
|
return len(self.errors) == 0
|
213
|
-
|
250
|
+
|
214
251
|
def get_errors(self) -> List[str]:
|
215
252
|
"""Get validation errors."""
|
216
253
|
return self.errors.copy()
|
217
|
-
|
254
|
+
|
218
255
|
def get_warnings(self) -> List[str]:
|
219
256
|
"""Get validation warnings."""
|
220
257
|
return self.warnings.copy()
|
221
|
-
|
258
|
+
|
222
259
|
def print_validation_report(self):
|
223
260
|
"""Print validation report."""
|
224
261
|
if self.errors:
|
225
262
|
logger.error("Configuration validation failed:")
|
226
263
|
for error in self.errors:
|
227
264
|
logger.error(f" - {error}")
|
228
|
-
|
265
|
+
|
229
266
|
if self.warnings:
|
230
267
|
logger.warning("Configuration warnings:")
|
231
268
|
for warning in self.warnings:
|
232
269
|
logger.warning(f" - {warning}")
|
233
|
-
|
270
|
+
|
234
271
|
if not self.errors and not self.warnings:
|
235
272
|
logger.info("Configuration validation passed")
|