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,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