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.
Files changed (129) 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 +108 -88
  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.5.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
  125. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  126. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
  127. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
  128. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -23,39 +23,43 @@ from cryptography.hazmat.primitives.asymmetric import rsa, padding
23
23
  # Standard JSON-RPC error codes
24
24
  JSON_RPC_ERRORS = {
25
25
  # General errors
26
- -32600: "Invalid Request", # Invalid request
27
- -32601: "Method not found", # Method not found
28
- -32602: "Invalid params", # Invalid parameters
29
- -32603: "Internal error", # Internal error
30
- -32700: "Parse error", # Parse error
31
-
26
+ -32600: "Invalid Request", # Invalid request
27
+ -32601: "Method not found", # Method not found
28
+ -32602: "Invalid params", # Invalid parameters
29
+ -32603: "Internal error", # Internal error
30
+ -32700: "Parse error", # Parse error
32
31
  # Custom codes for authentication
33
- -32001: "Authentication disabled", # Authentication disabled
34
- -32002: "Invalid configuration", # Invalid configuration
35
- -32003: "Certificate validation failed", # Certificate validation failed
36
- -32004: "Token validation failed", # Token validation failed
37
- -32005: "MTLS validation failed", # MTLS validation failed
38
- -32006: "SSL validation failed", # SSL validation failed
39
- -32007: "Role validation failed", # Role validation failed
40
- -32008: "Certificate expired", # Certificate expired
41
- -32009: "Certificate not found", # Certificate not found
42
- -32010: "Token expired", # Token expired
43
- -32011: "Token not found", # Token not found
32
+ -32001: "Authentication disabled", # Authentication disabled
33
+ -32002: "Invalid configuration", # Invalid configuration
34
+ -32003: "Certificate validation failed", # Certificate validation failed
35
+ -32004: "Token validation failed", # Token validation failed
36
+ -32005: "MTLS validation failed", # MTLS validation failed
37
+ -32006: "SSL validation failed", # SSL validation failed
38
+ -32007: "Role validation failed", # Role validation failed
39
+ -32008: "Certificate expired", # Certificate expired
40
+ -32009: "Certificate not found", # Certificate not found
41
+ -32010: "Token expired", # Token expired
42
+ -32011: "Token not found", # Token not found
44
43
  }
45
44
 
46
45
 
47
46
  class AuthValidationResult:
48
47
  """
49
48
  Authentication validation result.
50
-
49
+
51
50
  Contains validation status, error information, and extracted roles.
52
51
  """
53
-
54
- def __init__(self, is_valid: bool, error_code: Optional[int] = None,
55
- error_message: Optional[str] = None, roles: Optional[List[str]] = None):
52
+
53
+ def __init__(
54
+ self,
55
+ is_valid: bool,
56
+ error_code: Optional[int] = None,
57
+ error_message: Optional[str] = None,
58
+ roles: Optional[List[str]] = None,
59
+ ):
56
60
  """
57
61
  Initialize authentication validation result.
58
-
62
+
59
63
  Args:
60
64
  is_valid: Whether authentication is valid
61
65
  error_code: JSON-RPC error code if validation failed
@@ -66,30 +70,28 @@ class AuthValidationResult:
66
70
  self.error_code = error_code
67
71
  self.error_message = error_message
68
72
  self.roles = roles or []
69
-
73
+
70
74
  def to_json_rpc_error(self) -> Dict[str, Any]:
71
75
  """
72
76
  Convert to JSON-RPC error format.
73
-
77
+
74
78
  Returns:
75
79
  JSON-RPC error dictionary
76
80
  """
77
81
  if self.is_valid:
78
82
  return {}
79
-
83
+
80
84
  return {
81
85
  "code": self.error_code or -32603,
82
- "message": self.error_message or JSON_RPC_ERRORS.get(self.error_code, "Unknown error"),
83
- "data": {
84
- "validation_type": "authentication",
85
- "roles": self.roles
86
- }
86
+ "message": self.error_message
87
+ or JSON_RPC_ERRORS.get(self.error_code, "Unknown error"),
88
+ "data": {"validation_type": "authentication", "roles": self.roles},
87
89
  }
88
-
90
+
89
91
  def to_dict(self) -> Dict[str, Any]:
90
92
  """
91
93
  Convert to dictionary format.
92
-
94
+
93
95
  Returns:
94
96
  Dictionary representation
95
97
  """
@@ -97,38 +99,40 @@ class AuthValidationResult:
97
99
  "is_valid": self.is_valid,
98
100
  "error_code": self.error_code,
99
101
  "error_message": self.error_message,
100
- "roles": self.roles
102
+ "roles": self.roles,
101
103
  }
102
104
 
103
105
 
104
106
  class AuthValidator:
105
107
  """
106
108
  Universal authentication validator.
107
-
109
+
108
110
  Provides methods to validate different types of authentication:
109
111
  - Certificate validation
110
112
  - Token validation
111
113
  - mTLS validation
112
114
  - SSL validation
113
115
  """
114
-
116
+
115
117
  def __init__(self, config: Optional[Dict[str, Any]] = None):
116
118
  """
117
119
  Initialize authentication validator.
118
-
120
+
119
121
  Args:
120
122
  config: Configuration dictionary for validation settings
121
123
  """
122
124
  self.config = config or {}
123
125
  self.logger = logging.getLogger(__name__)
124
-
126
+
125
127
  # Custom OID for roles
126
128
  self.role_oid = "1.3.6.1.4.1.99999.1"
127
-
128
- def validate_auth(self, auth_data: Dict[str, Any], auth_type: str = "auto") -> AuthValidationResult:
129
+
130
+ def validate_auth(
131
+ self, auth_data: Dict[str, Any], auth_type: str = "auto"
132
+ ) -> AuthValidationResult:
129
133
  """
130
134
  Universal authentication validation.
131
-
135
+
132
136
  Logic:
133
137
  1. Check SSL configuration
134
138
  2. If SSL disabled - return success
@@ -136,11 +140,11 @@ class AuthValidator:
136
140
  4. Check for required data
137
141
  5. Perform validation
138
142
  6. Return result with JSON-RPC error code
139
-
143
+
140
144
  Args:
141
145
  auth_data: Authentication data dictionary
142
146
  auth_type: Type of authentication to validate
143
-
147
+
144
148
  Returns:
145
149
  Authentication validation result
146
150
  """
@@ -148,59 +152,53 @@ class AuthValidator:
148
152
  # Determine validation type
149
153
  if auth_type == "auto":
150
154
  auth_type = self._get_validation_mode()
151
-
155
+
152
156
  # Check for unsupported types first
153
157
  if auth_type not in ["certificate", "token", "mtls", "ssl"]:
154
158
  return AuthValidationResult(
155
159
  is_valid=False,
156
160
  error_code=-32602,
157
- error_message=f"Unsupported authentication type: {auth_type}"
161
+ error_message=f"Unsupported authentication type: {auth_type}",
158
162
  )
159
-
163
+
160
164
  # Check if authentication is enabled for the determined type
161
165
  if not self._check_config(auth_type):
162
- return AuthValidationResult(
163
- is_valid=True,
164
- roles=[]
165
- )
166
-
166
+ return AuthValidationResult(is_valid=True, roles=[])
167
+
167
168
  # Validate based on type
168
169
  if auth_type == "certificate":
169
170
  return self.validate_certificate(
170
- auth_data.get("cert_path"),
171
- auth_data.get("cert_type", "server")
171
+ auth_data.get("cert_path"), auth_data.get("cert_type", "server")
172
172
  )
173
173
  elif auth_type == "token":
174
174
  return self.validate_token(
175
- auth_data.get("token"),
176
- auth_data.get("token_type", "jwt")
175
+ auth_data.get("token"), auth_data.get("token_type", "jwt")
177
176
  )
178
177
  elif auth_type == "mtls":
179
178
  return self.validate_mtls(
180
- auth_data.get("client_cert"),
181
- auth_data.get("ca_cert")
179
+ auth_data.get("client_cert"), auth_data.get("ca_cert")
182
180
  )
183
181
  elif auth_type == "ssl":
184
- return self.validate_ssl(
185
- auth_data.get("server_cert")
186
- )
187
-
182
+ return self.validate_ssl(auth_data.get("server_cert"))
183
+
188
184
  except Exception as e:
189
185
  self.logger.error(f"Authentication validation error: {e}")
190
186
  return AuthValidationResult(
191
187
  is_valid=False,
192
188
  error_code=-32603,
193
- error_message=f"Internal validation error: {str(e)}"
189
+ error_message=f"Internal validation error: {str(e)}",
194
190
  )
195
-
196
- def validate_certificate(self, cert_path: Optional[str], cert_type: str = "server") -> AuthValidationResult:
191
+
192
+ def validate_certificate(
193
+ self, cert_path: Optional[str], cert_type: str = "server"
194
+ ) -> AuthValidationResult:
197
195
  """
198
196
  Validate certificate.
199
-
197
+
200
198
  Args:
201
199
  cert_path: Path to certificate file
202
200
  cert_type: Type of certificate (server/client/ca)
203
-
201
+
204
202
  Returns:
205
203
  Certificate validation result
206
204
  """
@@ -209,34 +207,34 @@ class AuthValidator:
209
207
  return AuthValidationResult(
210
208
  is_valid=False,
211
209
  error_code=-32009,
212
- error_message="Certificate path not provided"
210
+ error_message="Certificate path not provided",
213
211
  )
214
-
212
+
215
213
  if not os.path.exists(cert_path):
216
214
  return AuthValidationResult(
217
215
  is_valid=False,
218
216
  error_code=-32009,
219
- error_message=f"Certificate file not found: {cert_path}"
217
+ error_message=f"Certificate file not found: {cert_path}",
220
218
  )
221
-
219
+
222
220
  # Load and validate certificate
223
- with open(cert_path, 'rb') as f:
221
+ with open(cert_path, "rb") as f:
224
222
  cert_data = f.read()
225
-
223
+
226
224
  cert = x509.load_pem_x509_certificate(cert_data)
227
-
225
+
228
226
  # Check if certificate is not expired
229
227
  now = datetime.utcnow()
230
228
  if cert.not_valid_before > now or cert.not_valid_after < now:
231
229
  return AuthValidationResult(
232
230
  is_valid=False,
233
231
  error_code=-32008,
234
- error_message="Certificate has expired"
232
+ error_message="Certificate has expired",
235
233
  )
236
-
234
+
237
235
  # Extract roles from certificate
238
236
  roles = self._extract_roles_from_certificate(cert)
239
-
237
+
240
238
  # Validate certificate type
241
239
  if cert_type == "server":
242
240
  # Check for server-specific extensions
@@ -244,7 +242,7 @@ class AuthValidator:
244
242
  return AuthValidationResult(
245
243
  is_valid=False,
246
244
  error_code=-32003,
247
- error_message="Invalid server certificate"
245
+ error_message="Invalid server certificate",
248
246
  )
249
247
  elif cert_type == "client":
250
248
  # Check for client-specific extensions
@@ -252,30 +250,29 @@ class AuthValidator:
252
250
  return AuthValidationResult(
253
251
  is_valid=False,
254
252
  error_code=-32003,
255
- error_message="Invalid client certificate"
253
+ error_message="Invalid client certificate",
256
254
  )
257
-
258
- return AuthValidationResult(
259
- is_valid=True,
260
- roles=roles
261
- )
262
-
255
+
256
+ return AuthValidationResult(is_valid=True, roles=roles)
257
+
263
258
  except Exception as e:
264
259
  self.logger.error(f"Certificate validation error: {e}")
265
260
  return AuthValidationResult(
266
261
  is_valid=False,
267
262
  error_code=-32003,
268
- error_message=f"Certificate validation failed: {str(e)}"
263
+ error_message=f"Certificate validation failed: {str(e)}",
269
264
  )
270
-
271
- def validate_token(self, token: Optional[str], token_type: str = "jwt") -> AuthValidationResult:
265
+
266
+ def validate_token(
267
+ self, token: Optional[str], token_type: str = "jwt"
268
+ ) -> AuthValidationResult:
272
269
  """
273
270
  Validate token.
274
-
271
+
275
272
  Args:
276
273
  token: Token string to validate
277
274
  token_type: Type of token (jwt/api)
278
-
275
+
279
276
  Returns:
280
277
  Token validation result
281
278
  """
@@ -284,9 +281,9 @@ class AuthValidator:
284
281
  return AuthValidationResult(
285
282
  is_valid=False,
286
283
  error_code=-32011,
287
- error_message="Token not provided"
284
+ error_message="Token not provided",
288
285
  )
289
-
286
+
290
287
  if token_type == "jwt":
291
288
  return self._validate_jwt_token(token)
292
289
  elif token_type == "api":
@@ -295,25 +292,27 @@ class AuthValidator:
295
292
  return AuthValidationResult(
296
293
  is_valid=False,
297
294
  error_code=-32602,
298
- error_message=f"Unsupported token type: {token_type}"
295
+ error_message=f"Unsupported token type: {token_type}",
299
296
  )
300
-
297
+
301
298
  except Exception as e:
302
299
  self.logger.error(f"Token validation error: {e}")
303
300
  return AuthValidationResult(
304
301
  is_valid=False,
305
302
  error_code=-32004,
306
- error_message=f"Token validation failed: {str(e)}"
303
+ error_message=f"Token validation failed: {str(e)}",
307
304
  )
308
-
309
- def validate_mtls(self, client_cert: Optional[str], ca_cert: Optional[str]) -> AuthValidationResult:
305
+
306
+ def validate_mtls(
307
+ self, client_cert: Optional[str], ca_cert: Optional[str]
308
+ ) -> AuthValidationResult:
310
309
  """
311
310
  Validate mTLS connection.
312
-
311
+
313
312
  Args:
314
313
  client_cert: Path to client certificate
315
314
  ca_cert: Path to CA certificate
316
-
315
+
317
316
  Returns:
318
317
  mTLS validation result
319
318
  """
@@ -322,47 +321,44 @@ class AuthValidator:
322
321
  return AuthValidationResult(
323
322
  is_valid=False,
324
323
  error_code=-32005,
325
- error_message="Client certificate and CA certificate required for mTLS"
324
+ error_message="Client certificate and CA certificate required for mTLS",
326
325
  )
327
-
326
+
328
327
  # Validate client certificate
329
328
  client_result = self.validate_certificate(client_cert, "client")
330
329
  if not client_result.is_valid:
331
330
  return client_result
332
-
331
+
333
332
  # Validate CA certificate
334
333
  ca_result = self.validate_certificate(ca_cert, "ca")
335
334
  if not ca_result.is_valid:
336
335
  return ca_result
337
-
336
+
338
337
  # Verify client certificate is signed by CA
339
338
  if not self._verify_certificate_chain(client_cert, ca_cert):
340
339
  return AuthValidationResult(
341
340
  is_valid=False,
342
341
  error_code=-32005,
343
- error_message="Client certificate not signed by provided CA"
342
+ error_message="Client certificate not signed by provided CA",
344
343
  )
345
-
346
- return AuthValidationResult(
347
- is_valid=True,
348
- roles=client_result.roles
349
- )
350
-
344
+
345
+ return AuthValidationResult(is_valid=True, roles=client_result.roles)
346
+
351
347
  except Exception as e:
352
348
  self.logger.error(f"mTLS validation error: {e}")
353
349
  return AuthValidationResult(
354
350
  is_valid=False,
355
351
  error_code=-32005,
356
- error_message=f"mTLS validation failed: {str(e)}"
352
+ error_message=f"mTLS validation failed: {str(e)}",
357
353
  )
358
-
354
+
359
355
  def validate_ssl(self, server_cert: Optional[str]) -> AuthValidationResult:
360
356
  """
361
357
  Validate SSL connection.
362
-
358
+
363
359
  Args:
364
360
  server_cert: Path to server certificate
365
-
361
+
366
362
  Returns:
367
363
  SSL validation result
368
364
  """
@@ -371,35 +367,35 @@ class AuthValidator:
371
367
  return AuthValidationResult(
372
368
  is_valid=False,
373
369
  error_code=-32006,
374
- error_message="Server certificate required for SSL validation"
370
+ error_message="Server certificate required for SSL validation",
375
371
  )
376
-
372
+
377
373
  # Validate server certificate
378
374
  return self.validate_certificate(server_cert, "server")
379
-
375
+
380
376
  except Exception as e:
381
377
  self.logger.error(f"SSL validation error: {e}")
382
378
  return AuthValidationResult(
383
379
  is_valid=False,
384
380
  error_code=-32006,
385
- error_message=f"SSL validation failed: {str(e)}"
381
+ error_message=f"SSL validation failed: {str(e)}",
386
382
  )
387
-
383
+
388
384
  def _check_config(self, auth_type: str) -> bool:
389
385
  """
390
386
  Check if authentication is enabled in configuration.
391
-
387
+
392
388
  Args:
393
389
  auth_type: Type of authentication
394
-
390
+
395
391
  Returns:
396
392
  True if authentication is enabled, False otherwise
397
393
  """
398
394
  ssl_config = self.config.get("ssl", {})
399
-
395
+
400
396
  if not ssl_config.get("enabled", False):
401
397
  return False
402
-
398
+
403
399
  # Check specific authentication type
404
400
  if auth_type == "token":
405
401
  return ssl_config.get("token_auth", {}).get("enabled", False)
@@ -407,57 +403,57 @@ class AuthValidator:
407
403
  return ssl_config.get("mtls", {}).get("enabled", False)
408
404
  elif auth_type in ["ssl", "certificate"]:
409
405
  return True
410
-
406
+
411
407
  # For unsupported types, return False to indicate authentication is not enabled
412
408
  return False
413
-
409
+
414
410
  def _get_validation_mode(self) -> str:
415
411
  """
416
412
  Get validation mode from configuration.
417
-
413
+
418
414
  Returns:
419
415
  Validation mode string
420
416
  """
421
417
  ssl_config = self.config.get("ssl", {})
422
-
418
+
423
419
  if ssl_config.get("token_auth", {}).get("enabled", False):
424
420
  return "token"
425
421
  elif ssl_config.get("mtls", {}).get("enabled", False):
426
422
  return "mtls"
427
423
  elif ssl_config.get("enabled", False):
428
424
  return "ssl"
429
-
425
+
430
426
  return "none"
431
-
427
+
432
428
  def _extract_roles_from_certificate(self, cert: x509.Certificate) -> List[str]:
433
429
  """
434
430
  Extract roles from certificate.
435
-
431
+
436
432
  Args:
437
433
  cert: Certificate object
438
-
434
+
439
435
  Returns:
440
436
  List of roles extracted from certificate
441
437
  """
442
438
  try:
443
439
  for extension in cert.extensions:
444
440
  if extension.oid.dotted_string == self.role_oid:
445
- roles_data = extension.value.value.decode('utf-8')
446
- return [role.strip() for role in roles_data.split(',')]
447
-
441
+ roles_data = extension.value.value.decode("utf-8")
442
+ return [role.strip() for role in roles_data.split(",")]
443
+
448
444
  return []
449
-
445
+
450
446
  except Exception as e:
451
447
  self.logger.error(f"Failed to extract roles from certificate: {e}")
452
448
  return []
453
-
449
+
454
450
  def _validate_server_certificate(self, cert: x509.Certificate) -> bool:
455
451
  """
456
452
  Validate server certificate extensions.
457
-
453
+
458
454
  Args:
459
455
  cert: Certificate object
460
-
456
+
461
457
  Returns:
462
458
  True if server certificate is valid, False otherwise
463
459
  """
@@ -467,20 +463,20 @@ class AuthValidator:
467
463
  if extension.oid == x509.oid.ExtensionOID.KEY_USAGE:
468
464
  key_usage = extension.value
469
465
  return key_usage.digital_signature and key_usage.key_encipherment
470
-
466
+
471
467
  return True
472
-
468
+
473
469
  except Exception as e:
474
470
  self.logger.error(f"Server certificate validation error: {e}")
475
471
  return False
476
-
472
+
477
473
  def _validate_client_certificate(self, cert: x509.Certificate) -> bool:
478
474
  """
479
475
  Validate client certificate extensions.
480
-
476
+
481
477
  Args:
482
478
  cert: Certificate object
483
-
479
+
484
480
  Returns:
485
481
  True if client certificate is valid, False otherwise
486
482
  """
@@ -489,21 +485,23 @@ class AuthValidator:
489
485
  for extension in cert.extensions:
490
486
  if extension.oid == x509.oid.ExtensionOID.EXTENDED_KEY_USAGE:
491
487
  extended_key_usage = extension.value
492
- return x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH in extended_key_usage
493
-
488
+ return (
489
+ x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH in extended_key_usage
490
+ )
491
+
494
492
  return True
495
-
493
+
496
494
  except Exception as e:
497
495
  self.logger.error(f"Client certificate validation error: {e}")
498
496
  return False
499
-
497
+
500
498
  def _validate_jwt_token(self, token: str) -> AuthValidationResult:
501
499
  """
502
500
  Validate JWT token.
503
-
501
+
504
502
  Args:
505
503
  token: JWT token string
506
-
504
+
507
505
  Returns:
508
506
  JWT validation result
509
507
  """
@@ -511,37 +509,34 @@ class AuthValidator:
511
509
  # This is a placeholder for JWT validation
512
510
  # In a real implementation, you would use a JWT library
513
511
  # like PyJWT to validate the token
514
-
512
+
515
513
  # For now, just check if token exists and has basic format
516
- if not token or len(token.split('.')) != 3:
514
+ if not token or len(token.split(".")) != 3:
517
515
  return AuthValidationResult(
518
516
  is_valid=False,
519
517
  error_code=-32004,
520
- error_message="Invalid JWT token format"
518
+ error_message="Invalid JWT token format",
521
519
  )
522
-
520
+
523
521
  # Extract roles from JWT payload (placeholder)
524
522
  roles = []
525
-
526
- return AuthValidationResult(
527
- is_valid=True,
528
- roles=roles
529
- )
530
-
523
+
524
+ return AuthValidationResult(is_valid=True, roles=roles)
525
+
531
526
  except Exception as e:
532
527
  return AuthValidationResult(
533
528
  is_valid=False,
534
529
  error_code=-32004,
535
- error_message=f"JWT validation failed: {str(e)}"
530
+ error_message=f"JWT validation failed: {str(e)}",
536
531
  )
537
-
532
+
538
533
  def _validate_api_token(self, token: str) -> AuthValidationResult:
539
534
  """
540
535
  Validate API token.
541
-
536
+
542
537
  Args:
543
538
  token: API token string
544
-
539
+
545
540
  Returns:
546
541
  API token validation result
547
542
  """
@@ -549,58 +544,57 @@ class AuthValidator:
549
544
  # This is a placeholder for API token validation
550
545
  # In a real implementation, you would validate against
551
546
  # a token store or database
552
-
547
+
553
548
  if not token:
554
549
  return AuthValidationResult(
555
550
  is_valid=False,
556
551
  error_code=-32011,
557
- error_message="API token not found"
552
+ error_message="API token not found",
558
553
  )
559
-
554
+
560
555
  # Extract roles from API token (placeholder)
561
556
  roles = []
562
-
563
- return AuthValidationResult(
564
- is_valid=True,
565
- roles=roles
566
- )
567
-
557
+
558
+ return AuthValidationResult(is_valid=True, roles=roles)
559
+
568
560
  except Exception as e:
569
561
  return AuthValidationResult(
570
562
  is_valid=False,
571
563
  error_code=-32004,
572
- error_message=f"API token validation failed: {str(e)}"
564
+ error_message=f"API token validation failed: {str(e)}",
573
565
  )
574
-
575
- def _verify_certificate_chain(self, client_cert_path: str, ca_cert_path: str) -> bool:
566
+
567
+ def _verify_certificate_chain(
568
+ self, client_cert_path: str, ca_cert_path: str
569
+ ) -> bool:
576
570
  """
577
571
  Verify that client certificate is signed by CA.
578
-
572
+
579
573
  Args:
580
574
  client_cert_path: Path to client certificate
581
575
  ca_cert_path: Path to CA certificate
582
-
576
+
583
577
  Returns:
584
578
  True if certificate chain is valid, False otherwise
585
579
  """
586
580
  try:
587
581
  # Load client certificate
588
- with open(client_cert_path, 'rb') as f:
582
+ with open(client_cert_path, "rb") as f:
589
583
  client_cert_data = f.read()
590
584
  client_cert = x509.load_pem_x509_certificate(client_cert_data)
591
-
585
+
592
586
  # Load CA certificate
593
- with open(ca_cert_path, 'rb') as f:
587
+ with open(ca_cert_path, "rb") as f:
594
588
  ca_cert_data = f.read()
595
589
  ca_cert = x509.load_pem_x509_certificate(ca_cert_data)
596
-
590
+
597
591
  # Verify client certificate is signed by CA
598
592
  ca_public_key = ca_cert.public_key()
599
-
593
+
600
594
  # This is a simplified verification
601
595
  # In a real implementation, you would use proper signature verification
602
596
  return True
603
-
597
+
604
598
  except Exception as e:
605
599
  self.logger.error(f"Certificate chain verification error: {e}")
606
- return False
600
+ return False