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.
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 +41 -22
  7. mcp_security_framework/core/cert_manager.py +210 -147
  8. mcp_security_framework/core/permission_manager.py +9 -9
  9. mcp_security_framework/core/rate_limiter.py +2 -2
  10. mcp_security_framework/core/security_manager.py +284 -229
  11. mcp_security_framework/examples/__init__.py +6 -0
  12. mcp_security_framework/examples/comprehensive_example.py +349 -279
  13. mcp_security_framework/examples/django_example.py +247 -206
  14. mcp_security_framework/examples/fastapi_example.py +315 -283
  15. mcp_security_framework/examples/flask_example.py +274 -203
  16. mcp_security_framework/examples/gateway_example.py +304 -237
  17. mcp_security_framework/examples/microservice_example.py +258 -189
  18. mcp_security_framework/examples/standalone_example.py +255 -230
  19. mcp_security_framework/examples/test_all_examples.py +151 -135
  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 +119 -118
  23. mcp_security_framework/middleware/fastapi_middleware.py +156 -148
  24. mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
  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 +18 -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-1.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 +63 -66
  36. tests/test_cli/test_cert_cli.py +184 -146
  37. tests/test_cli/test_security_cli.py +274 -247
  38. tests/test_core/test_cert_manager.py +24 -10
  39. tests/test_core/test_security_manager.py +2 -2
  40. tests/test_examples/test_comprehensive_example.py +190 -137
  41. tests/test_examples/test_fastapi_example.py +124 -101
  42. tests/test_examples/test_flask_example.py +124 -101
  43. tests/test_examples/test_standalone_example.py +73 -80
  44. tests/test_integration/test_auth_flow.py +213 -197
  45. tests/test_integration/test_certificate_flow.py +180 -149
  46. tests/test_integration/test_fastapi_integration.py +108 -111
  47. tests/test_integration/test_flask_integration.py +141 -140
  48. tests/test_integration/test_standalone_integration.py +290 -259
  49. tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
  50. tests/test_middleware/test_fastapi_middleware.py +147 -132
  51. tests/test_middleware/test_flask_auth_middleware.py +260 -202
  52. tests/test_middleware/test_flask_middleware.py +201 -179
  53. tests/test_middleware/test_security_middleware.py +145 -130
  54. tests/test_utils/test_datetime_compat.py +147 -0
  55. mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
  56. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
  57. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
  58. {mcp_security_framework-1.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,91 +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
244
  # Mock security manager to raise exception
232
- self.mock_security_manager.authenticate_user.side_effect = Exception("Authentication error")
233
-
245
+ self.mock_security_manager.authenticate_user.side_effect = Exception(
246
+ "Authentication error"
247
+ )
248
+
234
249
  result = self.middleware._authenticate_request(self.mock_request)
235
-
250
+
236
251
  # Should handle exception gracefully
237
252
  assert result.is_valid is False
238
253
  assert result.error_code == -32005
239
254
  assert "All authentication methods failed" in result.error_message
240
-
255
+
241
256
  def test_validate_permissions_success(self):
242
257
  """Test successful permission validation."""
243
258
  auth_result = AuthResult(
@@ -245,17 +260,16 @@ class TestSecurityMiddleware:
245
260
  status=AuthStatus.SUCCESS,
246
261
  username="test_user",
247
262
  roles=["admin"],
248
- auth_method=AuthMethod.API_KEY
263
+ auth_method=AuthMethod.API_KEY,
249
264
  )
250
-
265
+
251
266
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
252
- is_valid=True,
253
- status=ValidationStatus.VALID
267
+ is_valid=True, status=ValidationStatus.VALID
254
268
  )
255
-
269
+
256
270
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
257
271
  assert result is True
258
-
272
+
259
273
  def test_validate_permissions_failure(self):
260
274
  """Test permission validation failure."""
261
275
  auth_result = AuthResult(
@@ -263,19 +277,19 @@ class TestSecurityMiddleware:
263
277
  status=AuthStatus.SUCCESS,
264
278
  username="test_user",
265
279
  roles=["user"],
266
- auth_method=AuthMethod.API_KEY
280
+ auth_method=AuthMethod.API_KEY,
267
281
  )
268
-
282
+
269
283
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
270
284
  is_valid=False,
271
285
  status=ValidationStatus.INVALID,
272
286
  error_code=-32007,
273
- error_message="Insufficient permissions"
287
+ error_message="Insufficient permissions",
274
288
  )
275
-
289
+
276
290
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
277
291
  assert result is False
278
-
292
+
279
293
  def test_validate_permissions_no_auth(self):
280
294
  """Test permission validation with invalid auth."""
281
295
  auth_result = AuthResult(
@@ -285,12 +299,12 @@ class TestSecurityMiddleware:
285
299
  roles=[],
286
300
  auth_method=None,
287
301
  error_code=-32005,
288
- error_message="Authentication failed"
302
+ error_message="Authentication failed",
289
303
  )
290
-
304
+
291
305
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
292
306
  assert result is False
293
-
307
+
294
308
  def test_validate_permissions_no_required_permissions(self):
295
309
  """Test permission validation with no required permissions."""
296
310
  auth_result = AuthResult(
@@ -298,13 +312,13 @@ class TestSecurityMiddleware:
298
312
  status=AuthStatus.SUCCESS,
299
313
  username="test_user",
300
314
  roles=["user"],
301
- auth_method=AuthMethod.API_KEY
315
+ auth_method=AuthMethod.API_KEY,
302
316
  )
303
-
317
+
304
318
  self.mock_request.required_permissions = []
305
319
  result = self.middleware._validate_permissions(self.mock_request, auth_result)
306
320
  assert result is True
307
-
321
+
308
322
  def test_validate_permissions_exception(self):
309
323
  """Test permission validation with exception."""
310
324
  auth_result = AuthResult(
@@ -312,75 +326,77 @@ class TestSecurityMiddleware:
312
326
  status=AuthStatus.SUCCESS,
313
327
  username="test_user",
314
328
  roles=["user"],
315
- auth_method=AuthMethod.API_KEY
329
+ auth_method=AuthMethod.API_KEY,
316
330
  )
317
-
318
- self.mock_security_manager.check_permissions.side_effect = Exception("Permission error")
319
-
331
+
332
+ self.mock_security_manager.check_permissions.side_effect = Exception(
333
+ "Permission error"
334
+ )
335
+
320
336
  with pytest.raises(SecurityMiddlewareError) as exc_info:
321
337
  self.middleware._validate_permissions(self.mock_request, auth_result)
322
-
338
+
323
339
  assert "Permission validation failed" in str(exc_info.value)
324
340
  assert exc_info.value.error_code == -32007
325
-
341
+
326
342
  def test_is_public_path_true(self):
327
343
  """Test public path check returning True."""
328
344
  self.mock_request.path = "/health"
329
345
  result = self.middleware._is_public_path(self.mock_request)
330
346
  assert result is True
331
-
347
+
332
348
  def test_is_public_path_false(self):
333
349
  """Test public path check returning False."""
334
350
  self.mock_request.path = "/api/private"
335
351
  result = self.middleware._is_public_path(self.mock_request)
336
352
  assert result is False
337
-
353
+
338
354
  def test_is_public_path_no_path(self):
339
355
  """Test public path check with no path."""
340
356
  self.mock_request.path = None
341
357
  result = self.middleware._is_public_path(self.mock_request)
342
358
  assert result is False
343
-
359
+
344
360
  def test_is_public_path_exception(self):
345
361
  """Test public path check with exception."""
346
362
  self.mock_request.path = Exception("Path error")
347
363
  result = self.middleware._is_public_path(self.mock_request)
348
364
  assert result is False
349
-
365
+
350
366
  def test_add_security_headers_success(self):
351
367
  """Test adding security headers successfully."""
352
368
  mock_response = Mock()
353
369
  mock_response.headers = {}
354
-
370
+
355
371
  self.middleware._add_security_headers(mock_response)
356
-
372
+
357
373
  # Check that standard security headers were added
358
374
  assert "X-Content-Type-Options" in mock_response.headers
359
375
  assert "X-Frame-Options" in mock_response.headers
360
376
  assert "X-XSS-Protection" in mock_response.headers
361
-
377
+
362
378
  def test_add_security_headers_with_custom_headers(self):
363
379
  """Test adding security headers with custom headers from config."""
364
380
  self.mock_security_manager.config.auth.security_headers = {
365
381
  "Custom-Security-Header": "custom_value"
366
382
  }
367
-
383
+
368
384
  mock_response = Mock()
369
385
  mock_response.headers = {}
370
-
386
+
371
387
  self.middleware._add_security_headers(mock_response)
372
-
388
+
373
389
  assert "Custom-Security-Header" in mock_response.headers
374
390
  assert mock_response.headers["Custom-Security-Header"] == "custom_value"
375
-
391
+
376
392
  def test_add_security_headers_exception(self):
377
393
  """Test adding security headers with exception."""
378
394
  mock_response = Mock()
379
395
  mock_response.headers = Exception("Header error")
380
-
396
+
381
397
  # Should not raise exception, just log error
382
398
  self.middleware._add_security_headers(mock_response)
383
-
399
+
384
400
  def test_log_security_event_success(self):
385
401
  """Test logging security event successfully."""
386
402
  details = {
@@ -388,112 +404,111 @@ class TestSecurityMiddleware:
388
404
  "ip_address": "127.0.0.1",
389
405
  "username": "test_user",
390
406
  "path": "/api/test",
391
- "method": "GET"
407
+ "method": "GET",
392
408
  }
393
-
394
- with patch.object(self.middleware.logger, 'info') as mock_logger:
409
+
410
+ with patch.object(self.middleware.logger, "info") as mock_logger:
395
411
  self.middleware._log_security_event("test_event", details)
396
412
  mock_logger.assert_called_once()
397
-
413
+
398
414
  def test_log_security_event_exception(self):
399
415
  """Test logging security event with exception."""
400
416
  details = Exception("Event error")
401
-
417
+
402
418
  # Should not raise exception, just log error
403
419
  self.middleware._log_security_event("test_event", details)
404
-
420
+
405
421
  def test_middleware_call_success(self):
406
422
  """Test successful middleware call."""
407
423
  mock_call_next = Mock()
408
424
  mock_response = Mock()
409
425
  mock_response.headers = {}
410
426
  mock_call_next.return_value = mock_response
411
-
427
+
412
428
  self.mock_request.api_key = "valid_key"
413
429
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
414
430
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
415
- is_valid=True,
416
- status=ValidationStatus.VALID
431
+ is_valid=True, status=ValidationStatus.VALID
417
432
  )
418
-
433
+
419
434
  result = self.middleware(self.mock_request, mock_call_next)
420
-
435
+
421
436
  assert result == mock_response
422
437
  mock_call_next.assert_called_once_with(self.mock_request)
423
-
438
+
424
439
  def test_middleware_call_rate_limit_exceeded(self):
425
440
  """Test middleware call with rate limit exceeded."""
426
441
  mock_call_next = Mock()
427
-
442
+
428
443
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = False
429
-
444
+
430
445
  result = self.middleware(self.mock_request, mock_call_next)
431
-
446
+
432
447
  assert result.status_code == 429
433
448
  assert "Rate limit exceeded" in result.body
434
449
  mock_call_next.assert_not_called()
435
-
450
+
436
451
  def test_middleware_call_public_path(self):
437
452
  """Test middleware call with public path."""
438
453
  mock_call_next = Mock()
439
454
  mock_response = Mock()
440
455
  mock_response.headers = {}
441
456
  mock_call_next.return_value = mock_response
442
-
457
+
443
458
  self.mock_request.path = "/health"
444
459
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
445
-
460
+
446
461
  result = self.middleware(self.mock_request, mock_call_next)
447
-
462
+
448
463
  assert result == mock_response
449
464
  mock_call_next.assert_called_once_with(self.mock_request)
450
-
465
+
451
466
  def test_middleware_call_authentication_failed(self):
452
467
  """Test middleware call with authentication failure."""
453
468
  mock_call_next = Mock()
454
-
469
+
455
470
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
456
471
  self.mock_request.api_key = "invalid_key"
457
-
472
+
458
473
  result = self.middleware(self.mock_request, mock_call_next)
459
-
474
+
460
475
  assert result.status_code == 401
461
476
  assert "Authentication failed" in result.body
462
477
  mock_call_next.assert_not_called()
463
-
478
+
464
479
  def test_middleware_call_permission_denied(self):
465
480
  """Test middleware call with permission denied."""
466
481
  mock_call_next = Mock()
467
-
482
+
468
483
  self.mock_security_manager.rate_limiter.check_rate_limit.return_value = True
469
484
  self.mock_request.api_key = "valid_key"
470
485
  self.mock_security_manager.check_permissions.return_value = ValidationResult(
471
486
  is_valid=False,
472
487
  status=ValidationStatus.INVALID,
473
488
  error_code=-32007,
474
- error_message="Permission denied"
489
+ error_message="Permission denied",
475
490
  )
476
-
491
+
477
492
  result = self.middleware(self.mock_request, mock_call_next)
478
-
493
+
479
494
  assert result.status_code == 403
480
495
  assert "Permission denied" in result.body
481
496
  mock_call_next.assert_not_called()
482
-
497
+
483
498
  def test_abstract_methods_validation(self):
484
499
  """Test that abstract methods are properly defined."""
485
500
  # Verify that all abstract methods are defined in the base class
486
501
  abstract_methods = [
487
- '_get_rate_limit_identifier',
488
- '_get_request_path',
489
- '_get_required_permissions',
490
- '_try_auth_method',
491
- '_apply_security_headers',
492
- '_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",
493
508
  ]
494
-
509
+
495
510
  for method_name in abstract_methods:
496
511
  assert hasattr(SecurityMiddleware, method_name)
497
512
  method = getattr(SecurityMiddleware, method_name)
498
- assert hasattr(method, '__isabstractmethod__')
513
+ assert hasattr(method, "__isabstractmethod__")
499
514
  assert method.__isabstractmethod__ is True