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.
Files changed (38) hide show
  1. mcp_security_framework/core/auth_manager.py +12 -2
  2. mcp_security_framework/core/cert_manager.py +247 -16
  3. mcp_security_framework/core/permission_manager.py +4 -0
  4. mcp_security_framework/core/rate_limiter.py +10 -0
  5. mcp_security_framework/core/security_manager.py +2 -0
  6. mcp_security_framework/examples/comprehensive_example.py +884 -0
  7. mcp_security_framework/examples/django_example.py +45 -12
  8. mcp_security_framework/examples/fastapi_example.py +826 -354
  9. mcp_security_framework/examples/flask_example.py +51 -11
  10. mcp_security_framework/examples/gateway_example.py +109 -17
  11. mcp_security_framework/examples/microservice_example.py +112 -16
  12. mcp_security_framework/examples/standalone_example.py +646 -430
  13. mcp_security_framework/examples/test_all_examples.py +556 -0
  14. mcp_security_framework/middleware/auth_middleware.py +1 -1
  15. mcp_security_framework/middleware/fastapi_auth_middleware.py +82 -14
  16. mcp_security_framework/middleware/flask_auth_middleware.py +154 -7
  17. mcp_security_framework/schemas/models.py +1 -0
  18. mcp_security_framework/utils/cert_utils.py +5 -5
  19. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/METADATA +1 -1
  20. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/RECORD +38 -32
  21. tests/conftest.py +306 -0
  22. tests/test_cli/test_cert_cli.py +13 -31
  23. tests/test_core/test_cert_manager.py +12 -12
  24. tests/test_examples/test_comprehensive_example.py +560 -0
  25. tests/test_examples/test_fastapi_example.py +214 -116
  26. tests/test_examples/test_flask_example.py +250 -131
  27. tests/test_examples/test_standalone_example.py +44 -99
  28. tests/test_integration/test_auth_flow.py +4 -4
  29. tests/test_integration/test_certificate_flow.py +1 -1
  30. tests/test_integration/test_fastapi_integration.py +39 -45
  31. tests/test_integration/test_flask_integration.py +4 -2
  32. tests/test_integration/test_standalone_integration.py +48 -48
  33. tests/test_middleware/test_fastapi_auth_middleware.py +724 -0
  34. tests/test_middleware/test_flask_auth_middleware.py +638 -0
  35. tests/test_middleware/test_security_middleware.py +9 -3
  36. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/WHEEL +0 -0
  37. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/entry_points.txt +0 -0
  38. {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 StandaloneExample
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 = StandaloneExample(config_path=self.config_path)
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 = StandaloneExample(config_path=self.config_path)
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 "data" in result
143
- assert result["user"]["username"] == "admin"
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 "data" in result
153
- assert result["user"]["username"] == "user"
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 = StandaloneExample(config_path=self.config_path)
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 "data" in result
167
- # The _execute_action method returns generic data, not specific users
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 (may succeed due to missing role configuration)
179
- assert "data" in result or "error" in result
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 (may succeed due to missing role configuration)
190
- assert "data" in result or "error" in result
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 = StandaloneExample(config_path=self.config_path)
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 = StandaloneExample(config_path=ssl_config_path)
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 = StandaloneExample(config_path=self.config_path)
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 = StandaloneExample(config_path=self.config_path)
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 "data" in create_result
275
- # The _execute_action method doesn't generate IDs, it just returns the data
276
- assert "data" in create_result["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 "data" in get_result
286
- # The _execute_action method returns generic data
287
- assert "data" in get_result["data"]
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
- StandaloneExample(config_path=invalid_config_path)
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 = StandaloneExample(config_path=self.config_path)
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 "data" in result
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 "data" in result
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 = StandaloneExample(config_path=self.config_path)
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 "data" in result
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 "data" in result
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 = StandaloneExample(config_path=self.config_path)
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 = StandaloneExample(config_path=self.config_path)
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 "data" in result or "error" in result
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 = StandaloneExample(config_path=self.config_path)
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 = StandaloneExample(config_path=self.config_path)
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 = StandaloneExample(config_path=self.config_path)
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 "data" in result
493
+ assert result["success"] is True