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,148 @@
1
+ """
2
+ Command Permission Middleware
3
+
4
+ This middleware checks permissions for specific commands based on user roles.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ from typing import Dict, Any, Optional, Callable, Awaitable
13
+ from fastapi import Request, Response
14
+ from starlette.middleware.base import BaseHTTPMiddleware
15
+
16
+ from mcp_proxy_adapter.core.logging import logger
17
+
18
+
19
+ class CommandPermissionMiddleware(BaseHTTPMiddleware):
20
+ """
21
+ Middleware for checking command permissions.
22
+
23
+ This middleware checks if the authenticated user has the required
24
+ permissions to execute specific commands.
25
+ """
26
+
27
+ def __init__(self, app, config: Dict[str, Any]):
28
+ """
29
+ Initialize command permission middleware.
30
+
31
+ Args:
32
+ app: FastAPI application
33
+ config: Configuration dictionary
34
+ """
35
+ super().__init__(app)
36
+ self.config = config
37
+
38
+ # Define command permissions
39
+ self.command_permissions = {
40
+ "echo": ["read"],
41
+ "health": ["read"],
42
+ "role_test": ["read"],
43
+ "config": ["read"],
44
+ "help": ["read"],
45
+ # Add more commands as needed
46
+ }
47
+
48
+ logger.info("Command permission middleware initialized")
49
+
50
+ async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
51
+ """
52
+ Process request and check command permissions.
53
+
54
+ Args:
55
+ request: Request object
56
+ call_next: Next handler
57
+
58
+ Returns:
59
+ Response object
60
+ """
61
+ # Only check permissions for /cmd endpoint
62
+ if request.url.path != "/cmd":
63
+ return await call_next(request)
64
+
65
+ try:
66
+ # Get request body
67
+ body = await request.body()
68
+ if not body:
69
+ return await call_next(request)
70
+
71
+ # Parse JSON-RPC request
72
+ try:
73
+ data = json.loads(body)
74
+ except json.JSONDecodeError:
75
+ return await call_next(request)
76
+
77
+ # Extract method (command name)
78
+ method = data.get("method")
79
+ if not method:
80
+ return await call_next(request)
81
+
82
+ # Check if method requires permissions
83
+ if method not in self.command_permissions:
84
+ return await call_next(request)
85
+
86
+ required_permissions = self.command_permissions[method]
87
+
88
+ # Get user info from request state
89
+ user_info = getattr(request.state, "user", None)
90
+ if not user_info:
91
+ logger.warning(f"No user info found for command {method}")
92
+ return await call_next(request)
93
+
94
+ user_roles = user_info.get("roles", [])
95
+ user_permissions = user_info.get("permissions", [])
96
+
97
+ logger.debug(f"Checking permissions for {method}: user_roles={user_roles}, required={required_permissions}")
98
+
99
+ # Check if user has required permissions
100
+ has_permission = self._check_permissions(user_roles, user_permissions, required_permissions)
101
+
102
+ if not has_permission:
103
+ logger.warning(f"Permission denied for {method}: user_roles={user_roles}, required={required_permissions}")
104
+
105
+ # Return permission denied response
106
+ error_response = {
107
+ "error": {
108
+ "code": 403,
109
+ "message": f"Permission denied: {method} requires {required_permissions}",
110
+ "type": "permission_denied"
111
+ }
112
+ }
113
+
114
+ return Response(
115
+ content=json.dumps(error_response),
116
+ status_code=403,
117
+ media_type="application/json"
118
+ )
119
+
120
+ logger.debug(f"Permission granted for {method}")
121
+ return await call_next(request)
122
+
123
+ except Exception as e:
124
+ logger.error(f"Error in command permission middleware: {e}")
125
+ return await call_next(request)
126
+
127
+ def _check_permissions(self, user_roles: list, user_permissions: list, required_permissions: list) -> bool:
128
+ """
129
+ Check if user has required permissions.
130
+
131
+ Args:
132
+ user_roles: User roles
133
+ user_permissions: User permissions
134
+ required_permissions: Required permissions
135
+
136
+ Returns:
137
+ True if user has required permissions
138
+ """
139
+ # Admin has all permissions
140
+ if "admin" in user_roles or "*" in user_permissions:
141
+ return True
142
+
143
+ # Check if user has all required permissions
144
+ for required in required_permissions:
145
+ if required not in user_permissions:
146
+ return False
147
+
148
+ return True
@@ -17,6 +17,15 @@ class ErrorHandlingMiddleware(BaseMiddleware):
17
17
  Middleware for handling and formatting errors.
18
18
  """
19
19
 
20
+ def __init__(self, app):
21
+ """
22
+ Initialize error handling middleware.
23
+
24
+ Args:
25
+ app: FastAPI application
26
+ """
27
+ super().__init__(app)
28
+
20
29
  async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
21
30
  """
22
31
  Processes request and catches errors.
@@ -0,0 +1,243 @@
1
+ """
2
+ Middleware Factory for creating and managing middleware components.
3
+
4
+ This module provides a factory for creating middleware components with proper
5
+ configuration and dependency management.
6
+ """
7
+
8
+ import logging
9
+ from typing import Dict, Any, List, Optional, Type
10
+
11
+ from fastapi import FastAPI
12
+
13
+ from mcp_proxy_adapter.core.logging import logger
14
+ from mcp_proxy_adapter.core.security_factory import SecurityFactory
15
+ from .base import BaseMiddleware
16
+ from .unified_security import UnifiedSecurityMiddleware
17
+ from .error_handling import ErrorHandlingMiddleware
18
+ from .logging import LoggingMiddleware
19
+ from .user_info_middleware import UserInfoMiddleware
20
+
21
+
22
+ class MiddlewareFactory:
23
+ """
24
+ Factory for creating and managing middleware components.
25
+
26
+ Provides methods to create middleware components with proper configuration
27
+ and dependency management.
28
+ """
29
+
30
+ def __init__(self, app: FastAPI, config: Dict[str, Any]):
31
+ """
32
+ Initialize middleware factory.
33
+
34
+ Args:
35
+ app: FastAPI application
36
+ config: Application configuration
37
+ """
38
+ self.app = app
39
+ self.config = config
40
+ self.middleware_stack: List[BaseMiddleware] = []
41
+
42
+ logger.info("Middleware factory initialized")
43
+
44
+ def create_security_middleware(self) -> Optional[UnifiedSecurityMiddleware]:
45
+ """
46
+ Create unified security middleware.
47
+
48
+ Returns:
49
+ UnifiedSecurityMiddleware instance or None if creation failed
50
+ """
51
+ try:
52
+ security_config = self.config.get("security", {})
53
+
54
+ if not security_config.get("enabled", True):
55
+ logger.info("Security middleware disabled by configuration")
56
+ return None
57
+
58
+ middleware = UnifiedSecurityMiddleware(self.app, self.config)
59
+ self.middleware_stack.append(middleware)
60
+
61
+ logger.info("Unified security middleware created successfully")
62
+ return middleware
63
+
64
+ except Exception as e:
65
+ logger.error(f"Failed to create unified security middleware: {e}")
66
+ return None
67
+
68
+ def create_error_handling_middleware(self) -> Optional[ErrorHandlingMiddleware]:
69
+ """
70
+ Create error handling middleware.
71
+
72
+ Returns:
73
+ ErrorHandlingMiddleware instance or None if creation failed
74
+ """
75
+ try:
76
+ # Import here to avoid circular imports
77
+ from .error_handling import ErrorHandlingMiddleware
78
+
79
+ middleware = ErrorHandlingMiddleware(self.app)
80
+ self.middleware_stack.append(middleware)
81
+
82
+ logger.info("Error handling middleware created successfully")
83
+ return middleware
84
+
85
+ except Exception as e:
86
+ logger.error(f"Failed to create error handling middleware: {e}")
87
+ return None
88
+
89
+ def create_logging_middleware(self) -> Optional[LoggingMiddleware]:
90
+ """
91
+ Create logging middleware.
92
+
93
+ Returns:
94
+ LoggingMiddleware instance or None if creation failed
95
+ """
96
+ try:
97
+ # Import here to avoid circular imports
98
+ from .logging import LoggingMiddleware
99
+
100
+ middleware = LoggingMiddleware(self.app, self.config)
101
+ self.middleware_stack.append(middleware)
102
+
103
+ logger.info("Logging middleware created successfully")
104
+ return middleware
105
+
106
+ except Exception as e:
107
+ logger.error(f"Failed to create logging middleware: {e}")
108
+ return None
109
+
110
+ def create_user_info_middleware(self) -> Optional[UserInfoMiddleware]:
111
+ """
112
+ Create user info middleware.
113
+
114
+ Returns:
115
+ UserInfoMiddleware instance or None if creation failed
116
+ """
117
+ try:
118
+ middleware = UserInfoMiddleware(self.app, self.config)
119
+ self.middleware_stack.append(middleware)
120
+
121
+ logger.info("User info middleware created successfully")
122
+ return middleware
123
+
124
+ except Exception as e:
125
+ logger.error(f"Failed to create user info middleware: {e}")
126
+ return None
127
+
128
+
129
+
130
+ def create_all_middleware(self) -> List[BaseMiddleware]:
131
+ """
132
+ Create all required middleware components.
133
+
134
+ Returns:
135
+ List of created middleware instances
136
+ """
137
+ middleware_list = []
138
+
139
+ # Create security middleware (unified)
140
+ security_middleware = self.create_security_middleware()
141
+ if security_middleware:
142
+ middleware_list.append(security_middleware)
143
+
144
+ # Create error handling middleware
145
+ error_middleware = self.create_error_handling_middleware()
146
+ if error_middleware:
147
+ middleware_list.append(error_middleware)
148
+
149
+ # Create logging middleware
150
+ logging_middleware = self.create_logging_middleware()
151
+ if logging_middleware:
152
+ middleware_list.append(logging_middleware)
153
+
154
+ # Create user info middleware
155
+ user_info_middleware = self.create_user_info_middleware()
156
+ if user_info_middleware:
157
+ middleware_list.append(user_info_middleware)
158
+
159
+ logger.info(f"Created {len(middleware_list)} middleware components")
160
+ return middleware_list
161
+
162
+
163
+
164
+ def get_middleware_by_type(self, middleware_type: Type[BaseMiddleware]) -> Optional[BaseMiddleware]:
165
+ """
166
+ Get middleware instance by type.
167
+
168
+ Args:
169
+ middleware_type: Type of middleware to find
170
+
171
+ Returns:
172
+ Middleware instance or None if not found
173
+ """
174
+ for middleware in self.middleware_stack:
175
+ if isinstance(middleware, middleware_type):
176
+ return middleware
177
+ return None
178
+
179
+ def get_security_middleware(self) -> Optional[UnifiedSecurityMiddleware]:
180
+ """
181
+ Get unified security middleware instance.
182
+
183
+ Returns:
184
+ UnifiedSecurityMiddleware instance or None if not found
185
+ """
186
+ return self.get_middleware_by_type(UnifiedSecurityMiddleware)
187
+
188
+ def validate_middleware_config(self) -> bool:
189
+ """
190
+ Validate middleware configuration.
191
+
192
+ Returns:
193
+ True if configuration is valid, False otherwise
194
+ """
195
+ try:
196
+ security_config = self.config.get("security", {})
197
+
198
+ # Validate security configuration
199
+ if not SecurityFactory.validate_config(self.config):
200
+ logger.error("Security configuration validation failed")
201
+ return False
202
+
203
+ # Validate middleware-specific configurations
204
+ if security_config.get("enabled", True):
205
+ # Check required fields for security middleware
206
+ auth_config = security_config.get("auth", {})
207
+ if not isinstance(auth_config, dict):
208
+ logger.error("Auth configuration must be a dictionary")
209
+ return False
210
+
211
+ ssl_config = security_config.get("ssl", {})
212
+ if not isinstance(ssl_config, dict):
213
+ logger.error("SSL configuration must be a dictionary")
214
+ return False
215
+
216
+ logger.info("Middleware configuration validation passed")
217
+ return True
218
+
219
+ except Exception as e:
220
+ logger.error(f"Middleware configuration validation failed: {e}")
221
+ return False
222
+
223
+ def get_middleware_info(self) -> Dict[str, Any]:
224
+ """
225
+ Get information about created middleware.
226
+
227
+ Returns:
228
+ Dictionary with middleware information
229
+ """
230
+ info = {
231
+ "total_middleware": len(self.middleware_stack),
232
+ "middleware_types": [],
233
+ "security_enabled": False
234
+ }
235
+
236
+ for middleware in self.middleware_stack:
237
+ middleware_type = type(middleware).__name__
238
+ info["middleware_types"].append(middleware_type)
239
+
240
+ if isinstance(middleware, UnifiedSecurityMiddleware):
241
+ info["security_enabled"] = True
242
+
243
+ return info
@@ -5,7 +5,7 @@ Middleware for request logging.
5
5
  import time
6
6
  import json
7
7
  import uuid
8
- from typing import Callable, Awaitable
8
+ from typing import Callable, Awaitable, Dict, Any
9
9
 
10
10
  from fastapi import Request, Response
11
11
 
@@ -17,6 +17,17 @@ class LoggingMiddleware(BaseMiddleware):
17
17
  Middleware for logging requests and responses.
18
18
  """
19
19
 
20
+ def __init__(self, app, config: Dict[str, Any] = None):
21
+ """
22
+ Initialize logging middleware.
23
+
24
+ Args:
25
+ app: FastAPI application
26
+ config: Application configuration (optional)
27
+ """
28
+ super().__init__(app)
29
+ self.config = config or {}
30
+
20
31
  async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
21
32
  """
22
33
  Processes request and logs information about it.
@@ -43,7 +54,13 @@ class LoggingMiddleware(BaseMiddleware):
43
54
  url = str(request.url)
44
55
  client_host = request.client.host if request.client else "unknown"
45
56
 
46
- req_logger.info(f"Request started: {method} {url} | Client: {client_host}")
57
+ # Check if this is an OpenAPI schema request (should be logged at DEBUG level)
58
+ is_openapi_request = "/openapi.json" in url
59
+
60
+ if is_openapi_request:
61
+ req_logger.debug(f"Request started: {method} {url} | Client: {client_host}")
62
+ else:
63
+ req_logger.info(f"Request started: {method} {url} | Client: {client_host}")
47
64
 
48
65
  # Log request body if not GET or HEAD
49
66
  if method not in ["GET", "HEAD"]:
@@ -79,8 +96,12 @@ class LoggingMiddleware(BaseMiddleware):
79
96
  process_time = time.time() - start_time
80
97
  status_code = response.status_code
81
98
 
82
- req_logger.info(f"Request completed: {method} {url} | Status: {status_code} | "
83
- f"Time: {process_time:.3f}s")
99
+ if is_openapi_request:
100
+ req_logger.debug(f"Request completed: {method} {url} | Status: {status_code} | "
101
+ f"Time: {process_time:.3f}s")
102
+ else:
103
+ req_logger.info(f"Request completed: {method} {url} | Status: {status_code} | "
104
+ f"Time: {process_time:.3f}s")
84
105
 
85
106
  # Add request ID to response headers
86
107
  response.headers["X-Request-ID"] = request_id
@@ -90,7 +111,12 @@ class LoggingMiddleware(BaseMiddleware):
90
111
  except Exception as e:
91
112
  # Log error
92
113
  process_time = time.time() - start_time
93
- req_logger.error(f"Request failed: {method} {url} | Error: {str(e)} | "
94
- f"Time: {process_time:.3f}s")
114
+
115
+ if is_openapi_request:
116
+ req_logger.debug(f"Request failed: {method} {url} | Error: {str(e)} | "
117
+ f"Time: {process_time:.3f}s")
118
+ else:
119
+ req_logger.error(f"Request failed: {method} {url} | Error: {str(e)} | "
120
+ f"Time: {process_time:.3f}s")
95
121
 
96
122
  raise
@@ -0,0 +1,135 @@
1
+ """
2
+ Protocol middleware module.
3
+
4
+ This module provides middleware for validating protocol access based on configuration.
5
+ """
6
+
7
+ from typing import Callable
8
+ from fastapi import Request, Response
9
+ from starlette.middleware.base import BaseHTTPMiddleware
10
+ from starlette.responses import JSONResponse
11
+
12
+ from mcp_proxy_adapter.core.protocol_manager import protocol_manager
13
+ from mcp_proxy_adapter.core.logging import logger
14
+
15
+
16
+ class ProtocolMiddleware(BaseHTTPMiddleware):
17
+ """
18
+ Middleware for protocol validation.
19
+
20
+ This middleware checks if the incoming request protocol is allowed
21
+ based on the protocol configuration.
22
+ """
23
+
24
+ def __init__(self, app, protocol_manager_instance=None):
25
+ """
26
+ Initialize protocol middleware.
27
+
28
+ Args:
29
+ app: FastAPI application
30
+ protocol_manager_instance: Protocol manager instance (optional)
31
+ """
32
+ super().__init__(app)
33
+ self.protocol_manager = protocol_manager_instance or protocol_manager
34
+
35
+ async def dispatch(self, request: Request, call_next: Callable) -> Response:
36
+ """
37
+ Process request through protocol middleware.
38
+
39
+ Args:
40
+ request: Incoming request
41
+ call_next: Next middleware/endpoint function
42
+
43
+ Returns:
44
+ Response object
45
+ """
46
+ try:
47
+ # Get protocol from request
48
+ protocol = self._get_request_protocol(request)
49
+
50
+ # Check if protocol is allowed
51
+ if not self.protocol_manager.is_protocol_allowed(protocol):
52
+ logger.warning(f"Protocol '{protocol}' not allowed for request to {request.url.path}")
53
+ return JSONResponse(
54
+ status_code=403,
55
+ content={
56
+ "error": "Protocol not allowed",
57
+ "message": f"Protocol '{protocol}' is not allowed. Allowed protocols: {self.protocol_manager.get_allowed_protocols()}",
58
+ "allowed_protocols": self.protocol_manager.get_allowed_protocols()
59
+ }
60
+ )
61
+
62
+ # Continue processing
63
+ response = await call_next(request)
64
+ return response
65
+
66
+ except Exception as e:
67
+ logger.error(f"Protocol middleware error: {e}")
68
+ return JSONResponse(
69
+ status_code=500,
70
+ content={
71
+ "error": "Protocol validation error",
72
+ "message": str(e)
73
+ }
74
+ )
75
+
76
+ def _get_request_protocol(self, request: Request) -> str:
77
+ """
78
+ Extract protocol from request.
79
+
80
+ Args:
81
+ request: FastAPI request object
82
+
83
+ Returns:
84
+ Protocol name (http, https, mtls)
85
+ """
86
+ # Check if request is secure (HTTPS)
87
+ if request.url.scheme:
88
+ scheme = request.url.scheme.lower()
89
+
90
+ # If HTTPS, check if client certificate is provided (MTLS)
91
+ if scheme == "https":
92
+ # Check for client certificate in headers or SSL context
93
+ if hasattr(request, 'scope') and 'ssl' in request.scope:
94
+ ssl_context = request.scope.get('ssl')
95
+ if ssl_context and hasattr(ssl_context, 'getpeercert'):
96
+ try:
97
+ cert = ssl_context.getpeercert()
98
+ if cert:
99
+ return "mtls"
100
+ except:
101
+ pass
102
+
103
+ # Check for client certificate in headers
104
+ if request.headers.get("ssl-client-cert") or request.headers.get("x-client-cert"):
105
+ return "mtls"
106
+
107
+ return "https"
108
+
109
+ return scheme
110
+
111
+ # Fallback to checking headers
112
+ if request.headers.get("x-forwarded-proto"):
113
+ return request.headers.get("x-forwarded-proto").lower()
114
+
115
+ # Default to HTTP
116
+ return "http"
117
+
118
+
119
+ def setup_protocol_middleware(app, protocol_manager_instance=None):
120
+ """
121
+ Setup protocol middleware for FastAPI application.
122
+
123
+ Args:
124
+ app: FastAPI application
125
+ protocol_manager_instance: Protocol manager instance (optional)
126
+ """
127
+ if protocol_manager_instance is None:
128
+ protocol_manager_instance = protocol_manager
129
+
130
+ # Only add middleware if protocol management is enabled
131
+ if protocol_manager_instance.enabled:
132
+ app.add_middleware(ProtocolMiddleware, protocol_manager_instance=protocol_manager_instance)
133
+ logger.info("Protocol middleware added to application")
134
+ else:
135
+ logger.debug("Protocol management is disabled, skipping protocol middleware")