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
@@ -1,365 +0,0 @@
1
- """
2
- Roles Middleware Adapter for backward compatibility.
3
-
4
- This module provides an adapter that maintains the same interface as RolesMiddleware
5
- while using the new SecurityMiddleware internally.
6
- """
7
-
8
- import json
9
- import logging
10
- from typing import Dict, List, Optional, Any, Set, Callable, Awaitable
11
- from pathlib import Path
12
- from cryptography import x509
13
-
14
- from fastapi import Request, Response
15
- from starlette.responses import JSONResponse
16
-
17
- from mcp_proxy_adapter.core.logging import logger
18
- from mcp_proxy_adapter.core.auth_validator import AuthValidator
19
- from mcp_proxy_adapter.core.role_utils import RoleUtils
20
- from mcp_proxy_adapter.core.certificate_utils import CertificateUtils
21
- from .base import BaseMiddleware
22
- from .security import SecurityMiddleware
23
-
24
-
25
- class RolesMiddlewareAdapter(BaseMiddleware):
26
- """
27
- Adapter for RolesMiddleware that uses SecurityMiddleware internally.
28
-
29
- Maintains the same interface as the original RolesMiddleware for backward compatibility.
30
- """
31
-
32
- def __init__(self, app, roles_config_path: str):
33
- """
34
- Initialize roles middleware adapter.
35
-
36
- Args:
37
- app: FastAPI application
38
- roles_config_path: Path to roles configuration file
39
- """
40
- super().__init__(app)
41
-
42
- # Store original parameters for backward compatibility
43
- self.roles_config_path = roles_config_path
44
- self.auth_validator = AuthValidator()
45
- self.role_utils = RoleUtils()
46
- self.certificate_utils = CertificateUtils()
47
-
48
- # Load roles configuration
49
- self.roles_config = self._load_roles_config()
50
-
51
- # Check if roles are enabled and config file exists
52
- if not self.roles_config.get("enabled", True):
53
- logger.info("Roles middleware disabled by configuration")
54
- self.enabled = False
55
- return
56
-
57
- # Extract configuration
58
- self.enabled = self.roles_config.get("enabled", True)
59
- self.default_policy = self.roles_config.get("default_policy", {})
60
- self.roles = self.roles_config.get("roles", {})
61
- self.server_roles = self.roles_config.get("server_roles", {})
62
- self.role_hierarchy = self.roles_config.get("role_hierarchy", {})
63
-
64
- # Create internal security middleware
65
- self.security_middleware = self._create_security_middleware()
66
-
67
- logger.info(f"RolesMiddlewareAdapter initialized: enabled={self.enabled}, "
68
- f"roles_count={len(self.roles)}, "
69
- f"server_roles_count={len(self.server_roles)}")
70
-
71
- def _load_roles_config(self) -> Dict[str, Any]:
72
- """
73
- Load roles configuration from file.
74
-
75
- Returns:
76
- Roles configuration dictionary
77
- """
78
- try:
79
- config_path = Path(self.roles_config_path)
80
- if not config_path.exists():
81
- logger.error(f"Roles config file not found: {self.roles_config_path}")
82
- logger.error("Roles middleware will be disabled. Please create the roles schema file.")
83
- return {"enabled": False}
84
-
85
- with open(config_path, 'r', encoding='utf-8') as f:
86
- config = json.load(f)
87
-
88
- logger.info(f"Roles configuration loaded from {self.roles_config_path}")
89
- return config
90
-
91
- except Exception as e:
92
- logger.error(f"Failed to load roles configuration: {e}")
93
- logger.error("Roles middleware will be disabled due to configuration error.")
94
- return {"enabled": False}
95
-
96
- def _create_security_middleware(self) -> SecurityMiddleware:
97
- """
98
- Create internal SecurityMiddleware with RolesMiddleware configuration.
99
-
100
- Returns:
101
- SecurityMiddleware instance
102
- """
103
- # Convert RolesMiddleware config to SecurityMiddleware config
104
- security_config = {
105
- "security": {
106
- "enabled": self.enabled,
107
- "auth": {
108
- "enabled": False
109
- },
110
- "ssl": {
111
- "enabled": False
112
- },
113
- "permissions": {
114
- "enabled": self.enabled,
115
- "roles_file": self.roles_config_path,
116
- "default_role": self.default_policy.get("default_role", "user"),
117
- "deny_by_default": self.default_policy.get("deny_by_default", True)
118
- },
119
- "rate_limit": {
120
- "enabled": False
121
- }
122
- }
123
- }
124
-
125
- return SecurityMiddleware(self.app, security_config)
126
-
127
- async def before_request(self, request: Request) -> None:
128
- """
129
- Process request before calling the main handler.
130
-
131
- Args:
132
- request: FastAPI request object
133
- """
134
- if not self.enabled:
135
- return
136
-
137
- try:
138
- # Use SecurityMiddleware for validation
139
- await self.security_middleware.before_request(request)
140
-
141
- # Additional roles-specific processing
142
- client_roles = self._get_client_roles(request)
143
- if client_roles:
144
- # Validate roles against server roles
145
- if not self._validate_roles(client_roles):
146
- raise ValueError("Access denied: insufficient roles")
147
-
148
- # Store roles in request state for backward compatibility
149
- request.state.client_roles = client_roles
150
- request.state.role_validation_passed = True
151
-
152
- logger.debug(f"Role validation successful for roles: {client_roles}")
153
-
154
- except Exception as e:
155
- logger.error(f"Role validation failed: {e}")
156
- raise
157
-
158
- def _get_client_roles(self, request: Request) -> List[str]:
159
- """
160
- Get client roles from various sources.
161
-
162
- Args:
163
- request: FastAPI request object
164
-
165
- Returns:
166
- List of client roles
167
- """
168
- roles = []
169
-
170
- # Get roles from request state (from other middleware)
171
- if hasattr(request.state, 'client_roles'):
172
- roles.extend(request.state.client_roles)
173
-
174
- # Get roles from certificate if available
175
- if hasattr(request.state, 'client_certificate'):
176
- cert_roles = self._extract_roles_from_certificate(request.state.client_certificate)
177
- roles.extend(cert_roles)
178
-
179
- # Get roles from headers
180
- header_roles = request.headers.get("X-Client-Roles")
181
- if header_roles:
182
- try:
183
- header_roles_list = json.loads(header_roles)
184
- if isinstance(header_roles_list, list):
185
- roles.extend(header_roles_list)
186
- except json.JSONDecodeError:
187
- # Treat as comma-separated string
188
- roles.extend([role.strip() for role in header_roles.split(",")])
189
-
190
- # Remove duplicates and return
191
- return list(set(roles))
192
-
193
- def _extract_roles_from_certificate(self, cert: x509.Certificate) -> List[str]:
194
- """
195
- Extract roles from client certificate.
196
-
197
- Args:
198
- cert: Client certificate
199
-
200
- Returns:
201
- List of roles
202
- """
203
- try:
204
- roles = []
205
-
206
- # Extract from subject alternative names
207
- san = cert.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
208
- if san:
209
- for name in san.value:
210
- if isinstance(name, x509.DNSName):
211
- if name.value.startswith("role="):
212
- role = name.value.split("=", 1)[1]
213
- roles.append(role)
214
-
215
- # Extract from subject
216
- subject = cert.subject
217
- for attr in subject:
218
- if attr.oid.dotted_string == "2.5.4.3": # Common Name
219
- if attr.value.startswith("role="):
220
- role = attr.value.split("=", 1)[1]
221
- roles.append(role)
222
-
223
- return roles
224
-
225
- except Exception as e:
226
- logger.error(f"Failed to extract roles from certificate: {e}")
227
- return []
228
-
229
- def _validate_roles(self, client_roles: List[str]) -> bool:
230
- """
231
- Validate client roles against server roles.
232
-
233
- Args:
234
- client_roles: List of client roles
235
-
236
- Returns:
237
- True if roles are valid, False otherwise
238
- """
239
- if not client_roles:
240
- return self.default_policy.get("allow_empty_roles", False)
241
-
242
- # Check if any client role matches server roles
243
- for client_role in client_roles:
244
- if client_role in self.server_roles:
245
- return True
246
-
247
- # Check role hierarchy
248
- if client_role in self.role_hierarchy:
249
- inherited_roles = self.role_hierarchy[client_role]
250
- for inherited_role in inherited_roles:
251
- if inherited_role in self.server_roles:
252
- return True
253
-
254
- return False
255
-
256
- def _get_default_config(self) -> Dict[str, Any]:
257
- """
258
- Get default roles configuration.
259
-
260
- Returns:
261
- Default roles configuration
262
- """
263
- return {
264
- "enabled": True,
265
- "default_policy": {
266
- "deny_by_default": True,
267
- "require_role_match": True,
268
- "case_sensitive": False,
269
- "allow_wildcard": True,
270
- "allow_empty_roles": False,
271
- "default_role": "user"
272
- },
273
- "roles": {
274
- "admin": {
275
- "description": "Administrator",
276
- "permissions": ["read", "write", "delete", "admin"],
277
- "priority": 100
278
- },
279
- "user": {
280
- "description": "Regular user",
281
- "permissions": ["read", "write"],
282
- "priority": 10
283
- },
284
- "guest": {
285
- "description": "Guest user",
286
- "permissions": ["read"],
287
- "priority": 1
288
- }
289
- },
290
- "server_roles": ["admin", "user"],
291
- "role_hierarchy": {
292
- "admin": ["user", "guest"],
293
- "user": ["guest"]
294
- }
295
- }
296
-
297
- def get_client_roles(self, request: Request) -> List[str]:
298
- """
299
- Get client roles from request state (backward compatibility).
300
-
301
- Args:
302
- request: Request object
303
-
304
- Returns:
305
- List of client roles
306
- """
307
- return getattr(request.state, 'client_roles', [])
308
-
309
- def is_role_validation_passed(self, request: Request) -> bool:
310
- """
311
- Check if role validation passed (backward compatibility).
312
-
313
- Args:
314
- request: Request object
315
-
316
- Returns:
317
- True if role validation passed, False otherwise
318
- """
319
- return getattr(request.state, 'role_validation_passed', False)
320
-
321
- def has_role(self, request: Request, required_role: str) -> bool:
322
- """
323
- Check if client has required role (backward compatibility).
324
-
325
- Args:
326
- request: Request object
327
- required_role: Required role
328
-
329
- Returns:
330
- True if client has required role, False otherwise
331
- """
332
- client_roles = self.get_client_roles(request)
333
- return required_role in client_roles
334
-
335
- def has_any_role(self, request: Request, required_roles: List[str]) -> bool:
336
- """
337
- Check if client has any of the required roles (backward compatibility).
338
-
339
- Args:
340
- request: Request object
341
- required_roles: List of required roles
342
-
343
- Returns:
344
- True if client has any of the required roles, False otherwise
345
- """
346
- client_roles = self.get_client_roles(request)
347
- return any(role in client_roles for role in required_roles)
348
-
349
- def get_server_roles(self) -> List[str]:
350
- """
351
- Get server roles (backward compatibility).
352
-
353
- Returns:
354
- List of server roles
355
- """
356
- return self.server_roles
357
-
358
- def get_role_hierarchy(self) -> Dict[str, List[str]]:
359
- """
360
- Get role hierarchy (backward compatibility).
361
-
362
- Returns:
363
- Role hierarchy dictionary
364
- """
365
- return self.role_hierarchy