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.
- mcp_proxy_adapter/__main__.py +32 -0
- mcp_proxy_adapter/api/app.py +290 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +38 -32
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +8 -1
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +394 -14
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +1045 -0
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +385 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +234 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/__init__.py +13 -4
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +93 -0
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/README.md +0 -124
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
- {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, [])
|