mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.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 (264) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +9 -4
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,141 @@
1
+ """
2
+ Role Test Command
3
+
4
+ This command tests role-based access control by checking user permissions.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import logging
11
+ from typing import Dict, Any, Optional
12
+ from mcp_proxy_adapter.commands.base import Command
13
+ from mcp_proxy_adapter.commands.result import SuccessResult
14
+
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class RoleTestCommandResult(SuccessResult):
20
+ """Result for role test command."""
21
+
22
+ def __init__(self, user_role: str, permissions: list, action: str, allowed: bool):
23
+ """
24
+ Initialize role test result.
25
+
26
+ Args:
27
+ user_role: User's role
28
+ permissions: User's permissions
29
+ action: Action being tested
30
+ allowed: Whether action is allowed
31
+ """
32
+ super().__init__()
33
+ self.user_role = user_role
34
+ self.permissions = permissions
35
+ self.action = action
36
+ self.allowed = allowed
37
+
38
+ def to_dict(self) -> Dict[str, Any]:
39
+ """Convert to dictionary."""
40
+ return {
41
+ "success": True,
42
+ "data": {
43
+ "user_role": self.user_role,
44
+ "permissions": self.permissions,
45
+ "action": self.action,
46
+ "allowed": self.allowed
47
+ },
48
+ "message": f"Action '{self.action}' {'allowed' if self.allowed else 'denied'} for role '{self.user_role}'"
49
+ }
50
+
51
+ def get_schema(self) -> Dict[str, Any]:
52
+ """Get JSON schema."""
53
+ return {
54
+ "type": "object",
55
+ "properties": {
56
+ "success": {"type": "boolean"},
57
+ "data": {
58
+ "type": "object",
59
+ "properties": {
60
+ "user_role": {"type": "string"},
61
+ "permissions": {"type": "array", "items": {"type": "string"}},
62
+ "action": {"type": "string"},
63
+ "allowed": {"type": "boolean"}
64
+ }
65
+ },
66
+ "message": {"type": "string"}
67
+ }
68
+ }
69
+
70
+
71
+ class RoleTestCommand(Command):
72
+ """Test role-based access control."""
73
+
74
+ name = "roletest"
75
+ descr = "Test role-based access control"
76
+ category = "security"
77
+ author = "Vasiliy Zdanovskiy"
78
+ email = "vasilyvz@gmail.com"
79
+
80
+ async def execute(self, **kwargs) -> RoleTestCommandResult:
81
+ """
82
+ Execute role test command.
83
+
84
+ Args:
85
+ **kwargs: Command parameters including context
86
+
87
+ Returns:
88
+ RoleTestCommandResult
89
+ """
90
+ # Extract parameters
91
+ action = kwargs.get("action", "read")
92
+ context = kwargs.get("context", {})
93
+
94
+ # Get user info from context
95
+ user_role = "guest" # Default
96
+ permissions = ["read"] # Default
97
+
98
+ if context:
99
+ user_info = context.get("user", {})
100
+ user_role = user_info.get("role", "guest")
101
+ permissions = user_info.get("permissions", ["read"])
102
+
103
+ # Check if action is allowed
104
+ allowed = self._check_permission(action, permissions)
105
+
106
+ logger.info(f"Role test: user={user_role}, action={action}, allowed={allowed}")
107
+
108
+ return RoleTestCommandResult(user_role, permissions, action, allowed)
109
+
110
+ @classmethod
111
+ def get_schema(cls) -> Dict[str, Any]:
112
+ """Get JSON schema for command parameters."""
113
+ return {
114
+ "type": "object",
115
+ "properties": {
116
+ "action": {
117
+ "type": "string",
118
+ "description": "Action to test",
119
+ "default": "read"
120
+ }
121
+ },
122
+ "additionalProperties": False
123
+ }
124
+
125
+ def _check_permission(self, action: str, permissions: list) -> bool:
126
+ """
127
+ Check if action is allowed for given permissions.
128
+
129
+ Args:
130
+ action: Action to check
131
+ permissions: User permissions
132
+
133
+ Returns:
134
+ True if allowed, False otherwise
135
+ """
136
+ # Admin has all permissions
137
+ if "*" in permissions:
138
+ return True
139
+
140
+ # Check specific permission
141
+ return action in permissions
@@ -0,0 +1,488 @@
1
+ """
2
+ Security Command - Direct Framework Integration
3
+
4
+ This command provides direct access to mcp_security_framework functionality
5
+ through JSON-RPC interface.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ from typing import Dict, Any, List, Optional
13
+ from pathlib import Path
14
+
15
+ from .base import Command
16
+ from .result import CommandResult, SuccessResult, ErrorResult
17
+ from mcp_proxy_adapter.core.security_integration import create_security_integration
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class SecurityResult(CommandResult):
23
+ """Result class for security operations."""
24
+
25
+ def __init__(self, operation: str, success: bool, data: Dict[str, Any] = None, error: str = None):
26
+ """
27
+ Initialize security result.
28
+
29
+ Args:
30
+ operation: Security operation performed
31
+ success: Whether operation was successful
32
+ data: Operation data
33
+ error: Error message if any
34
+ """
35
+ self.operation = operation
36
+ self.success = success
37
+ self.data = data or {}
38
+ self.error = error
39
+
40
+ def to_dict(self) -> Dict[str, Any]:
41
+ """Convert to dictionary."""
42
+ return {
43
+ "operation": self.operation,
44
+ "success": self.success,
45
+ "data": self.data,
46
+ "error": self.error
47
+ }
48
+
49
+ def get_schema(self) -> Dict[str, Any]:
50
+ """Get result schema."""
51
+ return {
52
+ "type": "object",
53
+ "properties": {
54
+ "operation": {"type": "string", "description": "Security operation performed"},
55
+ "success": {"type": "boolean", "description": "Whether operation was successful"},
56
+ "data": {"type": "object", "description": "Operation data"},
57
+ "error": {"type": "string", "description": "Error message if any"}
58
+ },
59
+ "required": ["operation", "success"]
60
+ }
61
+
62
+
63
+ class SecurityCommand(Command):
64
+ """
65
+ Security command using mcp_security_framework.
66
+
67
+ Provides direct access to security framework functionality:
68
+ - Authentication (API key, JWT, certificate)
69
+ - Certificate management
70
+ - Permission management
71
+ - Rate limiting
72
+ """
73
+
74
+ # Command metadata
75
+ name = "security"
76
+ version = "1.0.0"
77
+ descr = "Security operations using mcp_security_framework"
78
+ category = "security"
79
+ author = "MCP Proxy Adapter Team"
80
+ email = "team@mcp-proxy-adapter.com"
81
+ source_url = "https://github.com/mcp-proxy-adapter"
82
+ result_class = SecurityResult
83
+
84
+ def __init__(self, config: Dict[str, Any]):
85
+ """Initialize security command."""
86
+ super().__init__()
87
+ self.config = config
88
+ self.security_integration = create_security_integration(config)
89
+
90
+ if not self.security_integration:
91
+ logger.warning("Security framework not available, security command will fail")
92
+
93
+ async def execute(self, **kwargs) -> CommandResult:
94
+ """
95
+ Execute security command.
96
+
97
+ Args:
98
+ **kwargs: Command parameters including:
99
+ - action: Action to perform (auth, cert, permission, rate_limit, status)
100
+ - method: Authentication method (api_key, jwt, certificate)
101
+ - api_key: API key for authentication
102
+ - token: JWT token for authentication
103
+ - cert_path: Certificate path for operations
104
+ - common_name: Common name for certificate creation
105
+ - user_id: User ID for permission operations
106
+ - permission: Permission to check
107
+ - role: Role for operations
108
+ - identifier: Identifier for rate limiting
109
+
110
+ Returns:
111
+ CommandResult with security operation status
112
+ """
113
+ if not self.security_integration:
114
+ return SecurityResult(
115
+ operation="security",
116
+ success=False,
117
+ error="Security framework not available"
118
+ )
119
+
120
+ action = kwargs.get("action", "status")
121
+
122
+ try:
123
+ if action == "auth":
124
+ return await self._handle_auth(kwargs)
125
+ elif action == "cert":
126
+ return await self._handle_certificate(kwargs)
127
+ elif action == "permission":
128
+ return await self._handle_permission(kwargs)
129
+ elif action == "rate_limit":
130
+ return await self._handle_rate_limit(kwargs)
131
+ elif action == "status":
132
+ return await self._handle_status(kwargs)
133
+ else:
134
+ return SecurityResult(
135
+ operation=action,
136
+ success=False,
137
+ error=f"Unknown action: {action}"
138
+ )
139
+
140
+ except Exception as e:
141
+ logger.error(f"Security command error: {e}")
142
+ return SecurityResult(
143
+ operation=action,
144
+ success=False,
145
+ error=f"Security operation failed: {str(e)}"
146
+ )
147
+
148
+ async def _handle_auth(self, kwargs: Dict[str, Any]) -> SecurityResult:
149
+ """Handle authentication operations."""
150
+ method = kwargs.get("method", "api_key")
151
+
152
+ if method == "api_key":
153
+ api_key = kwargs.get("api_key")
154
+ if not api_key:
155
+ return SecurityResult(
156
+ operation="auth_api_key",
157
+ success=False,
158
+ error="API key required"
159
+ )
160
+
161
+ result = await self.security_integration.authenticate_api_key(api_key)
162
+ return SecurityResult(
163
+ operation="auth_api_key",
164
+ success=result.is_valid,
165
+ data={
166
+ "user_id": result.user_id,
167
+ "roles": result.roles,
168
+ "permissions": result.permissions
169
+ },
170
+ error=result.error_message if not result.is_valid else None
171
+ )
172
+
173
+ elif method == "jwt":
174
+ token = kwargs.get("token")
175
+ if not token:
176
+ return SecurityResult(
177
+ operation="auth_jwt",
178
+ success=False,
179
+ error="JWT token required"
180
+ )
181
+
182
+ result = await self.security_integration.authenticate_jwt(token)
183
+ return SecurityResult(
184
+ operation="auth_jwt",
185
+ success=result.is_valid,
186
+ data={
187
+ "user_id": result.user_id,
188
+ "roles": result.roles,
189
+ "permissions": result.permissions
190
+ },
191
+ error=result.error_message if not result.is_valid else None
192
+ )
193
+
194
+ elif method == "certificate":
195
+ cert_path = kwargs.get("cert_path")
196
+ if not cert_path:
197
+ return SecurityResult(
198
+ operation="auth_certificate",
199
+ success=False,
200
+ error="Certificate path required"
201
+ )
202
+
203
+ # Read certificate data
204
+ try:
205
+ with open(cert_path, 'rb') as f:
206
+ cert_data = f.read()
207
+
208
+ result = await self.security_integration.authenticate_certificate(cert_data)
209
+ return SecurityResult(
210
+ operation="auth_certificate",
211
+ success=result.is_valid,
212
+ data={
213
+ "user_id": result.user_id,
214
+ "roles": result.roles,
215
+ "permissions": result.permissions
216
+ },
217
+ error=result.error_message if not result.is_valid else None
218
+ )
219
+ except Exception as e:
220
+ return SecurityResult(
221
+ operation="auth_certificate",
222
+ success=False,
223
+ error=f"Failed to read certificate: {str(e)}"
224
+ )
225
+
226
+ else:
227
+ return SecurityResult(
228
+ operation="auth",
229
+ success=False,
230
+ error=f"Unknown authentication method: {method}"
231
+ )
232
+
233
+ async def _handle_certificate(self, kwargs: Dict[str, Any]) -> SecurityResult:
234
+ """Handle certificate operations."""
235
+ cert_action = kwargs.get("cert_action", "validate")
236
+
237
+ if cert_action == "create_ca":
238
+ common_name = kwargs.get("common_name")
239
+ if not common_name:
240
+ return SecurityResult(
241
+ operation="cert_create_ca",
242
+ success=False,
243
+ error="Common name required"
244
+ )
245
+
246
+ try:
247
+ cert_pair = await self.security_integration.create_ca_certificate(common_name)
248
+ return SecurityResult(
249
+ operation="cert_create_ca",
250
+ success=True,
251
+ data={
252
+ "cert_path": str(cert_pair.cert_path),
253
+ "key_path": str(cert_pair.key_path),
254
+ "common_name": common_name
255
+ }
256
+ )
257
+ except Exception as e:
258
+ return SecurityResult(
259
+ operation="cert_create_ca",
260
+ success=False,
261
+ error=f"Failed to create CA certificate: {str(e)}"
262
+ )
263
+
264
+ elif cert_action == "create_client":
265
+ common_name = kwargs.get("common_name")
266
+ if not common_name:
267
+ return SecurityResult(
268
+ operation="cert_create_client",
269
+ success=False,
270
+ error="Common name required"
271
+ )
272
+
273
+ try:
274
+ cert_pair = await self.security_integration.create_client_certificate(common_name)
275
+ return SecurityResult(
276
+ operation="cert_create_client",
277
+ success=True,
278
+ data={
279
+ "cert_path": str(cert_pair.cert_path),
280
+ "key_path": str(cert_pair.key_path),
281
+ "common_name": common_name
282
+ }
283
+ )
284
+ except Exception as e:
285
+ return SecurityResult(
286
+ operation="cert_create_client",
287
+ success=False,
288
+ error=f"Failed to create client certificate: {str(e)}"
289
+ )
290
+
291
+ elif cert_action == "validate":
292
+ cert_path = kwargs.get("cert_path")
293
+ if not cert_path:
294
+ return SecurityResult(
295
+ operation="cert_validate",
296
+ success=False,
297
+ error="Certificate path required"
298
+ )
299
+
300
+ try:
301
+ is_valid = await self.security_integration.validate_certificate(cert_path)
302
+ return SecurityResult(
303
+ operation="cert_validate",
304
+ success=is_valid,
305
+ data={"cert_path": cert_path, "valid": is_valid}
306
+ )
307
+ except Exception as e:
308
+ return SecurityResult(
309
+ operation="cert_validate",
310
+ success=False,
311
+ error=f"Failed to validate certificate: {str(e)}"
312
+ )
313
+
314
+ elif cert_action == "extract_roles":
315
+ cert_path = kwargs.get("cert_path")
316
+ if not cert_path:
317
+ return SecurityResult(
318
+ operation="cert_extract_roles",
319
+ success=False,
320
+ error="Certificate path required"
321
+ )
322
+
323
+ try:
324
+ roles = await self.security_integration.extract_roles_from_certificate(cert_path)
325
+ return SecurityResult(
326
+ operation="cert_extract_roles",
327
+ success=True,
328
+ data={"cert_path": cert_path, "roles": roles}
329
+ )
330
+ except Exception as e:
331
+ return SecurityResult(
332
+ operation="cert_extract_roles",
333
+ success=False,
334
+ error=f"Failed to extract roles: {str(e)}"
335
+ )
336
+
337
+ else:
338
+ return SecurityResult(
339
+ operation="cert",
340
+ success=False,
341
+ error=f"Unknown certificate action: {cert_action}"
342
+ )
343
+
344
+ async def _handle_permission(self, kwargs: Dict[str, Any]) -> SecurityResult:
345
+ """Handle permission operations."""
346
+ perm_action = kwargs.get("perm_action", "check")
347
+ user_id = kwargs.get("user_id")
348
+
349
+ if not user_id:
350
+ return SecurityResult(
351
+ operation="permission",
352
+ success=False,
353
+ error="User ID required"
354
+ )
355
+
356
+ if perm_action == "check":
357
+ permission = kwargs.get("permission")
358
+ if not permission:
359
+ return SecurityResult(
360
+ operation="permission_check",
361
+ success=False,
362
+ error="Permission required"
363
+ )
364
+
365
+ try:
366
+ has_permission = await self.security_integration.check_permission(user_id, permission)
367
+ return SecurityResult(
368
+ operation="permission_check",
369
+ success=True,
370
+ data={
371
+ "user_id": user_id,
372
+ "permission": permission,
373
+ "has_permission": has_permission
374
+ }
375
+ )
376
+ except Exception as e:
377
+ return SecurityResult(
378
+ operation="permission_check",
379
+ success=False,
380
+ error=f"Failed to check permission: {str(e)}"
381
+ )
382
+
383
+ elif perm_action == "get_roles":
384
+ try:
385
+ roles = await self.security_integration.get_user_roles(user_id)
386
+ return SecurityResult(
387
+ operation="permission_get_roles",
388
+ success=True,
389
+ data={"user_id": user_id, "roles": roles}
390
+ )
391
+ except Exception as e:
392
+ return SecurityResult(
393
+ operation="permission_get_roles",
394
+ success=False,
395
+ error=f"Failed to get user roles: {str(e)}"
396
+ )
397
+
398
+ elif perm_action == "add_role":
399
+ role = kwargs.get("role")
400
+ if not role:
401
+ return SecurityResult(
402
+ operation="permission_add_role",
403
+ success=False,
404
+ error="Role required"
405
+ )
406
+
407
+ try:
408
+ success = await self.security_integration.add_user_role(user_id, role)
409
+ return SecurityResult(
410
+ operation="permission_add_role",
411
+ success=success,
412
+ data={"user_id": user_id, "role": role, "added": success}
413
+ )
414
+ except Exception as e:
415
+ return SecurityResult(
416
+ operation="permission_add_role",
417
+ success=False,
418
+ error=f"Failed to add role: {str(e)}"
419
+ )
420
+
421
+ else:
422
+ return SecurityResult(
423
+ operation="permission",
424
+ success=False,
425
+ error=f"Unknown permission action: {perm_action}"
426
+ )
427
+
428
+ async def _handle_rate_limit(self, kwargs: Dict[str, Any]) -> SecurityResult:
429
+ """Handle rate limiting operations."""
430
+ identifier = kwargs.get("identifier")
431
+ if not identifier:
432
+ return SecurityResult(
433
+ operation="rate_limit",
434
+ success=False,
435
+ error="Identifier required"
436
+ )
437
+
438
+ try:
439
+ # Check rate limit
440
+ is_allowed = await self.security_integration.check_rate_limit(identifier)
441
+
442
+ if is_allowed:
443
+ # Increment counter
444
+ await self.security_integration.increment_rate_limit(identifier)
445
+
446
+ # Get rate limit info
447
+ info = await self.security_integration.get_rate_limit_info(identifier)
448
+
449
+ return SecurityResult(
450
+ operation="rate_limit_check",
451
+ success=True,
452
+ data={
453
+ "identifier": identifier,
454
+ "allowed": is_allowed,
455
+ "info": info
456
+ }
457
+ )
458
+ except Exception as e:
459
+ return SecurityResult(
460
+ operation="rate_limit_check",
461
+ success=False,
462
+ error=f"Failed to check rate limit: {str(e)}"
463
+ )
464
+
465
+ async def _handle_status(self, kwargs: Dict[str, Any]) -> SecurityResult:
466
+ """Handle status operations."""
467
+ try:
468
+ security_config = self.security_integration.get_security_config()
469
+
470
+ return SecurityResult(
471
+ operation="status",
472
+ success=True,
473
+ data={
474
+ "security_enabled": self.security_integration.is_security_enabled(),
475
+ "public_paths": self.security_integration.get_public_paths(),
476
+ "auth_enabled": security_config.auth.enabled,
477
+ "ssl_enabled": security_config.ssl.enabled,
478
+ "permissions_enabled": security_config.permissions.enabled,
479
+ "rate_limit_enabled": security_config.rate_limit.enabled,
480
+ "certificates_enabled": security_config.certificates.enabled
481
+ }
482
+ )
483
+ except Exception as e:
484
+ return SecurityResult(
485
+ operation="status",
486
+ success=False,
487
+ error=f"Failed to get status: {str(e)}"
488
+ )
@@ -243,7 +243,7 @@ class SSLSetupCommand(Command):
243
243
  logger.info("Checking SSL status")
244
244
 
245
245
  # Check if SSL is configured in the application
246
- from ..config import Config
246
+ from ...config import Config
247
247
  config = Config()
248
248
 
249
249
  ssl_config = config.get("ssl", {})
@@ -375,7 +375,7 @@ class SSLSetupCommand(Command):
375
375
  try:
376
376
  logger.info(f"Performing SSL config action: {action}")
377
377
 
378
- from ..config import Config
378
+ from ...config import Config
379
379
  config = Config()
380
380
 
381
381
  if action == "get":
@@ -40,7 +40,7 @@ class TokenManagementCommand(Command):
40
40
  self.logger = logging.getLogger(__name__)
41
41
 
42
42
  # Load configuration
43
- from ..config import config
43
+ from ...config import config
44
44
  self.token_config = config.get("ssl", {}).get("token_auth", {})
45
45
  self.tokens_file = self.token_config.get("tokens_file", "tokens.json")
46
46
  self.token_expiry = self.token_config.get("token_expiry", 3600)