mcp-proxy-adapter 2.0.1__py3-none-any.whl → 6.9.50__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.

Potentially problematic release.


This version of mcp-proxy-adapter might be problematic. Click here for more details.

Files changed (269) hide show
  1. mcp_proxy_adapter/__init__.py +47 -0
  2. mcp_proxy_adapter/__main__.py +13 -0
  3. mcp_proxy_adapter/api/__init__.py +0 -0
  4. mcp_proxy_adapter/api/app.py +66 -0
  5. mcp_proxy_adapter/api/core/__init__.py +18 -0
  6. mcp_proxy_adapter/api/core/app_factory.py +400 -0
  7. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  8. mcp_proxy_adapter/api/core/registration_context.py +356 -0
  9. mcp_proxy_adapter/api/core/registration_manager.py +307 -0
  10. mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
  11. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  12. mcp_proxy_adapter/api/handlers.py +181 -0
  13. mcp_proxy_adapter/api/middleware/__init__.py +21 -0
  14. mcp_proxy_adapter/api/middleware/base.py +54 -0
  15. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
  16. mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
  17. mcp_proxy_adapter/api/middleware/factory.py +147 -0
  18. mcp_proxy_adapter/api/middleware/logging.py +31 -0
  19. mcp_proxy_adapter/api/middleware/performance.py +51 -0
  20. mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
  21. mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
  22. mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
  23. mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
  24. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  25. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  26. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  27. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  28. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  29. mcp_proxy_adapter/api/schemas.py +270 -0
  30. mcp_proxy_adapter/api/tool_integration.py +131 -0
  31. mcp_proxy_adapter/api/tools.py +163 -0
  32. mcp_proxy_adapter/cli/__init__.py +12 -0
  33. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  34. mcp_proxy_adapter/cli/commands/client.py +100 -0
  35. mcp_proxy_adapter/cli/commands/config_generate.py +105 -0
  36. mcp_proxy_adapter/cli/commands/config_validate.py +94 -0
  37. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  38. mcp_proxy_adapter/cli/commands/server.py +174 -0
  39. mcp_proxy_adapter/cli/commands/sets.py +132 -0
  40. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  41. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  42. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  43. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  44. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  45. mcp_proxy_adapter/cli/main.py +63 -0
  46. mcp_proxy_adapter/cli/parser.py +338 -0
  47. mcp_proxy_adapter/cli/validators.py +231 -0
  48. mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
  49. mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
  50. mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
  51. mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
  52. mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
  53. mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
  54. mcp_proxy_adapter/client/proxy.py +123 -0
  55. mcp_proxy_adapter/commands/__init__.py +66 -0
  56. mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
  57. mcp_proxy_adapter/commands/base.py +389 -0
  58. mcp_proxy_adapter/commands/builtin_commands.py +30 -0
  59. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  60. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  61. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  62. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  63. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  64. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  65. mcp_proxy_adapter/commands/catalog_manager.py +97 -0
  66. mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
  67. mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
  68. mcp_proxy_adapter/commands/command_registry.py +298 -0
  69. mcp_proxy_adapter/commands/config_command.py +102 -0
  70. mcp_proxy_adapter/commands/dependency_container.py +40 -0
  71. mcp_proxy_adapter/commands/dependency_manager.py +143 -0
  72. mcp_proxy_adapter/commands/echo_command.py +48 -0
  73. mcp_proxy_adapter/commands/health_command.py +142 -0
  74. mcp_proxy_adapter/commands/help_command.py +175 -0
  75. mcp_proxy_adapter/commands/hooks.py +172 -0
  76. mcp_proxy_adapter/commands/key_management_command.py +484 -0
  77. mcp_proxy_adapter/commands/load_command.py +123 -0
  78. mcp_proxy_adapter/commands/plugins_command.py +246 -0
  79. mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
  80. mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
  81. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  82. mcp_proxy_adapter/commands/registration_status_command.py +76 -0
  83. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  84. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  85. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  86. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  87. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  88. mcp_proxy_adapter/commands/reload_command.py +136 -0
  89. mcp_proxy_adapter/commands/result.py +157 -0
  90. mcp_proxy_adapter/commands/role_test_command.py +99 -0
  91. mcp_proxy_adapter/commands/roles_management_command.py +502 -0
  92. mcp_proxy_adapter/commands/security_command.py +472 -0
  93. mcp_proxy_adapter/commands/settings_command.py +113 -0
  94. mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
  95. mcp_proxy_adapter/commands/token_management_command.py +500 -0
  96. mcp_proxy_adapter/commands/transport_management_command.py +129 -0
  97. mcp_proxy_adapter/commands/unload_command.py +92 -0
  98. mcp_proxy_adapter/config.py +32 -0
  99. mcp_proxy_adapter/core/__init__.py +8 -0
  100. mcp_proxy_adapter/core/app_factory.py +560 -0
  101. mcp_proxy_adapter/core/app_runner.py +318 -0
  102. mcp_proxy_adapter/core/auth_validator.py +508 -0
  103. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  104. mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
  105. mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
  106. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  107. mcp_proxy_adapter/core/certificate/certificate_validator.py +481 -0
  108. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
  109. mcp_proxy_adapter/core/certificate_utils.py +249 -0
  110. mcp_proxy_adapter/core/client.py +608 -0
  111. mcp_proxy_adapter/core/client_manager.py +271 -0
  112. mcp_proxy_adapter/core/client_security.py +411 -0
  113. mcp_proxy_adapter/core/config/__init__.py +18 -0
  114. mcp_proxy_adapter/core/config/config.py +237 -0
  115. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  116. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  117. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  118. mcp_proxy_adapter/core/config/simple_config.py +204 -0
  119. mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
  120. mcp_proxy_adapter/core/config/simple_config_validator.py +476 -0
  121. mcp_proxy_adapter/core/config_converter.py +252 -0
  122. mcp_proxy_adapter/core/config_validator.py +211 -0
  123. mcp_proxy_adapter/core/crl_utils.py +362 -0
  124. mcp_proxy_adapter/core/errors.py +276 -0
  125. mcp_proxy_adapter/core/job_manager.py +54 -0
  126. mcp_proxy_adapter/core/logging.py +250 -0
  127. mcp_proxy_adapter/core/mtls_asgi.py +140 -0
  128. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  129. mcp_proxy_adapter/core/mtls_proxy.py +229 -0
  130. mcp_proxy_adapter/core/mtls_server.py +154 -0
  131. mcp_proxy_adapter/core/protocol_manager.py +232 -0
  132. mcp_proxy_adapter/core/proxy/__init__.py +19 -0
  133. mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
  134. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
  135. mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
  136. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  137. mcp_proxy_adapter/core/proxy_client.py +184 -0
  138. mcp_proxy_adapter/core/proxy_registration.py +80 -0
  139. mcp_proxy_adapter/core/role_utils.py +103 -0
  140. mcp_proxy_adapter/core/security_adapter.py +343 -0
  141. mcp_proxy_adapter/core/security_factory.py +96 -0
  142. mcp_proxy_adapter/core/security_integration.py +342 -0
  143. mcp_proxy_adapter/core/server_adapter.py +251 -0
  144. mcp_proxy_adapter/core/server_engine.py +217 -0
  145. mcp_proxy_adapter/core/settings.py +260 -0
  146. mcp_proxy_adapter/core/signal_handler.py +107 -0
  147. mcp_proxy_adapter/core/ssl_utils.py +161 -0
  148. mcp_proxy_adapter/core/transport_manager.py +153 -0
  149. mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
  150. mcp_proxy_adapter/core/utils.py +101 -0
  151. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  152. mcp_proxy_adapter/core/validation/config_validator.py +219 -0
  153. mcp_proxy_adapter/core/validation/file_validator.py +131 -0
  154. mcp_proxy_adapter/core/validation/protocol_validator.py +205 -0
  155. mcp_proxy_adapter/core/validation/security_validator.py +140 -0
  156. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  157. mcp_proxy_adapter/custom_openapi.py +58 -0
  158. mcp_proxy_adapter/examples/__init__.py +16 -0
  159. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  160. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  161. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  162. mcp_proxy_adapter/examples/basic_framework/main.py +52 -0
  163. mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
  164. mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
  165. mcp_proxy_adapter/examples/check_config.py +413 -0
  166. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  167. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  168. mcp_proxy_adapter/examples/config_builder.py +234 -0
  169. mcp_proxy_adapter/examples/config_cli.py +282 -0
  170. mcp_proxy_adapter/examples/create_test_configs.py +174 -0
  171. mcp_proxy_adapter/examples/debug_request_state.py +130 -0
  172. mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
  173. mcp_proxy_adapter/examples/demo_client.py +287 -0
  174. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  175. mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
  176. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
  177. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
  178. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
  179. mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
  180. mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
  181. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
  182. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
  183. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
  184. mcp_proxy_adapter/examples/full_application/main.py +311 -0
  185. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -0
  186. mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
  187. mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
  188. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
  189. mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
  190. mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
  191. mcp_proxy_adapter/examples/generate_config.py +502 -0
  192. mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
  193. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  194. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  195. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  196. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  197. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  198. mcp_proxy_adapter/examples/required_certificates.py +208 -0
  199. mcp_proxy_adapter/examples/run_example.py +77 -0
  200. mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
  201. mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
  202. mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
  203. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  204. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  205. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  206. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  207. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  208. mcp_proxy_adapter/examples/security_test_client.py +72 -0
  209. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  210. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  211. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  212. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  213. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  214. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  215. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  216. mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
  217. mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
  218. mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
  219. mcp_proxy_adapter/examples/test_config.py +205 -0
  220. mcp_proxy_adapter/examples/test_config_builder.py +110 -0
  221. mcp_proxy_adapter/examples/test_examples.py +308 -0
  222. mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
  223. mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
  224. mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
  225. mcp_proxy_adapter/examples/universal_client.py +674 -0
  226. mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
  227. mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
  228. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
  229. mcp_proxy_adapter/integrations/__init__.py +25 -0
  230. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  231. mcp_proxy_adapter/main.py +311 -0
  232. mcp_proxy_adapter/openapi.py +375 -0
  233. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  234. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  235. mcp_proxy_adapter/schemas/roles.json +37 -0
  236. mcp_proxy_adapter/schemas/roles_schema.json +162 -0
  237. mcp_proxy_adapter/version.py +5 -0
  238. mcp_proxy_adapter-6.9.50.dist-info/METADATA +1088 -0
  239. mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
  240. {mcp_proxy_adapter-2.0.1.dist-info → mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
  241. mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
  242. mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
  243. adapters/__init__.py +0 -16
  244. analyzers/__init__.py +0 -14
  245. analyzers/docstring_analyzer.py +0 -199
  246. analyzers/type_analyzer.py +0 -151
  247. cli/__init__.py +0 -12
  248. cli/__main__.py +0 -79
  249. cli/command_runner.py +0 -233
  250. dispatchers/__init__.py +0 -14
  251. dispatchers/base_dispatcher.py +0 -85
  252. dispatchers/json_rpc_dispatcher.py +0 -198
  253. generators/__init__.py +0 -14
  254. generators/endpoint_generator.py +0 -172
  255. generators/openapi_generator.py +0 -254
  256. generators/rest_api_generator.py +0 -207
  257. mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
  258. mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
  259. mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
  260. mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
  261. openapi_schema/__init__.py +0 -38
  262. openapi_schema/command_registry.py +0 -312
  263. openapi_schema/rest_schema.py +0 -510
  264. openapi_schema/rpc_generator.py +0 -307
  265. openapi_schema/rpc_schema.py +0 -416
  266. validators/__init__.py +0 -14
  267. validators/base_validator.py +0 -23
  268. validators/docstring_validator.py +0 -75
  269. validators/metadata_validator.py +0 -76
@@ -0,0 +1,476 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Simple configuration validator ensuring required fields and files exist.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from dataclasses import dataclass
12
+ from typing import List
13
+
14
+ from .simple_config import SimpleConfigModel
15
+ from mcp_proxy_adapter.core.certificate.certificate_validator import (
16
+ CertificateValidator,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class ValidationError:
22
+ message: str
23
+
24
+
25
+ class SimpleConfigValidator:
26
+ """Validate SimpleConfigModel instances."""
27
+
28
+ def validate(self, model: SimpleConfigModel) -> List[ValidationError]:
29
+ errors: List[ValidationError] = []
30
+ errors.extend(self._validate_server(model))
31
+ errors.extend(self._validate_client(model))
32
+ errors.extend(self._validate_registration(model))
33
+ errors.extend(self._validate_auth(model))
34
+ return errors
35
+
36
+ def _validate_server(self, model: SimpleConfigModel) -> List[ValidationError]:
37
+ e: List[ValidationError] = []
38
+ s = model.server
39
+ if not s.host:
40
+ e.append(ValidationError("server.host is required"))
41
+ if not isinstance(s.port, int): # type: ignore[unreachable]
42
+ e.append(ValidationError("server.port must be integer"))
43
+ if s.protocol not in ("http", "https", "mtls"):
44
+ e.append(
45
+ ValidationError("server.protocol must be one of: http, https, mtls")
46
+ )
47
+ # Protocol-specific certificate requirements
48
+ if s.protocol == "mtls":
49
+ # For mtls: cert_file and key_file are required
50
+ # CA is required if use_system_ca=False (default), optional if use_system_ca=True
51
+ if not s.cert_file:
52
+ e.append(
53
+ ValidationError("server.cert_file is required for mtls protocol")
54
+ )
55
+ if not s.key_file:
56
+ e.append(
57
+ ValidationError("server.key_file is required for mtls protocol")
58
+ )
59
+ # CA validation is handled below based on use_system_ca flag
60
+ elif s.protocol == "https":
61
+ # For https: certificates are optional, but if one is specified, both must be
62
+ # If key is specified but cert is not - this is an error
63
+ if s.key_file and not s.cert_file:
64
+ e.append(
65
+ ValidationError(
66
+ "server.key_file is specified but server.cert_file is missing for https protocol"
67
+ )
68
+ )
69
+ if s.cert_file and not s.key_file:
70
+ e.append(
71
+ ValidationError(
72
+ "server.cert_file is specified but server.key_file is missing for https protocol"
73
+ )
74
+ )
75
+ # Files existence (if provided)
76
+ for label, path in (
77
+ ("cert_file", s.cert_file),
78
+ ("key_file", s.key_file),
79
+ ("ca_cert_file", s.ca_cert_file),
80
+ ("crl_file", s.crl_file),
81
+ ):
82
+ if path and not os.path.exists(path):
83
+ e.append(ValidationError(f"server.{label} not found: {path}"))
84
+
85
+ # Validate certificate validity if files exist
86
+ if (
87
+ s.cert_file
88
+ and s.key_file
89
+ and os.path.exists(s.cert_file)
90
+ and os.path.exists(s.key_file)
91
+ ):
92
+ # Check certificate-key match
93
+ if not CertificateValidator.validate_certificate_key_match(
94
+ s.cert_file, s.key_file
95
+ ):
96
+ e.append(
97
+ ValidationError("server.cert_file does not match server.key_file")
98
+ )
99
+ # Check certificate expiry
100
+ if not CertificateValidator.validate_certificate_not_expired(s.cert_file):
101
+ e.append(ValidationError("server.cert_file is expired"))
102
+
103
+ # Validate CRL if specified
104
+ if s.crl_file:
105
+ crl_valid, crl_error = CertificateValidator.validate_crl_file(s.crl_file)
106
+ if not crl_valid:
107
+ e.append(
108
+ ValidationError(f"server.crl_file validation failed: {crl_error}")
109
+ )
110
+ else:
111
+ # Check if certificate is revoked according to CRL
112
+ if not CertificateValidator.validate_certificate_not_revoked(
113
+ s.cert_file, s.crl_file
114
+ ):
115
+ e.append(
116
+ ValidationError(
117
+ "server.cert_file is revoked according to server.crl_file"
118
+ )
119
+ )
120
+
121
+ # Validate certificate chain - use CA cert if provided, otherwise check use_system_ca
122
+ if s.cert_file and os.path.exists(s.cert_file):
123
+ if s.ca_cert_file and os.path.exists(s.ca_cert_file):
124
+ # Use provided CA certificate (only CA from config is used by default)
125
+ if not CertificateValidator.validate_certificate_chain(
126
+ s.cert_file, s.ca_cert_file
127
+ ):
128
+ e.append(
129
+ ValidationError(
130
+ "server.cert_file is not signed by server.ca_cert_file"
131
+ )
132
+ )
133
+ else:
134
+ # CA not provided - check if system CA is allowed
135
+ if s.protocol == "mtls":
136
+ # For mTLS: CA is required if use_system_ca=False (default)
137
+ if s.use_system_ca:
138
+ # System CA is explicitly allowed
139
+ if not CertificateValidator.validate_certificate_with_system_store(
140
+ s.cert_file
141
+ ):
142
+ e.append(
143
+ ValidationError(
144
+ "server.cert_file is not valid according to system CA store"
145
+ )
146
+ )
147
+ else:
148
+ # System CA is not allowed (default) - CA must be provided for mTLS
149
+ e.append(
150
+ ValidationError(
151
+ "server.ca_cert_file is required for mtls protocol when use_system_ca is False"
152
+ )
153
+ )
154
+ elif s.protocol == "https":
155
+ # For HTTPS: CA is optional, but if not provided, use system CA if allowed
156
+ if s.use_system_ca:
157
+ # System CA is explicitly allowed
158
+ if not CertificateValidator.validate_certificate_with_system_store(
159
+ s.cert_file
160
+ ):
161
+ e.append(
162
+ ValidationError(
163
+ "server.cert_file is not valid according to system CA store"
164
+ )
165
+ )
166
+ # If use_system_ca=False and CA not provided for HTTPS, that's OK
167
+ # (HTTPS can work without explicit CA validation)
168
+
169
+ return e
170
+
171
+ def _validate_client(self, model: SimpleConfigModel) -> List[ValidationError]:
172
+ """
173
+ Validate client configuration (for connecting to external servers).
174
+ """
175
+ e: List[ValidationError] = []
176
+ c = model.client
177
+ if c.enabled:
178
+ if c.protocol not in ("http", "https", "mtls"):
179
+ e.append(
180
+ ValidationError(
181
+ "client.protocol must be one of: http, https, mtls"
182
+ )
183
+ )
184
+ # Protocol-specific certificate requirements
185
+ if c.protocol == "mtls":
186
+ if not c.cert_file:
187
+ e.append(
188
+ ValidationError(
189
+ "client.cert_file is required for mtls protocol when enabled"
190
+ )
191
+ )
192
+ if not c.key_file:
193
+ e.append(
194
+ ValidationError(
195
+ "client.key_file is required for mtls protocol when enabled"
196
+ )
197
+ )
198
+ elif c.protocol == "https":
199
+ if c.key_file and not c.cert_file:
200
+ e.append(
201
+ ValidationError(
202
+ "client.key_file is specified but client.cert_file is missing for https protocol"
203
+ )
204
+ )
205
+ if c.cert_file and not c.key_file:
206
+ e.append(
207
+ ValidationError(
208
+ "client.cert_file is specified but client.key_file is missing for https protocol"
209
+ )
210
+ )
211
+ # Files existence (if provided)
212
+ for label, path in (
213
+ ("cert_file", c.cert_file),
214
+ ("key_file", c.key_file),
215
+ ("ca_cert_file", c.ca_cert_file),
216
+ ("crl_file", c.crl_file),
217
+ ):
218
+ if path and not os.path.exists(path):
219
+ e.append(ValidationError(f"client.{label} not found: {path}"))
220
+
221
+ # Validate certificate validity if files exist
222
+ if (
223
+ c.cert_file
224
+ and c.key_file
225
+ and os.path.exists(c.cert_file)
226
+ and os.path.exists(c.key_file)
227
+ ):
228
+ # Check certificate-key match
229
+ if not CertificateValidator.validate_certificate_key_match(
230
+ c.cert_file, c.key_file
231
+ ):
232
+ e.append(
233
+ ValidationError(
234
+ "client.cert_file does not match client.key_file"
235
+ )
236
+ )
237
+ # Check certificate expiry
238
+ if not CertificateValidator.validate_certificate_not_expired(c.cert_file):
239
+ e.append(ValidationError("client.cert_file is expired"))
240
+
241
+ # Validate CRL if specified
242
+ if c.crl_file:
243
+ crl_valid, crl_error = CertificateValidator.validate_crl_file(c.crl_file)
244
+ if not crl_valid:
245
+ e.append(
246
+ ValidationError(f"client.crl_file validation failed: {crl_error}")
247
+ )
248
+ else:
249
+ # Check if certificate is revoked according to CRL
250
+ if not CertificateValidator.validate_certificate_not_revoked(
251
+ c.cert_file, c.crl_file
252
+ ):
253
+ e.append(
254
+ ValidationError(
255
+ "client.cert_file is revoked according to client.crl_file"
256
+ )
257
+ )
258
+
259
+ # Validate certificate chain
260
+ if c.cert_file and os.path.exists(c.cert_file):
261
+ if c.ca_cert_file and os.path.exists(c.ca_cert_file):
262
+ if not CertificateValidator.validate_certificate_chain(
263
+ c.cert_file, c.ca_cert_file
264
+ ):
265
+ e.append(
266
+ ValidationError(
267
+ "client.cert_file is not signed by client.ca_cert_file"
268
+ )
269
+ )
270
+ else:
271
+ if c.protocol == "mtls":
272
+ if c.use_system_ca:
273
+ if not CertificateValidator.validate_certificate_with_system_store(
274
+ c.cert_file
275
+ ):
276
+ e.append(
277
+ ValidationError(
278
+ "client.cert_file is not valid according to system CA store"
279
+ )
280
+ )
281
+ else:
282
+ e.append(
283
+ ValidationError(
284
+ "client.ca_cert_file is required for mtls protocol when use_system_ca is False"
285
+ )
286
+ )
287
+ elif c.protocol == "https":
288
+ if c.use_system_ca:
289
+ if not CertificateValidator.validate_certificate_with_system_store(
290
+ c.cert_file
291
+ ):
292
+ e.append(
293
+ ValidationError(
294
+ "client.cert_file is not valid according to system CA store"
295
+ )
296
+ )
297
+ return e
298
+
299
+ def _validate_registration(self, model: SimpleConfigModel) -> List[ValidationError]:
300
+ """
301
+ Validate registration configuration (for registering with proxy server).
302
+ """
303
+ e: List[ValidationError] = []
304
+ r = model.registration
305
+ if r.enabled:
306
+ if not r.host:
307
+ e.append(ValidationError("registration.host is required when enabled"))
308
+ if not isinstance(r.port, int): # type: ignore[unreachable]
309
+ e.append(ValidationError("registration.port must be integer"))
310
+ if r.protocol not in ("http", "https", "mtls"):
311
+ e.append(
312
+ ValidationError(
313
+ "registration.protocol must be one of: http, https, mtls"
314
+ )
315
+ )
316
+ # Validate server_id if provided
317
+ if r.server_id is not None and not isinstance(r.server_id, str):
318
+ e.append(ValidationError("registration.server_id must be a string"))
319
+ if r.server_id is not None and not r.server_id.strip():
320
+ e.append(ValidationError("registration.server_id cannot be empty"))
321
+ # Protocol-specific certificate requirements
322
+ if r.protocol == "mtls":
323
+ if not r.cert_file:
324
+ e.append(
325
+ ValidationError(
326
+ "registration.cert_file is required for mtls protocol when enabled"
327
+ )
328
+ )
329
+ if not r.key_file:
330
+ e.append(
331
+ ValidationError(
332
+ "registration.key_file is required for mtls protocol when enabled"
333
+ )
334
+ )
335
+ elif r.protocol == "https":
336
+ if r.key_file and not r.cert_file:
337
+ e.append(
338
+ ValidationError(
339
+ "registration.key_file is specified but registration.cert_file is missing for https protocol"
340
+ )
341
+ )
342
+ if r.cert_file and not r.key_file:
343
+ e.append(
344
+ ValidationError(
345
+ "registration.cert_file is specified but registration.key_file is missing for https protocol"
346
+ )
347
+ )
348
+ # Files existence (if provided)
349
+ for label, path in (
350
+ ("cert_file", r.cert_file),
351
+ ("key_file", r.key_file),
352
+ ("ca_cert_file", r.ca_cert_file),
353
+ ("crl_file", r.crl_file),
354
+ ):
355
+ if path and not os.path.exists(path):
356
+ e.append(ValidationError(f"registration.{label} not found: {path}"))
357
+
358
+ # Validate certificate validity if files exist
359
+ if (
360
+ r.cert_file
361
+ and r.key_file
362
+ and os.path.exists(r.cert_file)
363
+ and os.path.exists(r.key_file)
364
+ ):
365
+ # Check certificate-key match
366
+ if not CertificateValidator.validate_certificate_key_match(
367
+ r.cert_file, r.key_file
368
+ ):
369
+ e.append(
370
+ ValidationError(
371
+ "registration.cert_file does not match registration.key_file"
372
+ )
373
+ )
374
+ # Check certificate expiry
375
+ if not CertificateValidator.validate_certificate_not_expired(r.cert_file):
376
+ e.append(ValidationError("registration.cert_file is expired"))
377
+
378
+ # Validate CRL if specified
379
+ if r.crl_file:
380
+ crl_valid, crl_error = CertificateValidator.validate_crl_file(r.crl_file)
381
+ if not crl_valid:
382
+ e.append(
383
+ ValidationError(f"registration.crl_file validation failed: {crl_error}")
384
+ )
385
+ else:
386
+ # Check if certificate is revoked according to CRL
387
+ if not CertificateValidator.validate_certificate_not_revoked(
388
+ r.cert_file, r.crl_file
389
+ ):
390
+ e.append(
391
+ ValidationError(
392
+ "registration.cert_file is revoked according to registration.crl_file"
393
+ )
394
+ )
395
+
396
+ # Validate certificate chain
397
+ if r.cert_file and os.path.exists(r.cert_file):
398
+ if r.ca_cert_file and os.path.exists(r.ca_cert_file):
399
+ if not CertificateValidator.validate_certificate_chain(
400
+ r.cert_file, r.ca_cert_file
401
+ ):
402
+ e.append(
403
+ ValidationError(
404
+ "registration.cert_file is not signed by registration.ca_cert_file"
405
+ )
406
+ )
407
+ else:
408
+ if r.protocol == "mtls":
409
+ if r.use_system_ca:
410
+ if not CertificateValidator.validate_certificate_with_system_store(
411
+ r.cert_file
412
+ ):
413
+ e.append(
414
+ ValidationError(
415
+ "registration.cert_file is not valid according to system CA store"
416
+ )
417
+ )
418
+ else:
419
+ e.append(
420
+ ValidationError(
421
+ "registration.ca_cert_file is required for mtls protocol when use_system_ca is False"
422
+ )
423
+ )
424
+ elif r.protocol == "https":
425
+ if r.use_system_ca:
426
+ if not CertificateValidator.validate_certificate_with_system_store(
427
+ r.cert_file
428
+ ):
429
+ e.append(
430
+ ValidationError(
431
+ "registration.cert_file is not valid according to system CA store"
432
+ )
433
+ )
434
+
435
+ # Heartbeat
436
+ if not r.heartbeat.endpoint:
437
+ e.append(ValidationError("registration.heartbeat.endpoint is required"))
438
+ if not isinstance(r.heartbeat.interval, int) or r.heartbeat.interval <= 0:
439
+ e.append(
440
+ ValidationError(
441
+ "registration.heartbeat.interval must be positive integer"
442
+ )
443
+ )
444
+ # Registration endpoints
445
+ if not r.register_endpoint:
446
+ e.append(
447
+ ValidationError("registration.register_endpoint is required")
448
+ )
449
+ if not r.unregister_endpoint:
450
+ e.append(
451
+ ValidationError("registration.unregister_endpoint is required")
452
+ )
453
+ return e
454
+
455
+ def _validate_proxy_client(self, model: SimpleConfigModel) -> List[ValidationError]:
456
+ """
457
+ DEPRECATED: This method is kept for backward compatibility.
458
+ Use _validate_registration instead.
459
+ """
460
+ # This method is no longer used but kept for compatibility
461
+ return []
462
+
463
+ def _validate_auth(self, model: SimpleConfigModel) -> List[ValidationError]:
464
+ e: List[ValidationError] = []
465
+ a = model.auth
466
+ if a.use_roles and not a.use_token:
467
+ e.append(
468
+ ValidationError("auth.use_roles requires auth.use_token to be true")
469
+ )
470
+ if a.use_token and not a.tokens:
471
+ e.append(
472
+ ValidationError(
473
+ "auth.tokens must be provided when auth.use_token is true"
474
+ )
475
+ )
476
+ return e