mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.0.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 (212) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +209 -79
  3. mcp_proxy_adapter/api/handlers.py +16 -5
  4. mcp_proxy_adapter/api/middleware/__init__.py +14 -9
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  7. mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
  8. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  9. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  10. mcp_proxy_adapter/commands/__init__.py +7 -1
  11. mcp_proxy_adapter/commands/base.py +7 -4
  12. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  13. mcp_proxy_adapter/commands/command_registry.py +8 -0
  14. mcp_proxy_adapter/commands/echo_command.py +81 -0
  15. mcp_proxy_adapter/commands/health_command.py +1 -1
  16. mcp_proxy_adapter/commands/help_command.py +21 -14
  17. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  18. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  19. mcp_proxy_adapter/commands/security_command.py +488 -0
  20. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  21. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  22. mcp_proxy_adapter/config.py +323 -40
  23. mcp_proxy_adapter/core/app_factory.py +410 -0
  24. mcp_proxy_adapter/core/app_runner.py +272 -0
  25. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  26. mcp_proxy_adapter/core/client.py +574 -0
  27. mcp_proxy_adapter/core/client_manager.py +284 -0
  28. mcp_proxy_adapter/core/client_security.py +384 -0
  29. mcp_proxy_adapter/core/logging.py +8 -3
  30. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  31. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  32. mcp_proxy_adapter/core/protocol_manager.py +169 -10
  33. mcp_proxy_adapter/core/proxy_client.py +602 -0
  34. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  35. mcp_proxy_adapter/core/security_adapter.py +12 -15
  36. mcp_proxy_adapter/core/security_integration.py +286 -0
  37. mcp_proxy_adapter/core/server_adapter.py +282 -0
  38. mcp_proxy_adapter/core/server_engine.py +270 -0
  39. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  40. mcp_proxy_adapter/core/transport_manager.py +5 -5
  41. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  42. mcp_proxy_adapter/examples/__init__.py +13 -4
  43. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  44. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  45. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  46. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  47. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  48. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  49. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  50. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  51. mcp_proxy_adapter/examples/demo_client.py +275 -0
  52. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  53. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  54. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  55. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  56. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  57. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  58. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  59. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  60. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  61. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  62. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  63. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  64. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  65. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  66. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  67. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  68. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  69. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  70. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  71. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  72. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  73. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  74. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  75. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  76. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  77. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  78. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  79. mcp_proxy_adapter/examples/run_example.py +59 -0
  80. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  81. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  82. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  83. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  84. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  85. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  86. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  87. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  88. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  89. mcp_proxy_adapter/examples/test_config.py +148 -0
  90. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  91. mcp_proxy_adapter/examples/test_examples.py +281 -0
  92. mcp_proxy_adapter/examples/universal_client.py +620 -0
  93. mcp_proxy_adapter/main.py +66 -148
  94. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  95. mcp_proxy_adapter/version.py +5 -2
  96. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  97. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  98. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  99. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  100. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  101. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  102. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  103. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  104. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  105. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  106. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  107. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  108. mcp_proxy_adapter/api/middleware/security.py +0 -376
  109. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  110. mcp_proxy_adapter/examples/README.md +0 -124
  111. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  112. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  113. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  114. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  115. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  116. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  117. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  118. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  119. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  120. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  121. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  122. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  123. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  124. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  125. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  126. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  127. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  128. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  129. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  130. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  131. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  132. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  133. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  134. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  135. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  136. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  137. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  138. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  139. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  140. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  141. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  142. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  143. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  144. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  145. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  146. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  147. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  148. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  149. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  150. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  153. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  154. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  155. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  156. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  157. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  158. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  159. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  160. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  161. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  162. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  163. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  164. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  166. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  167. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  168. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  169. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  170. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  171. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  172. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  173. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  174. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  175. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  176. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  177. mcp_proxy_adapter/tests/__init__.py +0 -0
  178. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  180. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  181. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  182. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  183. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  184. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  185. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  186. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  187. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  188. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  189. mcp_proxy_adapter/tests/conftest.py +0 -131
  190. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  191. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  192. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  193. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  194. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  195. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  196. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  197. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  198. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  199. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  200. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  201. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  202. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  203. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  204. mcp_proxy_adapter/tests/test_config.py +0 -127
  205. mcp_proxy_adapter/tests/test_utils.py +0 -65
  206. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  207. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  208. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  209. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  210. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  211. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -1,381 +0,0 @@
1
- """
2
- Roles Middleware
3
-
4
- This module provides middleware for role-based access control (RBAC).
5
- Validates client roles against server roles and permissions.
6
-
7
- Author: MCP Proxy Adapter Team
8
- Version: 1.0.0
9
- """
10
-
11
- import json
12
- import logging
13
- from typing import Dict, List, Optional, Any, Set
14
- from pathlib import Path
15
- from cryptography import x509
16
-
17
- from fastapi import Request, Response
18
- from starlette.middleware.base import BaseHTTPMiddleware
19
-
20
- from ...core.auth_validator import AuthValidator
21
- from ...core.role_utils import RoleUtils
22
- from ...core.certificate_utils import CertificateUtils
23
- from .base import BaseMiddleware
24
-
25
- logger = logging.getLogger(__name__)
26
-
27
-
28
- class RolesMiddleware(BaseMiddleware):
29
- """
30
- Middleware for role-based access control.
31
-
32
- Validates client roles against server roles and permissions,
33
- integrates with mTLS middleware for certificate-based authentication.
34
- """
35
-
36
- def __init__(self, app, roles_config_path: str):
37
- """
38
- Initialize roles middleware.
39
-
40
- Args:
41
- app: FastAPI application
42
- roles_config_path: Path to roles configuration file
43
- """
44
- super().__init__(app)
45
- self.roles_config_path = roles_config_path
46
- self.auth_validator = AuthValidator()
47
- self.role_utils = RoleUtils()
48
- self.certificate_utils = CertificateUtils()
49
-
50
- # Load roles configuration
51
- self.roles_config = self._load_roles_config()
52
-
53
- # Check if roles are enabled and config file exists
54
- if not self.roles_config.get("enabled", True):
55
- logger.info("Roles middleware disabled by configuration")
56
- self.enabled = False
57
- return
58
-
59
- # Extract configuration
60
- self.enabled = self.roles_config.get("enabled", True)
61
- self.default_policy = self.roles_config.get("default_policy", {})
62
- self.roles = self.roles_config.get("roles", {})
63
- self.server_roles = self.roles_config.get("server_roles", {})
64
- self.role_hierarchy = self.roles_config.get("role_hierarchy", {})
65
-
66
- logger.info(f"Roles middleware initialized: enabled={self.enabled}, "
67
- f"roles_count={len(self.roles)}, "
68
- f"server_roles_count={len(self.server_roles)}")
69
-
70
- def _load_roles_config(self) -> Dict[str, Any]:
71
- """
72
- Load roles configuration from file.
73
-
74
- Returns:
75
- Roles configuration dictionary
76
- """
77
- try:
78
- config_path = Path(self.roles_config_path)
79
- if not config_path.exists():
80
- logger.error(f"Roles config file not found: {self.roles_config_path}")
81
- logger.error("Roles middleware will be disabled. Please create the roles schema file.")
82
- return {"enabled": False}
83
-
84
- with open(config_path, 'r', encoding='utf-8') as f:
85
- config = json.load(f)
86
-
87
- logger.info(f"Roles configuration loaded from {self.roles_config_path}")
88
- return config
89
-
90
- except Exception as e:
91
- logger.error(f"Failed to load roles configuration: {e}")
92
- logger.error("Roles middleware will be disabled due to configuration error.")
93
- return {"enabled": False}
94
-
95
- def _get_default_config(self) -> Dict[str, Any]:
96
- """
97
- Get default roles configuration.
98
-
99
- Returns:
100
- Default roles configuration
101
- """
102
- return {
103
- "enabled": True,
104
- "default_policy": {
105
- "deny_by_default": True,
106
- "require_role_match": True,
107
- "case_sensitive": False,
108
- "allow_wildcard": True
109
- },
110
- "roles": {
111
- "admin": {
112
- "description": "Administrator with full access",
113
- "allowed_servers": ["*"],
114
- "allowed_clients": ["*"],
115
- "permissions": ["read", "write", "delete", "admin"],
116
- "priority": 100
117
- }
118
- },
119
- "server_roles": {},
120
- "role_hierarchy": {}
121
- }
122
-
123
- async def before_request(self, request: Request) -> None:
124
- """
125
- Process request before calling the main handler.
126
-
127
- Args:
128
- request: FastAPI request object
129
- """
130
- if not self.enabled:
131
- return
132
-
133
- # Skip role validation for OpenAPI schema endpoint
134
- if request.url.path == "/openapi.json":
135
- return
136
-
137
- try:
138
- # Extract client roles from request state (set by mTLS middleware)
139
- client_roles = getattr(request.state, 'client_roles', [])
140
- client_cert = getattr(request.state, 'client_certificate', None)
141
-
142
- # If no roles from mTLS, try to extract from certificate
143
- if not client_roles and client_cert:
144
- client_roles = self._extract_roles_from_certificate(client_cert)
145
-
146
- # Extract server role from request
147
- server_role = self._extract_server_role(request)
148
-
149
- # Validate access based on roles
150
- if not self._validate_access(client_roles, server_role, request):
151
- raise ValueError("Access denied: insufficient roles or permissions")
152
-
153
- # Store roles in request state for downstream middleware
154
- request.state.client_roles = client_roles
155
- request.state.server_role = server_role
156
- request.state.role_validation_passed = True
157
-
158
- logger.debug(f"Role validation successful for client roles: {client_roles}, "
159
- f"server role: {server_role}")
160
-
161
- except Exception as e:
162
- logger.error(f"Role validation failed: {e}")
163
- request.state.role_validation_passed = False
164
- request.state.role_validation_error = str(e)
165
-
166
- def _extract_roles_from_certificate(self, cert: x509.Certificate) -> List[str]:
167
- """
168
- Extract roles from certificate using RoleUtils.
169
-
170
- Args:
171
- cert: Certificate object
172
-
173
- Returns:
174
- List of roles extracted from certificate
175
- """
176
- try:
177
- return self.role_utils.extract_roles_from_certificate_object(cert)
178
- except Exception as e:
179
- logger.error(f"Failed to extract roles from certificate: {e}")
180
- return []
181
-
182
- def _extract_server_role(self, request: Request) -> str:
183
- """
184
- Extract server role from request.
185
-
186
- Args:
187
- request: FastAPI request object
188
-
189
- Returns:
190
- Server role string
191
- """
192
- # Try to extract from path
193
- path = request.url.path
194
-
195
- # Check if path contains server role information
196
- if path.startswith('/api/'):
197
- parts = path.split('/')
198
- if len(parts) > 2:
199
- potential_role = parts[2]
200
- if potential_role in self.server_roles:
201
- return potential_role
202
-
203
- # Check from headers
204
- server_role_header = request.headers.get('X-Server-Role')
205
- if server_role_header and server_role_header in self.server_roles:
206
- return server_role_header
207
-
208
- # Default to 'basic_commands' if no specific role found
209
- return 'basic_commands'
210
-
211
- def _validate_access(self, client_roles: List[str], server_role: str,
212
- request: Request) -> bool:
213
- """
214
- Validate access based on client roles and server role.
215
-
216
- Args:
217
- client_roles: List of client roles
218
- server_role: Server role
219
- request: FastAPI request object
220
-
221
- Returns:
222
- True if access is allowed, False otherwise
223
- """
224
- # Check default policy
225
- deny_by_default = self.default_policy.get("deny_by_default", True)
226
- require_role_match = self.default_policy.get("require_role_match", True)
227
-
228
- # If no client roles and deny by default, deny access
229
- if not client_roles and deny_by_default:
230
- logger.warning("Access denied: no client roles provided and deny_by_default is True")
231
- return False
232
-
233
- # If no server role found, allow if not requiring role match
234
- if not server_role and not require_role_match:
235
- return True
236
-
237
- # Get server role configuration
238
- server_config = self.server_roles.get(server_role, {})
239
- required_roles = server_config.get("required_roles", [])
240
-
241
- # If no required roles specified, allow access
242
- if not required_roles:
243
- return True
244
-
245
- # Check if client has any of the required roles
246
- for client_role in client_roles:
247
- if self._has_required_role(client_role, required_roles):
248
- return True
249
-
250
- # Check role hierarchy
251
- for client_role in client_roles:
252
- if self._has_role_in_hierarchy(client_role, required_roles):
253
- return True
254
-
255
- logger.warning(f"Access denied: client roles {client_roles} do not match "
256
- f"required roles {required_roles} for server role {server_role}")
257
- return False
258
-
259
- def _has_required_role(self, client_role: str, required_roles: List[str]) -> bool:
260
- """
261
- Check if client role matches any required role.
262
-
263
- Args:
264
- client_role: Client role to check
265
- required_roles: List of required roles
266
-
267
- Returns:
268
- True if client role matches any required role
269
- """
270
- # Use RoleUtils for case-insensitive comparison
271
- for required_role in required_roles:
272
- if self.role_utils.compare_roles(client_role, required_role):
273
- return True
274
-
275
- # Check for wildcard
276
- if "*" in required_roles:
277
- return True
278
-
279
- return False
280
-
281
- def _has_role_in_hierarchy(self, client_role: str, required_roles: List[str]) -> bool:
282
- """
283
- Check if client role has any required role in its hierarchy.
284
-
285
- Args:
286
- client_role: Client role to check
287
- required_roles: List of required roles
288
-
289
- Returns:
290
- True if client role has any required role in hierarchy
291
- """
292
- # Get client role hierarchy
293
- client_hierarchy = self._get_role_hierarchy(client_role)
294
-
295
- # Check if any role in hierarchy matches required roles
296
- for hierarchy_role in client_hierarchy:
297
- if self._has_required_role(hierarchy_role, required_roles):
298
- return True
299
-
300
- return False
301
-
302
- def _get_role_hierarchy(self, role: str) -> List[str]:
303
- """
304
- Get role hierarchy for a given role.
305
-
306
- Args:
307
- role: Role to get hierarchy for
308
-
309
- Returns:
310
- List of roles in hierarchy
311
- """
312
- return self.role_hierarchy.get(role, [])
313
-
314
- def _validate_permissions(self, client_roles: List[str], required_permissions: List[str]) -> bool:
315
- """
316
- Validate if client roles have required permissions.
317
-
318
- Args:
319
- client_roles: List of client roles
320
- required_permissions: List of required permissions
321
-
322
- Returns:
323
- True if client has required permissions
324
- """
325
- for client_role in client_roles:
326
- role_config = self.roles.get(client_role, {})
327
- role_permissions = role_config.get("permissions", [])
328
-
329
- # Check if role has all required permissions
330
- if all(perm in role_permissions for perm in required_permissions):
331
- return True
332
-
333
- return False
334
-
335
- def get_client_roles(self, request: Request) -> List[str]:
336
- """
337
- Get client roles from request state.
338
-
339
- Args:
340
- request: FastAPI request object
341
-
342
- Returns:
343
- List of client roles
344
- """
345
- return getattr(request.state, 'client_roles', [])
346
-
347
- def get_server_role(self, request: Request) -> str:
348
- """
349
- Get server role from request state.
350
-
351
- Args:
352
- request: FastAPI request object
353
-
354
- Returns:
355
- Server role string
356
- """
357
- return getattr(request.state, 'server_role', '')
358
-
359
- def is_role_validation_passed(self, request: Request) -> bool:
360
- """
361
- Check if role validation passed.
362
-
363
- Args:
364
- request: FastAPI request object
365
-
366
- Returns:
367
- True if role validation passed
368
- """
369
- return getattr(request.state, 'role_validation_passed', False)
370
-
371
- def get_role_validation_error(self, request: Request) -> Optional[str]:
372
- """
373
- Get role validation error message.
374
-
375
- Args:
376
- request: FastAPI request object
377
-
378
- Returns:
379
- Error message if validation failed, None otherwise
380
- """
381
- return getattr(request.state, 'role_validation_error', None)