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