mcp-proxy-adapter 6.0.0__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 (212) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +209 -79
  3. mcp_proxy_adapter/api/handlers.py +16 -5
  4. mcp_proxy_adapter/api/middleware/__init__.py +14 -9
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  7. mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
  8. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  9. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  10. mcp_proxy_adapter/commands/__init__.py +7 -1
  11. mcp_proxy_adapter/commands/base.py +7 -4
  12. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  13. mcp_proxy_adapter/commands/command_registry.py +8 -0
  14. mcp_proxy_adapter/commands/echo_command.py +81 -0
  15. mcp_proxy_adapter/commands/health_command.py +1 -1
  16. mcp_proxy_adapter/commands/help_command.py +21 -14
  17. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  18. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  19. mcp_proxy_adapter/commands/security_command.py +488 -0
  20. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  21. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  22. mcp_proxy_adapter/config.py +323 -40
  23. mcp_proxy_adapter/core/app_factory.py +410 -0
  24. mcp_proxy_adapter/core/app_runner.py +272 -0
  25. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  26. mcp_proxy_adapter/core/client.py +574 -0
  27. mcp_proxy_adapter/core/client_manager.py +284 -0
  28. mcp_proxy_adapter/core/client_security.py +384 -0
  29. mcp_proxy_adapter/core/logging.py +8 -3
  30. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  31. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  32. mcp_proxy_adapter/core/protocol_manager.py +169 -10
  33. mcp_proxy_adapter/core/proxy_client.py +602 -0
  34. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  35. mcp_proxy_adapter/core/security_adapter.py +12 -15
  36. mcp_proxy_adapter/core/security_integration.py +286 -0
  37. mcp_proxy_adapter/core/server_adapter.py +282 -0
  38. mcp_proxy_adapter/core/server_engine.py +270 -0
  39. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  40. mcp_proxy_adapter/core/transport_manager.py +5 -5
  41. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  42. mcp_proxy_adapter/examples/__init__.py +13 -4
  43. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  44. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  45. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  46. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  47. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  48. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  49. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  50. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  51. mcp_proxy_adapter/examples/demo_client.py +275 -0
  52. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  53. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  54. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  55. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  56. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  57. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  58. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  59. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  60. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  61. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  62. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  63. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  64. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  65. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  66. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  67. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  68. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  69. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  70. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  71. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  72. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  73. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  74. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  75. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  76. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  77. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  78. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  79. mcp_proxy_adapter/examples/run_example.py +59 -0
  80. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  81. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  82. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  83. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  84. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  85. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  86. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  87. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  88. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  89. mcp_proxy_adapter/examples/test_config.py +148 -0
  90. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  91. mcp_proxy_adapter/examples/test_examples.py +281 -0
  92. mcp_proxy_adapter/examples/universal_client.py +620 -0
  93. mcp_proxy_adapter/main.py +66 -148
  94. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  95. mcp_proxy_adapter/version.py +5 -2
  96. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  97. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  98. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  99. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  100. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  101. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  102. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  103. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  104. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  105. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  106. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  107. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  108. mcp_proxy_adapter/api/middleware/security.py +0 -376
  109. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  110. mcp_proxy_adapter/examples/README.md +0 -124
  111. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  112. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  113. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  114. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  115. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  116. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  117. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  118. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  119. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  120. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  121. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  122. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  123. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  124. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  125. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  126. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  127. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  128. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  129. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  130. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  131. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  132. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  133. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  134. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  135. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  136. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  137. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  138. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  139. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  140. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  141. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  142. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  143. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  144. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  145. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  146. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  147. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  148. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  149. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  150. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  153. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  154. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  155. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  156. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  157. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  158. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  159. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  160. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  161. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  162. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  163. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  164. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  166. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  167. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  168. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  169. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  170. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  171. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  172. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  173. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  174. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  175. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  176. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  177. mcp_proxy_adapter/tests/__init__.py +0 -0
  178. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  180. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  181. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  182. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  183. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  184. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  185. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  186. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  187. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  188. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  189. mcp_proxy_adapter/tests/conftest.py +0 -131
  190. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  191. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  192. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  193. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  194. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  195. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  196. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  197. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  198. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  199. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  200. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  201. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  202. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  203. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  204. mcp_proxy_adapter/tests/test_config.py +0 -127
  205. mcp_proxy_adapter/tests/test_utils.py +0 -65
  206. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  207. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  208. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  209. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  210. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  211. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,550 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple Certificate Creation Script
4
+ This script creates basic certificates for testing using mcp_security_framework.
5
+ Author: Vasiliy Zdanovskiy
6
+ email: vasilyvz@gmail.com
7
+ """
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ import argparse
12
+ from pathlib import Path
13
+ # Import mcp_security_framework
14
+ try:
15
+ from mcp_security_framework.core.cert_manager import CertificateManager
16
+ from mcp_security_framework.schemas.config import CertificateConfig, CAConfig, ServerCertConfig, ClientCertConfig
17
+ from mcp_security_framework.schemas.models import CertificateType
18
+ SECURITY_FRAMEWORK_AVAILABLE = True
19
+ except ImportError:
20
+ SECURITY_FRAMEWORK_AVAILABLE = False
21
+ print("Warning: mcp_security_framework not available, falling back to OpenSSL")
22
+ class SimpleCertificateCreator:
23
+ """Create certificates using OpenSSL directly."""
24
+ def __init__(self, certs_dir: str = None, keys_dir: str = None):
25
+ # Use current working directory as base
26
+ cwd = Path.cwd()
27
+
28
+ if certs_dir:
29
+ self.certs_dir = Path(certs_dir).resolve()
30
+ else:
31
+ self.certs_dir = cwd / "certs"
32
+
33
+ if keys_dir:
34
+ self.keys_dir = Path(keys_dir).resolve()
35
+ else:
36
+ self.keys_dir = cwd / "keys"
37
+ # Create directories
38
+ self.certs_dir.mkdir(parents=True, exist_ok=True)
39
+ self.keys_dir.mkdir(parents=True, exist_ok=True)
40
+ print(f"🔧 Using certificates directory: {self.certs_dir}")
41
+ print(f"🔧 Using keys directory: {self.keys_dir}")
42
+ def run_command(self, cmd: list, description: str) -> bool:
43
+ """Run a command and handle errors."""
44
+ try:
45
+ print(f"🔧 {description}...")
46
+ # Use current working directory instead of project_root
47
+ result = subprocess.run(
48
+ cmd,
49
+ capture_output=True,
50
+ text=True,
51
+ check=True
52
+ )
53
+ print(f"✅ {description} completed successfully")
54
+ return True
55
+ except subprocess.CalledProcessError as e:
56
+ print(f"❌ {description} failed:")
57
+ print(f" Command: {' '.join(cmd)}")
58
+ print(f" Error: {e.stderr}")
59
+ return False
60
+ except Exception as e:
61
+ print(f"❌ {description} failed: {e}")
62
+ return False
63
+ def create_ca_certificate(self) -> bool:
64
+ """Create CA certificate using mcp_security_framework or OpenSSL fallback."""
65
+ ca_cert_path = self.certs_dir / "ca_cert.pem"
66
+ ca_key_path = self.keys_dir / "ca_key.pem"
67
+ if ca_cert_path.exists() and ca_key_path.exists():
68
+ print(f"ℹ️ CA certificate already exists: {ca_cert_path}")
69
+ return True
70
+ if SECURITY_FRAMEWORK_AVAILABLE:
71
+ return self._create_ca_certificate_with_framework()
72
+ else:
73
+ return self._create_ca_certificate_with_openssl()
74
+ def _create_ca_certificate_with_framework(self) -> bool:
75
+ """Create CA certificate using mcp_security_framework."""
76
+ try:
77
+ print("🔧 Creating CA certificate using mcp_security_framework...")
78
+ # Create CA certificate configuration
79
+ ca_config = CAConfig(
80
+ common_name="mcp_proxy_adapter_test_ca",
81
+ organization="Test Organization",
82
+ organizational_unit="Test Unit",
83
+ country="US",
84
+ state="Test State",
85
+ locality="Test City",
86
+ validity_years=10
87
+ )
88
+ # Create certificate manager
89
+ cert_config = CertificateConfig(
90
+ cert_storage_path=str(self.certs_dir),
91
+ key_storage_path=str(self.keys_dir),
92
+ default_validity_days=365,
93
+ key_size=2048,
94
+ hash_algorithm="sha256"
95
+ )
96
+ cert_manager = CertificateManager(cert_config)
97
+ # Create CA certificate
98
+ cert_pair = cert_manager.create_root_ca(ca_config)
99
+ if cert_pair and cert_pair.certificate_path and cert_pair.private_key_path:
100
+ # Rename the generated files to the expected names
101
+ generated_cert = Path(cert_pair.certificate_path)
102
+ generated_key = Path(cert_pair.private_key_path)
103
+ if generated_cert.exists() and generated_key.exists():
104
+ # Move to expected names
105
+ expected_cert = self.certs_dir / "ca_cert.pem"
106
+ expected_key = self.keys_dir / "ca_key.pem"
107
+ generated_cert.rename(expected_cert)
108
+ generated_key.rename(expected_key)
109
+ print("✅ CA certificate created successfully using mcp_security_framework")
110
+ return True
111
+ else:
112
+ print("❌ Generated CA certificate files not found")
113
+ return False
114
+ else:
115
+ print("❌ Failed to create CA certificate: Invalid certificate pair")
116
+ return False
117
+ except Exception as e:
118
+ print(f"❌ Error creating CA certificate with framework: {e}")
119
+ return False
120
+ def _create_ca_certificate_with_openssl(self) -> bool:
121
+ """Create CA certificate using OpenSSL fallback."""
122
+ ca_cert_path = self.certs_dir / "ca_cert.pem"
123
+ ca_key_path = self.keys_dir / "ca_key.pem"
124
+ # Create CA private key
125
+ key_cmd = [
126
+ "openssl", "genrsa", "-out", str(ca_key_path), "2048"
127
+ ]
128
+ if not self.run_command(key_cmd, "Creating CA private key"):
129
+ return False
130
+ # Create CA certificate
131
+ cert_cmd = [
132
+ "openssl", "req", "-new", "-x509", "-days", "3650",
133
+ "-key", str(ca_key_path),
134
+ "-out", str(ca_cert_path),
135
+ "-subj", "/C=US/ST=Test State/L=Test City/O=Test Organization/CN=MCP Proxy Adapter Test CA"
136
+ ]
137
+ return self.run_command(cert_cmd, "Creating CA certificate")
138
+ def create_server_certificate(self) -> bool:
139
+ """Create server certificate using mcp_security_framework or OpenSSL fallback."""
140
+ server_cert_path = self.certs_dir / "server_cert.pem"
141
+ server_key_path = self.certs_dir / "server_key.pem"
142
+ if server_cert_path.exists() and server_key_path.exists():
143
+ print("ℹ️ Server certificate already exists")
144
+ return True
145
+ if SECURITY_FRAMEWORK_AVAILABLE:
146
+ return self._create_server_certificate_with_framework()
147
+ else:
148
+ return self._create_server_certificate_with_openssl()
149
+ def _create_server_certificate_with_framework(self) -> bool:
150
+ """Create server certificate using mcp_security_framework."""
151
+ try:
152
+ print("🔧 Creating server certificate using mcp_security_framework...")
153
+ # Find CA certificate and key files
154
+ ca_cert_path = None
155
+ ca_key_path = None
156
+ # Look for CA certificate files with expected names
157
+ expected_ca_cert = self.certs_dir / "ca_cert.pem"
158
+ expected_ca_key = self.keys_dir / "ca_key.pem"
159
+ if expected_ca_cert.exists():
160
+ ca_cert_path = str(expected_ca_cert)
161
+ else:
162
+ # Fallback: look for CA certificate files with pattern
163
+ for cert_file in self.certs_dir.glob("*_ca.crt"):
164
+ ca_cert_path = str(cert_file)
165
+ break
166
+ if expected_ca_key.exists():
167
+ ca_key_path = str(expected_ca_key)
168
+ else:
169
+ # Fallback: look for CA key files with pattern
170
+ for key_file in self.keys_dir.glob("*_ca.key"):
171
+ ca_key_path = str(key_file)
172
+ break
173
+ if not ca_cert_path or not ca_key_path:
174
+ print("❌ CA certificate or key not found")
175
+ return False
176
+ # Create server certificate configuration
177
+ server_config = ServerCertConfig(
178
+ common_name="localhost",
179
+ organization="Test Organization",
180
+ organizational_unit="Test Unit",
181
+ country="US",
182
+ state="Test State",
183
+ locality="Test City",
184
+ subject_alt_names=["localhost", "127.0.0.1"],
185
+ validity_years=1,
186
+ ca_cert_path=ca_cert_path,
187
+ ca_key_path=ca_key_path
188
+ )
189
+ # Create certificate manager
190
+ cert_config = CertificateConfig(
191
+ cert_storage_path=str(self.certs_dir),
192
+ key_storage_path=str(self.certs_dir), # Server keys in certs dir
193
+ default_validity_days=365,
194
+ key_size=2048,
195
+ hash_algorithm="sha256"
196
+ )
197
+ cert_manager = CertificateManager(cert_config)
198
+ # Create server certificate
199
+ cert_pair = cert_manager.create_server_certificate(server_config)
200
+ if cert_pair and cert_pair.certificate_path and cert_pair.private_key_path:
201
+ # Rename the generated files to the expected names
202
+ generated_cert = Path(cert_pair.certificate_path)
203
+ generated_key = Path(cert_pair.private_key_path)
204
+ if generated_cert.exists() and generated_key.exists():
205
+ # Move to expected names
206
+ generated_cert.rename(self.certs_dir / "server_cert.pem")
207
+ generated_key.rename(self.certs_dir / "server_key.pem")
208
+ print("✅ Server certificate created successfully using mcp_security_framework")
209
+ return True
210
+ else:
211
+ print("❌ Generated certificate files not found")
212
+ return False
213
+ else:
214
+ print("❌ Failed to create server certificate: Invalid certificate pair")
215
+ return False
216
+ except Exception as e:
217
+ print(f"❌ Error creating server certificate with framework: {e}")
218
+ return False
219
+ def _create_server_certificate_with_openssl(self) -> bool:
220
+ """Create server certificate using OpenSSL fallback."""
221
+ server_cert_path = self.certs_dir / "server_cert.pem"
222
+ server_key_path = self.certs_dir / "server_key.pem"
223
+ # Create server private key
224
+ key_cmd = [
225
+ "openssl", "genrsa", "-out", str(server_key_path), "2048"
226
+ ]
227
+ if not self.run_command(key_cmd, "Creating server private key"):
228
+ return False
229
+ # Create server certificate signing request
230
+ csr_path = self.certs_dir / "server.csr"
231
+ csr_cmd = [
232
+ "openssl", "req", "-new",
233
+ "-key", str(server_key_path),
234
+ "-out", str(csr_path),
235
+ "-subj", "/C=US/ST=Test State/L=Test City/O=Test Organization/CN=localhost"
236
+ ]
237
+ if not self.run_command(csr_cmd, "Creating server CSR"):
238
+ return False
239
+ # Create server certificate
240
+ cert_cmd = [
241
+ "openssl", "x509", "-req", "-days", "730",
242
+ "-in", str(csr_path),
243
+ "-CA", str(self.certs_dir / "ca_cert.pem"),
244
+ "-CAkey", str(self.keys_dir / "ca_key.pem"),
245
+ "-CAcreateserial",
246
+ "-out", str(server_cert_path)
247
+ ]
248
+ success = self.run_command(cert_cmd, "Creating server certificate")
249
+ # Clean up CSR
250
+ if csr_path.exists():
251
+ csr_path.unlink()
252
+ return success
253
+ def create_client_certificate(self, name: str, common_name: str, roles: list = None, permissions: list = None) -> bool:
254
+ """Create client certificate using mcp_security_framework or OpenSSL fallback."""
255
+ cert_path = self.certs_dir / f"{name}_cert.pem"
256
+ key_path = self.certs_dir / f"{name}_key.pem"
257
+ if cert_path.exists() and key_path.exists():
258
+ print(f"ℹ️ Client certificate {name} already exists: {cert_path}")
259
+ return True
260
+ if SECURITY_FRAMEWORK_AVAILABLE:
261
+ return self._create_client_certificate_with_framework(name, common_name, roles, permissions)
262
+ else:
263
+ return self._create_client_certificate_with_openssl(name, common_name)
264
+ def _create_client_certificate_with_framework(self, name: str, common_name: str, roles: list = None, permissions: list = None) -> bool:
265
+ """Create client certificate using mcp_security_framework."""
266
+ try:
267
+ print(f"🔧 Creating client certificate {name} using mcp_security_framework...")
268
+ # Find CA certificate and key files
269
+ ca_cert_path = None
270
+ ca_key_path = None
271
+ # Look for CA certificate files with expected names
272
+ expected_ca_cert = self.certs_dir / "ca_cert.pem"
273
+ expected_ca_key = self.keys_dir / "ca_key.pem"
274
+ if expected_ca_cert.exists():
275
+ ca_cert_path = str(expected_ca_cert)
276
+ else:
277
+ # Fallback: look for CA certificate files with pattern
278
+ for cert_file in self.certs_dir.glob("*_ca.crt"):
279
+ ca_cert_path = str(cert_file)
280
+ break
281
+ if expected_ca_key.exists():
282
+ ca_key_path = str(expected_ca_key)
283
+ else:
284
+ # Fallback: look for CA key files with pattern
285
+ for key_file in self.keys_dir.glob("*_ca.key"):
286
+ ca_key_path = str(key_file)
287
+ break
288
+ if not ca_cert_path or not ca_key_path:
289
+ print("❌ CA certificate or key not found")
290
+ return False
291
+ # Create client certificate configuration
292
+ client_config = ClientCertConfig(
293
+ common_name=common_name,
294
+ organization="Test Organization",
295
+ organizational_unit="Test Unit",
296
+ country="US",
297
+ state="Test State",
298
+ locality="Test City",
299
+ validity_years=1,
300
+ ca_cert_path=ca_cert_path,
301
+ ca_key_path=ca_key_path
302
+ )
303
+ # Create certificate manager
304
+ cert_config = CertificateConfig(
305
+ cert_storage_path=str(self.certs_dir),
306
+ key_storage_path=str(self.certs_dir), # Client keys in certs dir
307
+ default_validity_days=365,
308
+ key_size=2048,
309
+ hash_algorithm="sha256"
310
+ )
311
+ cert_manager = CertificateManager(cert_config)
312
+ # Create client certificate
313
+ cert_pair = cert_manager.create_client_certificate(client_config)
314
+ if cert_pair and cert_pair.certificate_path and cert_pair.private_key_path:
315
+ # Rename the generated files to the expected names
316
+ generated_cert = Path(cert_pair.certificate_path)
317
+ generated_key = Path(cert_pair.private_key_path)
318
+ if generated_cert.exists() and generated_key.exists():
319
+ # Move to expected names
320
+ expected_cert = self.certs_dir / f"{name}_cert.pem"
321
+ expected_key = self.certs_dir / f"{name}_key.pem"
322
+ generated_cert.rename(expected_cert)
323
+ generated_key.rename(expected_key)
324
+ print(f"✅ Client certificate {name} created successfully using mcp_security_framework")
325
+ return True
326
+ else:
327
+ print(f"❌ Generated certificate files not found for {name}")
328
+ return False
329
+ else:
330
+ print(f"❌ Failed to create client certificate {name}: Invalid certificate pair")
331
+ return False
332
+ except Exception as e:
333
+ print(f"❌ Error creating client certificate {name} with framework: {e}")
334
+ return False
335
+ def _create_client_certificate_with_openssl(self, name: str, common_name: str) -> bool:
336
+ """Create client certificate using OpenSSL fallback."""
337
+ cert_path = self.certs_dir / f"{name}_cert.pem"
338
+ key_path = self.certs_dir / f"{name}_key.pem"
339
+ # Create client private key
340
+ key_cmd = [
341
+ "openssl", "genrsa", "-out", str(key_path), "2048"
342
+ ]
343
+ if not self.run_command(key_cmd, f"Creating {name} private key"):
344
+ return False
345
+ # Create client certificate signing request
346
+ csr_path = self.certs_dir / f"{name}.csr"
347
+ csr_cmd = [
348
+ "openssl", "req", "-new",
349
+ "-key", str(key_path),
350
+ "-out", str(csr_path),
351
+ "-subj", f"/C=US/ST=Test State/L=Test City/O=Test Organization/CN={common_name}"
352
+ ]
353
+ if not self.run_command(csr_cmd, f"Creating {name} CSR"):
354
+ return False
355
+ # Create client certificate
356
+ cert_cmd = [
357
+ "openssl", "x509", "-req", "-days", "730",
358
+ "-in", str(csr_path),
359
+ "-CA", str(self.certs_dir / "ca_cert.pem"),
360
+ "-CAkey", str(self.keys_dir / "ca_key.pem"),
361
+ "-CAcreateserial",
362
+ "-out", str(cert_path)
363
+ ]
364
+ success = self.run_command(cert_cmd, f"Creating {name} certificate")
365
+ # Clean up CSR
366
+ if csr_path.exists():
367
+ csr_path.unlink()
368
+ return success
369
+ def create_legacy_certificates(self) -> bool:
370
+ """Create legacy certificate files for compatibility."""
371
+ legacy_files = [
372
+ ("client_admin.crt", "client_admin.key", "admin"),
373
+ ("admin.crt", "admin.key", "admin"),
374
+ ("user.crt", "user.key", "user"),
375
+ ("readonly.crt", "readonly.key", "readonly")
376
+ ]
377
+ success = True
378
+ for cert_file, key_file, source_name in legacy_files:
379
+ cert_path = self.certs_dir / cert_file
380
+ key_path = self.certs_dir / key_file
381
+ if not cert_path.exists() or not key_path.exists():
382
+ source_cert = self.certs_dir / f"{source_name}_cert.pem"
383
+ source_key = self.certs_dir / f"{source_name}_key.pem"
384
+ if source_cert.exists() and source_key.exists():
385
+ self.run_command(["cp", str(source_cert), str(cert_path)], f"Creating {cert_file}")
386
+ self.run_command(["cp", str(source_key), str(key_path)], f"Creating {key_file}")
387
+ else:
388
+ print(f"⚠️ Source certificate {source_name} not found for {cert_file}")
389
+ # Don't fail the entire process for missing legacy certificates
390
+ continue
391
+ return True # Always return True for legacy certificates
392
+ def validate_certificates(self) -> bool:
393
+ """Validate all created certificates."""
394
+ print("\n🔍 Validating certificates...")
395
+ cert_files = [
396
+ "ca_cert.pem",
397
+ "server_cert.pem",
398
+ "admin_cert.pem",
399
+ "user_cert.pem",
400
+ "readonly_cert.pem",
401
+ "guest_cert.pem",
402
+ "proxy_cert.pem"
403
+ ]
404
+ success = True
405
+ for cert_file in cert_files:
406
+ cert_path = self.certs_dir / cert_file
407
+ if cert_path.exists():
408
+ try:
409
+ result = subprocess.run(
410
+ ["openssl", "x509", "-in", str(cert_path), "-text", "-noout"],
411
+ capture_output=True,
412
+ text=True,
413
+ check=True
414
+ )
415
+ print(f"✅ {cert_file}: Valid")
416
+ except subprocess.CalledProcessError:
417
+ print(f"❌ {cert_file}: Invalid")
418
+ success = False
419
+ else:
420
+ print(f"⚠️ {cert_file}: Not found")
421
+ return success
422
+ def create_all(self) -> bool:
423
+ """Create all certificates."""
424
+ print("🔐 Creating All Certificates for Security Testing")
425
+ print("=" * 60)
426
+ success = True
427
+ # 1. Create CA certificate
428
+ if not self.create_ca_certificate():
429
+ success = False
430
+ print("❌ Cannot continue without CA certificate")
431
+ return False
432
+ # 2. Create server certificate
433
+ if not self.create_server_certificate():
434
+ success = False
435
+ # 3. Create client certificates
436
+ print("\n👥 Creating client certificates...")
437
+ client_certs = [
438
+ ("admin", "admin-client", ["admin"], ["read", "write", "execute", "delete", "admin", "register", "unregister", "heartbeat", "discover"]),
439
+ ("user", "user-client", ["user"], ["read", "execute", "register", "unregister", "heartbeat", "discover"]),
440
+ ("readonly", "readonly-client", ["readonly"], ["read", "discover"]),
441
+ ("guest", "guest-client", ["guest"], ["read", "discover"]),
442
+ ("proxy", "proxy-client", ["proxy"], ["register", "unregister", "heartbeat", "discover"])
443
+ ]
444
+ for name, common_name, roles, permissions in client_certs:
445
+ if not self.create_client_certificate(name, common_name, roles, permissions):
446
+ success = False
447
+ # 4. Create legacy certificates
448
+ print("\n🔄 Creating legacy certificates...")
449
+ if not self.create_legacy_certificates():
450
+ success = False
451
+ # 5. Validate certificates
452
+ if not self.validate_certificates():
453
+ success = False
454
+ # Create compatibility symlinks
455
+ if success:
456
+ # CA certificate symlink
457
+ ca_cert = self.certs_dir / "ca_cert.pem"
458
+ expected_ca_cert = self.certs_dir / "mcp_proxy_adapter_ca_ca.crt"
459
+ if ca_cert.exists() and not expected_ca_cert.exists():
460
+ try:
461
+ expected_ca_cert.symlink_to(ca_cert)
462
+ print(f"✅ Created CA certificate symlink: {expected_ca_cert}")
463
+ except OSError:
464
+ # On Windows, symlink might require admin privileges, copy instead
465
+ import shutil
466
+ shutil.copy2(ca_cert, expected_ca_cert)
467
+ print(f"✅ Created CA certificate copy: {expected_ca_cert}")
468
+
469
+ # Server certificate copy
470
+ server_cert = self.certs_dir / "server_cert.pem"
471
+ expected_server_cert = self.certs_dir / "localhost_server.crt"
472
+ if server_cert.exists() and not expected_server_cert.exists():
473
+ import shutil
474
+ shutil.copy2(server_cert, expected_server_cert)
475
+ print(f"✅ Created server certificate copy: {expected_server_cert}")
476
+
477
+ # Server key symlink - check if it's in certs or keys directory
478
+ server_key_certs = self.certs_dir / "server_key.pem"
479
+ server_key_keys = self.keys_dir / "server_key.pem"
480
+
481
+ if server_key_certs.exists():
482
+ # Server key is in certs directory, move it to keys directory
483
+ import shutil
484
+ shutil.move(str(server_key_certs), str(server_key_keys))
485
+ print(f"✅ Moved server key to keys directory: {server_key_keys}")
486
+
487
+ if server_key_keys.exists():
488
+ expected_server_key = self.keys_dir / "localhost_server.key"
489
+ if not expected_server_key.exists():
490
+ import shutil
491
+ shutil.copy2(server_key_keys, expected_server_key)
492
+ print(f"✅ Created server key copy: {expected_server_key}")
493
+
494
+ # Print summary
495
+ print("\n" + "=" * 60)
496
+ print("📊 CERTIFICATE CREATION SUMMARY")
497
+ print("=" * 60)
498
+ if success:
499
+ print("✅ All certificates created successfully!")
500
+ print(f"📁 Certificates directory: {self.certs_dir}")
501
+ print(f"🔑 Keys directory: {self.keys_dir}")
502
+ print("\n📋 Created certificates:")
503
+ cert_files = list(self.certs_dir.glob("*.pem")) + list(self.certs_dir.glob("*.crt"))
504
+ for cert_file in sorted(cert_files):
505
+ print(f" - {cert_file.name}")
506
+ key_files = list(self.keys_dir.glob("*.pem")) + list(self.keys_dir.glob("*.key"))
507
+ for key_file in sorted(key_files):
508
+ print(f" - {key_file.name}")
509
+ else:
510
+ print("❌ Some certificates failed to create")
511
+ print("Check the error messages above")
512
+ return success
513
+ def main():
514
+ """Main function."""
515
+ parser = argparse.ArgumentParser(description="Create certificates for testing")
516
+ parser.add_argument("--certs-dir", help="Directory for certificates (default: ./certs)")
517
+ parser.add_argument("--keys-dir", help="Directory for keys (default: ./keys)")
518
+ args = parser.parse_args()
519
+
520
+ # If no directories specified, check if we're in a test environment
521
+ if not args.certs_dir and not args.keys_dir:
522
+ cwd = Path.cwd()
523
+ # Check if we're in a test environment by looking for typical directories
524
+ if (cwd / "configs").exists() and (cwd / "examples").exists():
525
+ # We're in a test environment, use current directory
526
+ certs_dir = cwd / "certs"
527
+ keys_dir = cwd / "keys"
528
+ else:
529
+ # Use default project structure
530
+ certs_dir = None
531
+ keys_dir = None
532
+ else:
533
+ certs_dir = args.certs_dir
534
+ keys_dir = args.keys_dir
535
+
536
+ creator = SimpleCertificateCreator(
537
+ certs_dir=certs_dir,
538
+ keys_dir=keys_dir
539
+ )
540
+ try:
541
+ success = creator.create_all()
542
+ sys.exit(0 if success else 1)
543
+ except KeyboardInterrupt:
544
+ print("\n⚠️ Certificate creation interrupted by user")
545
+ sys.exit(1)
546
+ except Exception as e:
547
+ print(f"\n❌ Certificate creation failed: {e}")
548
+ sys.exit(1)
549
+ if __name__ == "__main__":
550
+ main()
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Debug Request State - Проверка request.state
4
+ Этот скрипт проверяет, как middleware устанавливает информацию о пользователе в request.state.
5
+ Author: Vasiliy Zdanovskiy
6
+ email: vasilyvz@gmail.com
7
+ """
8
+ import asyncio
9
+ import json
10
+ import sys
11
+ from pathlib import Path
12
+ # Add project root to path
13
+ project_root = Path(__file__).parent.parent.parent
14
+ sys.path.insert(0, str(project_root))
15
+ from fastapi import FastAPI, Request
16
+ from fastapi.testclient import TestClient
17
+ from mcp_proxy_adapter.api.app import create_app
18
+ async def debug_request_state():
19
+ """Debug request state handling."""
20
+ print("🔍 ОТЛАДКА REQUEST.STATE")
21
+ print("=" * 50)
22
+ # Create test app with proper configuration
23
+ config_path = project_root / "mcp_proxy_adapter" / "examples" / "server_configs" / "config_http_token.json"
24
+ with open(config_path) as f:
25
+ config = json.load(f)
26
+ # Override global config for testing
27
+ import mcp_proxy_adapter.config
28
+ mcp_proxy_adapter.config.config = config
29
+ app = create_app(config)
30
+ client = TestClient(app)
31
+ print("📋 1. ТЕСТИРОВАНИЕ БЕЗ АУТЕНТИФИКАЦИИ")
32
+ print("-" * 30)
33
+ # Test without authentication
34
+ response = client.post("/cmd", json={
35
+ "jsonrpc": "2.0",
36
+ "method": "echo",
37
+ "params": {"message": "test"},
38
+ "id": 1
39
+ })
40
+ print(f"Status: {response.status_code}")
41
+ print(f"Response: {response.json()}")
42
+ print("\n📋 2. ТЕСТИРОВАНИЕ С ADMIN ТОКЕНОМ")
43
+ print("-" * 30)
44
+ # Test with admin token
45
+ response = client.post("/cmd",
46
+ json={
47
+ "jsonrpc": "2.0",
48
+ "method": "echo",
49
+ "params": {"message": "test"},
50
+ "id": 1
51
+ },
52
+ headers={"X-API-Key": "test-token-123"}
53
+ )
54
+ print(f"Status: {response.status_code}")
55
+ print(f"Response: {response.json()}")
56
+ print("\n📋 3. ТЕСТИРОВАНИЕ С USER ТОКЕНОМ")
57
+ print("-" * 30)
58
+ # Test with user token
59
+ response = client.post("/cmd",
60
+ json={
61
+ "jsonrpc": "2.0",
62
+ "method": "echo",
63
+ "params": {"message": "test"},
64
+ "id": 1
65
+ },
66
+ headers={"X-API-Key": "user-token-456"}
67
+ )
68
+ print(f"Status: {response.status_code}")
69
+ print(f"Response: {response.json()}")
70
+ print("\n📋 4. ТЕСТИРОВАНИЕ С READONLY ТОКЕНОМ")
71
+ print("-" * 30)
72
+ # Test with readonly token
73
+ response = client.post("/cmd",
74
+ json={
75
+ "jsonrpc": "2.0",
76
+ "method": "echo",
77
+ "params": {"message": "test"},
78
+ "id": 1
79
+ },
80
+ headers={"X-API-Key": "readonly-token-123"}
81
+ )
82
+ print(f"Status: {response.status_code}")
83
+ print(f"Response: {response.json()}")
84
+ print("\n📋 5. ТЕСТИРОВАНИЕ ROLE_TEST КОМАНДЫ")
85
+ print("-" * 30)
86
+ # Test role_test command with readonly token
87
+ response = client.post("/cmd",
88
+ json={
89
+ "jsonrpc": "2.0",
90
+ "method": "role_test",
91
+ "params": {"action": "write"},
92
+ "id": 1
93
+ },
94
+ headers={"X-API-Key": "readonly-token-123"}
95
+ )
96
+ print(f"Status: {response.status_code}")
97
+ print(f"Response: {response.json()}")
98
+ print("\n📋 6. АНАЛИЗ ПРОБЛЕМЫ")
99
+ print("-" * 30)
100
+ print("🔍 ПРОБЛЕМА: Readonly роль получает доступ к командам")
101
+ print("\n📋 ВОЗМОЖНЫЕ ПРИЧИНЫ:")
102
+ print("1. Framework middleware не устанавливает user info в request.state")
103
+ print("2. Нет проверки прав на уровне middleware")
104
+ print("3. Команды не проверяют права доступа")
105
+ print("4. Интеграция между middleware и командами не работает")
106
+ print("\n📋 РЕКОМЕНДАЦИИ:")
107
+ print("1. Добавить CommandPermissionMiddleware")
108
+ print("2. Убедиться, что framework middleware устанавливает user info")
109
+ print("3. Добавить проверку прав в команды")
110
+ print("4. Проверить интеграцию middleware")
111
+ if __name__ == "__main__":
112
+ asyncio.run(debug_request_state())