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,362 @@
1
+ """
2
+ CRL Utilities Module
3
+
4
+ This module provides utilities for working with Certificate Revocation Lists (CRL).
5
+ Supports both file-based and URL-based CRL sources.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ Version: 1.0.0
10
+ """
11
+
12
+ import logging
13
+ import os
14
+ import tempfile
15
+ from pathlib import Path
16
+ from typing import Optional, Union, Dict, Any
17
+ import requests
18
+ from requests.adapters import HTTPAdapter
19
+ from urllib3.util.retry import Retry
20
+
21
+ # Import mcp_security_framework CRL utilities
22
+ try:
23
+ from mcp_security_framework.utils.cert_utils import (
24
+ is_certificate_revoked,
25
+ validate_certificate_against_crl,
26
+ is_crl_valid,
27
+ get_crl_info,
28
+ )
29
+
30
+ SECURITY_FRAMEWORK_AVAILABLE = True
31
+ except ImportError:
32
+ SECURITY_FRAMEWORK_AVAILABLE = False
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class CRLManager:
38
+ """
39
+ Manager for Certificate Revocation Lists (CRL).
40
+
41
+ Supports both file-based and URL-based CRL sources.
42
+ Automatically downloads CRL from URLs and caches them locally.
43
+ """
44
+
45
+ def __init__(self, config: Dict[str, Any]):
46
+ """
47
+ Initialize CRL manager.
48
+
49
+ Args:
50
+ config: Configuration dictionary containing CRL settings
51
+ """
52
+ self.config = config
53
+ self.crl_enabled = config.get("crl_enabled", False)
54
+
55
+ # Only analyze CRL paths if certificates are enabled
56
+ certificates_enabled = config.get("certificates_enabled", True)
57
+ if certificates_enabled and self.crl_enabled:
58
+ self.crl_path = config.get("crl_path")
59
+ self.crl_url = config.get("crl_url")
60
+ self.crl_validity_days = config.get("crl_validity_days", 30)
61
+ else:
62
+ # Don't analyze CRL paths if certificates are disabled
63
+ self.crl_path = None
64
+ self.crl_url = None
65
+ self.crl_validity_days = 30
66
+
67
+ # Cache for downloaded CRL files
68
+ self._crl_cache: Dict[str, str] = {}
69
+
70
+ # Setup HTTP session with retry strategy
71
+ self._setup_http_session()
72
+
73
+ get_global_logger().info(
74
+ f"CRL Manager initialized - enabled: {self.crl_enabled}, certificates_enabled: {certificates_enabled}"
75
+ )
76
+
77
+ def _setup_http_session(self):
78
+ """Setup HTTP session with retry strategy for CRL downloads."""
79
+ self.session = requests.Session()
80
+
81
+ # Configure retry strategy
82
+ retry_strategy = Retry(
83
+ total=3,
84
+ backoff_factor=1,
85
+ status_forcelist=[429, 500, 502, 503, 504],
86
+ )
87
+
88
+ adapter = HTTPAdapter(max_retries=retry_strategy)
89
+ self.session.mount("http://", adapter)
90
+ self.session.mount("https://", adapter)
91
+
92
+ # Set timeout
93
+ self.session.timeout = 30
94
+
95
+ def get_crl_data(self) -> Optional[Union[str, bytes, Path]]:
96
+ """
97
+ Get CRL data from configured source.
98
+
99
+ Returns:
100
+ CRL data as string, bytes, or Path, or None if not available
101
+
102
+ Raises:
103
+ ValueError: If CRL is enabled but no source is configured
104
+ FileNotFoundError: If CRL file is not found
105
+ requests.RequestException: If CRL download fails
106
+ """
107
+ if not self.crl_enabled:
108
+ get_global_logger().debug("CRL is disabled, skipping CRL check")
109
+ return None
110
+
111
+ # Check if CRL URL is configured
112
+ if self.crl_url:
113
+ return self._get_crl_from_url()
114
+
115
+ # Check if CRL file path is configured
116
+ if self.crl_path:
117
+ return self._get_crl_from_file()
118
+
119
+ # If CRL is enabled but no source is configured, this is an error
120
+ if self.crl_enabled:
121
+ raise ValueError(
122
+ "CRL is enabled but neither crl_path nor crl_url is configured"
123
+ )
124
+
125
+ return None
126
+
127
+ def _get_crl_from_url(self) -> str:
128
+ """
129
+ Download CRL from URL.
130
+
131
+ Returns:
132
+ Path to downloaded CRL file
133
+
134
+ Raises:
135
+ requests.RequestException: If download fails
136
+ ValueError: If downloaded data is not valid CRL
137
+ """
138
+ try:
139
+ get_global_logger().info(f"Downloading CRL from URL: {self.crl_url}")
140
+
141
+ # Download CRL
142
+ response = self.session.get(self.crl_url)
143
+ response.raise_for_status()
144
+
145
+ # Validate content type
146
+ content_type = response.headers.get("content-type", "").lower()
147
+ if (
148
+ "application/pkix-crl" not in content_type
149
+ and "application/x-pkcs7-crl" not in content_type
150
+ ):
151
+ get_global_logger().warning(f"Unexpected content type for CRL: {content_type}")
152
+
153
+ # Save to temporary file
154
+ with tempfile.NamedTemporaryFile(
155
+ mode="wb", suffix=".crl", delete=False
156
+ ) as temp_file:
157
+ temp_file.write(response.content)
158
+ temp_file_path = temp_file.name
159
+
160
+ # Validate CRL format
161
+ if SECURITY_FRAMEWORK_AVAILABLE:
162
+ try:
163
+ is_crl_valid(temp_file_path)
164
+ get_global_logger().info(
165
+ f"CRL downloaded and validated successfully from {self.crl_url}"
166
+ )
167
+ except Exception as e:
168
+ os.unlink(temp_file_path)
169
+ raise ValueError(f"Downloaded CRL is not valid: {e}")
170
+ else:
171
+ get_global_logger().warning(
172
+ "mcp_security_framework not available, skipping CRL validation"
173
+ )
174
+
175
+ # Cache the file path
176
+ self._crl_cache[self.crl_url] = temp_file_path
177
+
178
+ return temp_file_path
179
+
180
+ except requests.RequestException as e:
181
+ get_global_logger().error(f"Failed to download CRL from {self.crl_url}: {e}")
182
+ raise
183
+ except Exception as e:
184
+ get_global_logger().error(f"CRL download failed: {e}")
185
+ raise
186
+
187
+ def _get_crl_from_file(self) -> str:
188
+ """
189
+ Get CRL from file path.
190
+
191
+ Returns:
192
+ Path to CRL file
193
+
194
+ Raises:
195
+ FileNotFoundError: If CRL file is not found
196
+ ValueError: If CRL file is not valid
197
+ """
198
+ if not os.path.exists(self.crl_path):
199
+ raise FileNotFoundError(f"CRL file not found: {self.crl_path}")
200
+
201
+ # Validate CRL format
202
+ if SECURITY_FRAMEWORK_AVAILABLE:
203
+ try:
204
+ is_crl_valid(self.crl_path)
205
+ get_global_logger().info(f"CRL file validated successfully: {self.crl_path}")
206
+ except Exception as e:
207
+ raise ValueError(f"CRL file is not valid: {e}")
208
+ else:
209
+ get_global_logger().warning(
210
+ "mcp_security_framework not available, skipping CRL validation"
211
+ )
212
+
213
+ return self.crl_path
214
+
215
+ def is_certificate_revoked(self, cert_path: str) -> bool:
216
+ """
217
+ Check if certificate is revoked according to CRL.
218
+
219
+ Args:
220
+ cert_path: Path to certificate file
221
+
222
+ Returns:
223
+ True if certificate is revoked, False otherwise
224
+
225
+ Raises:
226
+ ValueError: If CRL is enabled but not available
227
+ FileNotFoundError: If certificate file is not found
228
+ """
229
+ if not self.crl_enabled:
230
+ return False
231
+
232
+ if not SECURITY_FRAMEWORK_AVAILABLE:
233
+ get_global_logger().warning("mcp_security_framework not available, skipping CRL check")
234
+ return False
235
+
236
+ try:
237
+ crl_data = self.get_crl_data()
238
+ if not crl_data:
239
+ get_global_logger().warning("CRL is enabled but no CRL data is available")
240
+ return False
241
+
242
+ is_revoked = is_certificate_revoked(cert_path, crl_data)
243
+
244
+ if is_revoked:
245
+ get_global_logger().warning(f"Certificate is revoked according to CRL: {cert_path}")
246
+ else:
247
+ get_global_logger().debug(
248
+ f"Certificate is not revoked according to CRL: {cert_path}"
249
+ )
250
+
251
+ return is_revoked
252
+
253
+ except Exception as e:
254
+ get_global_logger().error(f"CRL check failed for certificate {cert_path}: {e}")
255
+ # For security, consider certificate invalid if CRL check fails
256
+ return True
257
+
258
+ def validate_certificate_against_crl(self, cert_path: str) -> Dict[str, Any]:
259
+ """
260
+ Validate certificate against CRL and return detailed status.
261
+
262
+ Args:
263
+ cert_path: Path to certificate file
264
+
265
+ Returns:
266
+ Dictionary containing validation results
267
+
268
+ Raises:
269
+ ValueError: If CRL is enabled but not available
270
+ FileNotFoundError: If certificate file is not found
271
+ """
272
+ if not self.crl_enabled:
273
+ return {
274
+ "is_revoked": False,
275
+ "crl_checked": False,
276
+ "crl_source": None,
277
+ "message": "CRL check is disabled",
278
+ }
279
+
280
+ if not SECURITY_FRAMEWORK_AVAILABLE:
281
+ get_global_logger().warning(
282
+ "mcp_security_framework not available, skipping CRL validation"
283
+ )
284
+ return {
285
+ "is_revoked": False,
286
+ "crl_checked": False,
287
+ "crl_source": None,
288
+ "message": "mcp_security_framework not available",
289
+ }
290
+
291
+ try:
292
+ crl_data = self.get_crl_data()
293
+ if not crl_data:
294
+ get_global_logger().warning("CRL is enabled but no CRL data is available")
295
+ return {
296
+ "is_revoked": True, # For security, consider invalid if CRL unavailable
297
+ "crl_checked": False,
298
+ "crl_source": None,
299
+ "message": "CRL is enabled but not available",
300
+ }
301
+
302
+ # Get CRL source info
303
+ crl_source = self.crl_url if self.crl_url else self.crl_path
304
+
305
+ # Validate certificate against CRL
306
+ result = validate_certificate_against_crl(cert_path, crl_data)
307
+
308
+ result["crl_checked"] = True
309
+ result["crl_source"] = crl_source
310
+
311
+ return result
312
+
313
+ except Exception as e:
314
+ get_global_logger().error(f"CRL validation failed for certificate {cert_path}: {e}")
315
+ # For security, consider certificate invalid if CRL validation fails
316
+ return {
317
+ "is_revoked": True,
318
+ "crl_checked": False,
319
+ "crl_source": self.crl_url if self.crl_url else self.crl_path,
320
+ "message": f"CRL validation failed: {e}",
321
+ }
322
+
323
+ def get_crl_info(self) -> Optional[Dict[str, Any]]:
324
+ """
325
+ Get information about the configured CRL.
326
+
327
+ Returns:
328
+ Dictionary containing CRL information, or None if CRL is not available
329
+ """
330
+ if not self.crl_enabled:
331
+ return None
332
+
333
+ if not SECURITY_FRAMEWORK_AVAILABLE:
334
+ get_global_logger().warning("mcp_security_framework not available, cannot get CRL info")
335
+ return None
336
+
337
+ try:
338
+ crl_data = self.get_crl_data()
339
+ if not crl_data:
340
+ return None
341
+
342
+ return get_crl_info(crl_data)
343
+
344
+ except Exception as e:
345
+ get_global_logger().error(f"Failed to get CRL info: {e}")
346
+ return None
347
+
348
+ def cleanup_cache(self):
349
+ """Clean up temporary CRL files."""
350
+ for url, temp_path in self._crl_cache.items():
351
+ try:
352
+ if os.path.exists(temp_path):
353
+ os.unlink(temp_path)
354
+ get_global_logger().debug(f"Cleaned up temporary CRL file: {temp_path}")
355
+ except Exception as e:
356
+ get_global_logger().warning(f"Failed to cleanup temporary CRL file {temp_path}: {e}")
357
+
358
+ self._crl_cache.clear()
359
+
360
+ def __del__(self):
361
+ """Cleanup when object is destroyed."""
362
+ self.cleanup_cache()
@@ -0,0 +1,276 @@
1
+ """
2
+ Module for defining errors and exceptions for the microservice.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any, Dict, List, Optional
7
+
8
+
9
+ class MicroserviceError(Exception):
10
+ """
11
+ Base class for all microservice exceptions.
12
+
13
+ Attributes:
14
+ message: Error message.
15
+ code: Error code.
16
+ data: Additional error data.
17
+ """
18
+
19
+ def __init__(
20
+ self, message: str, code: int = -32000, data: Optional[Dict[str, Any]] = None
21
+ ):
22
+ """
23
+ Initialize the error.
24
+
25
+ Args:
26
+ message: Error message.
27
+ code: Error code according to JSON-RPC standard.
28
+ data: Additional error data.
29
+ """
30
+ self.message = message
31
+ self.code = code
32
+ self.data = data or {}
33
+ super().__init__(message)
34
+
35
+ def to_dict(self) -> Dict[str, Any]:
36
+ """Convert error to dictionary format."""
37
+ result = {
38
+ "code": self.code,
39
+ "message": self.message
40
+ }
41
+ if self.data:
42
+ result["data"] = self.data
43
+ return result
44
+
45
+
46
+
47
+ class ParseError(MicroserviceError):
48
+ """
49
+ Error while parsing JSON request.
50
+ JSON-RPC Error code: -32700
51
+ """
52
+
53
+ def __init__(
54
+ self, message: str = "Parse error", data: Optional[Dict[str, Any]] = None
55
+ ):
56
+ super().__init__(message, code=-32700, data=data)
57
+
58
+
59
+ class InvalidRequestError(MicroserviceError):
60
+ """
61
+ Invalid JSON-RPC request format.
62
+ JSON-RPC Error code: -32600
63
+ """
64
+
65
+ def __init__(
66
+ self, message: str = "Invalid Request", data: Optional[Dict[str, Any]] = None
67
+ ):
68
+ super().__init__(message, code=-32600, data=data)
69
+
70
+
71
+ class MethodNotFoundError(MicroserviceError):
72
+ """
73
+ Method not found error.
74
+ JSON-RPC Error code: -32601
75
+ """
76
+
77
+ def __init__(
78
+ self, message: str = "Method not found", data: Optional[Dict[str, Any]] = None
79
+ ):
80
+ super().__init__(message, code=-32601, data=data)
81
+
82
+
83
+ class InvalidParamsError(MicroserviceError):
84
+ """
85
+ Invalid method parameters.
86
+ JSON-RPC Error code: -32602
87
+ """
88
+
89
+ def __init__(
90
+ self, message: str = "Invalid params", data: Optional[Dict[str, Any]] = None
91
+ ):
92
+ super().__init__(message, code=-32602, data=data)
93
+
94
+
95
+ class InternalError(MicroserviceError):
96
+ """
97
+ Internal server error.
98
+ JSON-RPC Error code: -32603
99
+ """
100
+
101
+ def __init__(
102
+ self, message: str = "Internal error", data: Optional[Dict[str, Any]] = None
103
+ ):
104
+ super().__init__(message, code=-32603, data=data)
105
+
106
+
107
+ class ValidationError(MicroserviceError):
108
+ """
109
+ Input data validation error.
110
+ JSON-RPC Error code: -32602 (using Invalid params code)
111
+ """
112
+
113
+ def __init__(
114
+ self, message: str = "Validation error", data: Optional[Dict[str, Any]] = None
115
+ ):
116
+ super().__init__(message, code=-32602, data=data)
117
+
118
+
119
+ class CommandError(MicroserviceError):
120
+ """
121
+ Command execution error.
122
+ JSON-RPC Error code: -32000 (server error)
123
+ """
124
+
125
+ def __init__(
126
+ self,
127
+ message: str = "Command execution error",
128
+ data: Optional[Dict[str, Any]] = None,
129
+ ):
130
+ super().__init__(message, code=-32000, data=data)
131
+
132
+
133
+ class NotFoundError(MicroserviceError):
134
+ """
135
+ "Not found" error.
136
+ JSON-RPC Error code: -32601 (using Method not found code)
137
+ """
138
+
139
+ def __init__(
140
+ self, message: str = "Resource not found", data: Optional[Dict[str, Any]] = None
141
+ ):
142
+ super().__init__(message, code=-32601, data=data)
143
+
144
+
145
+ class ConfigurationError(MicroserviceError):
146
+ """
147
+ Configuration error.
148
+ JSON-RPC Error code: -32603 (using Internal error code)
149
+ """
150
+
151
+ def __init__(
152
+ self,
153
+ message: str = "Configuration error",
154
+ data: Optional[Dict[str, Any]] = None,
155
+ ):
156
+ super().__init__(message, code=-32603, data=data)
157
+
158
+
159
+ class AuthenticationError(MicroserviceError):
160
+ """
161
+ Authentication error.
162
+ JSON-RPC Error code: -32001 (server error)
163
+ """
164
+
165
+ def __init__(
166
+ self,
167
+ message: str = "Authentication error",
168
+ data: Optional[Dict[str, Any]] = None,
169
+ ):
170
+ super().__init__(message, code=-32001, data=data)
171
+
172
+
173
+ class AuthorizationError(MicroserviceError):
174
+ """
175
+ Authorization error.
176
+ JSON-RPC Error code: -32002 (server error)
177
+ """
178
+
179
+ def __init__(
180
+ self,
181
+ message: str = "Authorization error",
182
+ data: Optional[Dict[str, Any]] = None,
183
+ ):
184
+ super().__init__(message, code=-32002, data=data)
185
+
186
+
187
+ class TimeoutError(MicroserviceError):
188
+ """
189
+ Timeout error.
190
+ JSON-RPC Error code: -32003 (server error)
191
+ """
192
+
193
+ def __init__(
194
+ self, message: str = "Timeout error", data: Optional[Dict[str, Any]] = None
195
+ ):
196
+ super().__init__(message, code=-32003, data=data)
197
+
198
+
199
+
200
+
201
+ @dataclass
202
+ class ValidationResult:
203
+ """Result of configuration validation."""
204
+ level: str # "error", "warning", "info"
205
+ message: str
206
+ section: Optional[str] = None
207
+ key: Optional[str] = None
208
+ suggestion: Optional[str] = None
209
+
210
+
211
+ class ConfigError(MicroserviceError):
212
+ """Configuration validation error."""
213
+
214
+ def __init__(self, message: str, validation_results: Optional[List[ValidationResult]] = None):
215
+ """
216
+ Initialize configuration error.
217
+
218
+ Args:
219
+ message: Error message
220
+ validation_results: List of validation results that caused the error
221
+ """
222
+ super().__init__(message, code=-32001, data={"type": "configuration_error"})
223
+ self.validation_results = validation_results or []
224
+
225
+
226
+
227
+ class MissingConfigKeyError(ConfigError):
228
+ """Missing required configuration key."""
229
+
230
+ def __init__(self, key: str, section: str = None):
231
+ location = f"{section}.{key}" if section else key
232
+ message = f"Required configuration key '{location}' is missing"
233
+ super().__init__(message)
234
+ self.key = key
235
+ self.section = section
236
+
237
+
238
+ class InvalidConfigValueError(ConfigError):
239
+ """Invalid configuration value."""
240
+
241
+ def __init__(self, key: str, value: Any, expected_type: str, section: str = None):
242
+ location = f"{section}.{key}" if section else key
243
+ message = f"Invalid value for '{location}': got {type(value).__name__}, expected {expected_type}"
244
+ super().__init__(message)
245
+ self.key = key
246
+ self.section = section
247
+ self.value = value
248
+ self.expected_type = expected_type
249
+
250
+
251
+ class MissingConfigSectionError(ConfigError):
252
+ """Missing required configuration section."""
253
+
254
+ def __init__(self, section: str):
255
+ message = f"Required configuration section '{section}' is missing"
256
+ super().__init__(message)
257
+ self.section = section
258
+
259
+
260
+ class MissingConfigFileError(ConfigError):
261
+ """Missing configuration file."""
262
+
263
+ def __init__(self, file_path: str):
264
+ message = f"Configuration file '{file_path}' does not exist"
265
+ super().__init__(message)
266
+ self.file_path = file_path
267
+
268
+
269
+ class InvalidConfigFileError(ConfigError):
270
+ """Invalid configuration file format."""
271
+
272
+ def __init__(self, file_path: str, reason: str):
273
+ message = f"Invalid configuration file '{file_path}': {reason}"
274
+ super().__init__(message)
275
+ self.file_path = file_path
276
+ self.reason = reason
@@ -0,0 +1,54 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Simple in-memory job manager for long-running demo commands.
6
+ Not for production use.
7
+ """
8
+
9
+ import asyncio
10
+ import uuid
11
+ from typing import Any, Dict, Optional, Awaitable
12
+
13
+
14
+ class JobRecord:
15
+ def __init__(self, task: asyncio.Task):
16
+ self.task = task
17
+ self.status = "running"
18
+ self.result: Optional[Any] = None
19
+ self.error: Optional[str] = None
20
+
21
+
22
+ _jobs: Dict[str, JobRecord] = {}
23
+
24
+
25
+ def enqueue_coroutine(coro: Awaitable[Any]) -> str:
26
+ job_id = str(uuid.uuid4())
27
+ task = asyncio.create_task(_run_job(job_id, coro))
28
+ _jobs[job_id] = JobRecord(task)
29
+ return job_id
30
+
31
+
32
+ async def _run_job(job_id: str, coro):
33
+ rec = _jobs[job_id]
34
+ try:
35
+ rec.result = await coro
36
+ rec.status = "completed"
37
+ except Exception as exc: # noqa: BLE001
38
+ rec.error = str(exc)
39
+ rec.status = "failed"
40
+
41
+
42
+ def get_job_status(job_id: str) -> Dict[str, Any]:
43
+ rec = _jobs.get(job_id)
44
+ if not rec:
45
+ return {"exists": False}
46
+ return {
47
+ "exists": True,
48
+ "status": rec.status,
49
+ "done": rec.task.done(),
50
+ "result": rec.result,
51
+ "error": rec.error,
52
+ }
53
+
54
+