mcp-proxy-adapter 6.4.43__py3-none-any.whl → 6.4.45__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/core/proxy_registration.py +100 -68
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +284 -0
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
- mcp_proxy_adapter/examples/config_builder.py +574 -0
- mcp_proxy_adapter/examples/config_cli.py +283 -0
- mcp_proxy_adapter/examples/create_test_configs.py +169 -266
- mcp_proxy_adapter/examples/generate_certificates_bugfix.py +374 -0
- mcp_proxy_adapter/examples/generate_certificates_cli.py +406 -0
- mcp_proxy_adapter/examples/generate_certificates_fixed.py +313 -0
- mcp_proxy_adapter/examples/generate_certificates_framework.py +366 -0
- mcp_proxy_adapter/examples/generate_certificates_openssl.py +391 -0
- mcp_proxy_adapter/examples/required_certificates.py +210 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +117 -13
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +41 -25
- mcp_proxy_adapter/examples/security_test_client.py +333 -86
- mcp_proxy_adapter/examples/test_config_builder.py +450 -0
- mcp_proxy_adapter/examples/update_config_certificates.py +136 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.4.43.dist-info → mcp_proxy_adapter-6.4.45.dist-info}/METADATA +81 -1
- {mcp_proxy_adapter-6.4.43.dist-info → mcp_proxy_adapter-6.4.45.dist-info}/RECORD +23 -20
- mcp_proxy_adapter-6.4.45.dist-info/entry_points.txt +12 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +0 -661
- mcp_proxy_adapter/examples/generate_certificates.py +0 -192
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +0 -515
- mcp_proxy_adapter/examples/generate_test_configs.py +0 -393
- mcp_proxy_adapter/examples/run_security_tests.py +0 -677
- mcp_proxy_adapter/examples/scripts/config_generator.py +0 -842
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +0 -673
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +0 -515
- mcp_proxy_adapter/examples/test_config_generator.py +0 -102
- mcp_proxy_adapter-6.4.43.dist-info/entry_points.txt +0 -2
- {mcp_proxy_adapter-6.4.43.dist-info → mcp_proxy_adapter-6.4.45.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.4.43.dist-info → mcp_proxy_adapter-6.4.45.dist-info}/top_level.txt +0 -0
@@ -30,14 +30,16 @@ sys.path.insert(0, str(current_dir))
|
|
30
30
|
|
31
31
|
# Import mcp_security_framework components
|
32
32
|
try:
|
33
|
-
from mcp_security_framework import
|
34
|
-
from mcp_security_framework.schemas.config import SSLConfig
|
33
|
+
from mcp_security_framework import SecurityManager, SecurityConfig, AuthConfig, PermissionConfig, SSLConfig
|
34
|
+
from mcp_security_framework.schemas.config import SSLConfig as SSLConfigSchema
|
35
35
|
|
36
36
|
_MCP_SECURITY_AVAILABLE = True
|
37
37
|
print("✅ mcp_security_framework available")
|
38
|
-
except ImportError:
|
39
|
-
|
40
|
-
print("
|
38
|
+
except ImportError as e:
|
39
|
+
print(f"❌ CRITICAL ERROR: mcp_security_framework is required but not available!")
|
40
|
+
print(f"❌ Import error: {e}")
|
41
|
+
print("❌ Please install mcp_security_framework: pip install mcp_security_framework")
|
42
|
+
raise ImportError("mcp_security_framework is required for security tests") from e
|
41
43
|
|
42
44
|
# Import cryptography components
|
43
45
|
try:
|
@@ -75,23 +77,19 @@ class SecurityTestClient:
|
|
75
77
|
self.cert_manager = None
|
76
78
|
self._security_available = _MCP_SECURITY_AVAILABLE
|
77
79
|
self._crypto_available = _CRYPTOGRAPHY_AVAILABLE
|
80
|
+
|
81
|
+
# Authentication configuration
|
82
|
+
self.auth_enabled = False
|
83
|
+
self.auth_methods = []
|
84
|
+
self.api_keys = {}
|
85
|
+
self.roles_file = None
|
78
86
|
|
79
87
|
if self._security_available:
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
key_file=None,
|
86
|
-
ca_cert_file=None,
|
87
|
-
verify_mode="CERT_NONE", # For testing
|
88
|
-
min_tls_version="TLSv1.2",
|
89
|
-
)
|
90
|
-
self.ssl_manager = SSLManager(ssl_config)
|
91
|
-
print("✅ SSL Manager initialized with mcp_security_framework")
|
92
|
-
except Exception as e:
|
93
|
-
print(f"⚠️ Failed to initialize SSL Manager: {e}")
|
94
|
-
self._security_available = False
|
88
|
+
# For testing purposes, we don't initialize SecurityManager
|
89
|
+
# because we're testing server configurations, not the framework itself
|
90
|
+
# SecurityManager will be used only when actually needed by the server
|
91
|
+
self.ssl_manager = None
|
92
|
+
print("✅ mcp_security_framework available for testing")
|
95
93
|
|
96
94
|
if not self._security_available:
|
97
95
|
print("ℹ️ Using standard SSL library for testing")
|
@@ -114,7 +112,7 @@ class SecurityTestClient:
|
|
114
112
|
},
|
115
113
|
"user": {
|
116
114
|
"cert": "certs/user_cert.pem",
|
117
|
-
"key": "
|
115
|
+
"key": "keys/user_key.pem",
|
118
116
|
},
|
119
117
|
"readonly": {
|
120
118
|
"cert": "certs/readonly_cert.pem",
|
@@ -122,52 +120,48 @@ class SecurityTestClient:
|
|
122
120
|
},
|
123
121
|
}
|
124
122
|
|
125
|
-
async def __aenter__(self):
|
126
|
-
"""Async context manager entry."""
|
127
|
-
timeout = ClientTimeout(total=30)
|
128
|
-
# Create SSL context for HTTPS connections
|
129
|
-
ssl_context = self.create_ssl_context()
|
130
|
-
connector = TCPConnector(ssl=ssl_context)
|
131
|
-
self.session = ClientSession(timeout=timeout, connector=connector)
|
132
|
-
return self
|
133
|
-
|
134
123
|
def create_ssl_context_for_mtls(self) -> ssl.SSLContext:
|
135
124
|
"""Create SSL context for mTLS connections."""
|
136
|
-
if
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
)
|
150
|
-
except Exception as e:
|
151
|
-
print(
|
152
|
-
f"⚠️ Failed to create mTLS context with mcp_security_framework: {e}"
|
153
|
-
)
|
154
|
-
print("ℹ️ Falling back to standard SSL")
|
125
|
+
# For mTLS, we need client certificates - check if they exist
|
126
|
+
cert_file = "./certs/user_cert.pem"
|
127
|
+
key_file = "./keys/user_key.pem"
|
128
|
+
ca_cert_file = "./certs/mcp_proxy_adapter_ca_ca.crt"
|
129
|
+
|
130
|
+
# CRITICAL: For mTLS, certificates are REQUIRED
|
131
|
+
if not os.path.exists(cert_file):
|
132
|
+
raise FileNotFoundError(f"CRITICAL ERROR: mTLS client certificate not found: {cert_file}")
|
133
|
+
if not os.path.exists(key_file):
|
134
|
+
raise FileNotFoundError(f"CRITICAL ERROR: mTLS client key not found: {key_file}")
|
135
|
+
|
136
|
+
# For testing, we use standard SSL library
|
137
|
+
# SecurityManager is not initialized for testing purposes
|
155
138
|
|
156
|
-
#
|
139
|
+
# Use standard SSL library for testing
|
157
140
|
ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
158
141
|
# For mTLS testing - client needs to present certificate to server
|
159
142
|
ssl_context.check_hostname = False
|
160
143
|
ssl_context.verify_mode = ssl.CERT_NONE # Don't verify server cert for testing
|
161
|
-
# Load client certificate and key for mTLS
|
162
|
-
cert_file =
|
163
|
-
key_file = "./certs/user_key.pem"
|
164
|
-
ca_cert_file = "./certs/mcp_proxy_adapter_ca_ca.crt"
|
165
|
-
if os.path.exists(cert_file) and os.path.exists(key_file):
|
166
|
-
ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file)
|
144
|
+
# Load client certificate and key for mTLS (we already checked they exist)
|
145
|
+
ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file)
|
167
146
|
if os.path.exists(ca_cert_file):
|
168
147
|
ssl_context.load_verify_locations(cafile=ca_cert_file)
|
169
148
|
return ssl_context
|
170
149
|
|
150
|
+
async def __aenter__(self):
|
151
|
+
"""Async context manager entry."""
|
152
|
+
timeout = ClientTimeout(total=30)
|
153
|
+
# Create SSL context only for HTTPS URLs
|
154
|
+
if self.base_url.startswith('https://'):
|
155
|
+
ssl_context = self.create_ssl_context()
|
156
|
+
connector = TCPConnector(ssl=ssl_context)
|
157
|
+
else:
|
158
|
+
# For HTTP URLs, use default connector without SSL
|
159
|
+
connector = TCPConnector()
|
160
|
+
|
161
|
+
# Create session
|
162
|
+
self.session = ClientSession(timeout=timeout, connector=connector)
|
163
|
+
return self
|
164
|
+
|
171
165
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
172
166
|
"""Async context manager exit."""
|
173
167
|
if self.session:
|
@@ -180,31 +174,18 @@ class SecurityTestClient:
|
|
180
174
|
ca_cert_file: Optional[str] = None,
|
181
175
|
) -> ssl.SSLContext:
|
182
176
|
"""Create SSL context for client."""
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
cert_file if cert_file and os.path.exists(cert_file) else None
|
194
|
-
),
|
195
|
-
client_key_file=(
|
196
|
-
key_file if key_file and os.path.exists(key_file) else None
|
197
|
-
),
|
198
|
-
verify_mode="CERT_NONE", # For testing
|
199
|
-
min_version="TLSv1.2",
|
200
|
-
)
|
201
|
-
except Exception as e:
|
202
|
-
print(
|
203
|
-
f"⚠️ Failed to create SSL context with mcp_security_framework: {e}"
|
204
|
-
)
|
205
|
-
print("ℹ️ Falling back to standard SSL")
|
177
|
+
# If certificates are provided, they must exist
|
178
|
+
if cert_file and not os.path.exists(cert_file):
|
179
|
+
raise FileNotFoundError(f"CRITICAL ERROR: SSL certificate not found: {cert_file}")
|
180
|
+
if key_file and not os.path.exists(key_file):
|
181
|
+
raise FileNotFoundError(f"CRITICAL ERROR: SSL key not found: {key_file}")
|
182
|
+
if ca_cert_file and not os.path.exists(ca_cert_file):
|
183
|
+
raise FileNotFoundError(f"CRITICAL ERROR: SSL CA certificate not found: {ca_cert_file}")
|
184
|
+
|
185
|
+
# For testing, we use standard SSL library
|
186
|
+
# SecurityManager is not initialized for testing purposes
|
206
187
|
|
207
|
-
#
|
188
|
+
# Use standard SSL library for testing
|
208
189
|
ssl_context = ssl.create_default_context()
|
209
190
|
# For testing with self-signed certificates
|
210
191
|
ssl_context.check_hostname = False
|
@@ -396,9 +377,9 @@ class SecurityTestClient:
|
|
396
377
|
async def test_authentication(self) -> TestResult:
|
397
378
|
"""Test authentication."""
|
398
379
|
if "api_key" in self.auth_methods:
|
399
|
-
# Use
|
400
|
-
|
401
|
-
return await self.test_echo_command(self.base_url, "api_key", token=
|
380
|
+
# Use admin API key value, not the key name
|
381
|
+
api_key_value = self.api_keys.get("admin", "admin-secret-key")
|
382
|
+
return await self.test_echo_command(self.base_url, "api_key", token=api_key_value)
|
402
383
|
elif "certificate" in self.auth_methods:
|
403
384
|
# For certificate auth, test with client certificate
|
404
385
|
return await self.test_echo_command(self.base_url, "certificate")
|
@@ -413,9 +394,78 @@ class SecurityTestClient:
|
|
413
394
|
|
414
395
|
async def test_negative_authentication(self) -> TestResult:
|
415
396
|
"""Test negative authentication (should fail)."""
|
416
|
-
|
417
|
-
|
418
|
-
|
397
|
+
start_time = time.time()
|
398
|
+
test_name = "Negative Authentication Test"
|
399
|
+
try:
|
400
|
+
headers = self.create_auth_headers("api_key", token="invalid-token")
|
401
|
+
data = {
|
402
|
+
"jsonrpc": "2.0",
|
403
|
+
"method": "echo",
|
404
|
+
"params": {"message": "Should fail with invalid token"},
|
405
|
+
"id": 1,
|
406
|
+
}
|
407
|
+
async with self.session.post(
|
408
|
+
f"{self.base_url}/cmd", headers=headers, json=data
|
409
|
+
) as response:
|
410
|
+
duration = time.time() - start_time
|
411
|
+
|
412
|
+
# Check if API key authentication is enabled
|
413
|
+
api_key_auth_enabled = self.auth_enabled and "api_key" in self.auth_methods
|
414
|
+
|
415
|
+
if api_key_auth_enabled:
|
416
|
+
# For configurations with API key auth, 401 is expected (success)
|
417
|
+
if response.status == 401:
|
418
|
+
return TestResult(
|
419
|
+
test_name=test_name,
|
420
|
+
server_url=self.base_url,
|
421
|
+
auth_type="api_key",
|
422
|
+
success=True,
|
423
|
+
status_code=response.status,
|
424
|
+
response_data={"expected": "authentication_failure"},
|
425
|
+
duration=duration,
|
426
|
+
)
|
427
|
+
else:
|
428
|
+
return TestResult(
|
429
|
+
test_name=test_name,
|
430
|
+
server_url=self.base_url,
|
431
|
+
auth_type="api_key",
|
432
|
+
success=False,
|
433
|
+
status_code=response.status,
|
434
|
+
error_message=f"Expected 401 Unauthorized, got {response.status}",
|
435
|
+
duration=duration,
|
436
|
+
)
|
437
|
+
else:
|
438
|
+
# For configurations without API key auth, 200 is expected (success)
|
439
|
+
if response.status == 200:
|
440
|
+
return TestResult(
|
441
|
+
test_name=test_name,
|
442
|
+
server_url=self.base_url,
|
443
|
+
auth_type="api_key",
|
444
|
+
success=True,
|
445
|
+
status_code=response.status,
|
446
|
+
response_data={"expected": "no_auth_required"},
|
447
|
+
duration=duration,
|
448
|
+
)
|
449
|
+
else:
|
450
|
+
return TestResult(
|
451
|
+
test_name=test_name,
|
452
|
+
server_url=self.base_url,
|
453
|
+
auth_type="api_key",
|
454
|
+
success=False,
|
455
|
+
status_code=response.status,
|
456
|
+
error_message=f"Expected 200 OK (no auth required), got {response.status}",
|
457
|
+
duration=duration,
|
458
|
+
)
|
459
|
+
except Exception as e:
|
460
|
+
duration = time.time() - start_time
|
461
|
+
return TestResult(
|
462
|
+
test_name=test_name,
|
463
|
+
server_url=self.base_url,
|
464
|
+
auth_type="api_key",
|
465
|
+
success=False,
|
466
|
+
error_message=f"Negative auth test error: {str(e)}",
|
467
|
+
duration=duration,
|
468
|
+
)
|
419
469
|
|
420
470
|
async def test_no_auth_required(self) -> TestResult:
|
421
471
|
"""Test that no authentication is required."""
|
@@ -821,6 +871,203 @@ class SecurityTestClient:
|
|
821
871
|
print(f" - {result.test_name} ({result.server_url})")
|
822
872
|
|
823
873
|
|
874
|
+
async def test_health(self) -> TestResult:
|
875
|
+
"""Test health check endpoint."""
|
876
|
+
return await self.test_health_check(self.base_url, "none")
|
877
|
+
|
878
|
+
async def test_command_execution(self) -> TestResult:
|
879
|
+
"""Test command execution."""
|
880
|
+
if self.auth_enabled and "api_key" in self.auth_methods:
|
881
|
+
# Use admin API key value, not the key name
|
882
|
+
api_key_value = self.api_keys.get("admin", "admin-secret-key")
|
883
|
+
return await self.test_echo_command(self.base_url, "api_key", token=api_key_value)
|
884
|
+
else:
|
885
|
+
return await self.test_echo_command(self.base_url, "none")
|
886
|
+
|
887
|
+
async def test_authentication(self) -> TestResult:
|
888
|
+
"""Test authentication."""
|
889
|
+
if "api_key" in self.auth_methods:
|
890
|
+
# Use admin API key value, not the key name
|
891
|
+
api_key_value = self.api_keys.get("admin", "admin-secret-key")
|
892
|
+
return await self.test_echo_command(self.base_url, "api_key", token=api_key_value)
|
893
|
+
elif "certificate" in self.auth_methods:
|
894
|
+
# For certificate auth, test with client certificate
|
895
|
+
return await self.test_echo_command(self.base_url, "certificate")
|
896
|
+
else:
|
897
|
+
return TestResult(
|
898
|
+
test_name="Authentication Test",
|
899
|
+
server_url=self.base_url,
|
900
|
+
auth_type="none",
|
901
|
+
success=False,
|
902
|
+
error_message="No authentication method available",
|
903
|
+
)
|
904
|
+
|
905
|
+
async def test_negative_authentication(self) -> TestResult:
|
906
|
+
"""Test negative authentication (should fail)."""
|
907
|
+
start_time = time.time()
|
908
|
+
test_name = "Negative Authentication Test"
|
909
|
+
try:
|
910
|
+
headers = self.create_auth_headers("api_key", token="invalid-token")
|
911
|
+
data = {
|
912
|
+
"jsonrpc": "2.0",
|
913
|
+
"method": "echo",
|
914
|
+
"params": {"message": "Should fail with invalid token"},
|
915
|
+
"id": 1,
|
916
|
+
}
|
917
|
+
async with self.session.post(
|
918
|
+
f"{self.base_url}/cmd", headers=headers, json=data
|
919
|
+
) as response:
|
920
|
+
duration = time.time() - start_time
|
921
|
+
|
922
|
+
# Check if API key authentication is enabled
|
923
|
+
api_key_auth_enabled = self.auth_enabled and "api_key" in self.auth_methods
|
924
|
+
|
925
|
+
if api_key_auth_enabled:
|
926
|
+
# For configurations with API key auth, 401 is expected (success)
|
927
|
+
if response.status == 401:
|
928
|
+
return TestResult(
|
929
|
+
test_name=test_name,
|
930
|
+
server_url=self.base_url,
|
931
|
+
auth_type="api_key",
|
932
|
+
success=True,
|
933
|
+
status_code=response.status,
|
934
|
+
response_data={"expected": "authentication_failure"},
|
935
|
+
duration=duration,
|
936
|
+
)
|
937
|
+
else:
|
938
|
+
return TestResult(
|
939
|
+
test_name=test_name,
|
940
|
+
server_url=self.base_url,
|
941
|
+
auth_type="api_key",
|
942
|
+
success=False,
|
943
|
+
status_code=response.status,
|
944
|
+
error_message=f"Expected 401 Unauthorized, got {response.status}",
|
945
|
+
duration=duration,
|
946
|
+
)
|
947
|
+
else:
|
948
|
+
# For configurations without API key auth, 200 is expected (success)
|
949
|
+
if response.status == 200:
|
950
|
+
return TestResult(
|
951
|
+
test_name=test_name,
|
952
|
+
server_url=self.base_url,
|
953
|
+
auth_type="api_key",
|
954
|
+
success=True,
|
955
|
+
status_code=response.status,
|
956
|
+
response_data={"expected": "no_auth_required"},
|
957
|
+
duration=duration,
|
958
|
+
)
|
959
|
+
else:
|
960
|
+
return TestResult(
|
961
|
+
test_name=test_name,
|
962
|
+
server_url=self.base_url,
|
963
|
+
auth_type="api_key",
|
964
|
+
success=False,
|
965
|
+
status_code=response.status,
|
966
|
+
error_message=f"Expected 200 OK (no auth required), got {response.status}",
|
967
|
+
duration=duration,
|
968
|
+
)
|
969
|
+
except Exception as e:
|
970
|
+
duration = time.time() - start_time
|
971
|
+
return TestResult(
|
972
|
+
test_name=test_name,
|
973
|
+
server_url=self.base_url,
|
974
|
+
auth_type="api_key",
|
975
|
+
success=False,
|
976
|
+
error_message=f"Negative auth test error: {str(e)}",
|
977
|
+
duration=duration,
|
978
|
+
)
|
979
|
+
|
980
|
+
async def test_no_auth_required(self) -> TestResult:
|
981
|
+
"""Test that no authentication is required."""
|
982
|
+
return await self.test_echo_command(self.base_url, "none")
|
983
|
+
|
984
|
+
async def test_role_based_access(self, server_url: str, auth_type: str, role: str = "admin") -> TestResult:
|
985
|
+
"""Test role-based access control."""
|
986
|
+
if not self.roles_file:
|
987
|
+
return TestResult(
|
988
|
+
test_name="Role-Based Access Test",
|
989
|
+
server_url=server_url,
|
990
|
+
auth_type=auth_type,
|
991
|
+
success=False,
|
992
|
+
error_message="Role-based access error: role is required for role-based access test",
|
993
|
+
)
|
994
|
+
|
995
|
+
# Use admin role for testing
|
996
|
+
if auth_type == "api_key":
|
997
|
+
api_key_value = self.api_keys.get("admin", "admin-secret-key")
|
998
|
+
return await self.test_echo_command(server_url, auth_type, token=api_key_value, role=role)
|
999
|
+
else:
|
1000
|
+
return await self.test_echo_command(server_url, auth_type, role=role)
|
1001
|
+
|
1002
|
+
async def test_role_permissions(self, server_url: str, auth_type: str, role: str = "admin", action: str = "read") -> TestResult:
|
1003
|
+
"""Test role permissions."""
|
1004
|
+
if not self.roles_file:
|
1005
|
+
return TestResult(
|
1006
|
+
test_name="Role Permissions Test",
|
1007
|
+
server_url=server_url,
|
1008
|
+
auth_type=auth_type,
|
1009
|
+
success=False,
|
1010
|
+
error_message="Role permissions test error: role is required for role permissions test",
|
1011
|
+
)
|
1012
|
+
|
1013
|
+
# Test with admin role
|
1014
|
+
if auth_type == "api_key":
|
1015
|
+
api_key_value = self.api_keys.get("admin", "admin-secret-key")
|
1016
|
+
return await self.test_echo_command(server_url, auth_type, token=api_key_value, role=role)
|
1017
|
+
else:
|
1018
|
+
return await self.test_echo_command(server_url, auth_type, role=role)
|
1019
|
+
|
1020
|
+
async def test_multiple_roles(self, server_url: str, auth_type: str) -> TestResult:
|
1021
|
+
"""Test multiple roles."""
|
1022
|
+
# Test with readonly role (should have read access)
|
1023
|
+
if auth_type == "api_key":
|
1024
|
+
api_key_value = self.api_keys.get("readonly", "readonly-token-123")
|
1025
|
+
result = await self.test_echo_command(server_url, auth_type, token=api_key_value, role="readonly")
|
1026
|
+
if result.success:
|
1027
|
+
return TestResult(
|
1028
|
+
test_name="Multiple Roles Test",
|
1029
|
+
server_url=server_url,
|
1030
|
+
auth_type=auth_type,
|
1031
|
+
success=True,
|
1032
|
+
response_data={"message": "Readonly role correctly has read access"},
|
1033
|
+
)
|
1034
|
+
else:
|
1035
|
+
return TestResult(
|
1036
|
+
test_name="Multiple Roles Test",
|
1037
|
+
server_url=server_url,
|
1038
|
+
auth_type=auth_type,
|
1039
|
+
success=False,
|
1040
|
+
error_message="Readonly role incorrectly denied read access",
|
1041
|
+
)
|
1042
|
+
elif auth_type == "certificate":
|
1043
|
+
# For certificate auth, test with user certificate (should have read access)
|
1044
|
+
result = await self.test_echo_command(server_url, auth_type, role="user")
|
1045
|
+
if result.success:
|
1046
|
+
return TestResult(
|
1047
|
+
test_name="Multiple Roles Test",
|
1048
|
+
server_url=server_url,
|
1049
|
+
auth_type=auth_type,
|
1050
|
+
success=True,
|
1051
|
+
response_data={"message": "User certificate correctly has read access"},
|
1052
|
+
)
|
1053
|
+
else:
|
1054
|
+
return TestResult(
|
1055
|
+
test_name="Multiple Roles Test",
|
1056
|
+
server_url=server_url,
|
1057
|
+
auth_type=auth_type,
|
1058
|
+
success=False,
|
1059
|
+
error_message="User certificate incorrectly denied read access",
|
1060
|
+
)
|
1061
|
+
else:
|
1062
|
+
return TestResult(
|
1063
|
+
test_name="Multiple Roles Test",
|
1064
|
+
server_url=server_url,
|
1065
|
+
auth_type=auth_type,
|
1066
|
+
success=False,
|
1067
|
+
error_message="Multiple roles test not implemented for this auth type",
|
1068
|
+
)
|
1069
|
+
|
1070
|
+
|
824
1071
|
async def main():
|
825
1072
|
"""Main function."""
|
826
1073
|
import argparse
|