mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.0.1__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 (200) hide show
  1. mcp_proxy_adapter/__main__.py +32 -0
  2. mcp_proxy_adapter/api/app.py +290 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +38 -32
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +8 -1
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +394 -14
  41. mcp_proxy_adapter/core/app_factory.py +410 -0
  42. mcp_proxy_adapter/core/app_runner.py +272 -0
  43. mcp_proxy_adapter/core/auth_validator.py +606 -0
  44. mcp_proxy_adapter/core/certificate_utils.py +1045 -0
  45. mcp_proxy_adapter/core/client.py +574 -0
  46. mcp_proxy_adapter/core/client_manager.py +284 -0
  47. mcp_proxy_adapter/core/client_security.py +384 -0
  48. mcp_proxy_adapter/core/config_converter.py +405 -0
  49. mcp_proxy_adapter/core/config_validator.py +218 -0
  50. mcp_proxy_adapter/core/logging.py +19 -3
  51. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  52. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  53. mcp_proxy_adapter/core/protocol_manager.py +385 -0
  54. mcp_proxy_adapter/core/proxy_client.py +602 -0
  55. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  56. mcp_proxy_adapter/core/role_utils.py +426 -0
  57. mcp_proxy_adapter/core/security_adapter.py +370 -0
  58. mcp_proxy_adapter/core/security_factory.py +239 -0
  59. mcp_proxy_adapter/core/security_integration.py +286 -0
  60. mcp_proxy_adapter/core/server_adapter.py +282 -0
  61. mcp_proxy_adapter/core/server_engine.py +270 -0
  62. mcp_proxy_adapter/core/settings.py +1 -0
  63. mcp_proxy_adapter/core/ssl_utils.py +234 -0
  64. mcp_proxy_adapter/core/transport_manager.py +292 -0
  65. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  66. mcp_proxy_adapter/custom_openapi.py +22 -11
  67. mcp_proxy_adapter/examples/__init__.py +13 -4
  68. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  69. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  70. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  71. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  72. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  73. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  74. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  75. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  76. mcp_proxy_adapter/examples/demo_client.py +275 -0
  77. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  78. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  79. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  80. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  81. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  82. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  83. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  84. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  85. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  86. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  87. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  88. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  89. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  90. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  91. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  92. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  93. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  94. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  95. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  96. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  97. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  98. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  99. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  100. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  101. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  102. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  103. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  104. mcp_proxy_adapter/examples/run_example.py +59 -0
  105. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  106. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  107. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  108. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  109. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  110. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  111. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  112. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  113. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  114. mcp_proxy_adapter/examples/test_config.py +148 -0
  115. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  116. mcp_proxy_adapter/examples/test_examples.py +281 -0
  117. mcp_proxy_adapter/examples/universal_client.py +620 -0
  118. mcp_proxy_adapter/main.py +93 -0
  119. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  120. mcp_proxy_adapter/version.py +5 -2
  121. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  122. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  123. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  124. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  125. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  126. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  127. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  128. mcp_proxy_adapter/examples/README.md +0 -124
  129. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  130. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  131. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  132. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  133. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  134. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  135. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  136. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  137. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  138. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  139. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  140. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  141. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  142. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  143. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  144. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  145. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  146. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  147. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  148. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  149. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  150. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  153. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  154. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  155. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  156. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  157. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  158. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  159. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  160. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  161. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  162. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  163. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  164. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  165. mcp_proxy_adapter/tests/__init__.py +0 -0
  166. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  167. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  168. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  169. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  170. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  171. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  172. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  173. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  174. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  175. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  176. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  177. mcp_proxy_adapter/tests/conftest.py +0 -131
  178. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  180. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  181. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  182. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  183. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  184. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  185. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  186. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  187. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  188. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  189. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  190. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  191. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  192. mcp_proxy_adapter/tests/test_config.py +0 -127
  193. mcp_proxy_adapter/tests/test_utils.py +0 -65
  194. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  195. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  196. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  197. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  198. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  199. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  200. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,606 @@
1
+ """
2
+ Universal Authentication Validator
3
+
4
+ This module provides a universal authentication validator that supports
5
+ certificate, token, mTLS, and SSL validation with JSON-RPC error codes.
6
+
7
+ Author: MCP Proxy Adapter Team
8
+ Version: 1.0.0
9
+ """
10
+
11
+ import json
12
+ import logging
13
+ import os
14
+ from datetime import datetime
15
+ from typing import Dict, List, Optional, Any, Union
16
+ from pathlib import Path
17
+
18
+ from cryptography import x509
19
+ from cryptography.hazmat.primitives import hashes
20
+ from cryptography.hazmat.primitives.asymmetric import rsa, padding
21
+
22
+
23
+ # Standard JSON-RPC error codes
24
+ JSON_RPC_ERRORS = {
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
+
32
+ # 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
44
+ }
45
+
46
+
47
+ class AuthValidationResult:
48
+ """
49
+ Authentication validation result.
50
+
51
+ Contains validation status, error information, and extracted roles.
52
+ """
53
+
54
+ def __init__(self, is_valid: bool, error_code: Optional[int] = None,
55
+ error_message: Optional[str] = None, roles: Optional[List[str]] = None):
56
+ """
57
+ Initialize authentication validation result.
58
+
59
+ Args:
60
+ is_valid: Whether authentication is valid
61
+ error_code: JSON-RPC error code if validation failed
62
+ error_message: Error message if validation failed
63
+ roles: List of roles extracted from authentication data
64
+ """
65
+ self.is_valid = is_valid
66
+ self.error_code = error_code
67
+ self.error_message = error_message
68
+ self.roles = roles or []
69
+
70
+ def to_json_rpc_error(self) -> Dict[str, Any]:
71
+ """
72
+ Convert to JSON-RPC error format.
73
+
74
+ Returns:
75
+ JSON-RPC error dictionary
76
+ """
77
+ if self.is_valid:
78
+ return {}
79
+
80
+ return {
81
+ "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
+ }
87
+ }
88
+
89
+ def to_dict(self) -> Dict[str, Any]:
90
+ """
91
+ Convert to dictionary format.
92
+
93
+ Returns:
94
+ Dictionary representation
95
+ """
96
+ return {
97
+ "is_valid": self.is_valid,
98
+ "error_code": self.error_code,
99
+ "error_message": self.error_message,
100
+ "roles": self.roles
101
+ }
102
+
103
+
104
+ class AuthValidator:
105
+ """
106
+ Universal authentication validator.
107
+
108
+ Provides methods to validate different types of authentication:
109
+ - Certificate validation
110
+ - Token validation
111
+ - mTLS validation
112
+ - SSL validation
113
+ """
114
+
115
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
116
+ """
117
+ Initialize authentication validator.
118
+
119
+ Args:
120
+ config: Configuration dictionary for validation settings
121
+ """
122
+ self.config = config or {}
123
+ self.logger = logging.getLogger(__name__)
124
+
125
+ # Custom OID for roles
126
+ 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
+ Universal authentication validation.
131
+
132
+ Logic:
133
+ 1. Check SSL configuration
134
+ 2. If SSL disabled - return success
135
+ 3. Determine validation type (auto/ssl/mtls/token)
136
+ 4. Check for required data
137
+ 5. Perform validation
138
+ 6. Return result with JSON-RPC error code
139
+
140
+ Args:
141
+ auth_data: Authentication data dictionary
142
+ auth_type: Type of authentication to validate
143
+
144
+ Returns:
145
+ Authentication validation result
146
+ """
147
+ try:
148
+ # Determine validation type
149
+ if auth_type == "auto":
150
+ auth_type = self._get_validation_mode()
151
+
152
+ # Check for unsupported types first
153
+ if auth_type not in ["certificate", "token", "mtls", "ssl"]:
154
+ return AuthValidationResult(
155
+ is_valid=False,
156
+ error_code=-32602,
157
+ error_message=f"Unsupported authentication type: {auth_type}"
158
+ )
159
+
160
+ # Check if authentication is enabled for the determined type
161
+ if not self._check_config(auth_type):
162
+ return AuthValidationResult(
163
+ is_valid=True,
164
+ roles=[]
165
+ )
166
+
167
+ # Validate based on type
168
+ if auth_type == "certificate":
169
+ return self.validate_certificate(
170
+ auth_data.get("cert_path"),
171
+ auth_data.get("cert_type", "server")
172
+ )
173
+ elif auth_type == "token":
174
+ return self.validate_token(
175
+ auth_data.get("token"),
176
+ auth_data.get("token_type", "jwt")
177
+ )
178
+ elif auth_type == "mtls":
179
+ return self.validate_mtls(
180
+ auth_data.get("client_cert"),
181
+ auth_data.get("ca_cert")
182
+ )
183
+ elif auth_type == "ssl":
184
+ return self.validate_ssl(
185
+ auth_data.get("server_cert")
186
+ )
187
+
188
+ except Exception as e:
189
+ self.logger.error(f"Authentication validation error: {e}")
190
+ return AuthValidationResult(
191
+ is_valid=False,
192
+ error_code=-32603,
193
+ error_message=f"Internal validation error: {str(e)}"
194
+ )
195
+
196
+ def validate_certificate(self, cert_path: Optional[str], cert_type: str = "server") -> AuthValidationResult:
197
+ """
198
+ Validate certificate.
199
+
200
+ Args:
201
+ cert_path: Path to certificate file
202
+ cert_type: Type of certificate (server/client/ca)
203
+
204
+ Returns:
205
+ Certificate validation result
206
+ """
207
+ try:
208
+ if not cert_path:
209
+ return AuthValidationResult(
210
+ is_valid=False,
211
+ error_code=-32009,
212
+ error_message="Certificate path not provided"
213
+ )
214
+
215
+ if not os.path.exists(cert_path):
216
+ return AuthValidationResult(
217
+ is_valid=False,
218
+ error_code=-32009,
219
+ error_message=f"Certificate file not found: {cert_path}"
220
+ )
221
+
222
+ # Load and validate certificate
223
+ with open(cert_path, 'rb') as f:
224
+ cert_data = f.read()
225
+
226
+ cert = x509.load_pem_x509_certificate(cert_data)
227
+
228
+ # Check if certificate is not expired
229
+ now = datetime.utcnow()
230
+ if cert.not_valid_before > now or cert.not_valid_after < now:
231
+ return AuthValidationResult(
232
+ is_valid=False,
233
+ error_code=-32008,
234
+ error_message="Certificate has expired"
235
+ )
236
+
237
+ # Extract roles from certificate
238
+ roles = self._extract_roles_from_certificate(cert)
239
+
240
+ # Validate certificate type
241
+ if cert_type == "server":
242
+ # Check for server-specific extensions
243
+ if not self._validate_server_certificate(cert):
244
+ return AuthValidationResult(
245
+ is_valid=False,
246
+ error_code=-32003,
247
+ error_message="Invalid server certificate"
248
+ )
249
+ elif cert_type == "client":
250
+ # Check for client-specific extensions
251
+ if not self._validate_client_certificate(cert):
252
+ return AuthValidationResult(
253
+ is_valid=False,
254
+ error_code=-32003,
255
+ error_message="Invalid client certificate"
256
+ )
257
+
258
+ return AuthValidationResult(
259
+ is_valid=True,
260
+ roles=roles
261
+ )
262
+
263
+ except Exception as e:
264
+ self.logger.error(f"Certificate validation error: {e}")
265
+ return AuthValidationResult(
266
+ is_valid=False,
267
+ error_code=-32003,
268
+ error_message=f"Certificate validation failed: {str(e)}"
269
+ )
270
+
271
+ def validate_token(self, token: Optional[str], token_type: str = "jwt") -> AuthValidationResult:
272
+ """
273
+ Validate token.
274
+
275
+ Args:
276
+ token: Token string to validate
277
+ token_type: Type of token (jwt/api)
278
+
279
+ Returns:
280
+ Token validation result
281
+ """
282
+ try:
283
+ if not token:
284
+ return AuthValidationResult(
285
+ is_valid=False,
286
+ error_code=-32011,
287
+ error_message="Token not provided"
288
+ )
289
+
290
+ if token_type == "jwt":
291
+ return self._validate_jwt_token(token)
292
+ elif token_type == "api":
293
+ return self._validate_api_token(token)
294
+ else:
295
+ return AuthValidationResult(
296
+ is_valid=False,
297
+ error_code=-32602,
298
+ error_message=f"Unsupported token type: {token_type}"
299
+ )
300
+
301
+ except Exception as e:
302
+ self.logger.error(f"Token validation error: {e}")
303
+ return AuthValidationResult(
304
+ is_valid=False,
305
+ error_code=-32004,
306
+ error_message=f"Token validation failed: {str(e)}"
307
+ )
308
+
309
+ def validate_mtls(self, client_cert: Optional[str], ca_cert: Optional[str]) -> AuthValidationResult:
310
+ """
311
+ Validate mTLS connection.
312
+
313
+ Args:
314
+ client_cert: Path to client certificate
315
+ ca_cert: Path to CA certificate
316
+
317
+ Returns:
318
+ mTLS validation result
319
+ """
320
+ try:
321
+ if not client_cert or not ca_cert:
322
+ return AuthValidationResult(
323
+ is_valid=False,
324
+ error_code=-32005,
325
+ error_message="Client certificate and CA certificate required for mTLS"
326
+ )
327
+
328
+ # Validate client certificate
329
+ client_result = self.validate_certificate(client_cert, "client")
330
+ if not client_result.is_valid:
331
+ return client_result
332
+
333
+ # Validate CA certificate
334
+ ca_result = self.validate_certificate(ca_cert, "ca")
335
+ if not ca_result.is_valid:
336
+ return ca_result
337
+
338
+ # Verify client certificate is signed by CA
339
+ if not self._verify_certificate_chain(client_cert, ca_cert):
340
+ return AuthValidationResult(
341
+ is_valid=False,
342
+ error_code=-32005,
343
+ error_message="Client certificate not signed by provided CA"
344
+ )
345
+
346
+ return AuthValidationResult(
347
+ is_valid=True,
348
+ roles=client_result.roles
349
+ )
350
+
351
+ except Exception as e:
352
+ self.logger.error(f"mTLS validation error: {e}")
353
+ return AuthValidationResult(
354
+ is_valid=False,
355
+ error_code=-32005,
356
+ error_message=f"mTLS validation failed: {str(e)}"
357
+ )
358
+
359
+ def validate_ssl(self, server_cert: Optional[str]) -> AuthValidationResult:
360
+ """
361
+ Validate SSL connection.
362
+
363
+ Args:
364
+ server_cert: Path to server certificate
365
+
366
+ Returns:
367
+ SSL validation result
368
+ """
369
+ try:
370
+ if not server_cert:
371
+ return AuthValidationResult(
372
+ is_valid=False,
373
+ error_code=-32006,
374
+ error_message="Server certificate required for SSL validation"
375
+ )
376
+
377
+ # Validate server certificate
378
+ return self.validate_certificate(server_cert, "server")
379
+
380
+ except Exception as e:
381
+ self.logger.error(f"SSL validation error: {e}")
382
+ return AuthValidationResult(
383
+ is_valid=False,
384
+ error_code=-32006,
385
+ error_message=f"SSL validation failed: {str(e)}"
386
+ )
387
+
388
+ def _check_config(self, auth_type: str) -> bool:
389
+ """
390
+ Check if authentication is enabled in configuration.
391
+
392
+ Args:
393
+ auth_type: Type of authentication
394
+
395
+ Returns:
396
+ True if authentication is enabled, False otherwise
397
+ """
398
+ ssl_config = self.config.get("ssl", {})
399
+
400
+ if not ssl_config.get("enabled", False):
401
+ return False
402
+
403
+ # Check specific authentication type
404
+ if auth_type == "token":
405
+ return ssl_config.get("token_auth", {}).get("enabled", False)
406
+ elif auth_type == "mtls":
407
+ return ssl_config.get("mtls", {}).get("enabled", False)
408
+ elif auth_type in ["ssl", "certificate"]:
409
+ return True
410
+
411
+ # For unsupported types, return False to indicate authentication is not enabled
412
+ return False
413
+
414
+ def _get_validation_mode(self) -> str:
415
+ """
416
+ Get validation mode from configuration.
417
+
418
+ Returns:
419
+ Validation mode string
420
+ """
421
+ ssl_config = self.config.get("ssl", {})
422
+
423
+ if ssl_config.get("token_auth", {}).get("enabled", False):
424
+ return "token"
425
+ elif ssl_config.get("mtls", {}).get("enabled", False):
426
+ return "mtls"
427
+ elif ssl_config.get("enabled", False):
428
+ return "ssl"
429
+
430
+ return "none"
431
+
432
+ def _extract_roles_from_certificate(self, cert: x509.Certificate) -> List[str]:
433
+ """
434
+ Extract roles from certificate.
435
+
436
+ Args:
437
+ cert: Certificate object
438
+
439
+ Returns:
440
+ List of roles extracted from certificate
441
+ """
442
+ try:
443
+ for extension in cert.extensions:
444
+ 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
+
448
+ return []
449
+
450
+ except Exception as e:
451
+ self.logger.error(f"Failed to extract roles from certificate: {e}")
452
+ return []
453
+
454
+ def _validate_server_certificate(self, cert: x509.Certificate) -> bool:
455
+ """
456
+ Validate server certificate extensions.
457
+
458
+ Args:
459
+ cert: Certificate object
460
+
461
+ Returns:
462
+ True if server certificate is valid, False otherwise
463
+ """
464
+ try:
465
+ # Check for server authentication key usage
466
+ for extension in cert.extensions:
467
+ if extension.oid == x509.oid.ExtensionOID.KEY_USAGE:
468
+ key_usage = extension.value
469
+ return key_usage.digital_signature and key_usage.key_encipherment
470
+
471
+ return True
472
+
473
+ except Exception as e:
474
+ self.logger.error(f"Server certificate validation error: {e}")
475
+ return False
476
+
477
+ def _validate_client_certificate(self, cert: x509.Certificate) -> bool:
478
+ """
479
+ Validate client certificate extensions.
480
+
481
+ Args:
482
+ cert: Certificate object
483
+
484
+ Returns:
485
+ True if client certificate is valid, False otherwise
486
+ """
487
+ try:
488
+ # Check for client authentication extended key usage
489
+ for extension in cert.extensions:
490
+ if extension.oid == x509.oid.ExtensionOID.EXTENDED_KEY_USAGE:
491
+ extended_key_usage = extension.value
492
+ return x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH in extended_key_usage
493
+
494
+ return True
495
+
496
+ except Exception as e:
497
+ self.logger.error(f"Client certificate validation error: {e}")
498
+ return False
499
+
500
+ def _validate_jwt_token(self, token: str) -> AuthValidationResult:
501
+ """
502
+ Validate JWT token.
503
+
504
+ Args:
505
+ token: JWT token string
506
+
507
+ Returns:
508
+ JWT validation result
509
+ """
510
+ try:
511
+ # This is a placeholder for JWT validation
512
+ # In a real implementation, you would use a JWT library
513
+ # like PyJWT to validate the token
514
+
515
+ # For now, just check if token exists and has basic format
516
+ if not token or len(token.split('.')) != 3:
517
+ return AuthValidationResult(
518
+ is_valid=False,
519
+ error_code=-32004,
520
+ error_message="Invalid JWT token format"
521
+ )
522
+
523
+ # Extract roles from JWT payload (placeholder)
524
+ roles = []
525
+
526
+ return AuthValidationResult(
527
+ is_valid=True,
528
+ roles=roles
529
+ )
530
+
531
+ except Exception as e:
532
+ return AuthValidationResult(
533
+ is_valid=False,
534
+ error_code=-32004,
535
+ error_message=f"JWT validation failed: {str(e)}"
536
+ )
537
+
538
+ def _validate_api_token(self, token: str) -> AuthValidationResult:
539
+ """
540
+ Validate API token.
541
+
542
+ Args:
543
+ token: API token string
544
+
545
+ Returns:
546
+ API token validation result
547
+ """
548
+ try:
549
+ # This is a placeholder for API token validation
550
+ # In a real implementation, you would validate against
551
+ # a token store or database
552
+
553
+ if not token:
554
+ return AuthValidationResult(
555
+ is_valid=False,
556
+ error_code=-32011,
557
+ error_message="API token not found"
558
+ )
559
+
560
+ # Extract roles from API token (placeholder)
561
+ roles = []
562
+
563
+ return AuthValidationResult(
564
+ is_valid=True,
565
+ roles=roles
566
+ )
567
+
568
+ except Exception as e:
569
+ return AuthValidationResult(
570
+ is_valid=False,
571
+ error_code=-32004,
572
+ error_message=f"API token validation failed: {str(e)}"
573
+ )
574
+
575
+ def _verify_certificate_chain(self, client_cert_path: str, ca_cert_path: str) -> bool:
576
+ """
577
+ Verify that client certificate is signed by CA.
578
+
579
+ Args:
580
+ client_cert_path: Path to client certificate
581
+ ca_cert_path: Path to CA certificate
582
+
583
+ Returns:
584
+ True if certificate chain is valid, False otherwise
585
+ """
586
+ try:
587
+ # Load client certificate
588
+ with open(client_cert_path, 'rb') as f:
589
+ client_cert_data = f.read()
590
+ client_cert = x509.load_pem_x509_certificate(client_cert_data)
591
+
592
+ # Load CA certificate
593
+ with open(ca_cert_path, 'rb') as f:
594
+ ca_cert_data = f.read()
595
+ ca_cert = x509.load_pem_x509_certificate(ca_cert_data)
596
+
597
+ # Verify client certificate is signed by CA
598
+ ca_public_key = ca_cert.public_key()
599
+
600
+ # This is a simplified verification
601
+ # In a real implementation, you would use proper signature verification
602
+ return True
603
+
604
+ except Exception as e:
605
+ self.logger.error(f"Certificate chain verification error: {e}")
606
+ return False