mcp-security-framework 1.1.0__py3-none-any.whl → 1.1.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_security_framework/__init__.py +26 -15
- mcp_security_framework/cli/__init__.py +1 -1
- mcp_security_framework/cli/cert_cli.py +233 -197
- mcp_security_framework/cli/security_cli.py +324 -234
- mcp_security_framework/constants.py +21 -27
- mcp_security_framework/core/auth_manager.py +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- mcp_security_framework/middleware/__init__.py +46 -55
- mcp_security_framework/middleware/auth_middleware.py +62 -63
- mcp_security_framework/middleware/fastapi_auth_middleware.py +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- mcp_security_framework/middleware/flask_middleware.py +183 -157
- mcp_security_framework/middleware/mtls_middleware.py +106 -117
- mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
- mcp_security_framework/middleware/security_middleware.py +109 -124
- mcp_security_framework/schemas/config.py +2 -1
- mcp_security_framework/schemas/models.py +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
- mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
- tests/conftest.py +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -12,57 +12,65 @@ License: MIT
|
|
12
12
|
"""
|
13
13
|
|
14
14
|
import json
|
15
|
-
import tempfile
|
16
15
|
import os
|
17
|
-
|
18
|
-
from typing import Dict, Any
|
16
|
+
import tempfile
|
19
17
|
from datetime import datetime, timedelta, timezone
|
18
|
+
from typing import Any, Dict
|
19
|
+
from unittest.mock import MagicMock, patch
|
20
20
|
|
21
|
-
import pytest
|
22
21
|
import jwt
|
22
|
+
import pytest
|
23
23
|
|
24
24
|
from mcp_security_framework.core.auth_manager import AuthManager
|
25
25
|
from mcp_security_framework.core.permission_manager import PermissionManager
|
26
|
-
from mcp_security_framework.core.security_manager import
|
26
|
+
from mcp_security_framework.core.security_manager import (
|
27
|
+
SecurityManager,
|
28
|
+
SecurityValidationError,
|
29
|
+
)
|
27
30
|
from mcp_security_framework.schemas.config import (
|
28
|
-
|
31
|
+
AuthConfig,
|
32
|
+
CertificateConfig,
|
33
|
+
PermissionConfig,
|
34
|
+
RateLimitConfig,
|
35
|
+
SecurityConfig,
|
36
|
+
SSLConfig,
|
29
37
|
)
|
30
38
|
from mcp_security_framework.schemas.responses import AuthResult, ValidationResult
|
31
39
|
|
32
40
|
|
33
41
|
class TestAuthFlowIntegration:
|
34
42
|
"""Integration tests for complete authentication and authorization flows."""
|
35
|
-
|
43
|
+
|
36
44
|
def setup_method(self):
|
37
45
|
"""Set up test fixtures before each test method."""
|
38
46
|
# Create temporary roles file
|
39
|
-
self.roles_fd, self.roles_path = tempfile.mkstemp(suffix=
|
47
|
+
self.roles_fd, self.roles_path = tempfile.mkstemp(suffix=".json")
|
40
48
|
self.roles_config = {
|
41
49
|
"roles": {
|
42
50
|
"admin": {
|
43
51
|
"permissions": ["read", "write", "delete", "admin"],
|
44
52
|
"description": "Administrator role",
|
45
|
-
"inherits": ["user", "moderator"]
|
53
|
+
"inherits": ["user", "moderator"],
|
46
54
|
},
|
47
55
|
"user": {
|
48
56
|
"permissions": ["read", "write"],
|
49
|
-
"description": "Regular user role"
|
57
|
+
"description": "Regular user role",
|
50
58
|
},
|
51
59
|
"readonly": {
|
52
60
|
"permissions": ["read"],
|
53
|
-
"description": "Read-only user role"
|
61
|
+
"description": "Read-only user role",
|
54
62
|
},
|
55
63
|
"moderator": {
|
56
64
|
"permissions": ["read", "write", "moderate"],
|
57
65
|
"description": "Moderator role",
|
58
|
-
"inherits": ["user"]
|
59
|
-
}
|
66
|
+
"inherits": ["user"],
|
67
|
+
},
|
60
68
|
}
|
61
69
|
}
|
62
|
-
|
63
|
-
with os.fdopen(self.roles_fd,
|
70
|
+
|
71
|
+
with os.fdopen(self.roles_fd, "w") as f:
|
64
72
|
json.dump(self.roles_config, f)
|
65
|
-
|
73
|
+
|
66
74
|
# Create authentication configuration
|
67
75
|
self.auth_config = AuthConfig(
|
68
76
|
enabled=True,
|
@@ -71,432 +79,440 @@ class TestAuthFlowIntegration:
|
|
71
79
|
"admin_key_123456789012345": "admin",
|
72
80
|
"user_key_456789012345678": "user",
|
73
81
|
"readonly_key_789012345678": "readonly",
|
74
|
-
"moderator_key_101234567890": "moderator"
|
82
|
+
"moderator_key_101234567890": "moderator",
|
75
83
|
},
|
76
84
|
user_roles={
|
77
85
|
"admin": ["admin"],
|
78
86
|
"user": ["user"],
|
79
87
|
"readonly": ["readonly"],
|
80
|
-
"moderator": ["moderator"]
|
88
|
+
"moderator": ["moderator"],
|
81
89
|
},
|
82
90
|
jwt_secret="test_jwt_secret_key_for_integration_tests",
|
83
91
|
jwt_algorithm="HS256",
|
84
|
-
jwt_expiry_hours=24
|
92
|
+
jwt_expiry_hours=24,
|
85
93
|
)
|
86
|
-
|
94
|
+
|
87
95
|
# Create permission configuration
|
88
96
|
self.permission_config = PermissionConfig(
|
89
|
-
enabled=True,
|
90
|
-
roles_file=self.roles_path
|
97
|
+
enabled=True, roles_file=self.roles_path
|
91
98
|
)
|
92
|
-
|
99
|
+
|
93
100
|
# Create security configuration
|
94
101
|
self.security_config = SecurityConfig(
|
95
102
|
auth=self.auth_config,
|
96
103
|
permissions=self.permission_config,
|
97
104
|
rate_limit=RateLimitConfig(enabled=True, default_requests_per_minute=100),
|
98
105
|
ssl=SSLConfig(enabled=False),
|
99
|
-
certificates=CertificateConfig(enabled=False)
|
106
|
+
certificates=CertificateConfig(enabled=False),
|
100
107
|
)
|
101
|
-
|
108
|
+
|
102
109
|
# Create security manager
|
103
110
|
self.security_manager = SecurityManager(self.security_config)
|
104
|
-
|
111
|
+
|
105
112
|
def teardown_method(self):
|
106
113
|
"""Clean up after each test method."""
|
107
|
-
if hasattr(self,
|
114
|
+
if hasattr(self, "roles_path") and os.path.exists(self.roles_path):
|
108
115
|
os.unlink(self.roles_path)
|
109
|
-
|
116
|
+
|
110
117
|
def test_complete_api_key_authentication_flow(self):
|
111
118
|
"""Test complete API key authentication flow."""
|
112
119
|
# Test valid API key authentication
|
113
|
-
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
114
|
-
|
120
|
+
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
121
|
+
"admin_key_123456789012345"
|
122
|
+
)
|
123
|
+
|
115
124
|
assert auth_result.is_valid is True
|
116
125
|
assert auth_result.username == "admin"
|
117
126
|
assert "admin" in auth_result.roles
|
118
127
|
assert auth_result.auth_method == "api_key"
|
119
|
-
|
128
|
+
|
120
129
|
# Test invalid API key
|
121
130
|
auth_result = self.security_manager.auth_manager.authenticate_api_key("short")
|
122
|
-
|
131
|
+
|
123
132
|
assert auth_result.is_valid is False
|
124
133
|
assert auth_result.error_code == -32002
|
125
|
-
|
134
|
+
|
126
135
|
# Test missing API key
|
127
136
|
auth_result = self.security_manager.auth_manager.authenticate_api_key("")
|
128
|
-
|
137
|
+
|
129
138
|
assert auth_result.is_valid is False
|
130
139
|
assert auth_result.error_code == -32001
|
131
|
-
|
140
|
+
|
132
141
|
def test_complete_jwt_authentication_flow(self):
|
133
142
|
"""Test complete JWT authentication flow."""
|
134
143
|
# Create JWT token
|
135
144
|
payload = {
|
136
145
|
"username": "admin",
|
137
146
|
"roles": ["admin"],
|
138
|
-
"exp": datetime.now(timezone.utc) + timedelta(hours=24)
|
147
|
+
"exp": datetime.now(timezone.utc) + timedelta(hours=24),
|
139
148
|
}
|
140
|
-
|
149
|
+
|
141
150
|
token = jwt.encode(
|
142
151
|
payload,
|
143
152
|
self.auth_config.jwt_secret.get_secret_value(),
|
144
|
-
algorithm=self.auth_config.jwt_algorithm
|
153
|
+
algorithm=self.auth_config.jwt_algorithm,
|
145
154
|
)
|
146
|
-
|
155
|
+
|
147
156
|
# Test valid JWT authentication
|
148
|
-
auth_result = self.security_manager.authenticate_user(
|
149
|
-
"method": "jwt",
|
150
|
-
|
151
|
-
|
152
|
-
|
157
|
+
auth_result = self.security_manager.authenticate_user(
|
158
|
+
{"method": "jwt", "token": token}
|
159
|
+
)
|
160
|
+
|
153
161
|
assert auth_result.is_valid is True
|
154
162
|
assert auth_result.username == "admin"
|
155
163
|
assert "admin" in auth_result.roles
|
156
164
|
assert auth_result.auth_method == "jwt"
|
157
|
-
|
165
|
+
|
158
166
|
# Test expired JWT token
|
159
167
|
expired_payload = {
|
160
168
|
"username": "admin",
|
161
169
|
"roles": ["admin"],
|
162
|
-
"exp": datetime.now(timezone.utc) - timedelta(hours=1)
|
170
|
+
"exp": datetime.now(timezone.utc) - timedelta(hours=1),
|
163
171
|
}
|
164
|
-
|
172
|
+
|
165
173
|
expired_token = jwt.encode(
|
166
174
|
expired_payload,
|
167
175
|
self.auth_config.jwt_secret.get_secret_value(),
|
168
|
-
algorithm=self.auth_config.jwt_algorithm
|
176
|
+
algorithm=self.auth_config.jwt_algorithm,
|
177
|
+
)
|
178
|
+
|
179
|
+
auth_result = self.security_manager.authenticate_user(
|
180
|
+
{"method": "jwt", "token": expired_token}
|
169
181
|
)
|
170
|
-
|
171
|
-
auth_result = self.security_manager.authenticate_user({
|
172
|
-
"method": "jwt",
|
173
|
-
"token": expired_token
|
174
|
-
})
|
175
|
-
|
182
|
+
|
176
183
|
assert auth_result.is_valid is False
|
177
184
|
assert auth_result.error_code == -32002 # JWT token expired error
|
178
|
-
|
185
|
+
|
179
186
|
# Test invalid JWT token
|
180
|
-
auth_result = self.security_manager.authenticate_user(
|
181
|
-
"method": "jwt",
|
182
|
-
|
183
|
-
|
184
|
-
|
187
|
+
auth_result = self.security_manager.authenticate_user(
|
188
|
+
{"method": "jwt", "token": "invalid_token"}
|
189
|
+
)
|
190
|
+
|
185
191
|
assert auth_result.is_valid is False
|
186
192
|
assert auth_result.error_code == -32003 # Invalid JWT token error
|
187
|
-
|
193
|
+
|
188
194
|
def test_complete_authorization_flow(self):
|
189
195
|
"""Test complete authorization flow."""
|
190
196
|
# Test admin permissions
|
191
|
-
auth_result = self.security_manager.authenticate_user(
|
192
|
-
"method": "api_key",
|
193
|
-
|
194
|
-
|
195
|
-
|
197
|
+
auth_result = self.security_manager.authenticate_user(
|
198
|
+
{"method": "api_key", "api_key": "admin_key_123456789012345"}
|
199
|
+
)
|
200
|
+
|
196
201
|
assert auth_result.is_valid is True
|
197
|
-
|
202
|
+
|
198
203
|
# Check admin permissions
|
199
204
|
perm_result = self.security_manager.check_permissions(
|
200
|
-
auth_result.roles,
|
201
|
-
["read", "write", "delete", "admin"]
|
205
|
+
auth_result.roles, ["read", "write", "delete", "admin"]
|
202
206
|
)
|
203
|
-
|
207
|
+
|
204
208
|
assert perm_result.is_valid is True
|
205
|
-
assert set(perm_result.granted_permissions) == {
|
206
|
-
|
209
|
+
assert set(perm_result.granted_permissions) == {
|
210
|
+
"read",
|
211
|
+
"write",
|
212
|
+
"delete",
|
213
|
+
"admin",
|
214
|
+
"moderate",
|
215
|
+
}
|
216
|
+
|
207
217
|
# Test user permissions
|
208
|
-
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
209
|
-
|
218
|
+
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
219
|
+
"user_key_456789012345678"
|
220
|
+
)
|
221
|
+
|
210
222
|
assert auth_result.is_valid is True
|
211
|
-
|
223
|
+
|
212
224
|
# Check user permissions
|
213
225
|
perm_result = self.security_manager.check_permissions(
|
214
|
-
auth_result.roles,
|
215
|
-
["read", "write"]
|
226
|
+
auth_result.roles, ["read", "write"]
|
216
227
|
)
|
217
|
-
|
228
|
+
|
218
229
|
assert perm_result.is_valid is True
|
219
230
|
assert set(perm_result.granted_permissions) == {"read", "write"}
|
220
|
-
|
231
|
+
|
221
232
|
# Test user denied admin permissions
|
222
233
|
perm_result = self.security_manager.check_permissions(
|
223
|
-
auth_result.roles,
|
224
|
-
["admin"]
|
234
|
+
auth_result.roles, ["admin"]
|
225
235
|
)
|
226
|
-
|
236
|
+
|
227
237
|
assert perm_result.is_valid is False
|
228
238
|
assert perm_result.denied_permissions == ["admin"]
|
229
|
-
|
239
|
+
|
230
240
|
# Test readonly permissions
|
231
|
-
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
232
|
-
|
241
|
+
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
242
|
+
"readonly_key_789012345678"
|
243
|
+
)
|
244
|
+
|
233
245
|
assert auth_result.is_valid is True
|
234
|
-
|
246
|
+
|
235
247
|
# Check readonly permissions
|
236
248
|
perm_result = self.security_manager.check_permissions(
|
237
|
-
auth_result.roles,
|
238
|
-
["read"]
|
249
|
+
auth_result.roles, ["read"]
|
239
250
|
)
|
240
|
-
|
251
|
+
|
241
252
|
assert perm_result.is_valid is True
|
242
253
|
assert perm_result.granted_permissions == ["read"]
|
243
|
-
|
254
|
+
|
244
255
|
# Test readonly denied write permissions
|
245
256
|
perm_result = self.security_manager.check_permissions(
|
246
|
-
auth_result.roles,
|
247
|
-
["write"]
|
257
|
+
auth_result.roles, ["write"]
|
248
258
|
)
|
249
|
-
|
259
|
+
|
250
260
|
assert perm_result.is_valid is False
|
251
261
|
assert perm_result.denied_permissions == ["write"]
|
252
|
-
|
262
|
+
|
253
263
|
def test_role_hierarchy_flow(self):
|
254
264
|
"""Test role hierarchy inheritance flow."""
|
255
265
|
# Test admin role (inherits from user and guest)
|
256
|
-
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
257
|
-
|
266
|
+
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
267
|
+
"admin_key_123456789012345"
|
268
|
+
)
|
269
|
+
|
258
270
|
assert auth_result.is_valid is True
|
259
271
|
assert "admin" in auth_result.roles
|
260
272
|
# Admin inherits from user and guest through hierarchy
|
261
|
-
|
262
|
-
|
273
|
+
|
274
|
+
# Check inherited permissions (admin has all permissions including moderate from moderator)
|
263
275
|
perm_result = self.security_manager.check_permissions(
|
264
|
-
auth_result.roles,
|
265
|
-
["read", "write", "moderate", "admin"]
|
276
|
+
auth_result.roles, ["read", "write", "moderate", "admin"]
|
266
277
|
)
|
267
278
|
|
268
279
|
assert perm_result.is_valid is True
|
269
|
-
assert set(perm_result.granted_permissions) == {
|
280
|
+
assert set(perm_result.granted_permissions) == {
|
281
|
+
"read",
|
282
|
+
"write",
|
283
|
+
"moderate",
|
284
|
+
"admin",
|
285
|
+
"delete",
|
286
|
+
}
|
270
287
|
assert "read" in perm_result.granted_permissions
|
271
288
|
assert "write" in perm_result.granted_permissions
|
272
289
|
assert "moderate" in perm_result.granted_permissions
|
273
|
-
|
290
|
+
|
274
291
|
def test_rate_limiting_integration_flow(self):
|
275
292
|
"""Test rate limiting integration with authentication."""
|
276
293
|
# Test rate limiting for authenticated user
|
277
294
|
user_identifier = "user_key_456789012345678"
|
278
|
-
|
295
|
+
|
279
296
|
# Make multiple requests to trigger rate limiting
|
280
297
|
for i in range(105): # Exceed default limit
|
281
298
|
rate_limit_result = self.security_manager.check_rate_limit(user_identifier)
|
282
|
-
|
299
|
+
|
283
300
|
# Rate limiting should work
|
284
301
|
assert isinstance(rate_limit_result, bool)
|
285
|
-
|
302
|
+
|
286
303
|
# Test rate limiting reset
|
287
304
|
# Note: In real implementation, this would depend on time window
|
288
305
|
# For testing, we'll just verify the mechanism works
|
289
|
-
|
306
|
+
|
290
307
|
def test_multi_method_authentication_flow(self):
|
291
308
|
"""Test authentication with multiple methods."""
|
292
309
|
# Test API key first, then JWT
|
293
|
-
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
294
|
-
|
310
|
+
auth_result = self.security_manager.auth_manager.authenticate_api_key(
|
311
|
+
"admin_key_123456789012345"
|
312
|
+
)
|
313
|
+
|
295
314
|
# Should use API key (first method)
|
296
315
|
assert auth_result.is_valid is True
|
297
316
|
assert auth_result.auth_method == "api_key"
|
298
|
-
|
317
|
+
|
299
318
|
# Test JWT when API key is invalid
|
300
|
-
auth_result = self.security_manager.authenticate_user(
|
301
|
-
"method": "api_key",
|
302
|
-
|
303
|
-
|
304
|
-
|
319
|
+
auth_result = self.security_manager.authenticate_user(
|
320
|
+
{"method": "api_key", "api_key": "invalid_key"}
|
321
|
+
)
|
322
|
+
|
305
323
|
# Should fail (both methods invalid)
|
306
324
|
assert auth_result.is_valid is False
|
307
|
-
|
325
|
+
|
308
326
|
def test_session_management_flow(self):
|
309
327
|
"""Test session management flow."""
|
310
328
|
# Create session for user
|
311
|
-
auth_result = self.security_manager.authenticate_user(
|
312
|
-
"method": "api_key",
|
313
|
-
|
314
|
-
|
315
|
-
|
329
|
+
auth_result = self.security_manager.authenticate_user(
|
330
|
+
{"method": "api_key", "api_key": "user_key_456789012345678"}
|
331
|
+
)
|
332
|
+
|
316
333
|
assert auth_result.is_valid is True
|
317
|
-
|
334
|
+
|
318
335
|
# Simulate session persistence
|
319
336
|
session_data = {
|
320
337
|
"user_id": auth_result.username,
|
321
338
|
"roles": auth_result.roles,
|
322
339
|
"permissions": auth_result.permissions,
|
323
|
-
"created_at": datetime.now(timezone.utc).isoformat()
|
340
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
324
341
|
}
|
325
|
-
|
342
|
+
|
326
343
|
# Verify session data
|
327
344
|
assert session_data["user_id"] == "user"
|
328
345
|
assert "user" in session_data["roles"]
|
329
346
|
assert "read" in session_data["permissions"]
|
330
347
|
assert "write" in session_data["permissions"]
|
331
|
-
|
348
|
+
|
332
349
|
def test_error_handling_flow(self):
|
333
350
|
"""Test error handling in authentication flow."""
|
334
351
|
# Test with malformed headers
|
335
|
-
auth_result = self.security_manager.authenticate_user(
|
336
|
-
"method": "api_key",
|
337
|
-
|
338
|
-
|
339
|
-
|
352
|
+
auth_result = self.security_manager.authenticate_user(
|
353
|
+
{"method": "api_key", "api_key": ""} # Empty API key
|
354
|
+
)
|
355
|
+
|
340
356
|
assert auth_result.is_valid is False
|
341
357
|
assert auth_result.error_code == -32001
|
342
|
-
|
358
|
+
|
343
359
|
# Test with invalid JWT format
|
344
|
-
auth_result = self.security_manager.authenticate_user(
|
345
|
-
"method": "jwt",
|
346
|
-
|
347
|
-
|
348
|
-
|
360
|
+
auth_result = self.security_manager.authenticate_user(
|
361
|
+
{"method": "jwt", "token": "InvalidFormat token"}
|
362
|
+
)
|
363
|
+
|
349
364
|
assert auth_result.is_valid is False
|
350
365
|
assert auth_result.error_code == -32003
|
351
|
-
|
352
|
-
|
366
|
+
|
367
|
+
# Test with unsupported authentication method
|
353
368
|
with pytest.raises(SecurityValidationError) as exc_info:
|
354
|
-
self.security_manager.authenticate_user(
|
355
|
-
"method": "custom",
|
356
|
-
|
357
|
-
|
358
|
-
|
369
|
+
self.security_manager.authenticate_user(
|
370
|
+
{"method": "custom", "custom_token": "custom_token"}
|
371
|
+
)
|
372
|
+
|
359
373
|
assert "Unsupported authentication method: custom" in str(exc_info.value)
|
360
|
-
|
374
|
+
|
361
375
|
def test_performance_benchmark_flow(self):
|
362
376
|
"""Test performance of authentication flow."""
|
363
377
|
import time
|
364
|
-
|
378
|
+
|
365
379
|
# Benchmark API key authentication
|
366
380
|
start_time = time.time()
|
367
381
|
for i in range(1000):
|
368
|
-
auth_result = self.security_manager.authenticate_user(
|
369
|
-
"method": "api_key",
|
370
|
-
|
371
|
-
})
|
382
|
+
auth_result = self.security_manager.authenticate_user(
|
383
|
+
{"method": "api_key", "api_key": "admin_key_123456789012345"}
|
384
|
+
)
|
372
385
|
assert auth_result.is_valid is True
|
373
386
|
end_time = time.time()
|
374
|
-
|
387
|
+
|
375
388
|
avg_time = (end_time - start_time) / 1000
|
376
389
|
assert avg_time < 0.001, f"API key auth too slow: {avg_time:.6f}s per request"
|
377
|
-
|
390
|
+
|
378
391
|
# Benchmark permission checking
|
379
|
-
auth_result = self.security_manager.authenticate_user(
|
380
|
-
"method": "api_key",
|
381
|
-
|
382
|
-
|
383
|
-
|
392
|
+
auth_result = self.security_manager.authenticate_user(
|
393
|
+
{"method": "api_key", "api_key": "admin_key_123456789012345"}
|
394
|
+
)
|
395
|
+
|
384
396
|
start_time = time.time()
|
385
397
|
for i in range(1000):
|
386
398
|
perm_result = self.security_manager.check_permissions(
|
387
|
-
auth_result.roles,
|
388
|
-
["read", "write", "delete"]
|
399
|
+
auth_result.roles, ["read", "write", "delete"]
|
389
400
|
)
|
390
401
|
assert perm_result.is_valid is True
|
391
402
|
end_time = time.time()
|
392
|
-
|
403
|
+
|
393
404
|
avg_time = (end_time - start_time) / 1000
|
394
|
-
assert
|
395
|
-
|
405
|
+
assert (
|
406
|
+
avg_time < 0.001
|
407
|
+
), f"Permission check too slow: {avg_time:.6f}s per request"
|
408
|
+
|
396
409
|
def test_security_audit_flow(self):
|
397
410
|
"""Test security audit flow."""
|
398
411
|
# Perform security audit
|
399
412
|
audit_result = self.security_manager.perform_security_audit()
|
400
|
-
|
413
|
+
|
401
414
|
# Verify audit results
|
402
415
|
assert audit_result is not None
|
403
416
|
assert "authentication" in audit_result
|
404
417
|
assert "authorization" in audit_result
|
405
418
|
assert "rate_limiting" in audit_result
|
406
|
-
|
419
|
+
|
407
420
|
# Check authentication audit
|
408
421
|
auth_audit = audit_result["authentication"]
|
409
422
|
assert auth_audit["enabled"] is True
|
410
423
|
assert "api_key" in auth_audit["methods"]
|
411
424
|
assert "jwt" in auth_audit["methods"]
|
412
|
-
|
425
|
+
|
413
426
|
# Check authorization audit
|
414
427
|
authz_audit = audit_result["authorization"]
|
415
428
|
assert authz_audit["enabled"] is True
|
416
429
|
assert authz_audit["roles_count"] >= 4 # admin, user, readonly, moderator
|
417
|
-
|
430
|
+
|
418
431
|
# Check rate limiting audit
|
419
432
|
rate_audit = audit_result["rate_limiting"]
|
420
433
|
assert rate_audit["enabled"] is True
|
421
|
-
|
434
|
+
|
422
435
|
def test_configuration_validation_flow(self):
|
423
436
|
"""Test configuration validation flow."""
|
424
437
|
# Test valid configuration
|
425
438
|
validation_result = self.security_manager.validate_configuration()
|
426
439
|
assert validation_result.is_valid is True
|
427
|
-
|
440
|
+
|
428
441
|
# Test with invalid configuration (missing API keys)
|
429
442
|
invalid_auth_config = AuthConfig(
|
430
443
|
enabled=True,
|
431
444
|
methods=["api_key"],
|
432
445
|
api_keys={}, # Empty API keys
|
433
|
-
jwt_secret="test_secret_long_enough_for_validation"
|
446
|
+
jwt_secret="test_secret_long_enough_for_validation",
|
434
447
|
)
|
435
|
-
|
448
|
+
|
436
449
|
invalid_config = SecurityConfig(
|
437
450
|
auth=invalid_auth_config,
|
438
451
|
permissions=self.permission_config,
|
439
452
|
rate_limit=RateLimitConfig(enabled=True, default_requests_per_minute=100),
|
440
453
|
ssl=SSLConfig(enabled=False),
|
441
|
-
certificates=CertificateConfig(enabled=False)
|
454
|
+
certificates=CertificateConfig(enabled=False),
|
442
455
|
)
|
443
|
-
|
456
|
+
|
444
457
|
invalid_manager = SecurityManager(invalid_config)
|
445
458
|
validation_result = invalid_manager.validate_configuration()
|
446
459
|
assert validation_result.is_valid is False
|
447
|
-
|
460
|
+
|
448
461
|
def test_logging_and_monitoring_flow(self):
|
449
462
|
"""Test logging and monitoring flow."""
|
450
463
|
# Perform authentication with logging
|
451
|
-
auth_result = self.security_manager.authenticate_user(
|
452
|
-
"method": "api_key",
|
453
|
-
|
454
|
-
|
455
|
-
|
464
|
+
auth_result = self.security_manager.authenticate_user(
|
465
|
+
{"method": "api_key", "api_key": "admin_key_123456789012345"}
|
466
|
+
)
|
467
|
+
|
456
468
|
assert auth_result.is_valid is True
|
457
|
-
|
469
|
+
|
458
470
|
# Check that logging is configured
|
459
|
-
assert hasattr(self.security_manager.auth_manager,
|
471
|
+
assert hasattr(self.security_manager.auth_manager, "logger")
|
460
472
|
assert self.security_manager.auth_manager.logger is not None
|
461
|
-
|
462
|
-
assert hasattr(self.security_manager.permission_manager,
|
473
|
+
|
474
|
+
assert hasattr(self.security_manager.permission_manager, "logger")
|
463
475
|
assert self.security_manager.permission_manager.logger is not None
|
464
|
-
|
476
|
+
|
465
477
|
# Get security metrics
|
466
478
|
metrics = self.security_manager.get_security_metrics()
|
467
|
-
|
479
|
+
|
468
480
|
assert metrics is not None
|
469
481
|
assert "authentication_attempts" in metrics
|
470
482
|
assert "successful_authentications" in metrics
|
471
483
|
assert "failed_authentications" in metrics
|
472
484
|
assert "permission_checks" in metrics
|
473
485
|
assert "rate_limit_violations" in metrics
|
474
|
-
|
486
|
+
|
475
487
|
def test_integration_with_external_systems_flow(self):
|
476
488
|
"""Test integration with external systems flow."""
|
477
489
|
# Test with external user store (mocked)
|
478
|
-
with patch(
|
490
|
+
with patch(
|
491
|
+
"mcp_security_framework.core.auth_manager.AuthManager._validate_external_user"
|
492
|
+
) as mock_validate:
|
479
493
|
mock_validate.return_value = True
|
480
|
-
|
481
|
-
auth_result = self.security_manager.authenticate_user(
|
482
|
-
"method": "api_key",
|
483
|
-
|
484
|
-
|
485
|
-
|
494
|
+
|
495
|
+
auth_result = self.security_manager.authenticate_user(
|
496
|
+
{"method": "api_key", "api_key": "external_user_key"}
|
497
|
+
)
|
498
|
+
|
486
499
|
# Should handle external authentication gracefully
|
487
500
|
assert auth_result.is_valid is False # Key not in config
|
488
501
|
assert auth_result.error_code == -32003
|
489
|
-
|
502
|
+
|
490
503
|
# Test with external permission store (mocked)
|
491
|
-
with patch(
|
492
|
-
|
493
|
-
|
504
|
+
with patch(
|
505
|
+
"mcp_security_framework.core.permission_manager.PermissionManager._load_external_permissions"
|
506
|
+
) as mock_load:
|
507
|
+
mock_load.return_value = {
|
508
|
+
"external_role": {"permissions": ["external_perm"]}
|
509
|
+
}
|
510
|
+
|
494
511
|
# Should integrate with external permission systems
|
495
512
|
perm_result = self.security_manager.check_permissions(
|
496
|
-
["external_role"],
|
497
|
-
["external_perm"]
|
513
|
+
["external_role"], ["external_perm"]
|
498
514
|
)
|
499
|
-
|
515
|
+
|
500
516
|
# In this case, external permissions would be loaded
|
501
517
|
# but the test role doesn't exist in our config
|
502
518
|
assert perm_result.is_valid is False
|