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.
Files changed (131) hide show
  1. mcp_proxy_adapter/__init__.py +9 -5
  2. mcp_proxy_adapter/__main__.py +1 -1
  3. mcp_proxy_adapter/api/app.py +227 -176
  4. mcp_proxy_adapter/api/handlers.py +68 -60
  5. mcp_proxy_adapter/api/middleware/__init__.py +7 -5
  6. mcp_proxy_adapter/api/middleware/base.py +19 -16
  7. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
  8. mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
  9. mcp_proxy_adapter/api/middleware/factory.py +50 -52
  10. mcp_proxy_adapter/api/middleware/logging.py +46 -30
  11. mcp_proxy_adapter/api/middleware/performance.py +19 -16
  12. mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
  13. mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
  14. mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
  15. mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
  16. mcp_proxy_adapter/api/schemas.py +69 -43
  17. mcp_proxy_adapter/api/tool_integration.py +83 -63
  18. mcp_proxy_adapter/api/tools.py +60 -50
  19. mcp_proxy_adapter/commands/__init__.py +15 -6
  20. mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
  21. mcp_proxy_adapter/commands/base.py +108 -112
  22. mcp_proxy_adapter/commands/builtin_commands.py +28 -18
  23. mcp_proxy_adapter/commands/catalog_manager.py +394 -265
  24. mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
  25. mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
  26. mcp_proxy_adapter/commands/command_registry.py +275 -226
  27. mcp_proxy_adapter/commands/config_command.py +48 -33
  28. mcp_proxy_adapter/commands/dependency_container.py +22 -23
  29. mcp_proxy_adapter/commands/dependency_manager.py +65 -56
  30. mcp_proxy_adapter/commands/echo_command.py +15 -15
  31. mcp_proxy_adapter/commands/health_command.py +31 -29
  32. mcp_proxy_adapter/commands/help_command.py +97 -61
  33. mcp_proxy_adapter/commands/hooks.py +65 -49
  34. mcp_proxy_adapter/commands/key_management_command.py +148 -147
  35. mcp_proxy_adapter/commands/load_command.py +58 -40
  36. mcp_proxy_adapter/commands/plugins_command.py +80 -54
  37. mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
  38. mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
  39. mcp_proxy_adapter/commands/reload_command.py +43 -37
  40. mcp_proxy_adapter/commands/result.py +26 -33
  41. mcp_proxy_adapter/commands/role_test_command.py +26 -26
  42. mcp_proxy_adapter/commands/roles_management_command.py +176 -173
  43. mcp_proxy_adapter/commands/security_command.py +134 -122
  44. mcp_proxy_adapter/commands/settings_command.py +47 -56
  45. mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
  46. mcp_proxy_adapter/commands/token_management_command.py +129 -158
  47. mcp_proxy_adapter/commands/transport_management_command.py +41 -36
  48. mcp_proxy_adapter/commands/unload_command.py +42 -37
  49. mcp_proxy_adapter/config.py +36 -35
  50. mcp_proxy_adapter/core/__init__.py +19 -21
  51. mcp_proxy_adapter/core/app_factory.py +30 -9
  52. mcp_proxy_adapter/core/app_runner.py +81 -64
  53. mcp_proxy_adapter/core/auth_validator.py +176 -182
  54. mcp_proxy_adapter/core/certificate_utils.py +469 -426
  55. mcp_proxy_adapter/core/client.py +155 -126
  56. mcp_proxy_adapter/core/client_manager.py +60 -54
  57. mcp_proxy_adapter/core/client_security.py +120 -91
  58. mcp_proxy_adapter/core/config_converter.py +176 -143
  59. mcp_proxy_adapter/core/config_validator.py +12 -4
  60. mcp_proxy_adapter/core/crl_utils.py +21 -7
  61. mcp_proxy_adapter/core/errors.py +64 -20
  62. mcp_proxy_adapter/core/logging.py +34 -29
  63. mcp_proxy_adapter/core/mtls_asgi.py +29 -25
  64. mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
  65. mcp_proxy_adapter/core/protocol_manager.py +154 -104
  66. mcp_proxy_adapter/core/proxy_client.py +202 -144
  67. mcp_proxy_adapter/core/proxy_registration.py +7 -3
  68. mcp_proxy_adapter/core/role_utils.py +139 -125
  69. mcp_proxy_adapter/core/security_adapter.py +88 -77
  70. mcp_proxy_adapter/core/security_factory.py +50 -44
  71. mcp_proxy_adapter/core/security_integration.py +72 -24
  72. mcp_proxy_adapter/core/server_adapter.py +68 -64
  73. mcp_proxy_adapter/core/server_engine.py +71 -53
  74. mcp_proxy_adapter/core/settings.py +68 -58
  75. mcp_proxy_adapter/core/ssl_utils.py +69 -56
  76. mcp_proxy_adapter/core/transport_manager.py +72 -60
  77. mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
  78. mcp_proxy_adapter/core/utils.py +4 -2
  79. mcp_proxy_adapter/custom_openapi.py +107 -99
  80. mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
  81. mcp_proxy_adapter/examples/commands/__init__.py +1 -1
  82. mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
  83. mcp_proxy_adapter/examples/debug_request_state.py +38 -19
  84. mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
  85. mcp_proxy_adapter/examples/demo_client.py +48 -36
  86. mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
  87. mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
  88. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
  89. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  90. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
  91. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  92. mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
  93. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
  94. mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
  95. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
  96. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  99. mcp_proxy_adapter/examples/full_application/main.py +27 -2
  100. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
  102. mcp_proxy_adapter/examples/generate_certificates.py +31 -16
  103. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
  104. mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
  105. mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
  106. mcp_proxy_adapter/examples/run_example.py +23 -5
  107. mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
  108. mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
  109. mcp_proxy_adapter/examples/run_security_tests.py +103 -41
  110. mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
  111. mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
  112. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
  113. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
  114. mcp_proxy_adapter/examples/security_test_client.py +196 -127
  115. mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
  116. mcp_proxy_adapter/examples/test_config.py +19 -4
  117. mcp_proxy_adapter/examples/test_config_generator.py +23 -7
  118. mcp_proxy_adapter/examples/test_examples.py +84 -56
  119. mcp_proxy_adapter/examples/universal_client.py +119 -62
  120. mcp_proxy_adapter/openapi.py +108 -115
  121. mcp_proxy_adapter/utils/config_generator.py +429 -274
  122. mcp_proxy_adapter/version.py +1 -2
  123. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
  125. mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
  126. mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
  127. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  128. mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
  130. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
  131. {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, create_jwt_token, verify_jwt_token
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, extract_roles_from_certificate,
23
- validate_certificate_chain, validate_certificate_format
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=ssl_section.get("verify_mode", "CERT_REQUIRED"),
92
+ verify_mode=verify_mode,
79
93
  min_tls_version=ssl_section.get("min_tls_version", "TLSv1.2"),
80
- check_hostname=ssl_section.get("check_hostname", True),
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(self, server_hostname: Optional[str] = None) -> Optional[ssl.SSLContext]:
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
- context.check_hostname = True
104
- context.verify_mode = ssl.CERT_REQUIRED
105
-
106
- logger.info(f"Client SSL context created for {server_hostname or 'unknown server'}")
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(self, user_id: str, roles: List[str],
133
- secret: str, algorithm: str = "HS256",
134
- expiry_hours: int = 24) -> str:
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
- "user_id": user_id,
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(self, cert_path: str, ca_cert_path: Optional[str] = None) -> bool:
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(self, auth_method: str = "api_key", **kwargs) -> Dict[str, str]:
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(self, server_config: Dict[str, Any]) -> Tuple[Optional[ssl.SSLContext], Dict[str, str]]:
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(config: Dict[str, Any]) -> Optional[ClientSecurityManager]:
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
  """