mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +27 -7
- mcp_proxy_adapter/api/app.py +209 -79
- mcp_proxy_adapter/api/handlers.py +16 -5
- mcp_proxy_adapter/api/middleware/__init__.py +14 -9
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/factory.py +36 -12
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
- mcp_proxy_adapter/commands/__init__.py +7 -1
- mcp_proxy_adapter/commands/base.py +7 -4
- mcp_proxy_adapter/commands/builtin_commands.py +8 -2
- mcp_proxy_adapter/commands/command_registry.py +8 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +1 -1
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
- mcp_proxy_adapter/commands/token_management_command.py +1 -1
- mcp_proxy_adapter/config.py +323 -40
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/certificate_utils.py +291 -73
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/logging.py +8 -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 +169 -10
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +299 -47
- mcp_proxy_adapter/core/security_adapter.py +12 -15
- mcp_proxy_adapter/core/security_integration.py +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/ssl_utils.py +13 -12
- mcp_proxy_adapter/core/transport_manager.py +5 -5
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/examples/__init__.py +13 -4
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +66 -148
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
- mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
- mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
- mcp_proxy_adapter/api/middleware/security.py +0 -376
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
- mcp_proxy_adapter/examples/README.md +0 -124
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
- mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
- mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -114
- 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 -566
- 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/auto_commands/test_command.py +0 -105
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
- 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/full_help_response.json +0 -1
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
- 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/loadable_commands/test_ignored.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
- mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
- 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/examples/simple_custom_commands/README.md +0 -149
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/schemas/roles_schema.json +0 -162
- 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 -270
- mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Debug Role Chain - Анализ цепочки блокировки ролей
|
4
|
+
Этот скрипт анализирует всю цепочку от аутентификации до блокировки доступа.
|
5
|
+
Author: Vasiliy Zdanovskiy
|
6
|
+
email: vasilyvz@gmail.com
|
7
|
+
"""
|
8
|
+
import asyncio
|
9
|
+
import json
|
10
|
+
import sys
|
11
|
+
from pathlib import Path
|
12
|
+
# Add project root to path
|
13
|
+
project_root = Path(__file__).parent.parent.parent
|
14
|
+
sys.path.insert(0, str(project_root))
|
15
|
+
from mcp_security_framework import SecurityManager, AuthManager, PermissionManager
|
16
|
+
from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, PermissionConfig
|
17
|
+
async def debug_role_chain():
|
18
|
+
"""Debug the complete role chain from authentication to blocking."""
|
19
|
+
print("🔍 АНАЛИЗ ЦЕПОЧКИ БЛОКИРОВКИ РОЛЕЙ")
|
20
|
+
print("=" * 60)
|
21
|
+
# Load configuration
|
22
|
+
config_path = project_root / "mcp_proxy_adapter" / "examples" / "server_configs" / "config_http_token.json"
|
23
|
+
with open(config_path) as f:
|
24
|
+
config = json.load(f)
|
25
|
+
security_config = config.get("security", {})
|
26
|
+
print("📋 1. КОНФИГУРАЦИЯ API КЛЮЧЕЙ")
|
27
|
+
print("-" * 30)
|
28
|
+
api_keys = security_config.get("auth", {}).get("api_keys", {})
|
29
|
+
for key, value in api_keys.items():
|
30
|
+
print(f" {key}: {value}")
|
31
|
+
print("\n📋 2. КОНФИГУРАЦИЯ РОЛЕЙ")
|
32
|
+
print("-" * 30)
|
33
|
+
roles_config = security_config.get("permissions", {}).get("roles", {})
|
34
|
+
for role, permissions in roles_config.items():
|
35
|
+
print(f" {role}: {permissions}")
|
36
|
+
print("\n📋 3. СОЗДАНИЕ КОМПОНЕНТОВ БЕЗОПАСНОСТИ")
|
37
|
+
print("-" * 30)
|
38
|
+
# Create permission config
|
39
|
+
perm_config = PermissionConfig(
|
40
|
+
roles_file=str(project_root / "mcp_proxy_adapter" / "examples" / "server_configs" / "roles.json"),
|
41
|
+
default_role="guest",
|
42
|
+
admin_role="admin",
|
43
|
+
role_hierarchy=security_config.get("permissions", {}).get("role_hierarchy", {}),
|
44
|
+
permission_cache_enabled=True,
|
45
|
+
permission_cache_ttl=300,
|
46
|
+
wildcard_permissions=False,
|
47
|
+
strict_mode=True,
|
48
|
+
roles=roles_config
|
49
|
+
)
|
50
|
+
# Create auth config
|
51
|
+
auth_config = AuthConfig(
|
52
|
+
enabled=security_config.get("auth", {}).get("enabled", True),
|
53
|
+
methods=security_config.get("auth", {}).get("methods", ["api_key"]),
|
54
|
+
api_keys=api_keys,
|
55
|
+
user_roles=security_config.get("auth", {}).get("user_roles", {}),
|
56
|
+
jwt_secret=security_config.get("auth", {}).get("jwt_secret"),
|
57
|
+
jwt_algorithm=security_config.get("auth", {}).get("jwt_algorithm", "HS256"),
|
58
|
+
jwt_expiry_hours=security_config.get("auth", {}).get("jwt_expiry_hours", 24),
|
59
|
+
certificate_auth=security_config.get("auth", {}).get("certificate_auth", False),
|
60
|
+
certificate_roles_oid=security_config.get("auth", {}).get("certificate_roles_oid"),
|
61
|
+
certificate_permissions_oid=security_config.get("auth", {}).get("certificate_permissions_oid"),
|
62
|
+
basic_auth=security_config.get("auth", {}).get("basic_auth", False),
|
63
|
+
oauth2_config=security_config.get("auth", {}).get("oauth2_config"),
|
64
|
+
public_paths=security_config.get("auth", {}).get("public_paths", [])
|
65
|
+
)
|
66
|
+
# Create security config
|
67
|
+
security_config_obj = SecurityConfig(
|
68
|
+
auth=auth_config,
|
69
|
+
permissions=perm_config
|
70
|
+
)
|
71
|
+
print("✅ Конфигурации созданы")
|
72
|
+
print("\n📋 4. ИНИЦИАЛИЗАЦИЯ МЕНЕДЖЕРОВ")
|
73
|
+
print("-" * 30)
|
74
|
+
# Initialize managers
|
75
|
+
permission_manager = PermissionManager(perm_config)
|
76
|
+
auth_manager = AuthManager(auth_config, permission_manager)
|
77
|
+
security_manager = SecurityManager(security_config_obj)
|
78
|
+
print("✅ Менеджеры инициализированы")
|
79
|
+
print("\n📋 5. ТЕСТИРОВАНИЕ АУТЕНТИФИКАЦИИ")
|
80
|
+
print("-" * 30)
|
81
|
+
# Test authentication with different tokens
|
82
|
+
test_tokens = {
|
83
|
+
"admin": "test-token-123",
|
84
|
+
"user": "user-token-456",
|
85
|
+
"readonly": "readonly-token-123",
|
86
|
+
"invalid": "invalid-token-999"
|
87
|
+
}
|
88
|
+
auth_results = {}
|
89
|
+
for role, token in test_tokens.items():
|
90
|
+
print(f"\n🔐 Тестирование токена для роли '{role}': {token}")
|
91
|
+
try:
|
92
|
+
result = auth_manager.authenticate_api_key(token)
|
93
|
+
auth_results[role] = result
|
94
|
+
print(f" ✅ Аутентификация: {'УСПЕШНА' if result.is_valid else 'НЕУДАЧНА'}")
|
95
|
+
if result.is_valid:
|
96
|
+
print(f" 👤 Пользователь: {result.username}")
|
97
|
+
print(f" 🏷️ Роли: {result.roles}")
|
98
|
+
print(f" 🔑 Метод: {result.auth_method}")
|
99
|
+
else:
|
100
|
+
print(f" ❌ Ошибка: {result.error_message}")
|
101
|
+
except Exception as e:
|
102
|
+
print(f" ❌ Исключение: {e}")
|
103
|
+
auth_results[role] = None
|
104
|
+
print("\n📋 6. ТЕСТИРОВАНИЕ ПРАВ ДОСТУПА")
|
105
|
+
print("-" * 30)
|
106
|
+
# Test permissions for different actions
|
107
|
+
test_actions = ["read", "write", "manage", "delete"]
|
108
|
+
for role, auth_result in auth_results.items():
|
109
|
+
if auth_result and auth_result.is_valid:
|
110
|
+
print(f"\n🔒 Тестирование прав для роли '{role}' (роли: {auth_result.roles})")
|
111
|
+
for action in test_actions:
|
112
|
+
try:
|
113
|
+
# Check permissions using permission manager
|
114
|
+
validation_result = permission_manager.validate_access(
|
115
|
+
auth_result.roles,
|
116
|
+
[action]
|
117
|
+
)
|
118
|
+
status = "✅ РАЗРЕШЕНО" if validation_result.is_valid else "❌ ЗАБЛОКИРОВАНО"
|
119
|
+
print(f" {action}: {status}")
|
120
|
+
if not validation_result.is_valid:
|
121
|
+
print(f" 📝 Причина: {validation_result.error_message}")
|
122
|
+
print(f" 🎯 Эффективные права: {validation_result.effective_permissions}")
|
123
|
+
print(f" ❌ Отсутствующие права: {validation_result.missing_permissions}")
|
124
|
+
except Exception as e:
|
125
|
+
print(f" {action}: ❌ ОШИБКА - {e}")
|
126
|
+
print("\n📋 7. ТЕСТИРОВАНИЕ ПОЛНОЙ ЦЕПОЧКИ")
|
127
|
+
print("-" * 30)
|
128
|
+
# Test complete request validation
|
129
|
+
for role, token in test_tokens.items():
|
130
|
+
print(f"\n🔄 Полная цепочка для роли '{role}'")
|
131
|
+
request_data = {
|
132
|
+
"api_key": token,
|
133
|
+
"required_permissions": ["write"],
|
134
|
+
"client_ip": "127.0.0.1"
|
135
|
+
}
|
136
|
+
try:
|
137
|
+
result = security_manager.validate_request(request_data)
|
138
|
+
status = "✅ УСПЕШНО" if result.is_valid else "❌ ЗАБЛОКИРОВАНО"
|
139
|
+
print(f" Результат: {status}")
|
140
|
+
if not result.is_valid:
|
141
|
+
print(f" 📝 Причина: {result.error_message}")
|
142
|
+
except Exception as e:
|
143
|
+
print(f" ❌ ОШИБКА: {e}")
|
144
|
+
print("\n📋 8. АНАЛИЗ ПРОБЛЕМЫ")
|
145
|
+
print("-" * 30)
|
146
|
+
print("🔍 ПРОБЛЕМА: Readonly роль получает доступ к write операциям")
|
147
|
+
print("\n📋 ВОЗМОЖНЫЕ ПРИЧИНЫ:")
|
148
|
+
print("1. Middleware не передает информацию о пользователе в request.state")
|
149
|
+
print("2. Framework middleware не блокирует доступ на уровне middleware")
|
150
|
+
print("3. Команда role_test не получает правильный контекст пользователя")
|
151
|
+
print("4. Интеграция между middleware и командами не работает")
|
152
|
+
print("\n📋 РЕКОМЕНДАЦИИ:")
|
153
|
+
print("1. Проверить, как framework middleware устанавливает user info")
|
154
|
+
print("2. Добавить проверку прав на уровне middleware")
|
155
|
+
print("3. Убедиться, что request.state содержит user info")
|
156
|
+
print("4. Проверить интеграцию между middleware и командами")
|
157
|
+
if __name__ == "__main__":
|
158
|
+
asyncio.run(debug_role_chain())
|
@@ -0,0 +1,275 @@
|
|
1
|
+
"""
|
2
|
+
Demo Client Script
|
3
|
+
This script demonstrates how to use the UniversalClient with different
|
4
|
+
authentication methods and connection types.
|
5
|
+
Author: Vasiliy Zdanovskiy
|
6
|
+
email: vasilyvz@gmail.com
|
7
|
+
"""
|
8
|
+
import asyncio
|
9
|
+
import json
|
10
|
+
import sys
|
11
|
+
from pathlib import Path
|
12
|
+
# Add project root to path
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
14
|
+
from examples.universal_client import UniversalClient, create_client_config
|
15
|
+
async def demo_from_config_file(config_file: str):
|
16
|
+
"""
|
17
|
+
Demo client using configuration from file.
|
18
|
+
Args:
|
19
|
+
config_file: Path to configuration file
|
20
|
+
"""
|
21
|
+
print(f"🚀 Demo from config file: {config_file}")
|
22
|
+
print("=" * 50)
|
23
|
+
try:
|
24
|
+
# Load configuration
|
25
|
+
with open(config_file, 'r') as f:
|
26
|
+
config = json.load(f)
|
27
|
+
print(f"Configuration loaded: {config.get('security', {}).get('auth_method', 'none')} auth")
|
28
|
+
# Create and use client
|
29
|
+
async with UniversalClient(config) as client:
|
30
|
+
# Test connection
|
31
|
+
success = await client.test_connection()
|
32
|
+
if success:
|
33
|
+
print("✅ Connection successful!")
|
34
|
+
# Test security features
|
35
|
+
security_results = await client.test_security_features()
|
36
|
+
print("\nSecurity Features:")
|
37
|
+
for feature, status in security_results.items():
|
38
|
+
status_icon = "✅" if status else "❌"
|
39
|
+
print(f" {status_icon} {feature}: {status}")
|
40
|
+
# Make API calls
|
41
|
+
await demo_api_calls(client)
|
42
|
+
else:
|
43
|
+
print("❌ Connection failed")
|
44
|
+
except FileNotFoundError:
|
45
|
+
print(f"❌ Configuration file not found: {config_file}")
|
46
|
+
except json.JSONDecodeError:
|
47
|
+
print(f"❌ Invalid JSON in configuration file: {config_file}")
|
48
|
+
except Exception as e:
|
49
|
+
print(f"❌ Demo failed: {e}")
|
50
|
+
async def demo_api_calls(client: UniversalClient):
|
51
|
+
"""Demonstrate various API calls."""
|
52
|
+
print("\n📡 API Calls Demo:")
|
53
|
+
print("-" * 30)
|
54
|
+
# Test health endpoint
|
55
|
+
try:
|
56
|
+
health = await client.get("/health")
|
57
|
+
print(f"Health: {health}")
|
58
|
+
except Exception as e:
|
59
|
+
print(f"Health check failed: {e}")
|
60
|
+
# Test status endpoint
|
61
|
+
try:
|
62
|
+
status = await client.get("/api/status")
|
63
|
+
print(f"Status: {status}")
|
64
|
+
except Exception as e:
|
65
|
+
print(f"Status check failed: {e}")
|
66
|
+
# Test JSON-RPC command
|
67
|
+
try:
|
68
|
+
command_data = {
|
69
|
+
"jsonrpc": "2.0",
|
70
|
+
"method": "test_command",
|
71
|
+
"params": {
|
72
|
+
"message": "Hello from universal client!",
|
73
|
+
"timestamp": "2024-01-01T00:00:00Z"
|
74
|
+
},
|
75
|
+
"id": 1
|
76
|
+
}
|
77
|
+
result = await client.post("/api/jsonrpc", command_data)
|
78
|
+
print(f"Command Result: {result}")
|
79
|
+
except Exception as e:
|
80
|
+
print(f"Command execution failed: {e}")
|
81
|
+
# Test security command if available
|
82
|
+
try:
|
83
|
+
security_data = {
|
84
|
+
"jsonrpc": "2.0",
|
85
|
+
"method": "security_command",
|
86
|
+
"params": {
|
87
|
+
"action": "get_status",
|
88
|
+
"include_certificates": True
|
89
|
+
},
|
90
|
+
"id": 2
|
91
|
+
}
|
92
|
+
result = await client.post("/api/jsonrpc", security_data)
|
93
|
+
print(f"Security Status: {result}")
|
94
|
+
except Exception as e:
|
95
|
+
print(f"Security command failed: {e}")
|
96
|
+
async def demo_all_configs():
|
97
|
+
"""Demo all available client configurations."""
|
98
|
+
print("🚀 Demo All Client Configurations")
|
99
|
+
print("=" * 50)
|
100
|
+
config_dir = Path(__file__).parent / "client_configs"
|
101
|
+
if not config_dir.exists():
|
102
|
+
print(f"❌ Config directory not found: {config_dir}")
|
103
|
+
return
|
104
|
+
config_files = list(config_dir.glob("*.json"))
|
105
|
+
if not config_files:
|
106
|
+
print(f"❌ No configuration files found in {config_dir}")
|
107
|
+
return
|
108
|
+
print(f"Found {len(config_files)} configuration files:")
|
109
|
+
for config_file in config_files:
|
110
|
+
print(f" - {config_file.name}")
|
111
|
+
print("\n" + "=" * 50)
|
112
|
+
for config_file in config_files:
|
113
|
+
await demo_from_config_file(str(config_file))
|
114
|
+
print("\n" + "-" * 50)
|
115
|
+
async def demo_programmatic_config():
|
116
|
+
"""Demo client with programmatically created configuration."""
|
117
|
+
print("🚀 Demo Programmatic Configuration")
|
118
|
+
print("=" * 50)
|
119
|
+
# Create different configurations programmatically
|
120
|
+
configs = [
|
121
|
+
{
|
122
|
+
"name": "API Key Client",
|
123
|
+
"config": create_client_config(
|
124
|
+
"http://localhost:8000",
|
125
|
+
"api_key",
|
126
|
+
api_key="demo_api_key_123"
|
127
|
+
)
|
128
|
+
},
|
129
|
+
{
|
130
|
+
"name": "JWT Client",
|
131
|
+
"config": create_client_config(
|
132
|
+
"http://localhost:8000",
|
133
|
+
"jwt",
|
134
|
+
username="demo_user",
|
135
|
+
password="demo_password",
|
136
|
+
secret="demo_jwt_secret"
|
137
|
+
)
|
138
|
+
},
|
139
|
+
{
|
140
|
+
"name": "Certificate Client",
|
141
|
+
"config": create_client_config(
|
142
|
+
"https://localhost:8443",
|
143
|
+
"certificate",
|
144
|
+
cert_file="./certs/client.crt",
|
145
|
+
key_file="./keys/client.key",
|
146
|
+
ca_cert_file="./certs/ca.crt"
|
147
|
+
)
|
148
|
+
},
|
149
|
+
{
|
150
|
+
"name": "Basic Auth Client",
|
151
|
+
"config": create_client_config(
|
152
|
+
"http://localhost:8000",
|
153
|
+
"basic",
|
154
|
+
username="demo_user",
|
155
|
+
password="demo_password"
|
156
|
+
)
|
157
|
+
}
|
158
|
+
]
|
159
|
+
for config_info in configs:
|
160
|
+
print(f"\n📋 Testing: {config_info['name']}")
|
161
|
+
print("-" * 30)
|
162
|
+
try:
|
163
|
+
async with UniversalClient(config_info["config"]) as client:
|
164
|
+
success = await client.test_connection()
|
165
|
+
if success:
|
166
|
+
print("✅ Connection successful!")
|
167
|
+
await demo_api_calls(client)
|
168
|
+
else:
|
169
|
+
print("❌ Connection failed")
|
170
|
+
except Exception as e:
|
171
|
+
print(f"❌ Test failed: {e}")
|
172
|
+
async def interactive_demo():
|
173
|
+
"""Interactive demo with user input."""
|
174
|
+
print("🚀 Interactive Client Demo")
|
175
|
+
print("=" * 50)
|
176
|
+
print("Available authentication methods:")
|
177
|
+
print("1. No authentication")
|
178
|
+
print("2. API Key")
|
179
|
+
print("3. JWT Token")
|
180
|
+
print("4. Certificate")
|
181
|
+
print("5. Basic Authentication")
|
182
|
+
try:
|
183
|
+
choice = input("\nSelect authentication method (1-5): ").strip()
|
184
|
+
auth_methods = {
|
185
|
+
"1": "none",
|
186
|
+
"2": "api_key",
|
187
|
+
"3": "jwt",
|
188
|
+
"4": "certificate",
|
189
|
+
"5": "basic"
|
190
|
+
}
|
191
|
+
if choice not in auth_methods:
|
192
|
+
print("❌ Invalid choice")
|
193
|
+
return
|
194
|
+
auth_method = auth_methods[choice]
|
195
|
+
# Get server URL
|
196
|
+
server_url = input("Enter server URL (default: http://localhost:8000): ").strip()
|
197
|
+
if not server_url:
|
198
|
+
server_url = "http://localhost:8000"
|
199
|
+
# Create configuration based on choice
|
200
|
+
config_kwargs = {}
|
201
|
+
if auth_method == "api_key":
|
202
|
+
api_key = input("Enter API key: ").strip()
|
203
|
+
if api_key:
|
204
|
+
config_kwargs["api_key"] = api_key
|
205
|
+
elif auth_method == "jwt":
|
206
|
+
username = input("Enter username: ").strip()
|
207
|
+
password = input("Enter password: ").strip()
|
208
|
+
secret = input("Enter JWT secret: ").strip()
|
209
|
+
if username and password and secret:
|
210
|
+
config_kwargs.update({
|
211
|
+
"username": username,
|
212
|
+
"password": password,
|
213
|
+
"secret": secret
|
214
|
+
})
|
215
|
+
elif auth_method == "certificate":
|
216
|
+
cert_file = input("Enter certificate file path: ").strip()
|
217
|
+
key_file = input("Enter key file path: ").strip()
|
218
|
+
ca_cert_file = input("Enter CA certificate file path: ").strip()
|
219
|
+
if cert_file and key_file:
|
220
|
+
config_kwargs.update({
|
221
|
+
"cert_file": cert_file,
|
222
|
+
"key_file": key_file,
|
223
|
+
"ca_cert_file": ca_cert_file
|
224
|
+
})
|
225
|
+
elif auth_method == "basic":
|
226
|
+
username = input("Enter username: ").strip()
|
227
|
+
password = input("Enter password: ").strip()
|
228
|
+
if username and password:
|
229
|
+
config_kwargs.update({
|
230
|
+
"username": username,
|
231
|
+
"password": password
|
232
|
+
})
|
233
|
+
# Create configuration
|
234
|
+
config = create_client_config(server_url, auth_method, **config_kwargs)
|
235
|
+
print(f"\nConfiguration created for {auth_method} authentication")
|
236
|
+
print(f"Server URL: {server_url}")
|
237
|
+
# Test connection
|
238
|
+
async with UniversalClient(config) as client:
|
239
|
+
success = await client.test_connection()
|
240
|
+
if success:
|
241
|
+
print("✅ Connection successful!")
|
242
|
+
await demo_api_calls(client)
|
243
|
+
else:
|
244
|
+
print("❌ Connection failed")
|
245
|
+
except KeyboardInterrupt:
|
246
|
+
print("\n\nDemo interrupted by user")
|
247
|
+
except Exception as e:
|
248
|
+
print(f"❌ Interactive demo failed: {e}")
|
249
|
+
def main():
|
250
|
+
"""Main demo function."""
|
251
|
+
if len(sys.argv) > 1:
|
252
|
+
command = sys.argv[1]
|
253
|
+
if command == "config":
|
254
|
+
if len(sys.argv) > 2:
|
255
|
+
config_file = sys.argv[2]
|
256
|
+
asyncio.run(demo_from_config_file(config_file))
|
257
|
+
else:
|
258
|
+
print("Usage: python demo_client.py config <config_file>")
|
259
|
+
elif command == "all":
|
260
|
+
asyncio.run(demo_all_configs())
|
261
|
+
elif command == "programmatic":
|
262
|
+
asyncio.run(demo_programmatic_config())
|
263
|
+
elif command == "interactive":
|
264
|
+
asyncio.run(interactive_demo())
|
265
|
+
else:
|
266
|
+
print("Unknown command. Available commands:")
|
267
|
+
print(" config <file> - Demo with config file")
|
268
|
+
print(" all - Demo all config files")
|
269
|
+
print(" programmatic - Demo programmatic configs")
|
270
|
+
print(" interactive - Interactive demo")
|
271
|
+
else:
|
272
|
+
# Default: demo all configs
|
273
|
+
asyncio.run(demo_all_configs())
|
274
|
+
if __name__ == "__main__":
|
275
|
+
main()
|
@@ -0,0 +1,9 @@
|
|
1
|
+
"""Basic Framework Example.
|
2
|
+
|
3
|
+
This example demonstrates the fundamental usage of MCP Proxy Adapter
|
4
|
+
with minimal configuration and basic command registration.
|
5
|
+
|
6
|
+
Note: This package provides a basic example of MCP Proxy Adapter usage.
|
7
|
+
The main application is created dynamically in main.py and not exported
|
8
|
+
as a global variable for this example.
|
9
|
+
"""
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Basic Framework Example
|
4
|
+
This example demonstrates the basic usage of the MCP Proxy Adapter framework
|
5
|
+
with minimal configuration and built-in commands.
|
6
|
+
Author: Vasiliy Zdanovskiy
|
7
|
+
email: vasilyvz@gmail.com
|
8
|
+
"""
|
9
|
+
import sys
|
10
|
+
import argparse
|
11
|
+
from pathlib import Path
|
12
|
+
# Add the framework to the path
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
14
|
+
from mcp_proxy_adapter.core.app_factory import create_and_run_server
|
15
|
+
def main():
|
16
|
+
"""Main entry point for the basic framework example."""
|
17
|
+
parser = argparse.ArgumentParser(description="Basic Framework Example")
|
18
|
+
parser.add_argument("--config", "-c", required=True, help="Path to configuration file")
|
19
|
+
parser.add_argument("--host", help="Server host")
|
20
|
+
parser.add_argument("--port", type=int, help="Server port")
|
21
|
+
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
22
|
+
args = parser.parse_args()
|
23
|
+
# Override configuration if specified
|
24
|
+
config_overrides = {}
|
25
|
+
if args.host:
|
26
|
+
config_overrides["host"] = args.host
|
27
|
+
if args.port:
|
28
|
+
config_overrides["port"] = args.port
|
29
|
+
if args.debug:
|
30
|
+
config_overrides["debug"] = True
|
31
|
+
print(f"🚀 Starting Basic Framework Example")
|
32
|
+
print(f"📋 Configuration: {args.config}")
|
33
|
+
print("=" * 50)
|
34
|
+
# Use the factory method to create and run the server
|
35
|
+
create_and_run_server(
|
36
|
+
config_path=args.config,
|
37
|
+
title="Basic Framework Example",
|
38
|
+
description="Basic MCP Proxy Adapter with minimal configuration",
|
39
|
+
version="1.0.0",
|
40
|
+
host=config_overrides.get("host", "0.0.0.0"),
|
41
|
+
log_level="debug" if config_overrides.get("debug", False) else "info"
|
42
|
+
)
|
43
|
+
if __name__ == "__main__":
|
44
|
+
main()
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""Full Application Example.
|
2
|
+
|
3
|
+
This example demonstrates advanced usage of MCP Proxy Adapter including:
|
4
|
+
- Proxy registration endpoints
|
5
|
+
- Custom command hooks
|
6
|
+
- Advanced security configurations
|
7
|
+
- Role-based access control
|
8
|
+
"""
|
9
|
+
|
10
|
+
from .main import get_app
|
11
|
+
app = get_app()
|
12
|
+
from .proxy_endpoints import router as proxy_router
|
@@ -0,0 +1,80 @@
|
|
1
|
+
"""
|
2
|
+
Custom Echo Command
|
3
|
+
This module demonstrates a custom command implementation for the full application example.
|
4
|
+
Author: Vasiliy Zdanovskiy
|
5
|
+
email: vasilyvz@gmail.com
|
6
|
+
"""
|
7
|
+
from typing import Dict, Any, Optional
|
8
|
+
from mcp_proxy_adapter.commands.base import BaseCommand
|
9
|
+
from mcp_proxy_adapter.commands.result import CommandResult
|
10
|
+
class CustomEchoResult(CommandResult):
|
11
|
+
"""Result class for custom echo command."""
|
12
|
+
def __init__(self, message: str, timestamp: str, echo_count: int):
|
13
|
+
self.message = message
|
14
|
+
self.timestamp = timestamp
|
15
|
+
self.echo_count = echo_count
|
16
|
+
def to_dict(self) -> Dict[str, Any]:
|
17
|
+
"""Convert result to dictionary."""
|
18
|
+
return {
|
19
|
+
"message": self.message,
|
20
|
+
"timestamp": self.timestamp,
|
21
|
+
"echo_count": self.echo_count,
|
22
|
+
"command_type": "custom_echo"
|
23
|
+
}
|
24
|
+
def get_schema(self) -> Dict[str, Any]:
|
25
|
+
"""Get result schema."""
|
26
|
+
return {
|
27
|
+
"type": "object",
|
28
|
+
"properties": {
|
29
|
+
"message": {"type": "string", "description": "Echoed message"},
|
30
|
+
"timestamp": {"type": "string", "description": "Timestamp of echo"},
|
31
|
+
"echo_count": {"type": "integer", "description": "Number of echoes"},
|
32
|
+
"command_type": {"type": "string", "description": "Command type"}
|
33
|
+
},
|
34
|
+
"required": ["message", "timestamp", "echo_count", "command_type"]
|
35
|
+
}
|
36
|
+
class CustomEchoCommand(BaseCommand):
|
37
|
+
"""Custom echo command implementation."""
|
38
|
+
def __init__(self):
|
39
|
+
super().__init__()
|
40
|
+
self.echo_count = 0
|
41
|
+
def get_name(self) -> str:
|
42
|
+
"""Get command name."""
|
43
|
+
return "custom_echo"
|
44
|
+
def get_description(self) -> str:
|
45
|
+
"""Get command description."""
|
46
|
+
return "Custom echo command with enhanced features"
|
47
|
+
def get_schema(self) -> Dict[str, Any]:
|
48
|
+
"""Get command schema."""
|
49
|
+
return {
|
50
|
+
"type": "object",
|
51
|
+
"properties": {
|
52
|
+
"message": {
|
53
|
+
"type": "string",
|
54
|
+
"description": "Message to echo",
|
55
|
+
"default": "Hello from custom echo!"
|
56
|
+
},
|
57
|
+
"repeat": {
|
58
|
+
"type": "integer",
|
59
|
+
"description": "Number of times to repeat",
|
60
|
+
"default": 1,
|
61
|
+
"minimum": 1,
|
62
|
+
"maximum": 10
|
63
|
+
}
|
64
|
+
},
|
65
|
+
"required": ["message"]
|
66
|
+
}
|
67
|
+
async def execute(self, params: Dict[str, Any]) -> CustomEchoResult:
|
68
|
+
"""Execute the custom echo command."""
|
69
|
+
message = params.get("message", "Hello from custom echo!")
|
70
|
+
repeat = min(max(params.get("repeat", 1), 1), 10)
|
71
|
+
self.echo_count += 1
|
72
|
+
from datetime import datetime
|
73
|
+
timestamp = datetime.now().isoformat()
|
74
|
+
# Repeat the message
|
75
|
+
echoed_message = " ".join([message] * repeat)
|
76
|
+
return CustomEchoResult(
|
77
|
+
message=echoed_message,
|
78
|
+
timestamp=timestamp,
|
79
|
+
echo_count=self.echo_count
|
80
|
+
)
|