mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.5__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/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +108 -88
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -16,15 +16,20 @@ from pathlib import Path
|
|
16
16
|
# Import framework utilities
|
17
17
|
try:
|
18
18
|
from mcp_security_framework.utils.crypto_utils import (
|
19
|
-
generate_api_key,
|
19
|
+
generate_api_key,
|
20
|
+
create_jwt_token,
|
21
|
+
verify_jwt_token,
|
20
22
|
)
|
21
23
|
from mcp_security_framework.utils.cert_utils import (
|
22
|
-
parse_certificate,
|
23
|
-
|
24
|
+
parse_certificate,
|
25
|
+
extract_roles_from_certificate,
|
26
|
+
validate_certificate_chain,
|
27
|
+
validate_certificate_format,
|
24
28
|
)
|
25
29
|
from mcp_security_framework.core.ssl_manager import SSLManager
|
26
30
|
from mcp_security_framework.schemas.config import SSLConfig, AuthConfig
|
27
31
|
from mcp_security_framework.schemas.models import AuthResult, ValidationResult
|
32
|
+
|
28
33
|
SECURITY_FRAMEWORK_AVAILABLE = True
|
29
34
|
except ImportError:
|
30
35
|
SECURITY_FRAMEWORK_AVAILABLE = False
|
@@ -40,36 +45,36 @@ from mcp_proxy_adapter.core.logging import logger
|
|
40
45
|
class ClientSecurityManager:
|
41
46
|
"""
|
42
47
|
Client-side security manager for MCP Proxy Adapter.
|
43
|
-
|
48
|
+
|
44
49
|
Provides secure client connections using mcp_security_framework utilities.
|
45
50
|
Handles authentication, certificate management, and SSL/TLS for client connections.
|
46
51
|
"""
|
47
|
-
|
52
|
+
|
48
53
|
def __init__(self, config: Dict[str, Any]):
|
49
54
|
"""
|
50
55
|
Initialize client security manager.
|
51
|
-
|
56
|
+
|
52
57
|
Args:
|
53
58
|
config: Security configuration
|
54
59
|
"""
|
55
60
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
56
61
|
raise ImportError("mcp_security_framework is not available")
|
57
|
-
|
62
|
+
|
58
63
|
self.config = config
|
59
64
|
self.security_config = config.get("security", {})
|
60
|
-
|
65
|
+
|
61
66
|
# Initialize SSL manager if needed
|
62
67
|
self.ssl_manager = None
|
63
68
|
if self.security_config.get("ssl", {}).get("enabled", False):
|
64
69
|
ssl_config = self._create_ssl_config()
|
65
70
|
self.ssl_manager = SSLManager(ssl_config)
|
66
|
-
|
71
|
+
|
67
72
|
logger.info("Client security manager initialized")
|
68
|
-
|
73
|
+
|
69
74
|
def _create_ssl_config(self) -> SSLConfig:
|
70
75
|
"""Create SSL configuration for client connections."""
|
71
76
|
ssl_section = self.security_config.get("ssl", {})
|
72
|
-
|
77
|
+
|
73
78
|
return SSLConfig(
|
74
79
|
enabled=ssl_section.get("enabled", False),
|
75
80
|
cert_file=ssl_section.get("client_cert_file"),
|
@@ -78,46 +83,50 @@ class ClientSecurityManager:
|
|
78
83
|
verify_mode=ssl_section.get("verify_mode", "CERT_REQUIRED"),
|
79
84
|
min_tls_version=ssl_section.get("min_tls_version", "TLSv1.2"),
|
80
85
|
check_hostname=ssl_section.get("check_hostname", True),
|
81
|
-
check_expiry=ssl_section.get("check_expiry", True)
|
86
|
+
check_expiry=ssl_section.get("check_expiry", True),
|
82
87
|
)
|
83
|
-
|
84
|
-
def create_client_ssl_context(
|
88
|
+
|
89
|
+
def create_client_ssl_context(
|
90
|
+
self, server_hostname: Optional[str] = None
|
91
|
+
) -> Optional[ssl.SSLContext]:
|
85
92
|
"""
|
86
93
|
Create SSL context for client connections.
|
87
|
-
|
94
|
+
|
88
95
|
Args:
|
89
96
|
server_hostname: Server hostname for SNI
|
90
|
-
|
97
|
+
|
91
98
|
Returns:
|
92
99
|
SSL context or None if SSL not enabled
|
93
100
|
"""
|
94
101
|
if not self.ssl_manager:
|
95
102
|
return None
|
96
|
-
|
103
|
+
|
97
104
|
try:
|
98
105
|
# Create client SSL context
|
99
106
|
context = self.ssl_manager.create_client_context()
|
100
|
-
|
107
|
+
|
101
108
|
if server_hostname:
|
102
109
|
# Set server hostname for SNI
|
103
110
|
context.check_hostname = True
|
104
111
|
context.verify_mode = ssl.CERT_REQUIRED
|
105
|
-
|
106
|
-
logger.info(
|
112
|
+
|
113
|
+
logger.info(
|
114
|
+
f"Client SSL context created for {server_hostname or 'unknown server'}"
|
115
|
+
)
|
107
116
|
return context
|
108
|
-
|
117
|
+
|
109
118
|
except Exception as e:
|
110
119
|
logger.error(f"Failed to create client SSL context: {e}")
|
111
120
|
return None
|
112
|
-
|
121
|
+
|
113
122
|
def generate_client_api_key(self, user_id: str, prefix: str = "mcp_proxy") -> str:
|
114
123
|
"""
|
115
124
|
Generate API key for client authentication.
|
116
|
-
|
125
|
+
|
117
126
|
Args:
|
118
127
|
user_id: User identifier
|
119
128
|
prefix: Key prefix
|
120
|
-
|
129
|
+
|
121
130
|
Returns:
|
122
131
|
Generated API key
|
123
132
|
"""
|
@@ -128,52 +137,55 @@ class ClientSecurityManager:
|
|
128
137
|
except Exception as e:
|
129
138
|
logger.error(f"Failed to generate API key: {e}")
|
130
139
|
raise
|
131
|
-
|
132
|
-
def create_client_jwt_token(
|
133
|
-
|
134
|
-
|
140
|
+
|
141
|
+
def create_client_jwt_token(
|
142
|
+
self,
|
143
|
+
user_id: str,
|
144
|
+
roles: List[str],
|
145
|
+
secret: str,
|
146
|
+
algorithm: str = "HS256",
|
147
|
+
expiry_hours: int = 24,
|
148
|
+
) -> str:
|
135
149
|
"""
|
136
150
|
Create JWT token for client authentication.
|
137
|
-
|
151
|
+
|
138
152
|
Args:
|
139
153
|
user_id: User identifier
|
140
154
|
roles: User roles
|
141
155
|
secret: JWT secret
|
142
156
|
algorithm: JWT algorithm
|
143
157
|
expiry_hours: Token expiry in hours
|
144
|
-
|
158
|
+
|
145
159
|
Returns:
|
146
160
|
JWT token
|
147
161
|
"""
|
148
162
|
try:
|
149
|
-
payload = {
|
150
|
-
|
151
|
-
"roles": roles,
|
152
|
-
"type": "client_proxy"
|
153
|
-
}
|
154
|
-
|
163
|
+
payload = {"user_id": user_id, "roles": roles, "type": "client_proxy"}
|
164
|
+
|
155
165
|
token = create_jwt_token(
|
156
166
|
payload=payload,
|
157
167
|
secret=secret,
|
158
168
|
algorithm=algorithm,
|
159
|
-
expiry_hours=expiry_hours
|
169
|
+
expiry_hours=expiry_hours,
|
160
170
|
)
|
161
|
-
|
171
|
+
|
162
172
|
logger.info(f"Created JWT token for user: {user_id}")
|
163
173
|
return token
|
164
|
-
|
174
|
+
|
165
175
|
except Exception as e:
|
166
176
|
logger.error(f"Failed to create JWT token: {e}")
|
167
177
|
raise
|
168
|
-
|
169
|
-
def validate_server_certificate(
|
178
|
+
|
179
|
+
def validate_server_certificate(
|
180
|
+
self, cert_path: str, ca_cert_path: Optional[str] = None
|
181
|
+
) -> bool:
|
170
182
|
"""
|
171
183
|
Validate server certificate before connection.
|
172
|
-
|
184
|
+
|
173
185
|
Args:
|
174
186
|
cert_path: Path to server certificate
|
175
187
|
ca_cert_path: Path to CA certificate
|
176
|
-
|
188
|
+
|
177
189
|
Returns:
|
178
190
|
True if certificate is valid
|
179
191
|
"""
|
@@ -182,33 +194,33 @@ class ClientSecurityManager:
|
|
182
194
|
if not validate_certificate_format(cert_path):
|
183
195
|
logger.error(f"Invalid certificate format: {cert_path}")
|
184
196
|
return False
|
185
|
-
|
197
|
+
|
186
198
|
# Validate certificate chain if CA provided
|
187
199
|
if ca_cert_path:
|
188
200
|
if not validate_certificate_chain(cert_path, ca_cert_path):
|
189
201
|
logger.error(f"Invalid certificate chain: {cert_path}")
|
190
202
|
return False
|
191
|
-
|
203
|
+
|
192
204
|
# Parse certificate and check basic properties
|
193
205
|
cert_info = parse_certificate(cert_path)
|
194
206
|
if not cert_info:
|
195
207
|
logger.error(f"Failed to parse certificate: {cert_path}")
|
196
208
|
return False
|
197
|
-
|
209
|
+
|
198
210
|
logger.info(f"Server certificate validated: {cert_path}")
|
199
211
|
return True
|
200
|
-
|
212
|
+
|
201
213
|
except Exception as e:
|
202
214
|
logger.error(f"Failed to validate server certificate: {e}")
|
203
215
|
return False
|
204
|
-
|
216
|
+
|
205
217
|
def extract_server_roles(self, cert_path: str) -> List[str]:
|
206
218
|
"""
|
207
219
|
Extract roles from server certificate.
|
208
|
-
|
220
|
+
|
209
221
|
Args:
|
210
222
|
cert_path: Path to server certificate
|
211
|
-
|
223
|
+
|
212
224
|
Returns:
|
213
225
|
List of roles extracted from certificate
|
214
226
|
"""
|
@@ -219,88 +231,94 @@ class ClientSecurityManager:
|
|
219
231
|
except Exception as e:
|
220
232
|
logger.error(f"Failed to extract roles from certificate: {e}")
|
221
233
|
return []
|
222
|
-
|
223
|
-
def get_client_auth_headers(
|
234
|
+
|
235
|
+
def get_client_auth_headers(
|
236
|
+
self, auth_method: str = "api_key", **kwargs
|
237
|
+
) -> Dict[str, str]:
|
224
238
|
"""
|
225
239
|
Get authentication headers for client requests.
|
226
|
-
|
240
|
+
|
227
241
|
Args:
|
228
242
|
auth_method: Authentication method (api_key, jwt, certificate)
|
229
243
|
**kwargs: Additional parameters
|
230
|
-
|
244
|
+
|
231
245
|
Returns:
|
232
246
|
Dictionary of authentication headers
|
233
247
|
"""
|
234
248
|
headers = {}
|
235
|
-
|
249
|
+
|
236
250
|
try:
|
237
251
|
if auth_method == "api_key":
|
238
252
|
api_key = kwargs.get("api_key")
|
239
253
|
if api_key:
|
240
254
|
headers["X-API-Key"] = api_key
|
241
255
|
headers["Authorization"] = f"Bearer {api_key}"
|
242
|
-
|
256
|
+
|
243
257
|
elif auth_method == "jwt":
|
244
258
|
token = kwargs.get("token")
|
245
259
|
if token:
|
246
260
|
headers["Authorization"] = f"Bearer {token}"
|
247
|
-
|
261
|
+
|
248
262
|
elif auth_method == "certificate":
|
249
263
|
# Certificate authentication is handled at SSL level
|
250
264
|
headers["X-Auth-Method"] = "certificate"
|
251
|
-
|
265
|
+
|
252
266
|
# Add common proxy headers
|
253
267
|
headers["X-Proxy-Type"] = "mcp_proxy_adapter"
|
254
268
|
headers["X-Client-Type"] = "proxy_client"
|
255
|
-
|
269
|
+
|
256
270
|
logger.debug(f"Created auth headers for method: {auth_method}")
|
257
271
|
return headers
|
258
|
-
|
272
|
+
|
259
273
|
except Exception as e:
|
260
274
|
logger.error(f"Failed to create auth headers: {e}")
|
261
275
|
return {}
|
262
|
-
|
263
|
-
def prepare_client_connection(
|
276
|
+
|
277
|
+
def prepare_client_connection(
|
278
|
+
self, server_config: Dict[str, Any]
|
279
|
+
) -> Tuple[Optional[ssl.SSLContext], Dict[str, str]]:
|
264
280
|
"""
|
265
281
|
Prepare secure client connection to server.
|
266
|
-
|
282
|
+
|
267
283
|
Args:
|
268
284
|
server_config: Server connection configuration
|
269
|
-
|
285
|
+
|
270
286
|
Returns:
|
271
287
|
Tuple of (SSL context, auth headers)
|
272
288
|
"""
|
273
289
|
ssl_context = None
|
274
290
|
auth_headers = {}
|
275
|
-
|
291
|
+
|
276
292
|
try:
|
277
293
|
# Create SSL context if needed
|
278
294
|
if server_config.get("ssl", False):
|
279
295
|
server_hostname = server_config.get("hostname")
|
280
296
|
ssl_context = self.create_client_ssl_context(server_hostname)
|
281
|
-
|
297
|
+
|
282
298
|
# Create authentication headers
|
283
299
|
auth_method = server_config.get("auth_method", "api_key")
|
284
300
|
auth_headers = self.get_client_auth_headers(
|
285
301
|
auth_method=auth_method,
|
286
302
|
api_key=server_config.get("api_key"),
|
287
|
-
token=server_config.get("token")
|
303
|
+
token=server_config.get("token"),
|
304
|
+
)
|
305
|
+
|
306
|
+
logger.info(
|
307
|
+
f"Prepared client connection for {server_config.get('hostname', 'unknown')}"
|
288
308
|
)
|
289
|
-
|
290
|
-
logger.info(f"Prepared client connection for {server_config.get('hostname', 'unknown')}")
|
291
309
|
return ssl_context, auth_headers
|
292
|
-
|
310
|
+
|
293
311
|
except Exception as e:
|
294
312
|
logger.error(f"Failed to prepare client connection: {e}")
|
295
313
|
return None, {}
|
296
|
-
|
314
|
+
|
297
315
|
def validate_server_response(self, response_headers: Dict[str, str]) -> bool:
|
298
316
|
"""
|
299
317
|
Validate server response for security compliance.
|
300
|
-
|
318
|
+
|
301
319
|
Args:
|
302
320
|
response_headers: Server response headers
|
303
|
-
|
321
|
+
|
304
322
|
Returns:
|
305
323
|
True if response is valid
|
306
324
|
"""
|
@@ -310,67 +328,69 @@ class ClientSecurityManager:
|
|
310
328
|
for header in required_headers:
|
311
329
|
if header not in response_headers:
|
312
330
|
logger.warning(f"Missing required header: {header}")
|
313
|
-
|
331
|
+
|
314
332
|
# Check for security headers
|
315
333
|
security_headers = ["X-Frame-Options", "X-Content-Type-Options"]
|
316
334
|
for header in security_headers:
|
317
335
|
if header in response_headers:
|
318
336
|
logger.debug(f"Found security header: {header}")
|
319
|
-
|
337
|
+
|
320
338
|
# Validate content type
|
321
339
|
content_type = response_headers.get("Content-Type", "")
|
322
340
|
if "application/json" not in content_type:
|
323
341
|
logger.warning(f"Unexpected content type: {content_type}")
|
324
|
-
|
342
|
+
|
325
343
|
return True
|
326
|
-
|
344
|
+
|
327
345
|
except Exception as e:
|
328
346
|
logger.error(f"Failed to validate server response: {e}")
|
329
347
|
return False
|
330
|
-
|
348
|
+
|
331
349
|
def get_client_certificate_info(self) -> Optional[Dict[str, Any]]:
|
332
350
|
"""
|
333
351
|
Get information about client certificate.
|
334
|
-
|
352
|
+
|
335
353
|
Returns:
|
336
354
|
Certificate information or None
|
337
355
|
"""
|
338
356
|
try:
|
339
357
|
ssl_config = self.security_config.get("ssl", {})
|
340
358
|
cert_path = ssl_config.get("client_cert_file")
|
341
|
-
|
359
|
+
|
342
360
|
if not cert_path or not Path(cert_path).exists():
|
343
361
|
return None
|
344
|
-
|
362
|
+
|
345
363
|
cert_info = parse_certificate(cert_path)
|
346
364
|
if cert_info:
|
347
365
|
roles = extract_roles_from_certificate(cert_path)
|
348
366
|
cert_info["roles"] = roles
|
349
367
|
return cert_info
|
350
|
-
|
368
|
+
|
351
369
|
return None
|
352
|
-
|
370
|
+
|
353
371
|
except Exception as e:
|
354
372
|
logger.error(f"Failed to get client certificate info: {e}")
|
355
373
|
return None
|
356
|
-
|
374
|
+
|
357
375
|
def is_ssl_enabled(self) -> bool:
|
358
376
|
"""Check if SSL is enabled for client connections."""
|
359
377
|
return self.security_config.get("ssl", {}).get("enabled", False)
|
360
|
-
|
378
|
+
|
361
379
|
def get_supported_auth_methods(self) -> List[str]:
|
362
380
|
"""Get list of supported authentication methods."""
|
363
381
|
return ["api_key", "jwt", "certificate"]
|
364
382
|
|
365
383
|
|
366
384
|
# Factory function for easy integration
|
367
|
-
def create_client_security_manager(
|
385
|
+
def create_client_security_manager(
|
386
|
+
config: Dict[str, Any]
|
387
|
+
) -> Optional[ClientSecurityManager]:
|
368
388
|
"""
|
369
389
|
Create client security manager instance.
|
370
|
-
|
390
|
+
|
371
391
|
Args:
|
372
392
|
config: Configuration dictionary
|
373
|
-
|
393
|
+
|
374
394
|
Returns:
|
375
395
|
ClientSecurityManager instance or None if framework not available
|
376
396
|
"""
|