mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.29__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.
Potentially problematic release.
This version of mcp-proxy-adapter might be problematic. Click here for more details.
- mcp_proxy_adapter/__init__.py +10 -0
- mcp_proxy_adapter/__main__.py +8 -21
- mcp_proxy_adapter/api/app.py +10 -913
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +243 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_manager.py +166 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +78 -199
- mcp_proxy_adapter/api/middleware/__init__.py +1 -44
- mcp_proxy_adapter/api/middleware/base.py +0 -42
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
- mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
- mcp_proxy_adapter/api/middleware/factory.py +0 -94
- mcp_proxy_adapter/api/middleware/logging.py +0 -112
- mcp_proxy_adapter/api/middleware/performance.py +0 -35
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
- mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
- mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +0 -61
- mcp_proxy_adapter/api/tool_integration.py +0 -117
- mcp_proxy_adapter/api/tools.py +0 -46
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +128 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +324 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
- mcp_proxy_adapter/client/proxy.py +45 -0
- mcp_proxy_adapter/commands/__init__.py +44 -28
- mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
- mcp_proxy_adapter/commands/base.py +19 -43
- mcp_proxy_adapter/commands/builtin_commands.py +0 -75
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +58 -928
- mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
- mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
- mcp_proxy_adapter/commands/command_registry.py +172 -904
- mcp_proxy_adapter/commands/config_command.py +0 -28
- mcp_proxy_adapter/commands/dependency_container.py +1 -70
- mcp_proxy_adapter/commands/dependency_manager.py +0 -128
- mcp_proxy_adapter/commands/echo_command.py +0 -34
- mcp_proxy_adapter/commands/health_command.py +0 -3
- mcp_proxy_adapter/commands/help_command.py +0 -159
- mcp_proxy_adapter/commands/hooks.py +0 -137
- mcp_proxy_adapter/commands/key_management_command.py +0 -25
- mcp_proxy_adapter/commands/load_command.py +7 -78
- mcp_proxy_adapter/commands/plugins_command.py +0 -16
- mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
- mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +0 -43
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +0 -80
- mcp_proxy_adapter/commands/result.py +25 -77
- mcp_proxy_adapter/commands/role_test_command.py +0 -44
- mcp_proxy_adapter/commands/roles_management_command.py +0 -199
- mcp_proxy_adapter/commands/security_command.py +0 -30
- mcp_proxy_adapter/commands/settings_command.py +0 -68
- mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
- mcp_proxy_adapter/commands/token_management_command.py +0 -1
- mcp_proxy_adapter/commands/transport_management_command.py +0 -20
- mcp_proxy_adapter/commands/unload_command.py +0 -71
- mcp_proxy_adapter/config.py +15 -626
- mcp_proxy_adapter/core/__init__.py +5 -39
- mcp_proxy_adapter/core/app_factory.py +14 -36
- mcp_proxy_adapter/core/app_runner.py +0 -27
- mcp_proxy_adapter/core/auth_validator.py +1 -93
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
- mcp_proxy_adapter/core/certificate_utils.py +64 -903
- mcp_proxy_adapter/core/client.py +0 -6
- mcp_proxy_adapter/core/client_manager.py +0 -19
- mcp_proxy_adapter/core/client_security.py +0 -2
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +195 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +112 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
- mcp_proxy_adapter/core/config_converter.py +0 -186
- mcp_proxy_adapter/core/config_validator.py +96 -1238
- mcp_proxy_adapter/core/errors.py +7 -42
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +2 -22
- mcp_proxy_adapter/core/mtls_asgi.py +0 -20
- mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
- mcp_proxy_adapter/core/mtls_proxy.py +0 -80
- mcp_proxy_adapter/core/mtls_server.py +3 -173
- mcp_proxy_adapter/core/protocol_manager.py +1 -191
- mcp_proxy_adapter/core/proxy/__init__.py +22 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +0 -1
- mcp_proxy_adapter/core/proxy_registration.py +36 -913
- mcp_proxy_adapter/core/role_utils.py +0 -308
- mcp_proxy_adapter/core/security_adapter.py +1 -36
- mcp_proxy_adapter/core/security_factory.py +1 -150
- mcp_proxy_adapter/core/security_integration.py +0 -33
- mcp_proxy_adapter/core/server_adapter.py +1 -40
- mcp_proxy_adapter/core/server_engine.py +2 -173
- mcp_proxy_adapter/core/settings.py +0 -127
- mcp_proxy_adapter/core/signal_handler.py +0 -65
- mcp_proxy_adapter/core/ssl_utils.py +19 -137
- mcp_proxy_adapter/core/transport_manager.py +0 -151
- mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
- mcp_proxy_adapter/core/utils.py +1 -182
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +211 -0
- mcp_proxy_adapter/core/validation/file_validator.py +73 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
- mcp_proxy_adapter/core/validation/security_validator.py +58 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +33 -652
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
- mcp_proxy_adapter/examples/check_config.py +0 -2
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/config_builder.py +13 -2
- mcp_proxy_adapter/examples/config_cli.py +0 -1
- mcp_proxy_adapter/examples/create_test_configs.py +0 -46
- mcp_proxy_adapter/examples/debug_request_state.py +0 -1
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
- mcp_proxy_adapter/examples/full_application/main.py +186 -150
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
- mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
- mcp_proxy_adapter/examples/generate_config.py +65 -11
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +0 -2
- mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
- mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +24 -1075
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
- mcp_proxy_adapter/examples/test_config.py +0 -3
- mcp_proxy_adapter/examples/test_config_builder.py +25 -405
- mcp_proxy_adapter/examples/test_examples.py +0 -1
- mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
- mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
- mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
- mcp_proxy_adapter/examples/universal_client.py +0 -6
- mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +70 -62
- mcp_proxy_adapter/openapi.py +0 -22
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/METADATA +2 -1
- mcp_proxy_adapter-6.9.29.dist-info/RECORD +235 -0
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/entry_points.txt +1 -1
- mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/top_level.txt +0 -0
|
@@ -9,8 +9,6 @@ Version: 1.0.0
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
|
-
from typing import List, Optional, Set
|
|
13
|
-
from cryptography import x509
|
|
14
12
|
|
|
15
13
|
|
|
16
14
|
class RoleUtils:
|
|
@@ -24,130 +22,14 @@ class RoleUtils:
|
|
|
24
22
|
ROLE_EXTENSION_OID = "1.3.6.1.4.1.99999.1"
|
|
25
23
|
|
|
26
24
|
@staticmethod
|
|
27
|
-
def extract_roles_from_certificate(cert_path: str) -> List[str]:
|
|
28
|
-
"""
|
|
29
|
-
Extract roles from certificate file.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
cert_path: Path to certificate file
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
List of roles extracted from certificate
|
|
36
|
-
"""
|
|
37
|
-
try:
|
|
38
|
-
with open(cert_path, "rb") as f:
|
|
39
|
-
cert_data = f.read()
|
|
40
|
-
|
|
41
|
-
cert = x509.load_pem_x509_certificate(cert_data)
|
|
42
|
-
|
|
43
|
-
# Extract roles from custom extension
|
|
44
|
-
for extension in cert.extensions:
|
|
45
|
-
if extension.oid.dotted_string == RoleUtils.ROLE_EXTENSION_OID:
|
|
46
|
-
roles_data = extension.value.value.decode("utf-8")
|
|
47
|
-
return [
|
|
48
|
-
role.strip() for role in roles_data.split(",") if role.strip()
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
return []
|
|
52
|
-
|
|
53
|
-
except Exception as e:
|
|
54
|
-
logging.getLogger(__name__).error(
|
|
55
|
-
f"Failed to extract roles from certificate {cert_path}: {e}"
|
|
56
|
-
)
|
|
57
|
-
return []
|
|
58
25
|
|
|
59
26
|
@staticmethod
|
|
60
|
-
def extract_roles_from_certificate_object(cert: x509.Certificate) -> List[str]:
|
|
61
|
-
"""
|
|
62
|
-
Extract roles from certificate object.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
cert: Certificate object
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
List of roles extracted from certificate
|
|
69
|
-
"""
|
|
70
|
-
try:
|
|
71
|
-
# Extract roles from custom extension
|
|
72
|
-
for extension in cert.extensions:
|
|
73
|
-
if extension.oid.dotted_string == RoleUtils.ROLE_EXTENSION_OID:
|
|
74
|
-
roles_data = extension.value.value.decode("utf-8")
|
|
75
|
-
return [
|
|
76
|
-
role.strip() for role in roles_data.split(",") if role.strip()
|
|
77
|
-
]
|
|
78
|
-
|
|
79
|
-
return []
|
|
80
|
-
|
|
81
|
-
except Exception as e:
|
|
82
|
-
logging.getLogger(__name__).error(
|
|
83
|
-
f"Failed to extract roles from certificate object: {e}"
|
|
84
|
-
)
|
|
85
|
-
return []
|
|
86
27
|
|
|
87
28
|
@staticmethod
|
|
88
|
-
def compare_roles(role1: str, role2: str) -> bool:
|
|
89
|
-
"""
|
|
90
|
-
Compare two roles (case-insensitive).
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
role1: First role to compare
|
|
94
|
-
role2: Second role to compare
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
True if roles are equal (case-insensitive), False otherwise
|
|
98
|
-
"""
|
|
99
|
-
if not role1 or not role2:
|
|
100
|
-
return False
|
|
101
|
-
|
|
102
|
-
return role1.lower().strip() == role2.lower().strip()
|
|
103
29
|
|
|
104
30
|
@staticmethod
|
|
105
|
-
def compare_role_lists(roles1: List[str], roles2: List[str]) -> bool:
|
|
106
|
-
"""
|
|
107
|
-
Compare two lists of roles (case-insensitive).
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
roles1: First list of roles
|
|
111
|
-
roles2: Second list of roles
|
|
112
|
-
|
|
113
|
-
Returns:
|
|
114
|
-
True if role lists are equal (case-insensitive), False otherwise
|
|
115
|
-
"""
|
|
116
|
-
if not roles1 and not roles2:
|
|
117
|
-
return True
|
|
118
|
-
|
|
119
|
-
if not roles1 or not roles2:
|
|
120
|
-
return False
|
|
121
|
-
|
|
122
|
-
# Normalize and sort both lists
|
|
123
|
-
normalized_roles1 = sorted(
|
|
124
|
-
[role.lower().strip() for role in roles1 if role.strip()]
|
|
125
|
-
)
|
|
126
|
-
normalized_roles2 = sorted(
|
|
127
|
-
[role.lower().strip() for role in roles2 if role.strip()]
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
return normalized_roles1 == normalized_roles2
|
|
131
31
|
|
|
132
32
|
@staticmethod
|
|
133
|
-
def validate_roles(roles: List[str]) -> bool:
|
|
134
|
-
"""
|
|
135
|
-
Validate list of roles.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
roles: List of roles to validate
|
|
139
|
-
|
|
140
|
-
Returns:
|
|
141
|
-
True if roles are valid, False otherwise
|
|
142
|
-
"""
|
|
143
|
-
if not isinstance(roles, list):
|
|
144
|
-
return False
|
|
145
|
-
|
|
146
|
-
for role in roles:
|
|
147
|
-
if not RoleUtils.validate_single_role(role):
|
|
148
|
-
return False
|
|
149
|
-
|
|
150
|
-
return True
|
|
151
33
|
|
|
152
34
|
@staticmethod
|
|
153
35
|
def validate_single_role(role: str) -> bool:
|
|
@@ -230,211 +112,21 @@ class RoleUtils:
|
|
|
230
112
|
return normalized
|
|
231
113
|
|
|
232
114
|
@staticmethod
|
|
233
|
-
def has_role(user_roles: List[str], required_role: str) -> bool:
|
|
234
|
-
"""
|
|
235
|
-
Check if user has required role.
|
|
236
|
-
|
|
237
|
-
Args:
|
|
238
|
-
user_roles: List of user roles
|
|
239
|
-
required_role: Required role to check
|
|
240
|
-
|
|
241
|
-
Returns:
|
|
242
|
-
True if user has required role, False otherwise
|
|
243
|
-
"""
|
|
244
|
-
if not user_roles or not required_role:
|
|
245
|
-
return False
|
|
246
|
-
|
|
247
|
-
normalized_required = RoleUtils.normalize_role(required_role)
|
|
248
|
-
normalized_user_roles = RoleUtils.normalize_roles(user_roles)
|
|
249
|
-
|
|
250
|
-
return normalized_required in normalized_user_roles
|
|
251
115
|
|
|
252
116
|
@staticmethod
|
|
253
|
-
def has_any_role(user_roles: List[str], required_roles: List[str]) -> bool:
|
|
254
|
-
"""
|
|
255
|
-
Check if user has any of the required roles.
|
|
256
|
-
|
|
257
|
-
Args:
|
|
258
|
-
user_roles: List of user roles
|
|
259
|
-
required_roles: List of required roles to check
|
|
260
|
-
|
|
261
|
-
Returns:
|
|
262
|
-
True if user has any required role, False otherwise
|
|
263
|
-
"""
|
|
264
|
-
if not user_roles or not required_roles:
|
|
265
|
-
return False
|
|
266
|
-
|
|
267
|
-
normalized_user_roles = RoleUtils.normalize_roles(user_roles)
|
|
268
|
-
normalized_required_roles = RoleUtils.normalize_roles(required_roles)
|
|
269
|
-
|
|
270
|
-
return any(role in normalized_user_roles for role in normalized_required_roles)
|
|
271
117
|
|
|
272
118
|
@staticmethod
|
|
273
|
-
def has_all_roles(user_roles: List[str], required_roles: List[str]) -> bool:
|
|
274
|
-
"""
|
|
275
|
-
Check if user has all required roles.
|
|
276
|
-
|
|
277
|
-
Args:
|
|
278
|
-
user_roles: List of user roles
|
|
279
|
-
required_roles: List of required roles to check
|
|
280
|
-
|
|
281
|
-
Returns:
|
|
282
|
-
True if user has all required roles, False otherwise
|
|
283
|
-
"""
|
|
284
|
-
if not user_roles or not required_roles:
|
|
285
|
-
return False
|
|
286
|
-
|
|
287
|
-
normalized_user_roles = RoleUtils.normalize_roles(user_roles)
|
|
288
|
-
normalized_required_roles = RoleUtils.normalize_roles(required_roles)
|
|
289
|
-
|
|
290
|
-
return all(role in normalized_user_roles for role in normalized_required_roles)
|
|
291
119
|
|
|
292
120
|
@staticmethod
|
|
293
|
-
def get_common_roles(roles1: List[str], roles2: List[str]) -> List[str]:
|
|
294
|
-
"""
|
|
295
|
-
Get common roles between two role lists.
|
|
296
|
-
|
|
297
|
-
Args:
|
|
298
|
-
roles1: First list of roles
|
|
299
|
-
roles2: Second list of roles
|
|
300
|
-
|
|
301
|
-
Returns:
|
|
302
|
-
List of common roles
|
|
303
|
-
"""
|
|
304
|
-
if not roles1 or not roles2:
|
|
305
|
-
return []
|
|
306
|
-
|
|
307
|
-
normalized_roles1 = set(RoleUtils.normalize_roles(roles1))
|
|
308
|
-
normalized_roles2 = set(RoleUtils.normalize_roles(roles2))
|
|
309
|
-
|
|
310
|
-
return list(normalized_roles1.intersection(normalized_roles2))
|
|
311
121
|
|
|
312
122
|
@staticmethod
|
|
313
|
-
def merge_roles(roles1: List[str], roles2: List[str]) -> List[str]:
|
|
314
|
-
"""
|
|
315
|
-
Merge two role lists (remove duplicates).
|
|
316
|
-
|
|
317
|
-
Args:
|
|
318
|
-
roles1: First list of roles
|
|
319
|
-
roles2: Second list of roles
|
|
320
|
-
|
|
321
|
-
Returns:
|
|
322
|
-
Merged list of roles without duplicates
|
|
323
|
-
"""
|
|
324
|
-
all_roles = (roles1 or []) + (roles2 or [])
|
|
325
|
-
return RoleUtils.normalize_roles(all_roles)
|
|
326
123
|
|
|
327
124
|
@staticmethod
|
|
328
|
-
def remove_roles(roles: List[str], roles_to_remove: List[str]) -> List[str]:
|
|
329
|
-
"""
|
|
330
|
-
Remove specified roles from role list.
|
|
331
|
-
|
|
332
|
-
Args:
|
|
333
|
-
roles: List of roles
|
|
334
|
-
roles_to_remove: List of roles to remove
|
|
335
|
-
|
|
336
|
-
Returns:
|
|
337
|
-
List of roles with specified roles removed
|
|
338
|
-
"""
|
|
339
|
-
if not roles:
|
|
340
|
-
return []
|
|
341
|
-
|
|
342
|
-
if not roles_to_remove:
|
|
343
|
-
return roles.copy()
|
|
344
|
-
|
|
345
|
-
normalized_roles = RoleUtils.normalize_roles(roles)
|
|
346
|
-
normalized_to_remove = set(RoleUtils.normalize_roles(roles_to_remove))
|
|
347
|
-
|
|
348
|
-
return [role for role in normalized_roles if role not in normalized_to_remove]
|
|
349
125
|
|
|
350
126
|
@staticmethod
|
|
351
|
-
def is_admin_role(role: str) -> bool:
|
|
352
|
-
"""
|
|
353
|
-
Check if role is an admin role.
|
|
354
|
-
|
|
355
|
-
Args:
|
|
356
|
-
role: Role to check
|
|
357
|
-
|
|
358
|
-
Returns:
|
|
359
|
-
True if role is admin, False otherwise
|
|
360
|
-
"""
|
|
361
|
-
if not role:
|
|
362
|
-
return False
|
|
363
|
-
|
|
364
|
-
admin_roles = {"admin", "administrator", "root", "superuser", "super-admin"}
|
|
365
|
-
normalized_role = RoleUtils.normalize_role(role)
|
|
366
|
-
|
|
367
|
-
return normalized_role in admin_roles
|
|
368
127
|
|
|
369
128
|
@staticmethod
|
|
370
|
-
def is_system_role(role: str) -> bool:
|
|
371
|
-
"""
|
|
372
|
-
Check if role is a system role.
|
|
373
|
-
|
|
374
|
-
Args:
|
|
375
|
-
role: Role to check
|
|
376
|
-
|
|
377
|
-
Returns:
|
|
378
|
-
True if role is system role, False otherwise
|
|
379
|
-
"""
|
|
380
|
-
if not role:
|
|
381
|
-
return False
|
|
382
|
-
|
|
383
|
-
system_roles = {"system", "service", "daemon", "internal", "system-user"}
|
|
384
|
-
normalized_role = RoleUtils.normalize_role(role)
|
|
385
|
-
|
|
386
|
-
return normalized_role in system_roles
|
|
387
129
|
|
|
388
130
|
@staticmethod
|
|
389
|
-
def get_role_hierarchy(role: str) -> List[str]:
|
|
390
|
-
"""
|
|
391
|
-
Get role hierarchy (parent roles).
|
|
392
|
-
|
|
393
|
-
Args:
|
|
394
|
-
role: Role to get hierarchy for
|
|
395
|
-
|
|
396
|
-
Returns:
|
|
397
|
-
List of parent roles in hierarchy
|
|
398
|
-
"""
|
|
399
|
-
if not role:
|
|
400
|
-
return []
|
|
401
|
-
|
|
402
|
-
normalized_role = RoleUtils.normalize_role(role)
|
|
403
|
-
|
|
404
|
-
# Define role hierarchy
|
|
405
|
-
hierarchy = {
|
|
406
|
-
"super-admin": ["admin", "user"],
|
|
407
|
-
"admin": ["user"],
|
|
408
|
-
"moderator": ["user"],
|
|
409
|
-
"user": [],
|
|
410
|
-
"guest": [],
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return hierarchy.get(normalized_role, [])
|
|
414
131
|
|
|
415
132
|
@staticmethod
|
|
416
|
-
def get_role_permissions(role: str) -> List[str]:
|
|
417
|
-
"""
|
|
418
|
-
Get permissions for a role.
|
|
419
|
-
|
|
420
|
-
Args:
|
|
421
|
-
role: Role to get permissions for
|
|
422
|
-
|
|
423
|
-
Returns:
|
|
424
|
-
List of permissions for the role
|
|
425
|
-
"""
|
|
426
|
-
if not role:
|
|
427
|
-
return []
|
|
428
|
-
|
|
429
|
-
normalized_role = RoleUtils.normalize_role(role)
|
|
430
|
-
|
|
431
|
-
# Define role permissions
|
|
432
|
-
permissions = {
|
|
433
|
-
"super-admin": ["read", "write", "delete", "admin", "system"],
|
|
434
|
-
"admin": ["read", "write", "delete", "admin"],
|
|
435
|
-
"moderator": ["read", "write", "moderate"],
|
|
436
|
-
"user": ["read", "write"],
|
|
437
|
-
"guest": ["read"],
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return permissions.get(normalized_role, [])
|
|
@@ -7,8 +7,7 @@ handling configuration conversion and request validation.
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import logging
|
|
10
|
-
from typing import Dict, Any,
|
|
11
|
-
from pathlib import Path
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
12
11
|
|
|
13
12
|
# Import mcp_security_framework components
|
|
14
13
|
try:
|
|
@@ -341,38 +340,4 @@ class SecurityAdapter:
|
|
|
341
340
|
get_global_logger().info(f"Total API keys loaded: {len(api_keys)}")
|
|
342
341
|
return api_keys
|
|
343
342
|
|
|
344
|
-
def create_middleware(self, framework: str = "fastapi"):
|
|
345
|
-
"""
|
|
346
|
-
Create framework-specific middleware.
|
|
347
|
-
|
|
348
|
-
Args:
|
|
349
|
-
framework: Framework type (fastapi, flask, etc.)
|
|
350
|
-
|
|
351
|
-
Returns:
|
|
352
|
-
Middleware instance
|
|
353
|
-
"""
|
|
354
|
-
if not self.security_manager:
|
|
355
|
-
get_global_logger().warning("Cannot create middleware: security framework not available")
|
|
356
|
-
return None
|
|
357
343
|
|
|
358
|
-
try:
|
|
359
|
-
if framework == "fastapi":
|
|
360
|
-
from mcp_security_framework.middleware import (
|
|
361
|
-
create_fastapi_security_middleware,
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
return create_fastapi_security_middleware(self.security_manager)
|
|
365
|
-
else:
|
|
366
|
-
raise ValueError(f"Unsupported framework: {framework}")
|
|
367
|
-
except Exception as e:
|
|
368
|
-
get_global_logger().error(f"Failed to create middleware: {e}")
|
|
369
|
-
return None
|
|
370
|
-
|
|
371
|
-
def is_available(self) -> bool:
|
|
372
|
-
"""
|
|
373
|
-
Check if security framework is available.
|
|
374
|
-
|
|
375
|
-
Returns:
|
|
376
|
-
True if framework is available, False otherwise
|
|
377
|
-
"""
|
|
378
|
-
return SECURITY_FRAMEWORK_AVAILABLE and self.security_manager is not None
|
|
@@ -6,7 +6,7 @@ and middleware components with proper configuration and error handling.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
-
from typing import Dict, Any
|
|
9
|
+
from typing import Dict, Any
|
|
10
10
|
|
|
11
11
|
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
12
12
|
from .security_adapter import SecurityAdapter
|
|
@@ -40,22 +40,6 @@ class SecurityFactory:
|
|
|
40
40
|
raise
|
|
41
41
|
|
|
42
42
|
@staticmethod
|
|
43
|
-
def create_security_manager(config: Dict[str, Any]):
|
|
44
|
-
"""
|
|
45
|
-
Create SecurityManager from configuration.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
config: mcp_proxy_adapter configuration dictionary
|
|
49
|
-
|
|
50
|
-
Returns:
|
|
51
|
-
SecurityManager instance or None if not available
|
|
52
|
-
"""
|
|
53
|
-
try:
|
|
54
|
-
adapter = SecurityFactory.create_security_adapter(config)
|
|
55
|
-
return adapter.security_manager
|
|
56
|
-
except Exception as e:
|
|
57
|
-
get_global_logger().error(f"Failed to create security manager: {e}")
|
|
58
|
-
return None
|
|
59
43
|
|
|
60
44
|
@staticmethod
|
|
61
45
|
def create_middleware(config: Dict[str, Any], framework: str = "fastapi"):
|
|
@@ -85,145 +69,12 @@ class SecurityFactory:
|
|
|
85
69
|
return None
|
|
86
70
|
|
|
87
71
|
@staticmethod
|
|
88
|
-
def create_fastapi_middleware(config: Dict[str, Any]):
|
|
89
|
-
"""
|
|
90
|
-
Create FastAPI-specific security middleware.
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
config: mcp_proxy_adapter configuration dictionary
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
FastAPI middleware instance or None if creation failed
|
|
97
|
-
"""
|
|
98
|
-
return SecurityFactory.create_middleware(config, "fastapi")
|
|
99
72
|
|
|
100
73
|
@staticmethod
|
|
101
|
-
def validate_config(config: Dict[str, Any]) -> bool:
|
|
102
|
-
"""
|
|
103
|
-
Validate security configuration.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
config: Configuration dictionary to validate
|
|
107
|
-
|
|
108
|
-
Returns:
|
|
109
|
-
True if configuration is valid, False otherwise
|
|
110
|
-
"""
|
|
111
|
-
try:
|
|
112
|
-
# Check if security section exists
|
|
113
|
-
security_config = config.get("security", {})
|
|
114
|
-
|
|
115
|
-
# Validate required fields
|
|
116
|
-
if not isinstance(security_config, dict):
|
|
117
|
-
get_global_logger().error("Security configuration must be a dictionary")
|
|
118
|
-
return False
|
|
119
|
-
|
|
120
|
-
# Validate auth configuration
|
|
121
|
-
auth_config = security_config.get("auth", {})
|
|
122
|
-
if not isinstance(auth_config, dict):
|
|
123
|
-
get_global_logger().error("Auth configuration must be a dictionary")
|
|
124
|
-
return False
|
|
125
|
-
|
|
126
|
-
# Validate SSL configuration
|
|
127
|
-
ssl_config = security_config.get("ssl", {})
|
|
128
|
-
if not isinstance(ssl_config, dict):
|
|
129
|
-
get_global_logger().error("SSL configuration must be a dictionary")
|
|
130
|
-
return False
|
|
131
|
-
|
|
132
|
-
# Validate permissions configuration
|
|
133
|
-
permissions_config = security_config.get("permissions", {})
|
|
134
|
-
if not isinstance(permissions_config, dict):
|
|
135
|
-
get_global_logger().error("Permissions configuration must be a dictionary")
|
|
136
|
-
return False
|
|
137
|
-
|
|
138
|
-
# Validate rate limit configuration
|
|
139
|
-
rate_limit_config = security_config.get("rate_limit", {})
|
|
140
|
-
if not isinstance(rate_limit_config, dict):
|
|
141
|
-
get_global_logger().error("Rate limit configuration must be a dictionary")
|
|
142
|
-
return False
|
|
143
|
-
|
|
144
|
-
get_global_logger().info("Security configuration validation passed")
|
|
145
|
-
return True
|
|
146
|
-
|
|
147
|
-
except Exception as e:
|
|
148
|
-
get_global_logger().error(f"Configuration validation failed: {e}")
|
|
149
|
-
return False
|
|
150
74
|
|
|
151
75
|
@staticmethod
|
|
152
|
-
def get_default_config() -> Dict[str, Any]:
|
|
153
|
-
"""
|
|
154
|
-
Get default security configuration.
|
|
155
|
-
|
|
156
|
-
Returns:
|
|
157
|
-
Default security configuration dictionary
|
|
158
|
-
"""
|
|
159
|
-
return {
|
|
160
|
-
"security": {
|
|
161
|
-
"framework": "mcp_security_framework",
|
|
162
|
-
"enabled": True,
|
|
163
|
-
"auth": {
|
|
164
|
-
"enabled": True,
|
|
165
|
-
"methods": ["api_key"],
|
|
166
|
-
"api_keys": {},
|
|
167
|
-
"jwt_secret": "",
|
|
168
|
-
"jwt_algorithm": "HS256",
|
|
169
|
-
},
|
|
170
|
-
"ssl": {
|
|
171
|
-
"enabled": False,
|
|
172
|
-
"cert_file": None,
|
|
173
|
-
"key_file": None,
|
|
174
|
-
"ca_cert": None,
|
|
175
|
-
"min_tls_version": "TLSv1.2",
|
|
176
|
-
"verify_client": False,
|
|
177
|
-
"client_cert_required": False,
|
|
178
|
-
},
|
|
179
|
-
"permissions": {
|
|
180
|
-
"enabled": True,
|
|
181
|
-
"roles_file": "roles.json",
|
|
182
|
-
"default_role": "user",
|
|
183
|
-
"deny_by_default": True,
|
|
184
|
-
},
|
|
185
|
-
"rate_limit": {
|
|
186
|
-
"enabled": True,
|
|
187
|
-
"requests_per_minute": 60,
|
|
188
|
-
"requests_per_hour": 1000,
|
|
189
|
-
"burst_limit": 10,
|
|
190
|
-
"by_ip": True,
|
|
191
|
-
"by_user": True,
|
|
192
|
-
},
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
76
|
|
|
196
77
|
@staticmethod
|
|
197
|
-
def merge_config(
|
|
198
|
-
base_config: Dict[str, Any], security_config: Dict[str, Any]
|
|
199
|
-
) -> Dict[str, Any]:
|
|
200
|
-
"""
|
|
201
|
-
Merge security configuration into base configuration.
|
|
202
|
-
|
|
203
|
-
Args:
|
|
204
|
-
base_config: Base configuration dictionary
|
|
205
|
-
security_config: Security configuration to merge
|
|
206
|
-
|
|
207
|
-
Returns:
|
|
208
|
-
Merged configuration dictionary
|
|
209
|
-
"""
|
|
210
|
-
try:
|
|
211
|
-
# Create a copy of base config
|
|
212
|
-
merged_config = base_config.copy()
|
|
213
|
-
|
|
214
|
-
# Merge security configuration
|
|
215
|
-
if "security" not in merged_config:
|
|
216
|
-
merged_config["security"] = {}
|
|
217
|
-
|
|
218
|
-
# Deep merge security configuration
|
|
219
|
-
SecurityFactory._deep_merge(merged_config["security"], security_config)
|
|
220
|
-
|
|
221
|
-
get_global_logger().info("Security configuration merged successfully")
|
|
222
|
-
return merged_config
|
|
223
|
-
|
|
224
|
-
except Exception as e:
|
|
225
|
-
get_global_logger().error(f"Failed to merge security configuration: {e}")
|
|
226
|
-
return base_config
|
|
227
78
|
|
|
228
79
|
@staticmethod
|
|
229
80
|
def _deep_merge(base_dict: Dict[str, Any], update_dict: Dict[str, Any]) -> None:
|
|
@@ -333,43 +333,10 @@ class SecurityIntegration:
|
|
|
333
333
|
return await self.rate_limiter.get_rate_limit_info(identifier)
|
|
334
334
|
|
|
335
335
|
# Middleware creation - direct use of framework middleware
|
|
336
|
-
def create_fastapi_middleware(self, app) -> FastAPISecurityMiddleware:
|
|
337
|
-
"""Create FastAPI security middleware."""
|
|
338
|
-
return FastAPISecurityMiddleware(app, self.security_config)
|
|
339
336
|
|
|
340
337
|
# Utility methods
|
|
341
|
-
def is_security_enabled(self) -> bool:
|
|
342
|
-
"""Check if security is enabled."""
|
|
343
|
-
return self.security_config.auth.enabled or self.security_config.ssl.enabled
|
|
344
338
|
|
|
345
|
-
def get_public_paths(self) -> List[str]:
|
|
346
|
-
"""Get public paths that bypass authentication."""
|
|
347
|
-
return self.security_config.auth.public_paths
|
|
348
339
|
|
|
349
|
-
def get_security_config(self) -> SecurityConfig:
|
|
350
|
-
"""Get security configuration."""
|
|
351
|
-
return self.security_config
|
|
352
340
|
|
|
353
341
|
|
|
354
342
|
# Factory function for easy integration
|
|
355
|
-
def create_security_integration(config: Dict[str, Any]) -> SecurityIntegration:
|
|
356
|
-
"""
|
|
357
|
-
Create security integration instance.
|
|
358
|
-
|
|
359
|
-
Args:
|
|
360
|
-
config: Configuration dictionary
|
|
361
|
-
|
|
362
|
-
Returns:
|
|
363
|
-
SecurityIntegration instance
|
|
364
|
-
|
|
365
|
-
Raises:
|
|
366
|
-
RuntimeError: If security integration cannot be created
|
|
367
|
-
"""
|
|
368
|
-
try:
|
|
369
|
-
return SecurityIntegration(config)
|
|
370
|
-
except ImportError as e:
|
|
371
|
-
get_global_logger().error(f"mcp_security_framework not available: {e}")
|
|
372
|
-
raise RuntimeError("Security framework is required but not available") from e
|
|
373
|
-
except Exception as e:
|
|
374
|
-
get_global_logger().error(f"Failed to create security integration: {e}")
|
|
375
|
-
raise RuntimeError(f"Security integration failed: {e}") from e
|
|
@@ -10,9 +10,9 @@ email: vasilyvz@gmail.com
|
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
12
|
from typing import Dict, Any, Optional
|
|
13
|
-
from pathlib import Path
|
|
14
13
|
|
|
15
14
|
from .server_engine import ServerEngineFactory, ServerEngine
|
|
15
|
+
from .logging import get_global_logger
|
|
16
16
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
@@ -75,20 +75,6 @@ class ServerConfigAdapter:
|
|
|
75
75
|
return hypercorn_ssl
|
|
76
76
|
|
|
77
77
|
@staticmethod
|
|
78
|
-
def get_optimal_engine_for_config(config: Dict[str, Any]) -> Optional[str]:
|
|
79
|
-
"""
|
|
80
|
-
Determine the optimal server engine for a given configuration.
|
|
81
|
-
|
|
82
|
-
Currently only hypercorn is supported.
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
config: Server configuration
|
|
86
|
-
|
|
87
|
-
Returns:
|
|
88
|
-
Optimal engine name (currently always "hypercorn")
|
|
89
|
-
"""
|
|
90
|
-
# Currently only hypercorn is supported
|
|
91
|
-
return "hypercorn"
|
|
92
78
|
|
|
93
79
|
@staticmethod
|
|
94
80
|
def validate_engine_compatibility(config: Dict[str, Any], engine_name: str) -> bool:
|
|
@@ -262,29 +248,4 @@ class UnifiedServerRunner:
|
|
|
262
248
|
|
|
263
249
|
return engine_config
|
|
264
250
|
|
|
265
|
-
def get_engine_info(self, engine_name: str) -> Dict[str, Any]:
|
|
266
|
-
"""
|
|
267
|
-
Get information about a specific engine.
|
|
268
|
-
|
|
269
|
-
Args:
|
|
270
|
-
engine_name: Name of the engine
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
Engine information dictionary
|
|
274
|
-
"""
|
|
275
|
-
return ServerConfigAdapter.get_engine_capabilities(engine_name)
|
|
276
251
|
|
|
277
|
-
def list_available_engines(self) -> Dict[str, Dict[str, Any]]:
|
|
278
|
-
"""
|
|
279
|
-
List all available engines with their capabilities.
|
|
280
|
-
|
|
281
|
-
Returns:
|
|
282
|
-
Dictionary mapping engine names to their capabilities
|
|
283
|
-
"""
|
|
284
|
-
engines_info = {}
|
|
285
|
-
for name, engine in self.available_engines.items():
|
|
286
|
-
engines_info[name] = {
|
|
287
|
-
"features": engine.get_supported_features(),
|
|
288
|
-
"config_schema": engine.get_config_schema(),
|
|
289
|
-
}
|
|
290
|
-
return engines_info
|