mcp-proxy-adapter 6.0.0__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 (259) 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 +7 -2
  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/unified_security.py +152 -0
  7. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  8. mcp_proxy_adapter/commands/__init__.py +7 -1
  9. mcp_proxy_adapter/commands/base.py +7 -4
  10. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  11. mcp_proxy_adapter/commands/command_registry.py +8 -0
  12. mcp_proxy_adapter/commands/echo_command.py +81 -0
  13. mcp_proxy_adapter/commands/help_command.py +21 -14
  14. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  15. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  16. mcp_proxy_adapter/commands/security_command.py +488 -0
  17. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  18. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  19. mcp_proxy_adapter/config.py +81 -21
  20. mcp_proxy_adapter/core/app_factory.py +326 -0
  21. mcp_proxy_adapter/core/client_security.py +384 -0
  22. mcp_proxy_adapter/core/logging.py +8 -3
  23. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  24. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  25. mcp_proxy_adapter/core/protocol_manager.py +9 -0
  26. mcp_proxy_adapter/core/proxy_client.py +602 -0
  27. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  28. mcp_proxy_adapter/core/security_adapter.py +12 -15
  29. mcp_proxy_adapter/core/security_integration.py +277 -0
  30. mcp_proxy_adapter/core/server_adapter.py +345 -0
  31. mcp_proxy_adapter/core/server_engine.py +364 -0
  32. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  33. mcp_proxy_adapter/examples/README.md +230 -97
  34. mcp_proxy_adapter/examples/README_EN.md +258 -0
  35. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  36. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  37. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  38. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  44. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  45. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  46. mcp_proxy_adapter/examples/cert_config.json +9 -0
  47. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  48. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  49. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  50. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  51. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  52. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  53. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  54. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  55. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  56. mcp_proxy_adapter/examples/certs/client.key +52 -0
  57. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  58. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  59. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  61. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  62. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  63. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  64. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  65. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  66. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  67. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  68. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/server.key +52 -0
  72. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  73. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  74. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  75. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  76. mcp_proxy_adapter/examples/certs/user.key +52 -0
  77. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  78. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  79. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  80. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  81. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  82. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  83. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  84. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  85. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  86. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  87. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  88. mcp_proxy_adapter/examples/demo_client.py +341 -0
  89. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  90. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  91. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  92. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  93. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  94. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  95. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  96. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  99. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  100. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  102. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  103. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  104. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  105. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  106. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  107. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  108. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  109. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  124. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  125. mcp_proxy_adapter/examples/roles.json +38 -0
  126. mcp_proxy_adapter/examples/run_example.py +81 -0
  127. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  128. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  129. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  130. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  131. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  132. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  133. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  134. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  135. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  136. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  137. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  138. mcp_proxy_adapter/examples/test_examples.py +344 -0
  139. mcp_proxy_adapter/examples/universal_client.py +628 -0
  140. mcp_proxy_adapter/main.py +21 -10
  141. mcp_proxy_adapter/utils/config_generator.py +639 -0
  142. mcp_proxy_adapter/version.py +2 -1
  143. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  144. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  145. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  146. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  147. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  148. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  149. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  150. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  151. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  152. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  153. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  154. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  155. mcp_proxy_adapter/api/middleware/security.py +0 -376
  156. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  157. mcp_proxy_adapter/examples/__init__.py +0 -7
  158. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  159. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  160. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  161. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  162. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  163. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  164. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  165. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  166. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  167. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  168. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  169. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  170. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  171. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  172. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  173. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  174. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  175. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  176. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  177. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  178. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  179. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  180. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  181. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  182. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  183. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  184. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  185. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  186. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  187. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  188. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  189. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  190. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  191. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  192. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  193. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  194. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  195. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  196. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  197. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  198. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  199. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  200. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  201. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  202. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  203. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  204. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  205. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  206. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  207. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  208. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  209. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  210. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  211. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  212. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  213. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  214. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  215. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  216. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  217. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  218. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  219. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  220. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  221. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  222. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  223. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  224. mcp_proxy_adapter/tests/__init__.py +0 -0
  225. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  226. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  227. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  228. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  229. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  230. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  231. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  232. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  233. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  234. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  235. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  236. mcp_proxy_adapter/tests/conftest.py +0 -131
  237. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  239. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  240. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  241. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  242. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  244. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  245. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  246. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  247. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  248. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  249. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  250. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  251. mcp_proxy_adapter/tests/test_config.py +0 -127
  252. mcp_proxy_adapter/tests/test_utils.py +0 -65
  253. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  254. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  255. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  256. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  257. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  258. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  259. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.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