mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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 +120 -91
- 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.6.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
- mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +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,84 +45,97 @@ 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
|
|
78
|
+
# Determine verify_mode based on verify_server setting
|
79
|
+
verify_server = ssl_section.get("verify_server", True)
|
80
|
+
if verify_server:
|
81
|
+
verify_mode = "CERT_REQUIRED"
|
82
|
+
check_hostname = True
|
83
|
+
else:
|
84
|
+
verify_mode = "CERT_NONE"
|
85
|
+
check_hostname = False
|
86
|
+
|
73
87
|
return SSLConfig(
|
74
88
|
enabled=ssl_section.get("enabled", False),
|
75
89
|
cert_file=ssl_section.get("client_cert_file"),
|
76
90
|
key_file=ssl_section.get("client_key_file"),
|
77
91
|
ca_cert_file=ssl_section.get("ca_cert_file"),
|
78
|
-
verify_mode=
|
92
|
+
verify_mode=verify_mode,
|
79
93
|
min_tls_version=ssl_section.get("min_tls_version", "TLSv1.2"),
|
80
|
-
check_hostname=
|
81
|
-
check_expiry=ssl_section.get("check_expiry", True)
|
94
|
+
check_hostname=check_hostname,
|
95
|
+
check_expiry=ssl_section.get("check_expiry", True),
|
82
96
|
)
|
83
|
-
|
84
|
-
def create_client_ssl_context(
|
97
|
+
|
98
|
+
def create_client_ssl_context(
|
99
|
+
self, server_hostname: Optional[str] = None
|
100
|
+
) -> Optional[ssl.SSLContext]:
|
85
101
|
"""
|
86
102
|
Create SSL context for client connections.
|
87
|
-
|
103
|
+
|
88
104
|
Args:
|
89
105
|
server_hostname: Server hostname for SNI
|
90
|
-
|
106
|
+
|
91
107
|
Returns:
|
92
108
|
SSL context or None if SSL not enabled
|
93
109
|
"""
|
94
110
|
if not self.ssl_manager:
|
95
111
|
return None
|
96
|
-
|
112
|
+
|
97
113
|
try:
|
98
114
|
# Create client SSL context
|
99
115
|
context = self.ssl_manager.create_client_context()
|
100
|
-
|
116
|
+
|
101
117
|
if server_hostname:
|
102
118
|
# Set server hostname for SNI
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
logger.info(
|
119
|
+
# SSL verification settings are already configured in SSLManager
|
120
|
+
# based on verify_server configuration in _create_ssl_config()
|
121
|
+
|
122
|
+
logger.info(
|
123
|
+
f"Client SSL context created for {server_hostname or 'unknown server'}"
|
124
|
+
)
|
107
125
|
return context
|
108
|
-
|
126
|
+
|
109
127
|
except Exception as e:
|
110
128
|
logger.error(f"Failed to create client SSL context: {e}")
|
111
129
|
return None
|
112
|
-
|
130
|
+
|
113
131
|
def generate_client_api_key(self, user_id: str, prefix: str = "mcp_proxy") -> str:
|
114
132
|
"""
|
115
133
|
Generate API key for client authentication.
|
116
|
-
|
134
|
+
|
117
135
|
Args:
|
118
136
|
user_id: User identifier
|
119
137
|
prefix: Key prefix
|
120
|
-
|
138
|
+
|
121
139
|
Returns:
|
122
140
|
Generated API key
|
123
141
|
"""
|
@@ -128,52 +146,55 @@ class ClientSecurityManager:
|
|
128
146
|
except Exception as e:
|
129
147
|
logger.error(f"Failed to generate API key: {e}")
|
130
148
|
raise
|
131
|
-
|
132
|
-
def create_client_jwt_token(
|
133
|
-
|
134
|
-
|
149
|
+
|
150
|
+
def create_client_jwt_token(
|
151
|
+
self,
|
152
|
+
user_id: str,
|
153
|
+
roles: List[str],
|
154
|
+
secret: str,
|
155
|
+
algorithm: str = "HS256",
|
156
|
+
expiry_hours: int = 24,
|
157
|
+
) -> str:
|
135
158
|
"""
|
136
159
|
Create JWT token for client authentication.
|
137
|
-
|
160
|
+
|
138
161
|
Args:
|
139
162
|
user_id: User identifier
|
140
163
|
roles: User roles
|
141
164
|
secret: JWT secret
|
142
165
|
algorithm: JWT algorithm
|
143
166
|
expiry_hours: Token expiry in hours
|
144
|
-
|
167
|
+
|
145
168
|
Returns:
|
146
169
|
JWT token
|
147
170
|
"""
|
148
171
|
try:
|
149
|
-
payload = {
|
150
|
-
|
151
|
-
"roles": roles,
|
152
|
-
"type": "client_proxy"
|
153
|
-
}
|
154
|
-
|
172
|
+
payload = {"user_id": user_id, "roles": roles, "type": "client_proxy"}
|
173
|
+
|
155
174
|
token = create_jwt_token(
|
156
175
|
payload=payload,
|
157
176
|
secret=secret,
|
158
177
|
algorithm=algorithm,
|
159
|
-
expiry_hours=expiry_hours
|
178
|
+
expiry_hours=expiry_hours,
|
160
179
|
)
|
161
|
-
|
180
|
+
|
162
181
|
logger.info(f"Created JWT token for user: {user_id}")
|
163
182
|
return token
|
164
|
-
|
183
|
+
|
165
184
|
except Exception as e:
|
166
185
|
logger.error(f"Failed to create JWT token: {e}")
|
167
186
|
raise
|
168
|
-
|
169
|
-
def validate_server_certificate(
|
187
|
+
|
188
|
+
def validate_server_certificate(
|
189
|
+
self, cert_path: str, ca_cert_path: Optional[str] = None
|
190
|
+
) -> bool:
|
170
191
|
"""
|
171
192
|
Validate server certificate before connection.
|
172
|
-
|
193
|
+
|
173
194
|
Args:
|
174
195
|
cert_path: Path to server certificate
|
175
196
|
ca_cert_path: Path to CA certificate
|
176
|
-
|
197
|
+
|
177
198
|
Returns:
|
178
199
|
True if certificate is valid
|
179
200
|
"""
|
@@ -182,33 +203,33 @@ class ClientSecurityManager:
|
|
182
203
|
if not validate_certificate_format(cert_path):
|
183
204
|
logger.error(f"Invalid certificate format: {cert_path}")
|
184
205
|
return False
|
185
|
-
|
206
|
+
|
186
207
|
# Validate certificate chain if CA provided
|
187
208
|
if ca_cert_path:
|
188
209
|
if not validate_certificate_chain(cert_path, ca_cert_path):
|
189
210
|
logger.error(f"Invalid certificate chain: {cert_path}")
|
190
211
|
return False
|
191
|
-
|
212
|
+
|
192
213
|
# Parse certificate and check basic properties
|
193
214
|
cert_info = parse_certificate(cert_path)
|
194
215
|
if not cert_info:
|
195
216
|
logger.error(f"Failed to parse certificate: {cert_path}")
|
196
217
|
return False
|
197
|
-
|
218
|
+
|
198
219
|
logger.info(f"Server certificate validated: {cert_path}")
|
199
220
|
return True
|
200
|
-
|
221
|
+
|
201
222
|
except Exception as e:
|
202
223
|
logger.error(f"Failed to validate server certificate: {e}")
|
203
224
|
return False
|
204
|
-
|
225
|
+
|
205
226
|
def extract_server_roles(self, cert_path: str) -> List[str]:
|
206
227
|
"""
|
207
228
|
Extract roles from server certificate.
|
208
|
-
|
229
|
+
|
209
230
|
Args:
|
210
231
|
cert_path: Path to server certificate
|
211
|
-
|
232
|
+
|
212
233
|
Returns:
|
213
234
|
List of roles extracted from certificate
|
214
235
|
"""
|
@@ -219,88 +240,94 @@ class ClientSecurityManager:
|
|
219
240
|
except Exception as e:
|
220
241
|
logger.error(f"Failed to extract roles from certificate: {e}")
|
221
242
|
return []
|
222
|
-
|
223
|
-
def get_client_auth_headers(
|
243
|
+
|
244
|
+
def get_client_auth_headers(
|
245
|
+
self, auth_method: str = "api_key", **kwargs
|
246
|
+
) -> Dict[str, str]:
|
224
247
|
"""
|
225
248
|
Get authentication headers for client requests.
|
226
|
-
|
249
|
+
|
227
250
|
Args:
|
228
251
|
auth_method: Authentication method (api_key, jwt, certificate)
|
229
252
|
**kwargs: Additional parameters
|
230
|
-
|
253
|
+
|
231
254
|
Returns:
|
232
255
|
Dictionary of authentication headers
|
233
256
|
"""
|
234
257
|
headers = {}
|
235
|
-
|
258
|
+
|
236
259
|
try:
|
237
260
|
if auth_method == "api_key":
|
238
261
|
api_key = kwargs.get("api_key")
|
239
262
|
if api_key:
|
240
263
|
headers["X-API-Key"] = api_key
|
241
264
|
headers["Authorization"] = f"Bearer {api_key}"
|
242
|
-
|
265
|
+
|
243
266
|
elif auth_method == "jwt":
|
244
267
|
token = kwargs.get("token")
|
245
268
|
if token:
|
246
269
|
headers["Authorization"] = f"Bearer {token}"
|
247
|
-
|
270
|
+
|
248
271
|
elif auth_method == "certificate":
|
249
272
|
# Certificate authentication is handled at SSL level
|
250
273
|
headers["X-Auth-Method"] = "certificate"
|
251
|
-
|
274
|
+
|
252
275
|
# Add common proxy headers
|
253
276
|
headers["X-Proxy-Type"] = "mcp_proxy_adapter"
|
254
277
|
headers["X-Client-Type"] = "proxy_client"
|
255
|
-
|
278
|
+
|
256
279
|
logger.debug(f"Created auth headers for method: {auth_method}")
|
257
280
|
return headers
|
258
|
-
|
281
|
+
|
259
282
|
except Exception as e:
|
260
283
|
logger.error(f"Failed to create auth headers: {e}")
|
261
284
|
return {}
|
262
|
-
|
263
|
-
def prepare_client_connection(
|
285
|
+
|
286
|
+
def prepare_client_connection(
|
287
|
+
self, server_config: Dict[str, Any]
|
288
|
+
) -> Tuple[Optional[ssl.SSLContext], Dict[str, str]]:
|
264
289
|
"""
|
265
290
|
Prepare secure client connection to server.
|
266
|
-
|
291
|
+
|
267
292
|
Args:
|
268
293
|
server_config: Server connection configuration
|
269
|
-
|
294
|
+
|
270
295
|
Returns:
|
271
296
|
Tuple of (SSL context, auth headers)
|
272
297
|
"""
|
273
298
|
ssl_context = None
|
274
299
|
auth_headers = {}
|
275
|
-
|
300
|
+
|
276
301
|
try:
|
277
302
|
# Create SSL context if needed
|
278
303
|
if server_config.get("ssl", False):
|
279
304
|
server_hostname = server_config.get("hostname")
|
280
305
|
ssl_context = self.create_client_ssl_context(server_hostname)
|
281
|
-
|
306
|
+
|
282
307
|
# Create authentication headers
|
283
308
|
auth_method = server_config.get("auth_method", "api_key")
|
284
309
|
auth_headers = self.get_client_auth_headers(
|
285
310
|
auth_method=auth_method,
|
286
311
|
api_key=server_config.get("api_key"),
|
287
|
-
token=server_config.get("token")
|
312
|
+
token=server_config.get("token"),
|
313
|
+
)
|
314
|
+
|
315
|
+
logger.info(
|
316
|
+
f"Prepared client connection for {server_config.get('hostname', 'unknown')}"
|
288
317
|
)
|
289
|
-
|
290
|
-
logger.info(f"Prepared client connection for {server_config.get('hostname', 'unknown')}")
|
291
318
|
return ssl_context, auth_headers
|
292
|
-
|
319
|
+
|
293
320
|
except Exception as e:
|
294
321
|
logger.error(f"Failed to prepare client connection: {e}")
|
295
322
|
return None, {}
|
296
|
-
|
323
|
+
|
297
324
|
def validate_server_response(self, response_headers: Dict[str, str]) -> bool:
|
298
325
|
"""
|
299
326
|
Validate server response for security compliance.
|
300
|
-
|
327
|
+
|
301
328
|
Args:
|
302
329
|
response_headers: Server response headers
|
303
|
-
|
330
|
+
|
304
331
|
Returns:
|
305
332
|
True if response is valid
|
306
333
|
"""
|
@@ -310,67 +337,69 @@ class ClientSecurityManager:
|
|
310
337
|
for header in required_headers:
|
311
338
|
if header not in response_headers:
|
312
339
|
logger.warning(f"Missing required header: {header}")
|
313
|
-
|
340
|
+
|
314
341
|
# Check for security headers
|
315
342
|
security_headers = ["X-Frame-Options", "X-Content-Type-Options"]
|
316
343
|
for header in security_headers:
|
317
344
|
if header in response_headers:
|
318
345
|
logger.debug(f"Found security header: {header}")
|
319
|
-
|
346
|
+
|
320
347
|
# Validate content type
|
321
348
|
content_type = response_headers.get("Content-Type", "")
|
322
349
|
if "application/json" not in content_type:
|
323
350
|
logger.warning(f"Unexpected content type: {content_type}")
|
324
|
-
|
351
|
+
|
325
352
|
return True
|
326
|
-
|
353
|
+
|
327
354
|
except Exception as e:
|
328
355
|
logger.error(f"Failed to validate server response: {e}")
|
329
356
|
return False
|
330
|
-
|
357
|
+
|
331
358
|
def get_client_certificate_info(self) -> Optional[Dict[str, Any]]:
|
332
359
|
"""
|
333
360
|
Get information about client certificate.
|
334
|
-
|
361
|
+
|
335
362
|
Returns:
|
336
363
|
Certificate information or None
|
337
364
|
"""
|
338
365
|
try:
|
339
366
|
ssl_config = self.security_config.get("ssl", {})
|
340
367
|
cert_path = ssl_config.get("client_cert_file")
|
341
|
-
|
368
|
+
|
342
369
|
if not cert_path or not Path(cert_path).exists():
|
343
370
|
return None
|
344
|
-
|
371
|
+
|
345
372
|
cert_info = parse_certificate(cert_path)
|
346
373
|
if cert_info:
|
347
374
|
roles = extract_roles_from_certificate(cert_path)
|
348
375
|
cert_info["roles"] = roles
|
349
376
|
return cert_info
|
350
|
-
|
377
|
+
|
351
378
|
return None
|
352
|
-
|
379
|
+
|
353
380
|
except Exception as e:
|
354
381
|
logger.error(f"Failed to get client certificate info: {e}")
|
355
382
|
return None
|
356
|
-
|
383
|
+
|
357
384
|
def is_ssl_enabled(self) -> bool:
|
358
385
|
"""Check if SSL is enabled for client connections."""
|
359
386
|
return self.security_config.get("ssl", {}).get("enabled", False)
|
360
|
-
|
387
|
+
|
361
388
|
def get_supported_auth_methods(self) -> List[str]:
|
362
389
|
"""Get list of supported authentication methods."""
|
363
390
|
return ["api_key", "jwt", "certificate"]
|
364
391
|
|
365
392
|
|
366
393
|
# Factory function for easy integration
|
367
|
-
def create_client_security_manager(
|
394
|
+
def create_client_security_manager(
|
395
|
+
config: Dict[str, Any]
|
396
|
+
) -> Optional[ClientSecurityManager]:
|
368
397
|
"""
|
369
398
|
Create client security manager instance.
|
370
|
-
|
399
|
+
|
371
400
|
Args:
|
372
401
|
config: Configuration dictionary
|
373
|
-
|
402
|
+
|
374
403
|
Returns:
|
375
404
|
ClientSecurityManager instance or None if framework not available
|
376
405
|
"""
|