mcp-security-framework 0.1.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_security_framework/core/auth_manager.py +12 -2
- mcp_security_framework/core/cert_manager.py +247 -16
- mcp_security_framework/core/permission_manager.py +4 -0
- mcp_security_framework/core/rate_limiter.py +10 -0
- mcp_security_framework/core/security_manager.py +2 -0
- mcp_security_framework/examples/comprehensive_example.py +884 -0
- mcp_security_framework/examples/django_example.py +45 -12
- mcp_security_framework/examples/fastapi_example.py +826 -354
- mcp_security_framework/examples/flask_example.py +51 -11
- mcp_security_framework/examples/gateway_example.py +109 -17
- mcp_security_framework/examples/microservice_example.py +112 -16
- mcp_security_framework/examples/standalone_example.py +646 -430
- mcp_security_framework/examples/test_all_examples.py +556 -0
- mcp_security_framework/middleware/auth_middleware.py +1 -1
- mcp_security_framework/middleware/fastapi_auth_middleware.py +82 -14
- mcp_security_framework/middleware/flask_auth_middleware.py +154 -7
- mcp_security_framework/schemas/models.py +1 -0
- mcp_security_framework/utils/cert_utils.py +5 -5
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/METADATA +1 -1
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/RECORD +38 -32
- tests/conftest.py +306 -0
- tests/test_cli/test_cert_cli.py +13 -31
- tests/test_core/test_cert_manager.py +12 -12
- tests/test_examples/test_comprehensive_example.py +560 -0
- tests/test_examples/test_fastapi_example.py +214 -116
- tests/test_examples/test_flask_example.py +250 -131
- tests/test_examples/test_standalone_example.py +44 -99
- tests/test_integration/test_auth_flow.py +4 -4
- tests/test_integration/test_certificate_flow.py +1 -1
- tests/test_integration/test_fastapi_integration.py +39 -45
- tests/test_integration/test_flask_integration.py +4 -2
- tests/test_integration/test_standalone_integration.py +48 -48
- tests/test_middleware/test_fastapi_auth_middleware.py +724 -0
- tests/test_middleware/test_flask_auth_middleware.py +638 -0
- tests/test_middleware/test_security_middleware.py +9 -3
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/WHEEL +0 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/top_level.txt +0 -0
@@ -21,7 +21,7 @@ from cryptography import x509
|
|
21
21
|
from cryptography.hazmat.primitives import hashes, serialization
|
22
22
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
23
23
|
|
24
|
-
from mcp_security_framework.examples.standalone_example import
|
24
|
+
from mcp_security_framework.examples.standalone_example import StandaloneSecurityExample
|
25
25
|
from mcp_security_framework.core.security_manager import SecurityManager
|
26
26
|
from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, RateLimitConfig, SSLConfig
|
27
27
|
|
@@ -37,9 +37,9 @@ class TestStandaloneIntegration:
|
|
37
37
|
"enabled": True,
|
38
38
|
"methods": ["api_key"],
|
39
39
|
"api_keys": {
|
40
|
-
"admin_key_123": "admin",
|
41
|
-
"user_key_456": "user",
|
42
|
-
"readonly_key_789": "readonly"
|
40
|
+
"admin_key_123": {"username": "admin", "roles": ["admin", "user"]},
|
41
|
+
"user_key_456": {"username": "user", "roles": ["user"]},
|
42
|
+
"readonly_key_789": {"username": "readonly", "roles": ["readonly"]}
|
43
43
|
}
|
44
44
|
},
|
45
45
|
"rate_limit": {
|
@@ -71,15 +71,15 @@ class TestStandaloneIntegration:
|
|
71
71
|
self.roles_config = {
|
72
72
|
"roles": {
|
73
73
|
"admin": {
|
74
|
-
"permissions": ["read", "write", "delete", "admin"],
|
74
|
+
"permissions": ["read:own", "write:own", "delete:own", "admin", "*"],
|
75
75
|
"description": "Administrator role"
|
76
76
|
},
|
77
77
|
"user": {
|
78
|
-
"permissions": ["read", "write"],
|
78
|
+
"permissions": ["read:own", "write:own"],
|
79
79
|
"description": "Regular user role"
|
80
80
|
},
|
81
81
|
"readonly": {
|
82
|
-
"permissions": ["read"],
|
82
|
+
"permissions": ["read:own"],
|
83
83
|
"description": "Read-only user role"
|
84
84
|
}
|
85
85
|
}
|
@@ -107,7 +107,7 @@ class TestStandaloneIntegration:
|
|
107
107
|
def test_standalone_full_integration(self):
|
108
108
|
"""Test complete standalone integration with security framework."""
|
109
109
|
# Create standalone example
|
110
|
-
example =
|
110
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
111
111
|
|
112
112
|
# Test that security manager is configured
|
113
113
|
assert example.security_manager is not None
|
@@ -120,7 +120,7 @@ class TestStandaloneIntegration:
|
|
120
120
|
|
121
121
|
def test_standalone_authentication_flow(self):
|
122
122
|
"""Test complete authentication flow in standalone application."""
|
123
|
-
example =
|
123
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
124
124
|
|
125
125
|
# Test unauthenticated request
|
126
126
|
result = example.process_request({
|
@@ -139,8 +139,8 @@ class TestStandaloneIntegration:
|
|
139
139
|
"resource": "/api/v1/users/me",
|
140
140
|
"identifier": "test_client"
|
141
141
|
})
|
142
|
-
assert "
|
143
|
-
assert result["
|
142
|
+
assert result["success"] is True
|
143
|
+
assert result["auth_result"]["username"] == "admin"
|
144
144
|
|
145
145
|
# Test authenticated request with different user
|
146
146
|
result = example.process_request({
|
@@ -149,12 +149,12 @@ class TestStandaloneIntegration:
|
|
149
149
|
"resource": "/api/v1/users/me",
|
150
150
|
"identifier": "test_client"
|
151
151
|
})
|
152
|
-
assert "
|
153
|
-
assert result["
|
152
|
+
assert result["success"] is True
|
153
|
+
assert result["auth_result"]["username"] == "user"
|
154
154
|
|
155
155
|
def test_standalone_authorization_flow(self):
|
156
156
|
"""Test complete authorization flow in standalone application."""
|
157
|
-
example =
|
157
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
158
158
|
|
159
159
|
# Test admin access to admin-only operation
|
160
160
|
result = example.process_request({
|
@@ -163,20 +163,19 @@ class TestStandaloneIntegration:
|
|
163
163
|
"resource": "/api/v1/admin/users",
|
164
164
|
"identifier": "test_client"
|
165
165
|
})
|
166
|
-
assert "
|
167
|
-
|
168
|
-
assert "data" in result["data"]
|
166
|
+
assert result["success"] is True
|
167
|
+
assert result["auth_result"]["username"] == "admin"
|
169
168
|
|
170
169
|
# Test regular user access to admin-only operation (should be denied)
|
171
|
-
# Note: Since roles are not loaded properly in test config, this may succeed
|
172
170
|
result = example.process_request({
|
173
171
|
"credentials": {"api_key": "user_key_456"},
|
174
172
|
"action": "read",
|
175
173
|
"resource": "/api/v1/admin/users",
|
176
174
|
"identifier": "test_client"
|
177
175
|
})
|
178
|
-
# Check that request is processed
|
179
|
-
assert
|
176
|
+
# Check that request is processed
|
177
|
+
assert isinstance(result, dict)
|
178
|
+
assert "success" in result
|
180
179
|
|
181
180
|
# Test readonly user access to write operation (should be denied)
|
182
181
|
result = example.process_request({
|
@@ -186,12 +185,13 @@ class TestStandaloneIntegration:
|
|
186
185
|
"data": {"name": "test", "value": "test_value"},
|
187
186
|
"identifier": "test_client"
|
188
187
|
})
|
189
|
-
# Check that request is processed
|
190
|
-
assert
|
188
|
+
# Check that request is processed
|
189
|
+
assert isinstance(result, dict)
|
190
|
+
assert "success" in result
|
191
191
|
|
192
192
|
def test_standalone_rate_limiting(self):
|
193
193
|
"""Test rate limiting in standalone application."""
|
194
|
-
example =
|
194
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
195
195
|
|
196
196
|
# Make multiple requests to trigger rate limiting
|
197
197
|
results = []
|
@@ -205,7 +205,6 @@ class TestStandaloneIntegration:
|
|
205
205
|
results.append(result)
|
206
206
|
|
207
207
|
# Check that rate limiting is working (requests are processed)
|
208
|
-
# Note: Rate limiting behavior may vary, so we just check that requests are processed
|
209
208
|
processed_requests = sum(1 for result in results if result.get("success") is True)
|
210
209
|
assert processed_requests > 0, "Some requests should be processed successfully"
|
211
210
|
|
@@ -227,7 +226,7 @@ class TestStandaloneIntegration:
|
|
227
226
|
with patch('mcp_security_framework.core.ssl_manager.SSLManager.create_server_context') as mock_ssl:
|
228
227
|
mock_ssl.return_value = MagicMock()
|
229
228
|
|
230
|
-
example =
|
229
|
+
example = StandaloneSecurityExample(config_path=ssl_config_path)
|
231
230
|
|
232
231
|
# Test that SSL is configured
|
233
232
|
assert example.config.ssl.enabled is False # SSL disabled for testing
|
@@ -237,7 +236,7 @@ class TestStandaloneIntegration:
|
|
237
236
|
|
238
237
|
def test_standalone_error_handling(self):
|
239
238
|
"""Test error handling in standalone application."""
|
240
|
-
example =
|
239
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
241
240
|
|
242
241
|
# Test invalid API key
|
243
242
|
result = example.process_request({
|
@@ -261,7 +260,7 @@ class TestStandaloneIntegration:
|
|
261
260
|
|
262
261
|
def test_standalone_data_operations(self):
|
263
262
|
"""Test data operations with security."""
|
264
|
-
example =
|
263
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
265
264
|
|
266
265
|
# Create data
|
267
266
|
create_result = example.process_request({
|
@@ -271,9 +270,9 @@ class TestStandaloneIntegration:
|
|
271
270
|
"data": {"name": "test_item", "value": "test_value"},
|
272
271
|
"identifier": "test_client"
|
273
272
|
})
|
274
|
-
assert "
|
275
|
-
# The
|
276
|
-
assert "data"
|
273
|
+
assert create_result["success"] is True
|
274
|
+
# The data is returned in the result
|
275
|
+
assert create_result["data"] == {"name": "test_item", "value": "test_value"}
|
277
276
|
|
278
277
|
# Retrieve data (simulate retrieval since _execute_action doesn't generate IDs)
|
279
278
|
get_result = example.process_request({
|
@@ -282,9 +281,9 @@ class TestStandaloneIntegration:
|
|
282
281
|
"resource": "/api/v1/data/test_item",
|
283
282
|
"identifier": "test_client"
|
284
283
|
})
|
285
|
-
assert "
|
286
|
-
# The
|
287
|
-
assert "
|
284
|
+
assert get_result["success"] is True
|
285
|
+
# The result contains the request information
|
286
|
+
assert get_result["resource"] == "/api/v1/data/test_item"
|
288
287
|
|
289
288
|
def test_standalone_configuration_validation(self):
|
290
289
|
"""Test configuration validation in standalone application."""
|
@@ -303,13 +302,13 @@ class TestStandaloneIntegration:
|
|
303
302
|
try:
|
304
303
|
# Should raise validation error
|
305
304
|
with pytest.raises(Exception):
|
306
|
-
|
305
|
+
StandaloneSecurityExample(config_path=invalid_config_path)
|
307
306
|
finally:
|
308
307
|
os.unlink(invalid_config_path)
|
309
308
|
|
310
309
|
def test_standalone_performance_benchmark(self):
|
311
310
|
"""Test performance of standalone application."""
|
312
|
-
example =
|
311
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
313
312
|
|
314
313
|
import time
|
315
314
|
|
@@ -322,7 +321,7 @@ class TestStandaloneIntegration:
|
|
322
321
|
"resource": "/api/v1/users/me",
|
323
322
|
"identifier": "test_client"
|
324
323
|
})
|
325
|
-
assert "
|
324
|
+
assert result["success"] is True
|
326
325
|
end_time = time.time()
|
327
326
|
|
328
327
|
avg_time = (end_time - start_time) / 100
|
@@ -339,7 +338,7 @@ class TestStandaloneIntegration:
|
|
339
338
|
"identifier": "test_client"
|
340
339
|
}
|
341
340
|
)
|
342
|
-
assert "
|
341
|
+
assert result["success"] is True
|
343
342
|
end_time = time.time()
|
344
343
|
|
345
344
|
avg_time = (end_time - start_time) / 50
|
@@ -347,7 +346,7 @@ class TestStandaloneIntegration:
|
|
347
346
|
|
348
347
|
def test_standalone_method_handling(self):
|
349
348
|
"""Test different HTTP method handling."""
|
350
|
-
example =
|
349
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
351
350
|
# Test GET method
|
352
351
|
result = example.process_request({
|
353
352
|
"credentials": {"api_key": "admin_key_123"},
|
@@ -355,7 +354,7 @@ class TestStandaloneIntegration:
|
|
355
354
|
"resource": "/api/v1/users/me",
|
356
355
|
"identifier": "test_client"
|
357
356
|
})
|
358
|
-
assert "
|
357
|
+
assert result["success"] is True
|
359
358
|
|
360
359
|
# Test POST method
|
361
360
|
result = example.process_request({
|
@@ -365,7 +364,7 @@ class TestStandaloneIntegration:
|
|
365
364
|
"data": {"name": "test", "value": "value"},
|
366
365
|
"identifier": "test_client"
|
367
366
|
})
|
368
|
-
assert "
|
367
|
+
assert result["success"] is True
|
369
368
|
|
370
369
|
# Test PUT method
|
371
370
|
result = example.process_request({
|
@@ -388,7 +387,7 @@ class TestStandaloneIntegration:
|
|
388
387
|
|
389
388
|
def test_standalone_path_routing(self):
|
390
389
|
"""Test path-based routing in standalone application."""
|
391
|
-
example =
|
390
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
392
391
|
# Test different paths
|
393
392
|
paths = [
|
394
393
|
"/api/v1/users/me",
|
@@ -411,7 +410,7 @@ class TestStandaloneIntegration:
|
|
411
410
|
|
412
411
|
def test_standalone_header_processing(self):
|
413
412
|
"""Test header processing in standalone application."""
|
414
|
-
example =
|
413
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
415
414
|
|
416
415
|
# Test with different API key combinations
|
417
416
|
api_key_combinations = [
|
@@ -428,11 +427,12 @@ class TestStandaloneIntegration:
|
|
428
427
|
"resource": "/api/v1/users/me",
|
429
428
|
"identifier": "test_client"
|
430
429
|
})
|
431
|
-
assert
|
430
|
+
assert isinstance(result, dict)
|
431
|
+
assert "success" in result
|
432
432
|
|
433
433
|
def test_standalone_body_processing(self):
|
434
434
|
"""Test body processing in standalone application."""
|
435
|
-
example =
|
435
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
436
436
|
# Test with different data types
|
437
437
|
data_combinations = [
|
438
438
|
None,
|
@@ -455,7 +455,7 @@ class TestStandaloneIntegration:
|
|
455
455
|
|
456
456
|
def test_standalone_security_manager_integration(self):
|
457
457
|
"""Test security manager integration in standalone application."""
|
458
|
-
example =
|
458
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
459
459
|
|
460
460
|
# Test that security manager methods are accessible
|
461
461
|
assert hasattr(example.security_manager, 'authenticate_user')
|
@@ -471,13 +471,13 @@ class TestStandaloneIntegration:
|
|
471
471
|
|
472
472
|
# Test permission checking
|
473
473
|
perm_result = example.security_manager.check_permissions(
|
474
|
-
["admin"], ["read", "write"]
|
474
|
+
["admin"], ["read:own", "write:own"]
|
475
475
|
)
|
476
476
|
assert perm_result.is_valid
|
477
477
|
|
478
478
|
def test_standalone_logging_integration(self):
|
479
479
|
"""Test logging integration in standalone application."""
|
480
|
-
example =
|
480
|
+
example = StandaloneSecurityExample(config_path=self.config_path)
|
481
481
|
|
482
482
|
# Test that logging is configured
|
483
483
|
assert hasattr(example.security_manager, 'logger')
|
@@ -490,4 +490,4 @@ class TestStandaloneIntegration:
|
|
490
490
|
"resource": "/api/v1/users/me",
|
491
491
|
"identifier": "test_client"
|
492
492
|
})
|
493
|
-
assert "
|
493
|
+
assert result["success"] is True
|