mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.1.0__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 (253) hide show
  1. mcp_proxy_adapter/__main__.py +12 -0
  2. mcp_proxy_adapter/api/app.py +254 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +36 -30
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +7 -0
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +159 -2
  41. mcp_proxy_adapter/core/app_factory.py +326 -0
  42. mcp_proxy_adapter/core/auth_validator.py +606 -0
  43. mcp_proxy_adapter/core/certificate_utils.py +827 -0
  44. mcp_proxy_adapter/core/client_security.py +384 -0
  45. mcp_proxy_adapter/core/config_converter.py +405 -0
  46. mcp_proxy_adapter/core/config_validator.py +218 -0
  47. mcp_proxy_adapter/core/logging.py +19 -3
  48. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  49. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  50. mcp_proxy_adapter/core/protocol_manager.py +235 -0
  51. mcp_proxy_adapter/core/proxy_client.py +602 -0
  52. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  53. mcp_proxy_adapter/core/role_utils.py +426 -0
  54. mcp_proxy_adapter/core/security_adapter.py +370 -0
  55. mcp_proxy_adapter/core/security_factory.py +239 -0
  56. mcp_proxy_adapter/core/security_integration.py +277 -0
  57. mcp_proxy_adapter/core/server_adapter.py +345 -0
  58. mcp_proxy_adapter/core/server_engine.py +364 -0
  59. mcp_proxy_adapter/core/settings.py +1 -0
  60. mcp_proxy_adapter/core/ssl_utils.py +233 -0
  61. mcp_proxy_adapter/core/transport_manager.py +292 -0
  62. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  63. mcp_proxy_adapter/custom_openapi.py +22 -11
  64. mcp_proxy_adapter/examples/README.md +230 -97
  65. mcp_proxy_adapter/examples/README_EN.md +258 -0
  66. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  67. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  68. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  69. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  70. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  71. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  72. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  73. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  74. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  75. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  76. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  77. mcp_proxy_adapter/examples/cert_config.json +9 -0
  78. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  79. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  80. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  81. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  82. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  83. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  84. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  85. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  86. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  87. mcp_proxy_adapter/examples/certs/client.key +52 -0
  88. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  89. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  90. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  91. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  92. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  93. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  94. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  95. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  96. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  97. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  98. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  99. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  100. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  101. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  102. mcp_proxy_adapter/examples/certs/server.key +52 -0
  103. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  104. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  105. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  106. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  107. mcp_proxy_adapter/examples/certs/user.key +52 -0
  108. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  109. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  110. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  111. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  112. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  113. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  114. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  115. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  116. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  117. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  118. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  119. mcp_proxy_adapter/examples/demo_client.py +341 -0
  120. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  121. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  122. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  123. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  124. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  125. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  126. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  127. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  128. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  129. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  130. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  131. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  132. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  133. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  134. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  135. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  136. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  137. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  138. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  139. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  140. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  141. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  142. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  143. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  144. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  145. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  146. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  147. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  148. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  149. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  150. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  151. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  152. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  153. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  154. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  155. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  156. mcp_proxy_adapter/examples/roles.json +38 -0
  157. mcp_proxy_adapter/examples/run_example.py +81 -0
  158. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  159. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  160. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  161. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  162. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  163. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  164. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  165. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  166. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  167. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  168. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  169. mcp_proxy_adapter/examples/test_examples.py +344 -0
  170. mcp_proxy_adapter/examples/universal_client.py +628 -0
  171. mcp_proxy_adapter/main.py +186 -0
  172. mcp_proxy_adapter/utils/config_generator.py +639 -0
  173. mcp_proxy_adapter/version.py +2 -1
  174. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  175. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  176. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  177. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  178. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  179. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  180. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  181. mcp_proxy_adapter/examples/__init__.py +0 -7
  182. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  183. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  184. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  185. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  186. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  187. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  188. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  189. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  190. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  191. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  192. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  193. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  194. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  195. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  196. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  197. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  198. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  199. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  200. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  201. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  202. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  203. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  204. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  206. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  207. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  208. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  209. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  210. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  211. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  212. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  213. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  214. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  215. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  216. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  217. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  218. mcp_proxy_adapter/tests/__init__.py +0 -0
  219. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  220. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  221. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  222. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  223. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  224. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  225. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  226. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  227. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  228. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  229. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  230. mcp_proxy_adapter/tests/conftest.py +0 -131
  231. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  232. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  233. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  234. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  235. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  236. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  237. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  238. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  239. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  240. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  241. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  242. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  243. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  244. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  245. mcp_proxy_adapter/tests/test_config.py +0 -127
  246. mcp_proxy_adapter/tests/test_utils.py +0 -65
  247. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  249. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  250. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  251. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  252. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  253. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,628 @@
1
+ """
2
+ Universal Client Example
3
+
4
+ This module demonstrates all possible secure connection methods to the server
5
+ using the mcp_security_framework. The client supports all authentication methods
6
+ and connection types supported by the security framework.
7
+
8
+ Author: Vasiliy Zdanovskiy
9
+ email: vasilyvz@gmail.com
10
+ """
11
+
12
+ import asyncio
13
+ import json
14
+ import ssl
15
+ import time
16
+ from typing import Dict, Any, Optional, List, Union
17
+ from urllib.parse import urljoin
18
+ from pathlib import Path
19
+
20
+ import aiohttp
21
+ import requests
22
+ from requests.exceptions import RequestException
23
+
24
+ # Import security framework components
25
+ try:
26
+ from mcp_security_framework import SecurityManager, AuthManager, CertificateManager
27
+ from mcp_security_framework.utils import generate_api_key, create_jwt_token, validate_jwt_token
28
+ from mcp_security_framework.utils import extract_roles_from_cert, validate_certificate_chain
29
+ from mcp_security_framework.utils import create_ssl_context, validate_server_certificate
30
+ SECURITY_FRAMEWORK_AVAILABLE = True
31
+ except ImportError:
32
+ SECURITY_FRAMEWORK_AVAILABLE = False
33
+ print("Warning: mcp_security_framework not available. Using basic HTTP client.")
34
+
35
+
36
+ class UniversalClient:
37
+ """
38
+ Universal client that demonstrates all possible secure connection methods.
39
+
40
+ Supports:
41
+ - HTTP/HTTPS connections
42
+ - API Key authentication
43
+ - JWT token authentication
44
+ - Certificate-based authentication
45
+ - SSL/TLS with custom certificates
46
+ - Role-based access control
47
+ - Rate limiting awareness
48
+ """
49
+
50
+ def __init__(self, config: Dict[str, Any]):
51
+ """
52
+ Initialize universal client with configuration.
53
+
54
+ Args:
55
+ config: Client configuration with security settings
56
+ """
57
+ self.config = config
58
+ self.base_url = config.get("server_url", "http://localhost:8000")
59
+ self.timeout = config.get("timeout", 30)
60
+ self.retry_attempts = config.get("retry_attempts", 3)
61
+ self.retry_delay = config.get("retry_delay", 1)
62
+
63
+ # Security configuration
64
+ self.security_config = config.get("security", {})
65
+ self.auth_method = self.security_config.get("auth_method", "none")
66
+
67
+ # Initialize security managers if framework is available
68
+ self.security_manager = None
69
+ self.auth_manager = None
70
+ self.cert_manager = None
71
+
72
+ if SECURITY_FRAMEWORK_AVAILABLE:
73
+ self._initialize_security_managers()
74
+
75
+ # Session management
76
+ self.session: Optional[aiohttp.ClientSession] = None
77
+ self.current_token: Optional[str] = None
78
+ self.token_expiry: Optional[float] = None
79
+
80
+ print(f"Universal client initialized with auth method: {self.auth_method}")
81
+
82
+ def _initialize_security_managers(self) -> None:
83
+ """Initialize security framework managers."""
84
+ try:
85
+ # Initialize security manager
86
+ self.security_manager = SecurityManager(self.security_config)
87
+
88
+ # Initialize auth manager
89
+ auth_config = self.security_config.get("auth", {})
90
+ self.auth_manager = AuthManager(auth_config)
91
+
92
+ # Initialize certificate manager
93
+ cert_config = self.security_config.get("certificates", {})
94
+ self.cert_manager = CertificateManager(cert_config)
95
+
96
+ print("Security framework managers initialized successfully")
97
+ except Exception as e:
98
+ print(f"Warning: Failed to initialize security managers: {e}")
99
+
100
+ async def __aenter__(self):
101
+ """Async context manager entry."""
102
+ await self.connect()
103
+ return self
104
+
105
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
106
+ """Async context manager exit."""
107
+ await self.disconnect()
108
+
109
+ async def connect(self) -> None:
110
+ """Establish connection with authentication."""
111
+ print(f"Connecting to {self.base_url} with {self.auth_method} authentication...")
112
+
113
+ # Perform authentication based on method
114
+ if self.auth_method == "api_key":
115
+ await self._authenticate_api_key()
116
+ elif self.auth_method == "jwt":
117
+ await self._authenticate_jwt()
118
+ elif self.auth_method == "certificate":
119
+ await self._authenticate_certificate()
120
+ elif self.auth_method == "basic":
121
+ await self._authenticate_basic()
122
+ else:
123
+ print("No authentication required")
124
+
125
+ print("Connection established successfully")
126
+
127
+ async def disconnect(self) -> None:
128
+ """Close connection and cleanup."""
129
+ if self.session:
130
+ await self.session.close()
131
+ self.session = None
132
+ print("Connection closed")
133
+
134
+ async def _authenticate_api_key(self) -> None:
135
+ """Authenticate using API key."""
136
+ api_key_config = self.security_config.get("api_key", {})
137
+ api_key = api_key_config.get("key")
138
+
139
+ if not api_key:
140
+ raise ValueError("API key not provided in configuration")
141
+
142
+ # Store API key for requests
143
+ self.current_token = api_key
144
+ print(f"Authenticated with API key: {api_key[:8]}...")
145
+
146
+ async def _authenticate_jwt(self) -> None:
147
+ """Authenticate using JWT token."""
148
+ jwt_config = self.security_config.get("jwt", {})
149
+
150
+ # Check if we have a stored token that's still valid
151
+ if self.current_token and self.token_expiry and time.time() < self.token_expiry:
152
+ print("Using existing JWT token")
153
+ return
154
+
155
+ # Get credentials for JWT
156
+ username = jwt_config.get("username")
157
+ password = jwt_config.get("password")
158
+ secret = jwt_config.get("secret")
159
+
160
+ if not all([username, password, secret]):
161
+ raise ValueError("JWT credentials not provided in configuration")
162
+
163
+ # Create JWT token
164
+ if SECURITY_FRAMEWORK_AVAILABLE:
165
+ self.current_token = create_jwt_token(
166
+ username,
167
+ secret,
168
+ expiry_hours=jwt_config.get("expiry_hours", 24)
169
+ )
170
+ else:
171
+ # Simple JWT creation (for demonstration)
172
+ import jwt
173
+ payload = {
174
+ "username": username,
175
+ "exp": time.time() + (jwt_config.get("expiry_hours", 24) * 3600)
176
+ }
177
+ self.current_token = jwt.encode(payload, secret, algorithm="HS256")
178
+
179
+ self.token_expiry = time.time() + (jwt_config.get("expiry_hours", 24) * 3600)
180
+ print(f"Authenticated with JWT token: {self.current_token[:20]}...")
181
+
182
+ async def _authenticate_certificate(self) -> None:
183
+ """Authenticate using client certificate."""
184
+ cert_config = self.security_config.get("certificate", {})
185
+
186
+ cert_file = cert_config.get("cert_file")
187
+ key_file = cert_config.get("key_file")
188
+
189
+ if not cert_file or not key_file:
190
+ raise ValueError("Certificate files not provided in configuration")
191
+
192
+ # Validate certificate
193
+ if SECURITY_FRAMEWORK_AVAILABLE and self.cert_manager:
194
+ try:
195
+ cert_info = self.cert_manager.validate_certificate(cert_file, key_file)
196
+ print(f"Certificate validated: {cert_info.get('subject', 'Unknown')}")
197
+
198
+ # Extract roles from certificate
199
+ roles = extract_roles_from_cert(cert_file)
200
+ if roles:
201
+ print(f"Certificate roles: {roles}")
202
+ except Exception as e:
203
+ print(f"Warning: Certificate validation failed: {e}")
204
+
205
+ print("Certificate authentication prepared")
206
+
207
+ async def _authenticate_basic(self) -> None:
208
+ """Authenticate using basic authentication."""
209
+ basic_config = self.security_config.get("basic", {})
210
+ username = basic_config.get("username")
211
+ password = basic_config.get("password")
212
+
213
+ if not username or not password:
214
+ raise ValueError("Basic auth credentials not provided in configuration")
215
+
216
+ import base64
217
+ credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
218
+ self.current_token = f"Basic {credentials}"
219
+ print(f"Authenticated with basic auth: {username}")
220
+
221
+ def _get_auth_headers(self) -> Dict[str, str]:
222
+ """Get authentication headers for requests."""
223
+ headers = {"Content-Type": "application/json"}
224
+
225
+ if not self.current_token:
226
+ return headers
227
+
228
+ if self.auth_method == "api_key":
229
+ api_key_config = self.security_config.get("api_key", {})
230
+ header_name = api_key_config.get("header", "X-API-Key")
231
+ headers[header_name] = self.current_token
232
+ elif self.auth_method == "jwt":
233
+ headers["Authorization"] = f"Bearer {self.current_token}"
234
+ elif self.auth_method == "basic":
235
+ headers["Authorization"] = self.current_token
236
+
237
+ return headers
238
+
239
+ def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
240
+ """Create SSL context for secure connections."""
241
+ if not SECURITY_FRAMEWORK_AVAILABLE:
242
+ return None
243
+
244
+ try:
245
+ ssl_config = self.security_config.get("ssl", {})
246
+
247
+ if not ssl_config.get("enabled", False):
248
+ return None
249
+
250
+ # Create SSL context using security framework
251
+ if self.security_manager:
252
+ return self.security_manager.create_client_ssl_context()
253
+
254
+ # Fallback SSL context creation
255
+ context = ssl.create_default_context()
256
+
257
+ # Add client certificate if provided
258
+ cert_config = self.security_config.get("certificate", {})
259
+ if cert_config.get("enabled", False):
260
+ cert_file = cert_config.get("cert_file")
261
+ key_file = cert_config.get("key_file")
262
+ if cert_file and key_file:
263
+ context.load_cert_chain(cert_file, key_file)
264
+
265
+ # Add CA certificate if provided
266
+ ca_cert_file = ssl_config.get("ca_cert_file")
267
+ if ca_cert_file:
268
+ context.load_verify_locations(ca_cert_file)
269
+
270
+ # Configure verification
271
+ if ssl_config.get("check_hostname", True):
272
+ context.check_hostname = True
273
+ context.verify_mode = ssl.CERT_REQUIRED
274
+ else:
275
+ context.check_hostname = False
276
+ context.verify_mode = ssl.CERT_NONE
277
+
278
+ return context
279
+ except Exception as e:
280
+ print(f"Warning: Failed to create SSL context: {e}")
281
+ return None
282
+
283
+ async def request(
284
+ self,
285
+ method: str,
286
+ endpoint: str,
287
+ data: Optional[Dict[str, Any]] = None,
288
+ headers: Optional[Dict[str, str]] = None
289
+ ) -> Dict[str, Any]:
290
+ """
291
+ Make authenticated request to server.
292
+
293
+ Args:
294
+ method: HTTP method (GET, POST, etc.)
295
+ endpoint: API endpoint
296
+ data: Request data
297
+ headers: Additional headers
298
+
299
+ Returns:
300
+ Response data
301
+ """
302
+ url = urljoin(self.base_url, endpoint)
303
+
304
+ # Prepare headers
305
+ request_headers = self._get_auth_headers()
306
+ if headers:
307
+ request_headers.update(headers)
308
+
309
+ # Create SSL context
310
+ ssl_context = self._create_ssl_context()
311
+
312
+ # Create connector with SSL context
313
+ connector = None
314
+ if ssl_context:
315
+ connector = aiohttp.TCPConnector(ssl=ssl_context)
316
+
317
+ try:
318
+ async with aiohttp.ClientSession(connector=connector) as session:
319
+ for attempt in range(self.retry_attempts):
320
+ try:
321
+ async with session.request(
322
+ method,
323
+ url,
324
+ json=data,
325
+ headers=request_headers,
326
+ timeout=aiohttp.ClientTimeout(total=self.timeout)
327
+ ) as response:
328
+ result = await response.json()
329
+
330
+ # Validate response if security framework available
331
+ if SECURITY_FRAMEWORK_AVAILABLE and self.security_manager:
332
+ self.security_manager.validate_server_response(dict(response.headers))
333
+
334
+ if response.status >= 400:
335
+ print(f"Request failed with status {response.status}: {result}")
336
+ return {"error": result, "status": response.status}
337
+
338
+ return result
339
+
340
+ except Exception as e:
341
+ print(f"Request attempt {attempt + 1} failed: {e}")
342
+ if attempt < self.retry_attempts - 1:
343
+ await asyncio.sleep(self.retry_delay)
344
+ else:
345
+ raise
346
+ finally:
347
+ if connector:
348
+ await connector.close()
349
+
350
+ async def get(self, endpoint: str, **kwargs) -> Dict[str, Any]:
351
+ """Make GET request."""
352
+ return await self.request("GET", endpoint, **kwargs)
353
+
354
+ async def post(self, endpoint: str, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
355
+ """Make POST request."""
356
+ return await self.request("POST", endpoint, data=data, **kwargs)
357
+
358
+ async def put(self, endpoint: str, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
359
+ """Make PUT request."""
360
+ return await self.request("PUT", endpoint, data=data, **kwargs)
361
+
362
+ async def delete(self, endpoint: str, **kwargs) -> Dict[str, Any]:
363
+ """Make DELETE request."""
364
+ return await self.request("DELETE", endpoint, **kwargs)
365
+
366
+ async def test_connection(self) -> bool:
367
+ """Test connection to server."""
368
+ try:
369
+ result = await self.get("/health")
370
+ if "error" not in result:
371
+ print("āœ… Connection test successful")
372
+ return True
373
+ else:
374
+ print(f"āŒ Connection test failed: {result}")
375
+ return False
376
+ except Exception as e:
377
+ print(f"āŒ Connection test failed: {e}")
378
+ return False
379
+
380
+ async def test_security_features(self) -> Dict[str, bool]:
381
+ """Test various security features."""
382
+ results = {}
383
+
384
+ # Test basic connectivity
385
+ results["connectivity"] = await self.test_connection()
386
+
387
+ # Test authentication
388
+ if self.auth_method != "none":
389
+ try:
390
+ result = await self.get("/api/auth/status")
391
+ results["authentication"] = "error" not in result
392
+ except:
393
+ results["authentication"] = False
394
+
395
+ # Test SSL/TLS
396
+ if self.base_url.startswith("https"):
397
+ results["ssl_tls"] = True
398
+ else:
399
+ results["ssl_tls"] = False
400
+
401
+ # Test certificate validation
402
+ if self.auth_method == "certificate" and SECURITY_FRAMEWORK_AVAILABLE:
403
+ results["certificate_validation"] = True
404
+ else:
405
+ results["certificate_validation"] = False
406
+
407
+ return results
408
+
409
+
410
+ def create_client_config(
411
+ server_url: str,
412
+ auth_method: str = "none",
413
+ **kwargs
414
+ ) -> Dict[str, Any]:
415
+ """
416
+ Create client configuration for different authentication methods.
417
+
418
+ Args:
419
+ server_url: Server URL
420
+ auth_method: Authentication method (none, api_key, jwt, certificate, basic)
421
+ **kwargs: Additional configuration parameters
422
+
423
+ Returns:
424
+ Client configuration dictionary
425
+ """
426
+ config = {
427
+ "server_url": server_url,
428
+ "timeout": 30,
429
+ "retry_attempts": 3,
430
+ "retry_delay": 1,
431
+ "security": {
432
+ "auth_method": auth_method
433
+ }
434
+ }
435
+
436
+ if auth_method == "api_key":
437
+ config["security"]["api_key"] = {
438
+ "key": kwargs.get("api_key", "your_api_key_here"),
439
+ "header": kwargs.get("header", "X-API-Key")
440
+ }
441
+
442
+ elif auth_method == "jwt":
443
+ config["security"]["jwt"] = {
444
+ "username": kwargs.get("username", "user"),
445
+ "password": kwargs.get("password", "password"),
446
+ "secret": kwargs.get("secret", "your_jwt_secret"),
447
+ "expiry_hours": kwargs.get("expiry_hours", 24)
448
+ }
449
+
450
+ elif auth_method == "certificate":
451
+ config["security"]["certificate"] = {
452
+ "enabled": True,
453
+ "cert_file": kwargs.get("cert_file", "./certs/client.crt"),
454
+ "key_file": kwargs.get("key_file", "./keys/client.key"),
455
+ "ca_cert_file": kwargs.get("ca_cert_file", "./certs/ca.crt")
456
+ }
457
+ config["security"]["ssl"] = {
458
+ "enabled": True,
459
+ "check_hostname": kwargs.get("check_hostname", True),
460
+ "ca_cert_file": kwargs.get("ca_cert_file", "./certs/ca.crt")
461
+ }
462
+
463
+ elif auth_method == "basic":
464
+ config["security"]["basic"] = {
465
+ "username": kwargs.get("username", "user"),
466
+ "password": kwargs.get("password", "password")
467
+ }
468
+
469
+ return config
470
+
471
+
472
+ async def demo_all_connection_methods():
473
+ """Demonstrate all possible connection methods."""
474
+ print("šŸš€ Universal Client Demo - All Connection Methods")
475
+ print("=" * 60)
476
+
477
+ # Test configurations for different auth methods
478
+ test_configs = [
479
+ {
480
+ "name": "No Authentication",
481
+ "config": create_client_config("http://localhost:8000", "none")
482
+ },
483
+ {
484
+ "name": "API Key Authentication",
485
+ "config": create_client_config(
486
+ "http://localhost:8000",
487
+ "api_key",
488
+ api_key="demo_api_key_123"
489
+ )
490
+ },
491
+ {
492
+ "name": "JWT Authentication",
493
+ "config": create_client_config(
494
+ "http://localhost:8000",
495
+ "jwt",
496
+ username="demo_user",
497
+ password="demo_password",
498
+ secret="demo_jwt_secret"
499
+ )
500
+ },
501
+ {
502
+ "name": "Basic Authentication",
503
+ "config": create_client_config(
504
+ "http://localhost:8000",
505
+ "basic",
506
+ username="demo_user",
507
+ password="demo_password"
508
+ )
509
+ },
510
+ {
511
+ "name": "Certificate Authentication (HTTPS)",
512
+ "config": create_client_config(
513
+ "https://localhost:8443",
514
+ "certificate",
515
+ cert_file="./certs/client.crt",
516
+ key_file="./keys/client.key",
517
+ ca_cert_file="./certs/ca.crt"
518
+ )
519
+ }
520
+ ]
521
+
522
+ for test_config in test_configs:
523
+ print(f"\nšŸ“‹ Testing: {test_config['name']}")
524
+ print("-" * 40)
525
+
526
+ try:
527
+ async with UniversalClient(test_config["config"]) as client:
528
+ # Test connection
529
+ success = await client.test_connection()
530
+
531
+ if success:
532
+ # Test security features
533
+ security_results = await client.test_security_features()
534
+ print("Security Features:")
535
+ for feature, status in security_results.items():
536
+ status_icon = "āœ…" if status else "āŒ"
537
+ print(f" {status_icon} {feature}: {status}")
538
+
539
+ # Make a test API call
540
+ try:
541
+ result = await client.get("/api/status")
542
+ print(f"API Status: {result}")
543
+ except Exception as e:
544
+ print(f"API call failed: {e}")
545
+ else:
546
+ print("āŒ Connection failed")
547
+
548
+ except Exception as e:
549
+ print(f"āŒ Test failed: {e}")
550
+
551
+ print("\nšŸŽ‰ Demo completed!")
552
+
553
+
554
+ async def demo_specific_connection(auth_method: str, **kwargs):
555
+ """
556
+ Demo specific connection method.
557
+
558
+ Args:
559
+ auth_method: Authentication method to test
560
+ **kwargs: Configuration parameters
561
+ """
562
+ print(f"šŸš€ Testing {auth_method} connection")
563
+ print("=" * 40)
564
+
565
+ config = create_client_config("http://localhost:8000", auth_method, **kwargs)
566
+
567
+ async with UniversalClient(config) as client:
568
+ # Test connection
569
+ success = await client.test_connection()
570
+
571
+ if success:
572
+ print("āœ… Connection successful!")
573
+
574
+ # Make some API calls
575
+ try:
576
+ # Get server status
577
+ status = await client.get("/api/status")
578
+ print(f"Server Status: {status}")
579
+
580
+ # Test command execution
581
+ command_data = {
582
+ "jsonrpc": "2.0",
583
+ "method": "test_command",
584
+ "params": {"message": "Hello from universal client!"},
585
+ "id": 1
586
+ }
587
+
588
+ result = await client.post("/api/jsonrpc", command_data)
589
+ print(f"Command Result: {result}")
590
+
591
+ except Exception as e:
592
+ print(f"API calls failed: {e}")
593
+ else:
594
+ print("āŒ Connection failed")
595
+
596
+
597
+ if __name__ == "__main__":
598
+ import sys
599
+
600
+ if len(sys.argv) > 1:
601
+ # Test specific auth method
602
+ auth_method = sys.argv[1]
603
+ kwargs = {}
604
+
605
+ if auth_method == "api_key":
606
+ kwargs["api_key"] = "demo_key_123"
607
+ elif auth_method == "jwt":
608
+ kwargs.update({
609
+ "username": "demo_user",
610
+ "password": "demo_password",
611
+ "secret": "demo_secret"
612
+ })
613
+ elif auth_method == "certificate":
614
+ kwargs.update({
615
+ "cert_file": "./certs/client.crt",
616
+ "key_file": "./keys/client.key",
617
+ "ca_cert_file": "./certs/ca.crt"
618
+ })
619
+ elif auth_method == "basic":
620
+ kwargs.update({
621
+ "username": "demo_user",
622
+ "password": "demo_password"
623
+ })
624
+
625
+ asyncio.run(demo_specific_connection(auth_method, **kwargs))
626
+ else:
627
+ # Demo all connection methods
628
+ asyncio.run(demo_all_connection_methods())