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
@@ -25,11 +25,11 @@ class RolesListResult(SuccessResult):
|
|
25
25
|
"""
|
26
26
|
Result for roles list command.
|
27
27
|
"""
|
28
|
-
|
28
|
+
|
29
29
|
def __init__(self, roles: List[Dict[str, Any]], total_count: int):
|
30
30
|
"""
|
31
31
|
Initialize roles list result.
|
32
|
-
|
32
|
+
|
33
33
|
Args:
|
34
34
|
roles: List of role configurations
|
35
35
|
total_count: Total number of roles
|
@@ -38,25 +38,25 @@ class RolesListResult(SuccessResult):
|
|
38
38
|
self.success = True
|
39
39
|
self.roles = roles
|
40
40
|
self.total_count = total_count
|
41
|
-
|
41
|
+
|
42
42
|
def to_dict(self) -> Dict[str, Any]:
|
43
43
|
"""
|
44
44
|
Convert to dictionary format.
|
45
|
-
|
45
|
+
|
46
46
|
Returns:
|
47
47
|
Dictionary representation
|
48
48
|
"""
|
49
49
|
return {
|
50
50
|
"success": self.success,
|
51
51
|
"roles": self.roles,
|
52
|
-
"total_count": self.total_count
|
52
|
+
"total_count": self.total_count,
|
53
53
|
}
|
54
|
-
|
54
|
+
|
55
55
|
@classmethod
|
56
56
|
def get_schema(cls) -> Dict[str, Any]:
|
57
57
|
"""
|
58
58
|
Get JSON schema for result.
|
59
|
-
|
59
|
+
|
60
60
|
Returns:
|
61
61
|
JSON schema
|
62
62
|
"""
|
@@ -71,15 +71,24 @@ class RolesListResult(SuccessResult):
|
|
71
71
|
"properties": {
|
72
72
|
"name": {"type": "string"},
|
73
73
|
"description": {"type": "string"},
|
74
|
-
"allowed_servers": {
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
"allowed_servers": {
|
75
|
+
"type": "array",
|
76
|
+
"items": {"type": "string"},
|
77
|
+
},
|
78
|
+
"allowed_clients": {
|
79
|
+
"type": "array",
|
80
|
+
"items": {"type": "string"},
|
81
|
+
},
|
82
|
+
"permissions": {
|
83
|
+
"type": "array",
|
84
|
+
"items": {"type": "string"},
|
85
|
+
},
|
86
|
+
"priority": {"type": "integer"},
|
87
|
+
},
|
88
|
+
},
|
80
89
|
},
|
81
|
-
"total_count": {"type": "integer"}
|
82
|
-
}
|
90
|
+
"total_count": {"type": "integer"},
|
91
|
+
},
|
83
92
|
}
|
84
93
|
|
85
94
|
|
@@ -87,11 +96,11 @@ class RolesCreateResult(SuccessResult):
|
|
87
96
|
"""
|
88
97
|
Result for roles create command.
|
89
98
|
"""
|
90
|
-
|
99
|
+
|
91
100
|
def __init__(self, role_name: str, role_config: Dict[str, Any]):
|
92
101
|
"""
|
93
102
|
Initialize roles create result.
|
94
|
-
|
103
|
+
|
95
104
|
Args:
|
96
105
|
role_name: Name of created role
|
97
106
|
role_config: Role configuration
|
@@ -100,25 +109,25 @@ class RolesCreateResult(SuccessResult):
|
|
100
109
|
self.success = True
|
101
110
|
self.role_name = role_name
|
102
111
|
self.role_config = role_config
|
103
|
-
|
112
|
+
|
104
113
|
def to_dict(self) -> Dict[str, Any]:
|
105
114
|
"""
|
106
115
|
Convert to dictionary format.
|
107
|
-
|
116
|
+
|
108
117
|
Returns:
|
109
118
|
Dictionary representation
|
110
119
|
"""
|
111
120
|
return {
|
112
121
|
"success": self.success,
|
113
122
|
"role_name": self.role_name,
|
114
|
-
"role_config": self.role_config
|
123
|
+
"role_config": self.role_config,
|
115
124
|
}
|
116
|
-
|
125
|
+
|
117
126
|
@classmethod
|
118
127
|
def get_schema(cls) -> Dict[str, Any]:
|
119
128
|
"""
|
120
129
|
Get JSON schema for result.
|
121
|
-
|
130
|
+
|
122
131
|
Returns:
|
123
132
|
JSON schema
|
124
133
|
"""
|
@@ -127,8 +136,8 @@ class RolesCreateResult(SuccessResult):
|
|
127
136
|
"properties": {
|
128
137
|
"success": {"type": "boolean"},
|
129
138
|
"role_name": {"type": "string"},
|
130
|
-
"role_config": {"type": "object"}
|
131
|
-
}
|
139
|
+
"role_config": {"type": "object"},
|
140
|
+
},
|
132
141
|
}
|
133
142
|
|
134
143
|
|
@@ -136,11 +145,11 @@ class RolesUpdateResult(SuccessResult):
|
|
136
145
|
"""
|
137
146
|
Result for roles update command.
|
138
147
|
"""
|
139
|
-
|
148
|
+
|
140
149
|
def __init__(self, role_name: str, role_config: Dict[str, Any]):
|
141
150
|
"""
|
142
151
|
Initialize roles update result.
|
143
|
-
|
152
|
+
|
144
153
|
Args:
|
145
154
|
role_name: Name of updated role
|
146
155
|
role_config: Updated role configuration
|
@@ -149,25 +158,25 @@ class RolesUpdateResult(SuccessResult):
|
|
149
158
|
self.success = True
|
150
159
|
self.role_name = role_name
|
151
160
|
self.role_config = role_config
|
152
|
-
|
161
|
+
|
153
162
|
def to_dict(self) -> Dict[str, Any]:
|
154
163
|
"""
|
155
164
|
Convert to dictionary format.
|
156
|
-
|
165
|
+
|
157
166
|
Returns:
|
158
167
|
Dictionary representation
|
159
168
|
"""
|
160
169
|
return {
|
161
170
|
"success": self.success,
|
162
171
|
"role_name": self.role_name,
|
163
|
-
"role_config": self.role_config
|
172
|
+
"role_config": self.role_config,
|
164
173
|
}
|
165
|
-
|
174
|
+
|
166
175
|
@classmethod
|
167
176
|
def get_schema(cls) -> Dict[str, Any]:
|
168
177
|
"""
|
169
178
|
Get JSON schema for result.
|
170
|
-
|
179
|
+
|
171
180
|
Returns:
|
172
181
|
JSON schema
|
173
182
|
"""
|
@@ -176,8 +185,8 @@ class RolesUpdateResult(SuccessResult):
|
|
176
185
|
"properties": {
|
177
186
|
"success": {"type": "boolean"},
|
178
187
|
"role_name": {"type": "string"},
|
179
|
-
"role_config": {"type": "object"}
|
180
|
-
}
|
188
|
+
"role_config": {"type": "object"},
|
189
|
+
},
|
181
190
|
}
|
182
191
|
|
183
192
|
|
@@ -185,35 +194,32 @@ class RolesDeleteResult(SuccessResult):
|
|
185
194
|
"""
|
186
195
|
Result for roles delete command.
|
187
196
|
"""
|
188
|
-
|
197
|
+
|
189
198
|
def __init__(self, role_name: str):
|
190
199
|
"""
|
191
200
|
Initialize roles delete result.
|
192
|
-
|
201
|
+
|
193
202
|
Args:
|
194
203
|
role_name: Name of deleted role
|
195
204
|
"""
|
196
205
|
super().__init__()
|
197
206
|
self.success = True
|
198
207
|
self.role_name = role_name
|
199
|
-
|
208
|
+
|
200
209
|
def to_dict(self) -> Dict[str, Any]:
|
201
210
|
"""
|
202
211
|
Convert to dictionary format.
|
203
|
-
|
212
|
+
|
204
213
|
Returns:
|
205
214
|
Dictionary representation
|
206
215
|
"""
|
207
|
-
return {
|
208
|
-
|
209
|
-
"role_name": self.role_name
|
210
|
-
}
|
211
|
-
|
216
|
+
return {"success": self.success, "role_name": self.role_name}
|
217
|
+
|
212
218
|
@classmethod
|
213
219
|
def get_schema(cls) -> Dict[str, Any]:
|
214
220
|
"""
|
215
221
|
Get JSON schema for result.
|
216
|
-
|
222
|
+
|
217
223
|
Returns:
|
218
224
|
JSON schema
|
219
225
|
"""
|
@@ -221,8 +227,8 @@ class RolesDeleteResult(SuccessResult):
|
|
221
227
|
"type": "object",
|
222
228
|
"properties": {
|
223
229
|
"success": {"type": "boolean"},
|
224
|
-
"role_name": {"type": "string"}
|
225
|
-
}
|
230
|
+
"role_name": {"type": "string"},
|
231
|
+
},
|
226
232
|
}
|
227
233
|
|
228
234
|
|
@@ -230,11 +236,11 @@ class RolesValidateResult(SuccessResult):
|
|
230
236
|
"""
|
231
237
|
Result for roles validate command.
|
232
238
|
"""
|
233
|
-
|
239
|
+
|
234
240
|
def __init__(self, role_name: str, is_valid: bool, validation_errors: List[str]):
|
235
241
|
"""
|
236
242
|
Initialize roles validate result.
|
237
|
-
|
243
|
+
|
238
244
|
Args:
|
239
245
|
role_name: Name of validated role
|
240
246
|
is_valid: Whether role is valid
|
@@ -245,11 +251,11 @@ class RolesValidateResult(SuccessResult):
|
|
245
251
|
self.role_name = role_name
|
246
252
|
self.is_valid = is_valid
|
247
253
|
self.validation_errors = validation_errors
|
248
|
-
|
254
|
+
|
249
255
|
def to_dict(self) -> Dict[str, Any]:
|
250
256
|
"""
|
251
257
|
Convert to dictionary format.
|
252
|
-
|
258
|
+
|
253
259
|
Returns:
|
254
260
|
Dictionary representation
|
255
261
|
"""
|
@@ -257,14 +263,14 @@ class RolesValidateResult(SuccessResult):
|
|
257
263
|
"success": self.success,
|
258
264
|
"role_name": self.role_name,
|
259
265
|
"is_valid": self.is_valid,
|
260
|
-
"validation_errors": self.validation_errors
|
266
|
+
"validation_errors": self.validation_errors,
|
261
267
|
}
|
262
|
-
|
268
|
+
|
263
269
|
@classmethod
|
264
270
|
def get_schema(cls) -> Dict[str, Any]:
|
265
271
|
"""
|
266
272
|
Get JSON schema for result.
|
267
|
-
|
273
|
+
|
268
274
|
Returns:
|
269
275
|
JSON schema
|
270
276
|
"""
|
@@ -274,8 +280,8 @@ class RolesValidateResult(SuccessResult):
|
|
274
280
|
"success": {"type": "boolean"},
|
275
281
|
"role_name": {"type": "string"},
|
276
282
|
"is_valid": {"type": "boolean"},
|
277
|
-
"validation_errors": {"type": "array", "items": {"type": "string"}}
|
278
|
-
}
|
283
|
+
"validation_errors": {"type": "array", "items": {"type": "string"}},
|
284
|
+
},
|
279
285
|
}
|
280
286
|
|
281
287
|
|
@@ -283,7 +289,7 @@ class RolesManagementCommand(Command):
|
|
283
289
|
"""
|
284
290
|
Command for managing roles in the role-based access control system.
|
285
291
|
"""
|
286
|
-
|
292
|
+
|
287
293
|
name = "roles_management"
|
288
294
|
version = "1.0.0"
|
289
295
|
descr = "Manage roles in the role-based access control system"
|
@@ -291,22 +297,22 @@ class RolesManagementCommand(Command):
|
|
291
297
|
author = "MCP Proxy Adapter Team"
|
292
298
|
email = "team@mcp-proxy-adapter.com"
|
293
299
|
source_url = "https://github.com/mcp-proxy-adapter"
|
294
|
-
|
300
|
+
|
295
301
|
def __init__(self, roles_config_path: str = "schemas/roles_schema.json"):
|
296
302
|
"""
|
297
303
|
Initialize roles management command.
|
298
|
-
|
304
|
+
|
299
305
|
Args:
|
300
306
|
roles_config_path: Path to roles configuration file
|
301
307
|
"""
|
302
308
|
self.roles_config_path = roles_config_path
|
303
309
|
self.role_utils = RoleUtils()
|
304
310
|
self.roles_config = self._load_roles_config()
|
305
|
-
|
311
|
+
|
306
312
|
def _load_roles_config(self) -> Dict[str, Any]:
|
307
313
|
"""
|
308
314
|
Load roles configuration from file.
|
309
|
-
|
315
|
+
|
310
316
|
Returns:
|
311
317
|
Roles configuration dictionary
|
312
318
|
"""
|
@@ -315,16 +321,16 @@ class RolesManagementCommand(Command):
|
|
315
321
|
if not config_path.exists():
|
316
322
|
logger.warning(f"Roles config file not found: {self.roles_config_path}")
|
317
323
|
return {"roles": {}, "server_roles": {}, "role_hierarchy": {}}
|
318
|
-
|
319
|
-
with open(config_path,
|
324
|
+
|
325
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
320
326
|
config = json.load(f)
|
321
|
-
|
327
|
+
|
322
328
|
return config
|
323
|
-
|
329
|
+
|
324
330
|
except Exception as e:
|
325
331
|
logger.error(f"Failed to load roles configuration: {e}")
|
326
332
|
return {"roles": {}, "server_roles": {}, "role_hierarchy": {}}
|
327
|
-
|
333
|
+
|
328
334
|
def _save_roles_config(self) -> None:
|
329
335
|
"""
|
330
336
|
Save roles configuration to file.
|
@@ -332,29 +338,29 @@ class RolesManagementCommand(Command):
|
|
332
338
|
try:
|
333
339
|
config_path = Path(self.roles_config_path)
|
334
340
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
335
|
-
|
336
|
-
with open(config_path,
|
341
|
+
|
342
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
337
343
|
json.dump(self.roles_config, f, indent=2, ensure_ascii=False)
|
338
|
-
|
344
|
+
|
339
345
|
logger.info(f"Roles configuration saved to {self.roles_config_path}")
|
340
|
-
|
346
|
+
|
341
347
|
except Exception as e:
|
342
348
|
logger.error(f"Failed to save roles configuration: {e}")
|
343
349
|
raise InternalError(f"Failed to save roles configuration: {e}")
|
344
|
-
|
350
|
+
|
345
351
|
async def execute(self, **kwargs) -> CommandResult:
|
346
352
|
"""
|
347
353
|
Execute roles management command.
|
348
|
-
|
354
|
+
|
349
355
|
Args:
|
350
356
|
**kwargs: Command parameters including 'action' and role-specific parameters
|
351
|
-
|
357
|
+
|
352
358
|
Returns:
|
353
359
|
Command result
|
354
360
|
"""
|
355
361
|
try:
|
356
362
|
action = kwargs.get("action")
|
357
|
-
|
363
|
+
|
358
364
|
if action == "list":
|
359
365
|
return await self.roles_list(**kwargs)
|
360
366
|
elif action == "create":
|
@@ -366,31 +372,36 @@ class RolesManagementCommand(Command):
|
|
366
372
|
elif action == "validate":
|
367
373
|
return await self.roles_validate(**kwargs)
|
368
374
|
else:
|
369
|
-
raise ValidationError(
|
370
|
-
|
371
|
-
|
375
|
+
raise ValidationError(
|
376
|
+
f"Invalid action: {action}. "
|
377
|
+
f"Valid actions: list, create, update, delete, validate"
|
378
|
+
)
|
379
|
+
|
372
380
|
except Exception as e:
|
373
381
|
logger.error(f"Roles management command failed: {e}")
|
374
382
|
return ErrorResult(str(e))
|
375
|
-
|
383
|
+
|
376
384
|
async def roles_list(self, **kwargs) -> RolesListResult:
|
377
385
|
"""
|
378
386
|
List all roles.
|
379
|
-
|
387
|
+
|
380
388
|
Args:
|
381
389
|
**kwargs: Additional parameters (filter, limit, offset)
|
382
|
-
|
390
|
+
|
383
391
|
Returns:
|
384
392
|
Roles list result
|
385
393
|
"""
|
386
394
|
roles = self.roles_config.get("roles", {})
|
387
|
-
|
395
|
+
|
388
396
|
# Apply filters if specified
|
389
397
|
filter_name = kwargs.get("filter")
|
390
398
|
if filter_name:
|
391
|
-
roles = {
|
392
|
-
|
393
|
-
|
399
|
+
roles = {
|
400
|
+
name: config
|
401
|
+
for name, config in roles.items()
|
402
|
+
if filter_name.lower() in name.lower()
|
403
|
+
}
|
404
|
+
|
394
405
|
# Convert to list format
|
395
406
|
roles_list = []
|
396
407
|
for name, config in roles.items():
|
@@ -400,202 +411,212 @@ class RolesManagementCommand(Command):
|
|
400
411
|
"allowed_servers": config.get("allowed_servers", []),
|
401
412
|
"allowed_clients": config.get("allowed_clients", []),
|
402
413
|
"permissions": config.get("permissions", []),
|
403
|
-
"priority": config.get("priority", 0)
|
414
|
+
"priority": config.get("priority", 0),
|
404
415
|
}
|
405
416
|
roles_list.append(role_info)
|
406
|
-
|
417
|
+
|
407
418
|
# Apply pagination
|
408
419
|
limit = kwargs.get("limit")
|
409
420
|
offset = kwargs.get("offset", 0)
|
410
|
-
|
421
|
+
|
411
422
|
if limit:
|
412
|
-
roles_list = roles_list[offset:offset + limit]
|
423
|
+
roles_list = roles_list[offset : offset + limit]
|
413
424
|
elif offset:
|
414
425
|
roles_list = roles_list[offset:]
|
415
|
-
|
426
|
+
|
416
427
|
return RolesListResult(roles_list, len(roles))
|
417
|
-
|
428
|
+
|
418
429
|
async def roles_create(self, **kwargs) -> RolesCreateResult:
|
419
430
|
"""
|
420
431
|
Create a new role.
|
421
|
-
|
432
|
+
|
422
433
|
Args:
|
423
434
|
**kwargs: Role parameters (role_name, description, allowed_servers, etc.)
|
424
|
-
|
435
|
+
|
425
436
|
Returns:
|
426
437
|
Roles create result
|
427
438
|
"""
|
428
439
|
role_name = kwargs.get("role_name")
|
429
440
|
if not role_name:
|
430
441
|
raise ValidationError("role_name is required")
|
431
|
-
|
442
|
+
|
432
443
|
# Validate role name
|
433
444
|
if not self.role_utils.validate_single_role(role_name):
|
434
445
|
raise ValidationError(f"Invalid role name: {role_name}")
|
435
|
-
|
446
|
+
|
436
447
|
# Check if role already exists
|
437
448
|
if role_name in self.roles_config.get("roles", {}):
|
438
449
|
raise ValidationError(f"Role {role_name} already exists")
|
439
|
-
|
450
|
+
|
440
451
|
# Create role configuration
|
441
452
|
role_config = {
|
442
453
|
"description": kwargs.get("description", ""),
|
443
454
|
"allowed_servers": kwargs.get("allowed_servers", []),
|
444
455
|
"allowed_clients": kwargs.get("allowed_clients", []),
|
445
456
|
"permissions": kwargs.get("permissions", []),
|
446
|
-
"priority": kwargs.get("priority", 0)
|
457
|
+
"priority": kwargs.get("priority", 0),
|
447
458
|
}
|
448
|
-
|
459
|
+
|
449
460
|
# Validate role configuration
|
450
461
|
validation_errors = self._validate_role_config(role_config)
|
451
462
|
if validation_errors:
|
452
|
-
raise ValidationError(
|
453
|
-
|
463
|
+
raise ValidationError(
|
464
|
+
f"Invalid role configuration: {', '.join(validation_errors)}"
|
465
|
+
)
|
466
|
+
|
454
467
|
# Add role to configuration
|
455
468
|
if "roles" not in self.roles_config:
|
456
469
|
self.roles_config["roles"] = {}
|
457
|
-
|
470
|
+
|
458
471
|
self.roles_config["roles"][role_name] = role_config
|
459
|
-
|
472
|
+
|
460
473
|
# Save configuration
|
461
474
|
self._save_roles_config()
|
462
|
-
|
475
|
+
|
463
476
|
logger.info(f"Role {role_name} created successfully")
|
464
477
|
return RolesCreateResult(role_name, role_config)
|
465
|
-
|
478
|
+
|
466
479
|
async def roles_update(self, **kwargs) -> RolesUpdateResult:
|
467
480
|
"""
|
468
481
|
Update an existing role.
|
469
|
-
|
482
|
+
|
470
483
|
Args:
|
471
484
|
**kwargs: Role parameters (role_name, description, allowed_servers, etc.)
|
472
|
-
|
485
|
+
|
473
486
|
Returns:
|
474
487
|
Roles update result
|
475
488
|
"""
|
476
489
|
role_name = kwargs.get("role_name")
|
477
490
|
if not role_name:
|
478
491
|
raise ValidationError("role_name is required")
|
479
|
-
|
492
|
+
|
480
493
|
# Check if role exists
|
481
494
|
if role_name not in self.roles_config.get("roles", {}):
|
482
495
|
raise NotFoundError(f"Role {role_name} not found")
|
483
|
-
|
496
|
+
|
484
497
|
# Get existing configuration
|
485
498
|
existing_config = self.roles_config["roles"][role_name]
|
486
|
-
|
499
|
+
|
487
500
|
# Update configuration with new values
|
488
501
|
updated_config = existing_config.copy()
|
489
|
-
for key in [
|
502
|
+
for key in [
|
503
|
+
"description",
|
504
|
+
"allowed_servers",
|
505
|
+
"allowed_clients",
|
506
|
+
"permissions",
|
507
|
+
"priority",
|
508
|
+
]:
|
490
509
|
if key in kwargs:
|
491
510
|
updated_config[key] = kwargs[key]
|
492
|
-
|
511
|
+
|
493
512
|
# Validate updated configuration
|
494
513
|
validation_errors = self._validate_role_config(updated_config)
|
495
514
|
if validation_errors:
|
496
|
-
raise ValidationError(
|
497
|
-
|
515
|
+
raise ValidationError(
|
516
|
+
f"Invalid role configuration: {', '.join(validation_errors)}"
|
517
|
+
)
|
518
|
+
|
498
519
|
# Update role configuration
|
499
520
|
self.roles_config["roles"][role_name] = updated_config
|
500
|
-
|
521
|
+
|
501
522
|
# Save configuration
|
502
523
|
self._save_roles_config()
|
503
|
-
|
524
|
+
|
504
525
|
logger.info(f"Role {role_name} updated successfully")
|
505
526
|
return RolesUpdateResult(role_name, updated_config)
|
506
|
-
|
527
|
+
|
507
528
|
async def roles_delete(self, **kwargs) -> RolesDeleteResult:
|
508
529
|
"""
|
509
530
|
Delete a role.
|
510
|
-
|
531
|
+
|
511
532
|
Args:
|
512
533
|
**kwargs: Role parameters (role_name)
|
513
|
-
|
534
|
+
|
514
535
|
Returns:
|
515
536
|
Roles delete result
|
516
537
|
"""
|
517
538
|
role_name = kwargs.get("role_name")
|
518
539
|
if not role_name:
|
519
540
|
raise ValidationError("role_name is required")
|
520
|
-
|
541
|
+
|
521
542
|
# Check if role exists
|
522
543
|
if role_name not in self.roles_config.get("roles", {}):
|
523
544
|
raise NotFoundError(f"Role {role_name} not found")
|
524
|
-
|
545
|
+
|
525
546
|
# Check if role is system role
|
526
547
|
if self.role_utils.is_system_role(role_name):
|
527
548
|
raise ValidationError(f"Cannot delete system role: {role_name}")
|
528
|
-
|
549
|
+
|
529
550
|
# Remove role from configuration
|
530
551
|
del self.roles_config["roles"][role_name]
|
531
|
-
|
552
|
+
|
532
553
|
# Remove from role hierarchy
|
533
554
|
if "role_hierarchy" in self.roles_config:
|
534
555
|
if role_name in self.roles_config["role_hierarchy"]:
|
535
556
|
del self.roles_config["role_hierarchy"][role_name]
|
536
|
-
|
557
|
+
|
537
558
|
# Remove from other roles' hierarchies
|
538
559
|
for other_role, hierarchy in self.roles_config["role_hierarchy"].items():
|
539
560
|
if role_name in hierarchy:
|
540
561
|
hierarchy.remove(role_name)
|
541
|
-
|
562
|
+
|
542
563
|
# Save configuration
|
543
564
|
self._save_roles_config()
|
544
|
-
|
565
|
+
|
545
566
|
logger.info(f"Role {role_name} deleted successfully")
|
546
567
|
return RolesDeleteResult(role_name)
|
547
|
-
|
568
|
+
|
548
569
|
async def roles_validate(self, **kwargs) -> RolesValidateResult:
|
549
570
|
"""
|
550
571
|
Validate a role configuration.
|
551
|
-
|
572
|
+
|
552
573
|
Args:
|
553
574
|
**kwargs: Role parameters (role_name or role_config)
|
554
|
-
|
575
|
+
|
555
576
|
Returns:
|
556
577
|
Roles validate result
|
557
578
|
"""
|
558
579
|
role_name = kwargs.get("role_name")
|
559
580
|
role_config = kwargs.get("role_config")
|
560
|
-
|
581
|
+
|
561
582
|
if not role_name and not role_config:
|
562
583
|
raise ValidationError("Either role_name or role_config is required")
|
563
|
-
|
584
|
+
|
564
585
|
validation_errors = []
|
565
|
-
|
586
|
+
|
566
587
|
if role_name:
|
567
588
|
# Validate existing role
|
568
589
|
if role_name not in self.roles_config.get("roles", {}):
|
569
590
|
validation_errors.append(f"Role {role_name} not found")
|
570
591
|
else:
|
571
592
|
role_config = self.roles_config["roles"][role_name]
|
572
|
-
|
593
|
+
|
573
594
|
if role_config:
|
574
595
|
# Validate role configuration
|
575
596
|
config_errors = self._validate_role_config(role_config)
|
576
597
|
validation_errors.extend(config_errors)
|
577
|
-
|
598
|
+
|
578
599
|
is_valid = len(validation_errors) == 0
|
579
|
-
|
600
|
+
|
580
601
|
return RolesValidateResult(role_name or "unknown", is_valid, validation_errors)
|
581
|
-
|
602
|
+
|
582
603
|
def _validate_role_config(self, role_config: Dict[str, Any]) -> List[str]:
|
583
604
|
"""
|
584
605
|
Validate role configuration.
|
585
|
-
|
606
|
+
|
586
607
|
Args:
|
587
608
|
role_config: Role configuration to validate
|
588
|
-
|
609
|
+
|
589
610
|
Returns:
|
590
611
|
List of validation errors
|
591
612
|
"""
|
592
613
|
errors = []
|
593
|
-
|
614
|
+
|
594
615
|
# Validate description
|
595
616
|
description = role_config.get("description", "")
|
596
617
|
if not isinstance(description, str):
|
597
618
|
errors.append("description must be a string")
|
598
|
-
|
619
|
+
|
599
620
|
# Validate allowed_servers
|
600
621
|
allowed_servers = role_config.get("allowed_servers", [])
|
601
622
|
if not isinstance(allowed_servers, list):
|
@@ -604,7 +625,7 @@ class RolesManagementCommand(Command):
|
|
604
625
|
for server in allowed_servers:
|
605
626
|
if not isinstance(server, str):
|
606
627
|
errors.append("allowed_servers must contain only strings")
|
607
|
-
|
628
|
+
|
608
629
|
# Validate allowed_clients
|
609
630
|
allowed_clients = role_config.get("allowed_clients", [])
|
610
631
|
if not isinstance(allowed_clients, list):
|
@@ -613,7 +634,7 @@ class RolesManagementCommand(Command):
|
|
613
634
|
for client in allowed_clients:
|
614
635
|
if not isinstance(client, str):
|
615
636
|
errors.append("allowed_clients must contain only strings")
|
616
|
-
|
637
|
+
|
617
638
|
# Validate permissions
|
618
639
|
permissions = role_config.get("permissions", [])
|
619
640
|
if not isinstance(permissions, list):
|
@@ -622,21 +643,21 @@ class RolesManagementCommand(Command):
|
|
622
643
|
for permission in permissions:
|
623
644
|
if not isinstance(permission, str):
|
624
645
|
errors.append("permissions must contain only strings")
|
625
|
-
|
646
|
+
|
626
647
|
# Validate priority
|
627
648
|
priority = role_config.get("priority", 0)
|
628
649
|
if not isinstance(priority, int):
|
629
650
|
errors.append("priority must be an integer")
|
630
651
|
elif priority < 0:
|
631
652
|
errors.append("priority must be non-negative")
|
632
|
-
|
653
|
+
|
633
654
|
return errors
|
634
|
-
|
655
|
+
|
635
656
|
@classmethod
|
636
657
|
def get_schema(cls) -> Dict[str, Any]:
|
637
658
|
"""
|
638
659
|
Get JSON schema for command parameters.
|
639
|
-
|
660
|
+
|
640
661
|
Returns:
|
641
662
|
JSON schema
|
642
663
|
"""
|
@@ -646,52 +667,34 @@ class RolesManagementCommand(Command):
|
|
646
667
|
"action": {
|
647
668
|
"type": "string",
|
648
669
|
"enum": ["list", "create", "update", "delete", "validate"],
|
649
|
-
"description": "Action to perform"
|
650
|
-
},
|
651
|
-
"role_name": {
|
652
|
-
"type": "string",
|
653
|
-
"description": "Name of the role"
|
654
|
-
},
|
655
|
-
"description": {
|
656
|
-
"type": "string",
|
657
|
-
"description": "Role description"
|
670
|
+
"description": "Action to perform",
|
658
671
|
},
|
672
|
+
"role_name": {"type": "string", "description": "Name of the role"},
|
673
|
+
"description": {"type": "string", "description": "Role description"},
|
659
674
|
"allowed_servers": {
|
660
675
|
"type": "array",
|
661
676
|
"items": {"type": "string"},
|
662
|
-
"description": "List of allowed servers"
|
677
|
+
"description": "List of allowed servers",
|
663
678
|
},
|
664
679
|
"allowed_clients": {
|
665
680
|
"type": "array",
|
666
681
|
"items": {"type": "string"},
|
667
|
-
"description": "List of allowed clients"
|
682
|
+
"description": "List of allowed clients",
|
668
683
|
},
|
669
684
|
"permissions": {
|
670
685
|
"type": "array",
|
671
686
|
"items": {"type": "string"},
|
672
|
-
"description": "List of permissions"
|
673
|
-
},
|
674
|
-
"priority": {
|
675
|
-
"type": "integer",
|
676
|
-
"description": "Role priority"
|
687
|
+
"description": "List of permissions",
|
677
688
|
},
|
689
|
+
"priority": {"type": "integer", "description": "Role priority"},
|
678
690
|
"role_config": {
|
679
691
|
"type": "object",
|
680
|
-
"description": "Complete role configuration"
|
692
|
+
"description": "Complete role configuration",
|
681
693
|
},
|
682
|
-
"filter": {
|
683
|
-
|
684
|
-
|
685
|
-
},
|
686
|
-
"limit": {
|
687
|
-
"type": "integer",
|
688
|
-
"description": "Limit for list action"
|
689
|
-
},
|
690
|
-
"offset": {
|
691
|
-
"type": "integer",
|
692
|
-
"description": "Offset for list action"
|
693
|
-
}
|
694
|
+
"filter": {"type": "string", "description": "Filter for list action"},
|
695
|
+
"limit": {"type": "integer", "description": "Limit for list action"},
|
696
|
+
"offset": {"type": "integer", "description": "Offset for list action"},
|
694
697
|
},
|
695
698
|
"required": ["action"],
|
696
|
-
"additionalProperties": False
|
697
|
-
}
|
699
|
+
"additionalProperties": False,
|
700
|
+
}
|