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