mcp-proxy-adapter 4.1.1__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 (200) hide show
  1. mcp_proxy_adapter/__main__.py +32 -0
  2. mcp_proxy_adapter/api/app.py +290 -33
  3. mcp_proxy_adapter/api/handlers.py +32 -6
  4. mcp_proxy_adapter/api/middleware/__init__.py +38 -32
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
  7. mcp_proxy_adapter/api/middleware/factory.py +243 -0
  8. mcp_proxy_adapter/api/middleware/logging.py +32 -6
  9. mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
  10. mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
  11. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  12. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  13. mcp_proxy_adapter/commands/__init__.py +19 -4
  14. mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
  15. mcp_proxy_adapter/commands/base.py +66 -32
  16. mcp_proxy_adapter/commands/builtin_commands.py +95 -0
  17. mcp_proxy_adapter/commands/catalog_manager.py +838 -0
  18. mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
  19. mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
  20. mcp_proxy_adapter/commands/command_registry.py +711 -354
  21. mcp_proxy_adapter/commands/dependency_manager.py +245 -0
  22. mcp_proxy_adapter/commands/echo_command.py +81 -0
  23. mcp_proxy_adapter/commands/health_command.py +8 -1
  24. mcp_proxy_adapter/commands/help_command.py +21 -14
  25. mcp_proxy_adapter/commands/hooks.py +200 -167
  26. mcp_proxy_adapter/commands/key_management_command.py +506 -0
  27. mcp_proxy_adapter/commands/load_command.py +176 -0
  28. mcp_proxy_adapter/commands/plugins_command.py +235 -0
  29. mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
  30. mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
  31. mcp_proxy_adapter/commands/reload_command.py +48 -50
  32. mcp_proxy_adapter/commands/result.py +1 -0
  33. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  34. mcp_proxy_adapter/commands/roles_management_command.py +697 -0
  35. mcp_proxy_adapter/commands/security_command.py +488 -0
  36. mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
  37. mcp_proxy_adapter/commands/token_management_command.py +529 -0
  38. mcp_proxy_adapter/commands/transport_management_command.py +144 -0
  39. mcp_proxy_adapter/commands/unload_command.py +158 -0
  40. mcp_proxy_adapter/config.py +394 -14
  41. mcp_proxy_adapter/core/app_factory.py +410 -0
  42. mcp_proxy_adapter/core/app_runner.py +272 -0
  43. mcp_proxy_adapter/core/auth_validator.py +606 -0
  44. mcp_proxy_adapter/core/certificate_utils.py +1045 -0
  45. mcp_proxy_adapter/core/client.py +574 -0
  46. mcp_proxy_adapter/core/client_manager.py +284 -0
  47. mcp_proxy_adapter/core/client_security.py +384 -0
  48. mcp_proxy_adapter/core/config_converter.py +405 -0
  49. mcp_proxy_adapter/core/config_validator.py +218 -0
  50. mcp_proxy_adapter/core/logging.py +19 -3
  51. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  52. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  53. mcp_proxy_adapter/core/protocol_manager.py +385 -0
  54. mcp_proxy_adapter/core/proxy_client.py +602 -0
  55. mcp_proxy_adapter/core/proxy_registration.py +522 -0
  56. mcp_proxy_adapter/core/role_utils.py +426 -0
  57. mcp_proxy_adapter/core/security_adapter.py +370 -0
  58. mcp_proxy_adapter/core/security_factory.py +239 -0
  59. mcp_proxy_adapter/core/security_integration.py +286 -0
  60. mcp_proxy_adapter/core/server_adapter.py +282 -0
  61. mcp_proxy_adapter/core/server_engine.py +270 -0
  62. mcp_proxy_adapter/core/settings.py +1 -0
  63. mcp_proxy_adapter/core/ssl_utils.py +234 -0
  64. mcp_proxy_adapter/core/transport_manager.py +292 -0
  65. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  66. mcp_proxy_adapter/custom_openapi.py +22 -11
  67. mcp_proxy_adapter/examples/__init__.py +13 -4
  68. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  69. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  70. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  71. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  72. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  73. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  74. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  75. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  76. mcp_proxy_adapter/examples/demo_client.py +275 -0
  77. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  78. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  79. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  80. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  81. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  82. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  83. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  84. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  85. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  86. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  87. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  88. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  89. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  90. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  91. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  92. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  93. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  94. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  95. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  96. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  97. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  98. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  99. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  100. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  101. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  102. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  103. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  104. mcp_proxy_adapter/examples/run_example.py +59 -0
  105. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  106. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  107. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  108. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  109. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  110. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  111. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  112. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  113. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  114. mcp_proxy_adapter/examples/test_config.py +148 -0
  115. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  116. mcp_proxy_adapter/examples/test_examples.py +281 -0
  117. mcp_proxy_adapter/examples/universal_client.py +620 -0
  118. mcp_proxy_adapter/main.py +93 -0
  119. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  120. mcp_proxy_adapter/version.py +5 -2
  121. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  122. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  123. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  124. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  125. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  126. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  127. mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
  128. mcp_proxy_adapter/examples/README.md +0 -124
  129. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  130. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  131. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  132. mcp_proxy_adapter/examples/basic_server/config.json +0 -35
  133. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  134. mcp_proxy_adapter/examples/basic_server/server.py +0 -103
  135. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  136. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  137. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
  138. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  139. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  140. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  141. mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
  142. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  143. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  144. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  145. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  146. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  147. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  148. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  149. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  150. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
  153. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  154. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  155. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  156. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  157. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  158. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  159. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  160. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  161. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  162. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  163. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  164. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  165. mcp_proxy_adapter/tests/__init__.py +0 -0
  166. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  167. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  168. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  169. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  170. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  171. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  172. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  173. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  174. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  175. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  176. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  177. mcp_proxy_adapter/tests/conftest.py +0 -131
  178. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  180. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  181. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  182. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  183. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  184. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  185. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  186. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  187. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  188. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  189. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  190. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  191. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  192. mcp_proxy_adapter/tests/test_config.py +0 -127
  193. mcp_proxy_adapter/tests/test_utils.py +0 -65
  194. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  195. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  196. mcp_proxy_adapter/tests/unit/test_config.py +0 -217
  197. mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
  198. mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
  199. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  200. {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,426 @@
1
+ """
2
+ Role Utilities
3
+
4
+ This module provides utilities for working with roles extracted from certificates.
5
+ Includes functions for role extraction, comparison, validation, and normalization.
6
+
7
+ Author: MCP Proxy Adapter Team
8
+ Version: 1.0.0
9
+ """
10
+
11
+ import logging
12
+ from typing import List, Optional, Set
13
+ from cryptography import x509
14
+
15
+
16
+ class RoleUtils:
17
+ """
18
+ Utilities for working with roles from certificates.
19
+
20
+ Provides methods for extracting, comparing, validating, and normalizing roles.
21
+ """
22
+
23
+ # Custom OID for roles in certificates
24
+ ROLE_EXTENSION_OID = "1.3.6.1.4.1.99999.1"
25
+
26
+ @staticmethod
27
+ def extract_roles_from_certificate(cert_path: str) -> List[str]:
28
+ """
29
+ Extract roles from certificate file.
30
+
31
+ Args:
32
+ cert_path: Path to certificate file
33
+
34
+ Returns:
35
+ List of roles extracted from certificate
36
+ """
37
+ try:
38
+ with open(cert_path, 'rb') as f:
39
+ cert_data = f.read()
40
+
41
+ cert = x509.load_pem_x509_certificate(cert_data)
42
+
43
+ # Extract roles from custom extension
44
+ for extension in cert.extensions:
45
+ if extension.oid.dotted_string == RoleUtils.ROLE_EXTENSION_OID:
46
+ roles_data = extension.value.value.decode('utf-8')
47
+ return [role.strip() for role in roles_data.split(',') if role.strip()]
48
+
49
+ return []
50
+
51
+ except Exception as e:
52
+ logging.getLogger(__name__).error(f"Failed to extract roles from certificate {cert_path}: {e}")
53
+ return []
54
+
55
+ @staticmethod
56
+ def extract_roles_from_certificate_object(cert: x509.Certificate) -> List[str]:
57
+ """
58
+ Extract roles from certificate object.
59
+
60
+ Args:
61
+ cert: Certificate object
62
+
63
+ Returns:
64
+ List of roles extracted from certificate
65
+ """
66
+ try:
67
+ # Extract roles from custom extension
68
+ for extension in cert.extensions:
69
+ if extension.oid.dotted_string == RoleUtils.ROLE_EXTENSION_OID:
70
+ roles_data = extension.value.value.decode('utf-8')
71
+ return [role.strip() for role in roles_data.split(',') if role.strip()]
72
+
73
+ return []
74
+
75
+ except Exception as e:
76
+ logging.getLogger(__name__).error(f"Failed to extract roles from certificate object: {e}")
77
+ return []
78
+
79
+ @staticmethod
80
+ def compare_roles(role1: str, role2: str) -> bool:
81
+ """
82
+ Compare two roles (case-insensitive).
83
+
84
+ Args:
85
+ role1: First role to compare
86
+ role2: Second role to compare
87
+
88
+ Returns:
89
+ True if roles are equal (case-insensitive), False otherwise
90
+ """
91
+ if not role1 or not role2:
92
+ return False
93
+
94
+ return role1.lower().strip() == role2.lower().strip()
95
+
96
+ @staticmethod
97
+ def compare_role_lists(roles1: List[str], roles2: List[str]) -> bool:
98
+ """
99
+ Compare two lists of roles (case-insensitive).
100
+
101
+ Args:
102
+ roles1: First list of roles
103
+ roles2: Second list of roles
104
+
105
+ Returns:
106
+ True if role lists are equal (case-insensitive), False otherwise
107
+ """
108
+ if not roles1 and not roles2:
109
+ return True
110
+
111
+ if not roles1 or not roles2:
112
+ return False
113
+
114
+ # Normalize and sort both lists
115
+ normalized_roles1 = sorted([role.lower().strip() for role in roles1 if role.strip()])
116
+ normalized_roles2 = sorted([role.lower().strip() for role in roles2 if role.strip()])
117
+
118
+ return normalized_roles1 == normalized_roles2
119
+
120
+ @staticmethod
121
+ def validate_roles(roles: List[str]) -> bool:
122
+ """
123
+ Validate list of roles.
124
+
125
+ Args:
126
+ roles: List of roles to validate
127
+
128
+ Returns:
129
+ True if roles are valid, False otherwise
130
+ """
131
+ if not isinstance(roles, list):
132
+ return False
133
+
134
+ for role in roles:
135
+ if not RoleUtils.validate_single_role(role):
136
+ return False
137
+
138
+ return True
139
+
140
+ @staticmethod
141
+ def validate_single_role(role: str) -> bool:
142
+ """
143
+ Validate a single role.
144
+
145
+ Args:
146
+ role: Role string to validate
147
+
148
+ Returns:
149
+ True if role is valid, False otherwise
150
+ """
151
+ if not isinstance(role, str):
152
+ return False
153
+
154
+ # Check if role is not empty after trimming
155
+ if not role.strip():
156
+ return False
157
+
158
+ # Check for valid characters (alphanumeric, hyphens, underscores)
159
+ valid_chars = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_')
160
+ role_chars = set(role.lower())
161
+
162
+ if not role_chars.issubset(valid_chars):
163
+ return False
164
+
165
+ # Check length (1-50 characters)
166
+ if len(role) < 1 or len(role) > 50:
167
+ return False
168
+
169
+ return True
170
+
171
+ @staticmethod
172
+ def normalize_role(role: str) -> str:
173
+ """
174
+ Normalize role string.
175
+
176
+ Args:
177
+ role: Role string to normalize
178
+
179
+ Returns:
180
+ Normalized role string
181
+ """
182
+ if not role:
183
+ return ""
184
+
185
+ # Convert to lowercase and trim whitespace
186
+ normalized = role.lower().strip()
187
+
188
+ # Replace multiple spaces with single space
189
+ normalized = ' '.join(normalized.split())
190
+
191
+ # Replace spaces with hyphens
192
+ normalized = normalized.replace(' ', '-')
193
+
194
+ return normalized
195
+
196
+ @staticmethod
197
+ def normalize_roles(roles: List[str]) -> List[str]:
198
+ """
199
+ Normalize list of roles.
200
+
201
+ Args:
202
+ roles: List of roles to normalize
203
+
204
+ Returns:
205
+ List of normalized roles
206
+ """
207
+ if not roles:
208
+ return []
209
+
210
+ normalized = []
211
+ for role in roles:
212
+ normalized_role = RoleUtils.normalize_role(role)
213
+ if normalized_role and normalized_role not in normalized:
214
+ normalized.append(normalized_role)
215
+
216
+ return normalized
217
+
218
+ @staticmethod
219
+ def has_role(user_roles: List[str], required_role: str) -> bool:
220
+ """
221
+ Check if user has required role.
222
+
223
+ Args:
224
+ user_roles: List of user roles
225
+ required_role: Required role to check
226
+
227
+ Returns:
228
+ True if user has required role, False otherwise
229
+ """
230
+ if not user_roles or not required_role:
231
+ return False
232
+
233
+ normalized_required = RoleUtils.normalize_role(required_role)
234
+ normalized_user_roles = RoleUtils.normalize_roles(user_roles)
235
+
236
+ return normalized_required in normalized_user_roles
237
+
238
+ @staticmethod
239
+ def has_any_role(user_roles: List[str], required_roles: List[str]) -> bool:
240
+ """
241
+ Check if user has any of the required roles.
242
+
243
+ Args:
244
+ user_roles: List of user roles
245
+ required_roles: List of required roles to check
246
+
247
+ Returns:
248
+ True if user has any required role, False otherwise
249
+ """
250
+ if not user_roles or not required_roles:
251
+ return False
252
+
253
+ normalized_user_roles = RoleUtils.normalize_roles(user_roles)
254
+ normalized_required_roles = RoleUtils.normalize_roles(required_roles)
255
+
256
+ return any(role in normalized_user_roles for role in normalized_required_roles)
257
+
258
+ @staticmethod
259
+ def has_all_roles(user_roles: List[str], required_roles: List[str]) -> bool:
260
+ """
261
+ Check if user has all required roles.
262
+
263
+ Args:
264
+ user_roles: List of user roles
265
+ required_roles: List of required roles to check
266
+
267
+ Returns:
268
+ True if user has all required roles, False otherwise
269
+ """
270
+ if not user_roles or not required_roles:
271
+ return False
272
+
273
+ normalized_user_roles = RoleUtils.normalize_roles(user_roles)
274
+ normalized_required_roles = RoleUtils.normalize_roles(required_roles)
275
+
276
+ return all(role in normalized_user_roles for role in normalized_required_roles)
277
+
278
+ @staticmethod
279
+ def get_common_roles(roles1: List[str], roles2: List[str]) -> List[str]:
280
+ """
281
+ Get common roles between two role lists.
282
+
283
+ Args:
284
+ roles1: First list of roles
285
+ roles2: Second list of roles
286
+
287
+ Returns:
288
+ List of common roles
289
+ """
290
+ if not roles1 or not roles2:
291
+ return []
292
+
293
+ normalized_roles1 = set(RoleUtils.normalize_roles(roles1))
294
+ normalized_roles2 = set(RoleUtils.normalize_roles(roles2))
295
+
296
+ return list(normalized_roles1.intersection(normalized_roles2))
297
+
298
+ @staticmethod
299
+ def merge_roles(roles1: List[str], roles2: List[str]) -> List[str]:
300
+ """
301
+ Merge two role lists (remove duplicates).
302
+
303
+ Args:
304
+ roles1: First list of roles
305
+ roles2: Second list of roles
306
+
307
+ Returns:
308
+ Merged list of roles without duplicates
309
+ """
310
+ all_roles = (roles1 or []) + (roles2 or [])
311
+ return RoleUtils.normalize_roles(all_roles)
312
+
313
+ @staticmethod
314
+ def remove_roles(roles: List[str], roles_to_remove: List[str]) -> List[str]:
315
+ """
316
+ Remove specified roles from role list.
317
+
318
+ Args:
319
+ roles: List of roles
320
+ roles_to_remove: List of roles to remove
321
+
322
+ Returns:
323
+ List of roles with specified roles removed
324
+ """
325
+ if not roles:
326
+ return []
327
+
328
+ if not roles_to_remove:
329
+ return roles.copy()
330
+
331
+ normalized_roles = RoleUtils.normalize_roles(roles)
332
+ normalized_to_remove = set(RoleUtils.normalize_roles(roles_to_remove))
333
+
334
+ return [role for role in normalized_roles if role not in normalized_to_remove]
335
+
336
+ @staticmethod
337
+ def is_admin_role(role: str) -> bool:
338
+ """
339
+ Check if role is an admin role.
340
+
341
+ Args:
342
+ role: Role to check
343
+
344
+ Returns:
345
+ True if role is admin, False otherwise
346
+ """
347
+ if not role:
348
+ return False
349
+
350
+ admin_roles = {'admin', 'administrator', 'root', 'superuser', 'super-admin'}
351
+ normalized_role = RoleUtils.normalize_role(role)
352
+
353
+ return normalized_role in admin_roles
354
+
355
+ @staticmethod
356
+ def is_system_role(role: str) -> bool:
357
+ """
358
+ Check if role is a system role.
359
+
360
+ Args:
361
+ role: Role to check
362
+
363
+ Returns:
364
+ True if role is system role, False otherwise
365
+ """
366
+ if not role:
367
+ return False
368
+
369
+ system_roles = {'system', 'service', 'daemon', 'internal', 'system-user'}
370
+ normalized_role = RoleUtils.normalize_role(role)
371
+
372
+ return normalized_role in system_roles
373
+
374
+ @staticmethod
375
+ def get_role_hierarchy(role: str) -> List[str]:
376
+ """
377
+ Get role hierarchy (parent roles).
378
+
379
+ Args:
380
+ role: Role to get hierarchy for
381
+
382
+ Returns:
383
+ List of parent roles in hierarchy
384
+ """
385
+ if not role:
386
+ return []
387
+
388
+ normalized_role = RoleUtils.normalize_role(role)
389
+
390
+ # Define role hierarchy
391
+ hierarchy = {
392
+ 'super-admin': ['admin', 'user'],
393
+ 'admin': ['user'],
394
+ 'moderator': ['user'],
395
+ 'user': [],
396
+ 'guest': []
397
+ }
398
+
399
+ return hierarchy.get(normalized_role, [])
400
+
401
+ @staticmethod
402
+ def get_role_permissions(role: str) -> List[str]:
403
+ """
404
+ Get permissions for a role.
405
+
406
+ Args:
407
+ role: Role to get permissions for
408
+
409
+ Returns:
410
+ List of permissions for the role
411
+ """
412
+ if not role:
413
+ return []
414
+
415
+ normalized_role = RoleUtils.normalize_role(role)
416
+
417
+ # Define role permissions
418
+ permissions = {
419
+ 'super-admin': ['read', 'write', 'delete', 'admin', 'system'],
420
+ 'admin': ['read', 'write', 'delete', 'admin'],
421
+ 'moderator': ['read', 'write', 'moderate'],
422
+ 'user': ['read', 'write'],
423
+ 'guest': ['read']
424
+ }
425
+
426
+ return permissions.get(normalized_role, [])