mcp-security-framework 0.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.
Files changed (58) hide show
  1. mcp_security_framework/__init__.py +26 -15
  2. mcp_security_framework/cli/__init__.py +1 -1
  3. mcp_security_framework/cli/cert_cli.py +233 -197
  4. mcp_security_framework/cli/security_cli.py +324 -234
  5. mcp_security_framework/constants.py +21 -27
  6. mcp_security_framework/core/auth_manager.py +49 -20
  7. mcp_security_framework/core/cert_manager.py +398 -104
  8. mcp_security_framework/core/permission_manager.py +13 -9
  9. mcp_security_framework/core/rate_limiter.py +10 -0
  10. mcp_security_framework/core/security_manager.py +286 -229
  11. mcp_security_framework/examples/__init__.py +6 -0
  12. mcp_security_framework/examples/comprehensive_example.py +954 -0
  13. mcp_security_framework/examples/django_example.py +276 -202
  14. mcp_security_framework/examples/fastapi_example.py +897 -393
  15. mcp_security_framework/examples/flask_example.py +311 -200
  16. mcp_security_framework/examples/gateway_example.py +373 -214
  17. mcp_security_framework/examples/microservice_example.py +337 -172
  18. mcp_security_framework/examples/standalone_example.py +719 -478
  19. mcp_security_framework/examples/test_all_examples.py +572 -0
  20. mcp_security_framework/middleware/__init__.py +46 -55
  21. mcp_security_framework/middleware/auth_middleware.py +62 -63
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +179 -110
  23. mcp_security_framework/middleware/fastapi_middleware.py +156 -148
  24. mcp_security_framework/middleware/flask_auth_middleware.py +267 -107
  25. mcp_security_framework/middleware/flask_middleware.py +183 -157
  26. mcp_security_framework/middleware/mtls_middleware.py +106 -117
  27. mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
  28. mcp_security_framework/middleware/security_middleware.py +109 -124
  29. mcp_security_framework/schemas/config.py +2 -1
  30. mcp_security_framework/schemas/models.py +19 -6
  31. mcp_security_framework/utils/cert_utils.py +14 -8
  32. mcp_security_framework/utils/datetime_compat.py +116 -0
  33. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
  34. mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
  35. tests/conftest.py +303 -0
  36. tests/test_cli/test_cert_cli.py +194 -174
  37. tests/test_cli/test_security_cli.py +274 -247
  38. tests/test_core/test_cert_manager.py +33 -19
  39. tests/test_core/test_security_manager.py +2 -2
  40. tests/test_examples/test_comprehensive_example.py +613 -0
  41. tests/test_examples/test_fastapi_example.py +290 -169
  42. tests/test_examples/test_flask_example.py +304 -162
  43. tests/test_examples/test_standalone_example.py +106 -168
  44. tests/test_integration/test_auth_flow.py +214 -198
  45. tests/test_integration/test_certificate_flow.py +181 -150
  46. tests/test_integration/test_fastapi_integration.py +140 -149
  47. tests/test_integration/test_flask_integration.py +144 -141
  48. tests/test_integration/test_standalone_integration.py +331 -300
  49. tests/test_middleware/test_fastapi_auth_middleware.py +745 -0
  50. tests/test_middleware/test_fastapi_middleware.py +147 -132
  51. tests/test_middleware/test_flask_auth_middleware.py +696 -0
  52. tests/test_middleware/test_flask_middleware.py +201 -179
  53. tests/test_middleware/test_security_middleware.py +151 -130
  54. tests/test_utils/test_datetime_compat.py +147 -0
  55. mcp_security_framework-0.1.0.dist-info/RECORD +0 -76
  56. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
  57. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
  58. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -19,76 +19,87 @@ Version: 1.0.0
19
19
  License: MIT
20
20
  """
21
21
 
22
+ from typing import Any, Dict, List
23
+ from unittest.mock import MagicMock, Mock, patch
24
+
22
25
  import pytest
23
- from unittest.mock import Mock, patch, MagicMock
24
- from typing import Dict, List, Any
25
26
 
27
+ from mcp_security_framework.core.security_manager import SecurityManager
26
28
  from mcp_security_framework.middleware.security_middleware import (
27
29
  SecurityMiddleware,
28
- SecurityMiddlewareError
30
+ SecurityMiddlewareError,
31
+ )
32
+ from mcp_security_framework.schemas.config import (
33
+ AuthConfig,
34
+ RateLimitConfig,
35
+ SecurityConfig,
36
+ )
37
+ from mcp_security_framework.schemas.models import (
38
+ AuthMethod,
39
+ AuthResult,
40
+ AuthStatus,
41
+ ValidationResult,
42
+ ValidationStatus,
29
43
  )
30
- from mcp_security_framework.core.security_manager import SecurityManager
31
- from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, RateLimitConfig
32
- from mcp_security_framework.schemas.models import AuthResult, ValidationResult, ValidationStatus, AuthStatus, AuthMethod
33
44
 
34
45
 
35
46
  class MockSecurityMiddleware(SecurityMiddleware):
36
47
  """
37
48
  Mock implementation of SecurityMiddleware for testing.
38
-
49
+
39
50
  This class implements all abstract methods to allow testing
40
51
  of the base SecurityMiddleware functionality.
41
52
  """
42
-
53
+
43
54
  def __call__(self, request: Any, call_next: Any) -> Any:
44
55
  """Mock implementation of __call__ method."""
45
56
  # Check rate limit
46
57
  if not self._check_rate_limit(request):
47
58
  return self._create_error_response(429, "Rate limit exceeded")
48
-
59
+
49
60
  # Check if public path
50
61
  if self._is_public_path(request):
51
62
  response = call_next(request)
52
63
  self._add_security_headers(response)
53
64
  return response
54
-
65
+
55
66
  # Authenticate request
56
67
  auth_result = self._authenticate_request(request)
57
68
  if not auth_result.is_valid:
58
69
  return self._create_error_response(401, "Authentication failed")
59
-
70
+
60
71
  # Validate permissions
61
72
  if not self._validate_permissions(request, auth_result):
62
73
  return self._create_error_response(403, "Permission denied")
63
-
74
+
64
75
  # Process request
65
76
  response = call_next(request)
66
77
  self._add_security_headers(response)
67
78
  return response
68
-
79
+
69
80
  def _get_rate_limit_identifier(self, request: Any) -> str:
70
81
  """Mock implementation to get rate limit identifier."""
71
- return getattr(request, 'client_ip', '127.0.0.1')
72
-
82
+ return getattr(request, "client_ip", "127.0.0.1")
83
+
73
84
  def _get_request_path(self, request: Any) -> str:
74
85
  """Mock implementation to get request path."""
75
- return getattr(request, 'path', '/')
76
-
86
+ return getattr(request, "path", "/")
87
+
77
88
  def _get_required_permissions(self, request: Any) -> List[str]:
78
89
  """Mock implementation to get required permissions."""
79
- return getattr(request, 'required_permissions', [])
80
-
90
+ return getattr(request, "required_permissions", [])
91
+
81
92
  def _try_auth_method(self, request: Any, method: str) -> AuthResult:
82
93
  """Mock implementation to try authentication method."""
83
94
  if method == "api_key":
84
- api_key = getattr(request, 'api_key', None)
95
+ api_key = getattr(request, "api_key", None)
85
96
  if api_key == "valid_key":
86
97
  return AuthResult(
87
98
  is_valid=True,
88
99
  status=AuthStatus.SUCCESS,
89
100
  username="test_user",
90
101
  roles=["user"],
91
- auth_method=AuthMethod.API_KEY
102
+ auth_method=AuthMethod.API_KEY,
92
103
  )
93
104
  return AuthResult(
94
105
  is_valid=False,
@@ -97,14 +108,14 @@ class MockSecurityMiddleware(SecurityMiddleware):
97
108
  roles=[],
98
109
  auth_method=AuthMethod.API_KEY if method == "api_key" else None,
99
110
  error_code=-32005,
100
- error_message="Authentication failed"
111
+ error_message="Authentication failed",
101
112
  )
102
-
113
+
103
114
  def _apply_security_headers(self, response: Any, headers: Dict[str, str]) -> None:
104
115
  """Mock implementation to apply security headers."""
105
- if hasattr(response, 'headers'):
116
+ if hasattr(response, "headers"):
106
117
  response.headers.update(headers)
107
-
118
+
108
119
  def _create_error_response(self, status_code: int, message: str) -> Any:
109
120
  """Mock implementation to create error response."""
110
121
  response = Mock()
@@ -115,7 +126,7 @@ class MockSecurityMiddleware(SecurityMiddleware):
115
126
 
116
127
  class TestSecurityMiddleware:
117
128
  """Test suite for SecurityMiddleware class."""
118
-
129
+
119
130
  def setup_method(self):
120
131
  """Set up test fixtures before each test method."""
121
132
  # Create mock security manager
@@ -125,27 +136,25 @@ class TestSecurityMiddleware:
125
136
  enabled=True,
126
137
  methods=["api_key", "jwt"],
127
138
  public_paths=["/health", "/docs"],
128
- jwt_secret="test_jwt_secret_key"
139
+ jwt_secret="test_jwt_secret_key",
129
140
  ),
130
141
  rate_limit=RateLimitConfig(
131
- enabled=True,
132
- default_requests_per_minute=100,
133
- window_size_seconds=60
134
- )
142
+ enabled=True, default_requests_per_minute=100, window_size_seconds=60
143
+ ),
135
144
  )
136
-
145
+
137
146
  # Setup rate_limiter mock
138
147
  self.mock_security_manager.rate_limiter = Mock()
139
-
148
+
140
149
  # Create mock request
141
150
  self.mock_request = Mock()
142
151
  self.mock_request.client_ip = "127.0.0.1"
143
152
  self.mock_request.path = "/api/test"
144
153
  self.mock_request.required_permissions = ["read"]
145
-
154
+
146
155
  # Create middleware instance
147
156
  self.middleware = MockSecurityMiddleware(self.mock_security_manager)
148
-
157
+
149
158
  def test_initialization_success(self):
150
159
  """Test successful middleware initialization."""
151
160
  assert self.middleware.security_manager == self.mock_security_manager
@@ -153,85 +162,97 @@ class TestSecurityMiddleware:
153
162
  assert len(self.middleware._public_paths) == 2
154
163
  assert "/health" in self.middleware._public_paths
155
164
  assert "/docs" in self.middleware._public_paths
156
-
165
+
157
166
  def test_initialization_invalid_security_manager(self):
158
167
  """Test initialization with invalid security manager."""
159
168
  with pytest.raises(SecurityMiddlewareError) as exc_info:
160
169
  MockSecurityMiddleware("invalid_manager")
161
-
170
+
162
171
  assert "Invalid security manager" in str(exc_info.value)
163
172
  assert exc_info.value.error_code == -32003
164
-
173
+
165
174
  def test_check_rate_limit_disabled(self):
166
175
  """Test rate limiting when disabled."""
167
176
  self.mock_security_manager.config.rate_limit.enabled = False
168
177
  result = self.middleware._check_rate_limit(self.mock_request)
169
178
  assert result is True
170
-
179
+
171
180
  def test_check_rate_limit_enabled_success(self):
172
181
  """Test successful rate limit check."""
173
182
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
174
183
  result = self.middleware._check_rate_limit(self.mock_request)
175
184
  assert result is True
176
- self.mock_security_manager.rate_limiter.check_rate_limit.assert_called_once_with("127.0.0.1")
177
-
185
+ self.mock_security_manager.rate_limiter.check_rate_limit.assert_called_once_with(
186
+ "127.0.0.1"
187
+ )
188
+
178
189
  def test_check_rate_limit_enabled_exceeded(self):
179
190
  """Test rate limit exceeded."""
180
191
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = False
181
192
  result = self.middleware._check_rate_limit(self.mock_request)
182
193
  assert result is False
183
-
194
+
184
195
  def test_check_rate_limit_no_identifier(self):
185
196
  """Test rate limiting with no identifier."""
186
197
  self.mock_request.client_ip = None
187
198
  result = self.middleware._check_rate_limit(self.mock_request)
188
199
  assert result is True
189
-
200
+
190
201
  def test_check_rate_limit_exception(self):
191
202
  """Test rate limiting with exception."""
192
- self.mock_security_manager.rate_limiter.check_rate_limit.side_effect = Exception("Rate limit error")
193
-
203
+ self.mock_security_manager.rate_limiter.check_rate_limit.side_effect = (
204
+ Exception("Rate limit error")
205
+ )
206
+
194
207
  with pytest.raises(SecurityMiddlewareError) as exc_info:
195
208
  self.middleware._check_rate_limit(self.mock_request)
196
-
209
+
197
210
  assert "Rate limit check failed" in str(exc_info.value)
198
211
  assert exc_info.value.error_code == -32004
199
-
212
+
200
213
  def test_authenticate_request_disabled(self):
201
214
  """Test authentication when disabled."""
202
215
  self.mock_security_manager.config.auth.enabled = False
203
216
  result = self.middleware._authenticate_request(self.mock_request)
204
-
217
+
205
218
  assert result.is_valid is True
206
219
  assert result.status == AuthStatus.SUCCESS
207
220
  assert result.username == "anonymous"
208
221
  assert result.auth_method is None
209
-
222
+
210
223
  def test_authenticate_request_success(self):
211
224
  """Test successful authentication."""
212
225
  self.mock_request.api_key = "valid_key"
213
226
  result = self.middleware._authenticate_request(self.mock_request)
214
-
227
+
215
228
  assert result.is_valid is True
216
229
  assert result.username == "test_user"
217
230
  assert result.auth_method == "api_key"
218
231
  assert result.roles == ["user"]
219
-
232
+
220
233
  def test_authenticate_request_failure(self):
221
234
  """Test authentication failure."""
222
235
  self.mock_request.api_key = "invalid_key"
223
236
  result = self.middleware._authenticate_request(self.mock_request)
224
-
237
+
225
238
  assert result.is_valid is False
226
239
  assert result.error_code == -32005
227
240
  assert "All authentication methods failed" in result.error_message
228
-
241
+
229
242
  def test_authenticate_request_exception(self):
230
243
  """Test authentication with exception."""
231
- # This test is skipped as it's not critical for basic functionality
232
- # The exception handling is tested in other middleware tests
233
- pytest.skip("Exception handling tested in other middleware tests")
234
-
244
+ # Mock security manager to raise exception
245
+ self.mock_security_manager.authenticate_user.side_effect = Exception(
246
+ "Authentication error"
247
+ )
248
+
249
+ result = self.middleware._authenticate_request(self.mock_request)
250
+
251
+ # Should handle exception gracefully
252
+ assert result.is_valid is False
253
+ assert result.error_code == -32005
254
+ assert "All authentication methods failed" in result.error_message
255
+
235
256
  def test_validate_permissions_success(self):
236
257
  """Test successful permission validation."""
237
258
  auth_result = AuthResult(
@@ -239,17 +260,16 @@ class TestSecurityMiddleware:
239
260
  status=AuthStatus.SUCCESS,
240
261
  username="test_user",
241
262
  roles=["admin"],
242
- auth_method=AuthMethod.API_KEY
263
+ auth_method=AuthMethod.API_KEY,
243
264
  )
244
-
265
+
245
266
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
246
- is_valid=True,
247
- status=ValidationStatus.VALID
267
+ is_valid=True, status=ValidationStatus.VALID
248
268
  )
249
-
269
+
250
270
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
251
271
  assert result is True
252
-
272
+
253
273
  def test_validate_permissions_failure(self):
254
274
  """Test permission validation failure."""
255
275
  auth_result = AuthResult(
@@ -257,19 +277,19 @@ class TestSecurityMiddleware:
257
277
  status=AuthStatus.SUCCESS,
258
278
  username="test_user",
259
279
  roles=["user"],
260
- auth_method=AuthMethod.API_KEY
280
+ auth_method=AuthMethod.API_KEY,
261
281
  )
262
-
282
+
263
283
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
264
284
  is_valid=False,
265
285
  status=ValidationStatus.INVALID,
266
286
  error_code=-32007,
267
- error_message="Insufficient permissions"
287
+ error_message="Insufficient permissions",
268
288
  )
269
-
289
+
270
290
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
271
291
  assert result is False
272
-
292
+
273
293
  def test_validate_permissions_no_auth(self):
274
294
  """Test permission validation with invalid auth."""
275
295
  auth_result = AuthResult(
@@ -279,12 +299,12 @@ class TestSecurityMiddleware:
279
299
  roles=[],
280
300
  auth_method=None,
281
301
  error_code=-32005,
282
- error_message="Authentication failed"
302
+ error_message="Authentication failed",
283
303
  )
284
-
304
+
285
305
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
286
306
  assert result is False
287
-
307
+
288
308
  def test_validate_permissions_no_required_permissions(self):
289
309
  """Test permission validation with no required permissions."""
290
310
  auth_result = AuthResult(
@@ -292,13 +312,13 @@ class TestSecurityMiddleware:
292
312
  status=AuthStatus.SUCCESS,
293
313
  username="test_user",
294
314
  roles=["user"],
295
- auth_method=AuthMethod.API_KEY
315
+ auth_method=AuthMethod.API_KEY,
296
316
  )
297
-
317
+
298
318
  self.mock_request.required_permissions = []
299
319
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
300
320
  assert result is True
301
-
321
+
302
322
  def test_validate_permissions_exception(self):
303
323
  """Test permission validation with exception."""
304
324
  auth_result = AuthResult(
@@ -306,75 +326,77 @@ class TestSecurityMiddleware:
306
326
  status=AuthStatus.SUCCESS,
307
327
  username="test_user",
308
328
  roles=["user"],
309
- auth_method=AuthMethod.API_KEY
329
+ auth_method=AuthMethod.API_KEY,
330
+ )
331
+
332
+ self.mock_security_manager.check_permissions.side_effect = Exception(
333
+ "Permission error"
310
334
  )
311
-
312
- self.mock_security_manager.check_permissions.side_effect = Exception("Permission error")
313
-
335
+
314
336
  with pytest.raises(SecurityMiddlewareError) as exc_info:
315
337
  self.middleware._validate_permissions(self.mock_request, auth_result)
316
-
338
+
317
339
  assert "Permission validation failed" in str(exc_info.value)
318
340
  assert exc_info.value.error_code == -32007
319
-
341
+
320
342
  def test_is_public_path_true(self):
321
343
  """Test public path check returning True."""
322
344
  self.mock_request.path = "/health"
323
345
  result = self.middleware._is_public_path(self.mock_request)
324
346
  assert result is True
325
-
347
+
326
348
  def test_is_public_path_false(self):
327
349
  """Test public path check returning False."""
328
350
  self.mock_request.path = "/api/private"
329
351
  result = self.middleware._is_public_path(self.mock_request)
330
352
  assert result is False
331
-
353
+
332
354
  def test_is_public_path_no_path(self):
333
355
  """Test public path check with no path."""
334
356
  self.mock_request.path = None
335
357
  result = self.middleware._is_public_path(self.mock_request)
336
358
  assert result is False
337
-
359
+
338
360
  def test_is_public_path_exception(self):
339
361
  """Test public path check with exception."""
340
362
  self.mock_request.path = Exception("Path error")
341
363
  result = self.middleware._is_public_path(self.mock_request)
342
364
  assert result is False
343
-
365
+
344
366
  def test_add_security_headers_success(self):
345
367
  """Test adding security headers successfully."""
346
368
  mock_response = Mock()
347
369
  mock_response.headers = {}
348
-
370
+
349
371
  self.middleware._add_security_headers(mock_response)
350
-
372
+
351
373
  # Check that standard security headers were added
352
374
  assert "X-Content-Type-Options" in mock_response.headers
353
375
  assert "X-Frame-Options" in mock_response.headers
354
376
  assert "X-XSS-Protection" in mock_response.headers
355
-
377
+
356
378
  def test_add_security_headers_with_custom_headers(self):
357
379
  """Test adding security headers with custom headers from config."""
358
380
  self.mock_security_manager.config.auth.security_headers = {
359
381
  "Custom-Security-Header": "custom_value"
360
382
  }
361
-
383
+
362
384
  mock_response = Mock()
363
385
  mock_response.headers = {}
364
-
386
+
365
387
  self.middleware._add_security_headers(mock_response)
366
-
388
+
367
389
  assert "Custom-Security-Header" in mock_response.headers
368
390
  assert mock_response.headers["Custom-Security-Header"] == "custom_value"
369
-
391
+
370
392
  def test_add_security_headers_exception(self):
371
393
  """Test adding security headers with exception."""
372
394
  mock_response = Mock()
373
395
  mock_response.headers = Exception("Header error")
374
-
396
+
375
397
  # Should not raise exception, just log error
376
398
  self.middleware._add_security_headers(mock_response)
377
-
399
+
378
400
  def test_log_security_event_success(self):
379
401
  """Test logging security event successfully."""
380
402
  details = {
@@ -382,112 +404,111 @@ class TestSecurityMiddleware:
382
404
  "ip_address": "127.0.0.1",
383
405
  "username": "test_user",
384
406
  "path": "/api/test",
385
- "method": "GET"
407
+ "method": "GET",
386
408
  }
387
-
388
- with patch.object(self.middleware.logger, 'info') as mock_logger:
409
+
410
+ with patch.object(self.middleware.logger, "info") as mock_logger:
389
411
  self.middleware._log_security_event("test_event", details)
390
412
  mock_logger.assert_called_once()
391
-
413
+
392
414
  def test_log_security_event_exception(self):
393
415
  """Test logging security event with exception."""
394
416
  details = Exception("Event error")
395
-
417
+
396
418
  # Should not raise exception, just log error
397
419
  self.middleware._log_security_event("test_event", details)
398
-
420
+
399
421
  def test_middleware_call_success(self):
400
422
  """Test successful middleware call."""
401
423
  mock_call_next = Mock()
402
424
  mock_response = Mock()
403
425
  mock_response.headers = {}
404
426
  mock_call_next.return_value = mock_response
405
-
427
+
406
428
  self.mock_request.api_key = "valid_key"
407
429
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
408
430
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
409
- is_valid=True,
410
- status=ValidationStatus.VALID
431
+ is_valid=True, status=ValidationStatus.VALID
411
432
  )
412
-
433
+
413
434
  result = self.middleware(self.mock_request, mock_call_next)
414
-
435
+
415
436
  assert result == mock_response
416
437
  mock_call_next.assert_called_once_with(self.mock_request)
417
-
438
+
418
439
  def test_middleware_call_rate_limit_exceeded(self):
419
440
  """Test middleware call with rate limit exceeded."""
420
441
  mock_call_next = Mock()
421
-
442
+
422
443
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = False
423
-
444
+
424
445
  result = self.middleware(self.mock_request, mock_call_next)
425
-
446
+
426
447
  assert result.status_code == 429
427
448
  assert "Rate limit exceeded" in result.body
428
449
  mock_call_next.assert_not_called()
429
-
450
+
430
451
  def test_middleware_call_public_path(self):
431
452
  """Test middleware call with public path."""
432
453
  mock_call_next = Mock()
433
454
  mock_response = Mock()
434
455
  mock_response.headers = {}
435
456
  mock_call_next.return_value = mock_response
436
-
457
+
437
458
  self.mock_request.path = "/health"
438
459
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
439
-
460
+
440
461
  result = self.middleware(self.mock_request, mock_call_next)
441
-
462
+
442
463
  assert result == mock_response
443
464
  mock_call_next.assert_called_once_with(self.mock_request)
444
-
465
+
445
466
  def test_middleware_call_authentication_failed(self):
446
467
  """Test middleware call with authentication failure."""
447
468
  mock_call_next = Mock()
448
-
469
+
449
470
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
450
471
  self.mock_request.api_key = "invalid_key"
451
-
472
+
452
473
  result = self.middleware(self.mock_request, mock_call_next)
453
-
474
+
454
475
  assert result.status_code == 401
455
476
  assert "Authentication failed" in result.body
456
477
  mock_call_next.assert_not_called()
457
-
478
+
458
479
  def test_middleware_call_permission_denied(self):
459
480
  """Test middleware call with permission denied."""
460
481
  mock_call_next = Mock()
461
-
482
+
462
483
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
463
484
  self.mock_request.api_key = "valid_key"
464
485
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
465
486
  is_valid=False,
466
487
  status=ValidationStatus.INVALID,
467
488
  error_code=-32007,
468
- error_message="Permission denied"
489
+ error_message="Permission denied",
469
490
  )
470
-
491
+
471
492
  result = self.middleware(self.mock_request, mock_call_next)
472
-
493
+
473
494
  assert result.status_code == 403
474
495
  assert "Permission denied" in result.body
475
496
  mock_call_next.assert_not_called()
476
-
497
+
477
498
  def test_abstract_methods_validation(self):
478
499
  """Test that abstract methods are properly defined."""
479
500
  # Verify that all abstract methods are defined in the base class
480
501
  abstract_methods = [
481
- '_get_rate_limit_identifier',
482
- '_get_request_path',
483
- '_get_required_permissions',
484
- '_try_auth_method',
485
- '_apply_security_headers',
486
- '_create_error_response'
502
+ "_get_rate_limit_identifier",
503
+ "_get_request_path",
504
+ "_get_required_permissions",
505
+ "_try_auth_method",
506
+ "_apply_security_headers",
507
+ "_create_error_response",
487
508
  ]
488
-
509
+
489
510
  for method_name in abstract_methods:
490
511
  assert hasattr(SecurityMiddleware, method_name)
491
512
  method = getattr(SecurityMiddleware, method_name)
492
- assert hasattr(method, '__isabstractmethod__')
513
+ assert hasattr(method, "__isabstractmethod__")
493
514
  assert method.__isabstractmethod__ is True