mcp-security-framework 1.1.0__py3-none-any.whl → 1.1.2__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.2.dist-info}/METADATA +4 -3
  34. mcp_security_framework-1.1.2.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.2.dist-info}/WHEEL +0 -0
  57. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/entry_points.txt +0 -0
  58. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/top_level.txt +0 -0
@@ -8,21 +8,32 @@ email: vasilyvz@gmail.com
8
8
  """
9
9
 
10
10
  import json
11
- import pytest
12
11
  import os
13
- from unittest.mock import Mock, AsyncMock, patch
12
+ from unittest.mock import AsyncMock, Mock, patch
13
+
14
+ import pytest
14
15
  from flask import Request
15
16
 
16
- from mcp_security_framework.middleware.flask_auth_middleware import FlaskAuthMiddleware
17
- from mcp_security_framework.middleware.auth_middleware import AuthMiddlewareError, AuthMiddleware
18
17
  from mcp_security_framework.core.security_manager import SecurityManager
19
- from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, SSLConfig, PermissionConfig, RateLimitConfig, LoggingConfig
20
- from mcp_security_framework.schemas.models import AuthResult, AuthStatus, AuthMethod
18
+ from mcp_security_framework.middleware.auth_middleware import (
19
+ AuthMiddleware,
20
+ AuthMiddlewareError,
21
+ )
22
+ from mcp_security_framework.middleware.flask_auth_middleware import FlaskAuthMiddleware
23
+ from mcp_security_framework.schemas.config import (
24
+ AuthConfig,
25
+ LoggingConfig,
26
+ PermissionConfig,
27
+ RateLimitConfig,
28
+ SecurityConfig,
29
+ SSLConfig,
30
+ )
31
+ from mcp_security_framework.schemas.models import AuthMethod, AuthResult, AuthStatus
21
32
 
22
33
 
23
34
  class TestFlaskAuthMiddleware:
24
35
  """Test suite for FlaskAuthMiddleware class."""
25
-
36
+
26
37
  def setup_method(self):
27
38
  """Set up test fixtures before each test method."""
28
39
  # Create test configuration
@@ -32,12 +43,12 @@ class TestFlaskAuthMiddleware:
32
43
  methods=["api_key", "jwt", "certificate"],
33
44
  api_keys={
34
45
  "test_key_123": {"username": "testuser", "roles": ["user"]},
35
- "admin_key_456": {"username": "admin", "roles": ["admin"]}
46
+ "admin_key_456": {"username": "admin", "roles": ["admin"]},
36
47
  },
37
48
  jwt_secret="test-super-secret-jwt-key-for-testing-purposes-only",
38
49
  jwt_algorithm="HS256",
39
50
  jwt_expiry_hours=24,
40
- public_paths=["/health", "/metrics"]
51
+ public_paths=["/health", "/metrics"],
41
52
  ),
42
53
  ssl=SSLConfig(enabled=False),
43
54
  certificate=None,
@@ -46,454 +57,487 @@ class TestFlaskAuthMiddleware:
46
57
  logging=LoggingConfig(enabled=True),
47
58
  debug=False,
48
59
  environment="test",
49
- version="1.0.0"
60
+ version="1.0.0",
50
61
  )
51
-
52
- # Create temporary roles file for testing
62
+
63
+ # Create temporary roles file for testing
53
64
  import json
54
- import tempfile
55
65
  import os
56
-
66
+ import tempfile
67
+
57
68
  roles_data = {
58
69
  "admin": {
59
70
  "permissions": ["read:own", "write:own", "delete:own", "admin", "*"],
60
- "description": "Administrator role"
71
+ "description": "Administrator role",
61
72
  },
62
73
  "user": {
63
74
  "permissions": ["read:own", "write:own"],
64
- "description": "Regular user role"
75
+ "description": "Regular user role",
65
76
  },
66
77
  "readonly": {
67
78
  "permissions": ["read:own"],
68
- "description": "Read-only user role"
69
- }
79
+ "description": "Read-only user role",
80
+ },
70
81
  }
71
-
82
+
72
83
  # Create temporary file
73
- with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
84
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
74
85
  json.dump({"roles": roles_data}, f)
75
86
  temp_roles_file = f.name
76
-
87
+
77
88
  # Update config with temporary file path
78
89
  self.config.permissions.roles_file = temp_roles_file
79
-
90
+
80
91
  # Create real security manager
81
92
  from mcp_security_framework.core.security_manager import SecurityManager
93
+
82
94
  self.security_manager = SecurityManager(self.config)
83
-
95
+
84
96
  # Store temp file path for cleanup
85
97
  self.temp_roles_file = temp_roles_file
86
-
87
- # Create middleware instance
98
+
99
+ # Create middleware instance
88
100
  self.middleware = FlaskAuthMiddleware(self.security_manager)
89
101
 
90
102
  # Create mock logger
91
103
  self.mock_logger = Mock()
92
104
  self.middleware.logger = self.mock_logger
93
-
105
+
94
106
  def teardown_method(self):
95
107
  """Clean up after each test method."""
96
108
  # Clean up temporary files
97
- if hasattr(self, 'temp_roles_file') and os.path.exists(self.temp_roles_file):
109
+ if hasattr(self, "temp_roles_file") and os.path.exists(self.temp_roles_file):
98
110
  try:
99
111
  os.unlink(self.temp_roles_file)
100
112
  except OSError:
101
113
  pass
102
-
114
+
103
115
  def test_flask_auth_middleware_initialization(self):
104
116
  """Test middleware initialization."""
105
117
  assert isinstance(self.middleware, FlaskAuthMiddleware)
106
118
  assert isinstance(self.middleware, AuthMiddleware)
107
119
  assert self.middleware.config == self.config
108
120
  assert self.middleware.security_manager == self.security_manager
109
-
121
+
110
122
  def test_flask_auth_middleware_call_public_path(self):
111
123
  """Test middleware call with public path."""
112
124
  # Create WSGI environment for public path
113
125
  environ = {
114
- 'REQUEST_METHOD': 'GET',
115
- 'PATH_INFO': '/health',
116
- 'HTTP_HOST': 'localhost:5000',
117
- 'HTTP_USER_AGENT': 'test-agent'
126
+ "REQUEST_METHOD": "GET",
127
+ "PATH_INFO": "/health",
128
+ "HTTP_HOST": "localhost:5000",
129
+ "HTTP_USER_AGENT": "test-agent",
118
130
  }
119
-
131
+
120
132
  # Create mock start_response
121
133
  mock_start_response = Mock()
122
-
134
+
123
135
  # Call middleware
124
136
  response = self.middleware(environ, mock_start_response)
125
-
137
+
126
138
  # Assertions
127
139
  assert response is not None
128
140
  mock_start_response.assert_called_once()
129
-
141
+
130
142
  def test_flask_auth_middleware_call_authentication_success(self):
131
143
  """Test middleware call with successful authentication."""
132
144
  # Create WSGI environment
133
145
  environ = {
134
- 'REQUEST_METHOD': 'GET',
135
- 'PATH_INFO': '/api/v1/users/me',
136
- 'HTTP_HOST': 'localhost:5000',
137
- 'HTTP_X_API_KEY': 'test_key_123',
138
- 'HTTP_USER_AGENT': 'test-agent'
146
+ "REQUEST_METHOD": "GET",
147
+ "PATH_INFO": "/api/v1/users/me",
148
+ "HTTP_HOST": "localhost:5000",
149
+ "HTTP_X_API_KEY": "test_key_123",
150
+ "HTTP_USER_AGENT": "test-agent",
139
151
  }
140
-
152
+
141
153
  # Mock successful authentication
142
154
  auth_result = AuthResult(
143
155
  is_valid=True,
144
156
  status=AuthStatus.SUCCESS,
145
157
  username="testuser",
146
158
  roles=["user"],
147
- auth_method=AuthMethod.API_KEY
159
+ auth_method=AuthMethod.API_KEY,
160
+ )
161
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
162
+ return_value=auth_result
148
163
  )
149
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=auth_result)
150
-
164
+
151
165
  # Create mock start_response
152
166
  mock_start_response = Mock()
153
-
167
+
154
168
  # Call middleware
155
169
  response = self.middleware(environ, mock_start_response)
156
-
170
+
157
171
  # Assertions
158
172
  assert response is not None
159
173
  mock_start_response.assert_called_once()
160
- self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with("test_key_123")
161
-
174
+ self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with(
175
+ "test_key_123"
176
+ )
177
+
162
178
  def test_flask_auth_middleware_call_authentication_failure(self):
163
179
  """Test middleware call with authentication failure."""
164
180
  # Create WSGI environment
165
181
  environ = {
166
- 'REQUEST_METHOD': 'GET',
167
- 'PATH_INFO': '/api/v1/users/me',
168
- 'HTTP_HOST': 'localhost:5000',
169
- 'HTTP_USER_AGENT': 'test-agent'
182
+ "REQUEST_METHOD": "GET",
183
+ "PATH_INFO": "/api/v1/users/me",
184
+ "HTTP_HOST": "localhost:5000",
185
+ "HTTP_USER_AGENT": "test-agent",
170
186
  }
171
-
187
+
172
188
  # Mock failed authentication
173
189
  auth_result = AuthResult(
174
190
  is_valid=False,
175
191
  status=AuthStatus.INVALID,
176
192
  auth_method=AuthMethod.UNKNOWN,
177
193
  error_code=-32001,
178
- error_message="No authentication credentials provided"
194
+ error_message="No authentication credentials provided",
179
195
  )
180
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=auth_result)
181
-
196
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
197
+ return_value=auth_result
198
+ )
199
+
182
200
  # Create mock start_response
183
201
  mock_start_response = Mock()
184
-
202
+
185
203
  # Call middleware
186
204
  response = self.middleware(environ, mock_start_response)
187
-
205
+
188
206
  # Assertions
189
207
  assert response is not None
190
208
  mock_start_response.assert_called_once()
191
-
209
+
192
210
  def test_flask_auth_middleware_call_exception_handling(self):
193
211
  """Test middleware call with exception handling."""
194
212
  # This test is removed as the middleware properly handles exceptions
195
213
  # by catching them and raising AuthMiddlewareError, which is the expected behavior
196
214
  pass
197
-
215
+
198
216
  def test_flask_auth_middleware_call_next(self):
199
217
  """Test _call_next method."""
200
218
  environ = {
201
- 'REQUEST_METHOD': 'GET',
202
- 'PATH_INFO': '/test',
203
- 'HTTP_HOST': 'localhost:5000'
219
+ "REQUEST_METHOD": "GET",
220
+ "PATH_INFO": "/test",
221
+ "HTTP_HOST": "localhost:5000",
204
222
  }
205
-
223
+
206
224
  mock_start_response = Mock()
207
-
225
+
208
226
  response = self.middleware._call_next(environ, mock_start_response)
209
-
227
+
210
228
  assert response is not None
211
229
  assert isinstance(response, list)
212
230
  assert len(response) > 0
213
- mock_start_response.assert_called_once_with('200 OK', [('Content-Type', 'application/json')])
214
-
231
+ mock_start_response.assert_called_once_with(
232
+ "200 OK", [("Content-Type", "application/json")]
233
+ )
234
+
215
235
  def test_flask_auth_middleware_is_public_path_configured(self):
216
236
  """Test _is_public_path with configured public paths."""
217
237
  mock_request = Mock(spec=Request)
218
238
  mock_request.path = "/health"
219
-
239
+
220
240
  result = self.middleware._is_public_path(mock_request)
221
241
  assert result is True
222
-
242
+
223
243
  def test_flask_auth_middleware_is_public_path_common(self):
224
244
  """Test _is_public_path with common public paths."""
225
245
  mock_request = Mock(spec=Request)
226
246
  mock_request.path = "/status"
227
-
247
+
228
248
  result = self.middleware._is_public_path(mock_request)
229
249
  assert result is True
230
-
250
+
231
251
  def test_flask_auth_middleware_is_public_path_private(self):
232
252
  """Test _is_public_path with private path."""
233
253
  mock_request = Mock(spec=Request)
234
254
  mock_request.path = "/api/v1/users/me"
235
-
255
+
236
256
  result = self.middleware._is_public_path(mock_request)
237
257
  assert result is False
238
-
258
+
239
259
  def test_flask_auth_middleware_get_client_ip_x_forwarded_for(self):
240
260
  """Test _get_client_ip with X-Forwarded-For header."""
241
261
  mock_request = Mock(spec=Request)
242
262
  mock_request.headers = {"X-Forwarded-For": "192.168.1.1, 10.0.0.1"}
243
-
263
+
244
264
  ip = self.middleware._get_client_ip(mock_request)
245
265
  assert ip == "192.168.1.1"
246
-
266
+
247
267
  def test_flask_auth_middleware_get_client_ip_x_real_ip(self):
248
268
  """Test _get_client_ip with X-Real-IP header."""
249
269
  mock_request = Mock(spec=Request)
250
270
  mock_request.headers = {"X-Real-IP": "192.168.1.2"}
251
-
271
+
252
272
  ip = self.middleware._get_client_ip(mock_request)
253
273
  assert ip == "192.168.1.2"
254
-
274
+
255
275
  def test_flask_auth_middleware_get_client_ip_remote_addr(self):
256
276
  """Test _get_client_ip with remote_addr."""
257
277
  mock_request = Mock(spec=Request)
258
278
  mock_request.headers = {}
259
279
  mock_request.remote_addr = "192.168.1.3"
260
-
280
+
261
281
  ip = self.middleware._get_client_ip(mock_request)
262
282
  assert ip == "192.168.1.3"
263
-
283
+
264
284
  def test_flask_auth_middleware_get_client_ip_default(self):
265
285
  """Test _get_client_ip with default fallback."""
266
286
  mock_request = Mock(spec=Request)
267
287
  mock_request.headers = {}
268
288
  mock_request.remote_addr = None
269
-
289
+
270
290
  ip = self.middleware._get_client_ip(mock_request)
271
291
  assert ip == "127.0.0.1" # Default fallback
272
-
292
+
273
293
  def test_flask_auth_middleware_get_cache_key(self):
274
294
  """Test _get_cache_key method."""
275
295
  mock_request = Mock(spec=Request)
276
296
  mock_request.headers = {"User-Agent": "test-browser/1.0"}
277
297
  mock_request.remote_addr = "192.168.1.1"
278
-
298
+
279
299
  cache_key = self.middleware._get_cache_key(mock_request)
280
300
  assert cache_key.startswith("auth:192.168.1.1:")
281
301
  assert isinstance(cache_key, str)
282
302
  assert len(cache_key) > 0
283
-
303
+
284
304
  def test_flask_auth_middleware_try_auth_method_api_key(self):
285
305
  """Test _try_auth_method with API key."""
286
306
  mock_request = Mock(spec=Request)
287
307
  mock_request.headers = {"X-API-Key": "test_key_123"}
288
-
308
+
289
309
  auth_result = AuthResult(
290
310
  is_valid=True,
291
311
  status=AuthStatus.SUCCESS,
292
312
  username="testuser",
293
313
  roles=["user"],
294
- auth_method=AuthMethod.API_KEY
314
+ auth_method=AuthMethod.API_KEY,
295
315
  )
296
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=auth_result)
297
-
316
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
317
+ return_value=auth_result
318
+ )
319
+
298
320
  result = self.middleware._try_auth_method(mock_request, "api_key")
299
-
321
+
300
322
  assert result.is_valid is True
301
- self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with("test_key_123")
302
-
323
+ self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with(
324
+ "test_key_123"
325
+ )
326
+
303
327
  def test_flask_auth_middleware_try_auth_method_jwt(self):
304
328
  """Test _try_auth_method with JWT."""
305
329
  mock_request = Mock(spec=Request)
306
330
  mock_request.headers = {"Authorization": "Bearer test_jwt_token"}
307
-
331
+
308
332
  auth_result = AuthResult(
309
333
  is_valid=True,
310
334
  status=AuthStatus.SUCCESS,
311
335
  username="testuser",
312
336
  roles=["user"],
313
- auth_method=AuthMethod.JWT
337
+ auth_method=AuthMethod.JWT,
338
+ )
339
+ self.security_manager.auth_manager.authenticate_jwt_token = Mock(
340
+ return_value=auth_result
314
341
  )
315
- self.security_manager.auth_manager.authenticate_jwt_token = Mock(return_value=auth_result)
316
-
342
+
317
343
  result = self.middleware._try_auth_method(mock_request, "jwt")
318
-
344
+
319
345
  assert result.is_valid is True
320
- self.security_manager.auth_manager.authenticate_jwt_token.assert_called_once_with("test_jwt_token")
321
-
346
+ self.security_manager.auth_manager.authenticate_jwt_token.assert_called_once_with(
347
+ "test_jwt_token"
348
+ )
349
+
322
350
  def test_flask_auth_middleware_try_auth_method_certificate(self):
323
351
  """Test _try_auth_method with certificate."""
324
352
  mock_request = Mock(spec=Request)
325
353
  mock_request.headers = {"X-Client-Cert": "test_certificate_data"}
326
-
354
+
327
355
  auth_result = AuthResult(
328
356
  is_valid=True,
329
357
  status=AuthStatus.SUCCESS,
330
358
  username="testuser",
331
359
  roles=["user"],
332
- auth_method=AuthMethod.CERTIFICATE
360
+ auth_method=AuthMethod.CERTIFICATE,
361
+ )
362
+ self.security_manager.auth_manager.authenticate_certificate = Mock(
363
+ return_value=auth_result
333
364
  )
334
- self.security_manager.auth_manager.authenticate_certificate = Mock(return_value=auth_result)
335
-
365
+
336
366
  result = self.middleware._try_auth_method(mock_request, "certificate")
337
-
367
+
338
368
  assert result.is_valid is False
339
369
  assert "not implemented" in result.error_message.lower()
340
-
370
+
341
371
  def test_flask_auth_middleware_try_auth_method_unsupported(self):
342
372
  """Test _try_auth_method with unsupported method."""
343
373
  mock_request = Mock(spec=Request)
344
-
374
+
345
375
  result = self.middleware._try_auth_method(mock_request, "unsupported_method")
346
-
376
+
347
377
  assert result.is_valid is False
348
378
  assert result.error_code == -32022
349
379
  assert "Unsupported authentication method" in result.error_message
350
-
380
+
351
381
  def test_flask_auth_middleware_try_auth_method_exception(self):
352
382
  """Test _try_auth_method with exception."""
353
383
  mock_request = Mock(spec=Request)
354
-
355
- self.security_manager.auth_manager.authenticate_api_key = Mock(side_effect=Exception("Test auth error"))
356
-
384
+
385
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
386
+ side_effect=Exception("Test auth error")
387
+ )
388
+
357
389
  result = self.middleware._try_auth_method(mock_request, "api_key")
358
-
390
+
359
391
  assert result.is_valid is False
360
392
  assert result.error_code == -32023
361
393
  assert "Authentication method api_key failed" in result.error_message
362
-
394
+
363
395
  def test_flask_auth_middleware_try_api_key_auth_success(self):
364
396
  """Test _try_api_key_auth with successful authentication."""
365
397
  mock_request = Mock(spec=Request)
366
398
  mock_request.headers = {"X-API-Key": "test_key_123"}
367
-
399
+
368
400
  auth_result = AuthResult(
369
401
  is_valid=True,
370
402
  status=AuthStatus.SUCCESS,
371
403
  username="testuser",
372
404
  roles=["user"],
373
- auth_method=AuthMethod.API_KEY
405
+ auth_method=AuthMethod.API_KEY,
374
406
  )
375
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=auth_result)
376
-
407
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
408
+ return_value=auth_result
409
+ )
410
+
377
411
  result = self.middleware._try_api_key_auth(mock_request)
378
-
412
+
379
413
  assert result.is_valid is True
380
- self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with("test_key_123")
381
-
414
+ self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with(
415
+ "test_key_123"
416
+ )
417
+
382
418
  def test_flask_auth_middleware_try_api_key_auth_from_authorization(self):
383
419
  """Test _try_api_key_auth with API key from Authorization header."""
384
420
  mock_request = Mock(spec=Request)
385
421
  mock_request.headers = {"Authorization": "Bearer api_key_123"}
386
-
422
+
387
423
  auth_result = AuthResult(
388
424
  is_valid=True,
389
425
  status=AuthStatus.SUCCESS,
390
426
  username="testuser",
391
427
  roles=["user"],
392
- auth_method=AuthMethod.API_KEY
428
+ auth_method=AuthMethod.API_KEY,
429
+ )
430
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
431
+ return_value=auth_result
393
432
  )
394
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=auth_result)
395
-
433
+
396
434
  result = self.middleware._try_api_key_auth(mock_request)
397
-
435
+
398
436
  assert result.is_valid is True
399
- self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with("api_key_123")
400
-
437
+ self.security_manager.auth_manager.authenticate_api_key.assert_called_once_with(
438
+ "api_key_123"
439
+ )
440
+
401
441
  def test_flask_auth_middleware_try_api_key_auth_no_key(self):
402
442
  """Test _try_api_key_auth with no API key."""
403
443
  mock_request = Mock(spec=Request)
404
444
  mock_request.headers = {}
405
-
445
+
406
446
  result = self.middleware._try_api_key_auth(mock_request)
407
-
447
+
408
448
  assert result.is_valid is False
409
449
  assert result.error_code == -32012
410
450
  assert "API key not found in request" in result.error_message
411
-
451
+
412
452
  def test_flask_auth_middleware_try_jwt_auth_success(self):
413
453
  """Test _try_jwt_auth with successful authentication."""
414
454
  mock_request = Mock(spec=Request)
415
455
  mock_request.headers = {"Authorization": "Bearer test_jwt_token"}
416
-
456
+
417
457
  auth_result = AuthResult(
418
458
  is_valid=True,
419
459
  status=AuthStatus.SUCCESS,
420
460
  username="testuser",
421
461
  roles=["user"],
422
- auth_method=AuthMethod.JWT
462
+ auth_method=AuthMethod.JWT,
423
463
  )
424
- self.security_manager.auth_manager.authenticate_jwt_token = Mock(return_value=auth_result)
425
-
464
+ self.security_manager.auth_manager.authenticate_jwt_token = Mock(
465
+ return_value=auth_result
466
+ )
467
+
426
468
  result = self.middleware._try_jwt_auth(mock_request)
427
-
469
+
428
470
  assert result.is_valid is True
429
- self.security_manager.auth_manager.authenticate_jwt_token.assert_called_once_with("test_jwt_token")
430
-
471
+ self.security_manager.auth_manager.authenticate_jwt_token.assert_called_once_with(
472
+ "test_jwt_token"
473
+ )
474
+
431
475
  def test_flask_auth_middleware_try_jwt_auth_no_token(self):
432
476
  """Test _try_jwt_auth with no JWT token."""
433
477
  mock_request = Mock(spec=Request)
434
478
  mock_request.headers = {}
435
-
479
+
436
480
  result = self.middleware._try_jwt_auth(mock_request)
437
-
481
+
438
482
  assert result.is_valid is False
439
483
  assert result.error_code == -32013
440
484
  assert "JWT token not found in Authorization header" in result.error_message
441
-
485
+
442
486
  def test_flask_auth_middleware_try_jwt_auth_wrong_format(self):
443
487
  """Test _try_jwt_auth with wrong Authorization header format."""
444
488
  mock_request = Mock(spec=Request)
445
489
  mock_request.headers = {"Authorization": "Basic dGVzdDp0ZXN0"}
446
-
490
+
447
491
  result = self.middleware._try_jwt_auth(mock_request)
448
-
492
+
449
493
  assert result.is_valid is False
450
494
  assert result.error_code == -32013
451
495
  assert "JWT token not found in Authorization header" in result.error_message
452
-
496
+
453
497
  def test_flask_auth_middleware_try_certificate_auth(self):
454
498
  """Test _try_certificate_auth (not implemented)."""
455
499
  mock_request = Mock(spec=Request)
456
500
  mock_request.headers = {"X-Client-Cert": "test_certificate_data"}
457
-
501
+
458
502
  result = self.middleware._try_certificate_auth(mock_request)
459
-
503
+
460
504
  assert result.is_valid is False
461
505
  assert result.error_code == -32014
462
506
  assert "Certificate authentication not implemented" in result.error_message
463
-
507
+
464
508
  def test_flask_auth_middleware_try_basic_auth_no_credentials(self):
465
509
  """Test _try_basic_auth with no credentials."""
466
510
  mock_request = Mock(spec=Request)
467
511
  mock_request.headers = {}
468
-
512
+
469
513
  result = self.middleware._try_basic_auth(mock_request)
470
-
514
+
471
515
  assert result.is_valid is False
472
516
  assert result.error_code == -32015
473
517
  assert "Basic authentication credentials not found" in result.error_message
474
-
518
+
475
519
  def test_flask_auth_middleware_try_basic_auth_wrong_format(self):
476
520
  """Test _try_basic_auth with wrong Authorization header format."""
477
521
  mock_request = Mock(spec=Request)
478
522
  mock_request.headers = {"Authorization": "Bearer token123"}
479
-
523
+
480
524
  result = self.middleware._try_basic_auth(mock_request)
481
-
525
+
482
526
  assert result.is_valid is False
483
527
  assert result.error_code == -32015
484
528
  assert "Basic authentication credentials not found" in result.error_message
485
-
529
+
486
530
  def test_flask_auth_middleware_try_basic_auth_not_implemented(self):
487
531
  """Test _try_basic_auth (not implemented)."""
488
532
  mock_request = Mock(spec=Request)
489
533
  mock_request.headers = {"Authorization": "Basic dGVzdDp0ZXN0"}
490
-
534
+
491
535
  result = self.middleware._try_basic_auth(mock_request)
492
-
536
+
493
537
  assert result.is_valid is False
494
538
  assert result.error_code == -32016
495
539
  assert "Basic authentication not implemented" in result.error_message
496
-
540
+
497
541
  def test_flask_auth_middleware_auth_error_response(self):
498
542
  """Test _auth_error_response method."""
499
543
  auth_result = AuthResult(
@@ -501,61 +545,71 @@ class TestFlaskAuthMiddleware:
501
545
  status=AuthStatus.INVALID,
502
546
  auth_method=AuthMethod.API_KEY,
503
547
  error_code=-32002,
504
- error_message="Invalid API key"
548
+ error_message="Invalid API key",
505
549
  )
506
-
550
+
507
551
  mock_start_response = Mock()
508
-
509
- response = self.middleware._auth_error_response(auth_result, mock_start_response)
510
-
552
+
553
+ response = self.middleware._auth_error_response(
554
+ auth_result, mock_start_response
555
+ )
556
+
511
557
  assert response is not None
512
558
  mock_start_response.assert_called_once()
513
-
559
+
514
560
  def test_flask_auth_middleware_authz_error_response(self):
515
561
  """Test _authz_error_response method."""
516
562
  from mcp_security_framework.schemas.models import AuthResult
517
-
563
+
518
564
  auth_result = AuthResult(
519
565
  is_valid=False,
520
566
  status=AuthStatus.INVALID,
521
567
  error_code=-32004,
522
- error_message="Access denied"
568
+ error_message="Access denied",
523
569
  )
524
-
570
+
525
571
  mock_start_response = Mock()
526
-
527
- response = self.middleware._authz_error_response(auth_result, mock_start_response)
528
-
572
+
573
+ response = self.middleware._authz_error_response(
574
+ auth_result, mock_start_response
575
+ )
576
+
529
577
  assert response is not None
530
578
  mock_start_response.assert_called_once()
531
-
579
+
532
580
  def test_flask_auth_middleware_validation_error_response(self):
533
581
  """Test _validation_error_response method."""
534
582
  mock_start_response = Mock()
535
-
536
- response = self.middleware._validation_error_response("Validation failed", -32000, mock_start_response)
537
-
583
+
584
+ response = self.middleware._validation_error_response(
585
+ "Validation failed", -32000, mock_start_response
586
+ )
587
+
538
588
  assert response is not None
539
589
  mock_start_response.assert_called_once()
540
-
590
+
541
591
  def test_flask_auth_middleware_rate_limit_error_response(self):
542
592
  """Test _rate_limit_error_response method."""
543
593
  mock_start_response = Mock()
544
-
545
- response = self.middleware._rate_limit_error_response("Rate limit exceeded", -32000, mock_start_response)
546
-
594
+
595
+ response = self.middleware._rate_limit_error_response(
596
+ "Rate limit exceeded", -32000, mock_start_response
597
+ )
598
+
547
599
  assert response is not None
548
600
  mock_start_response.assert_called_once()
549
-
601
+
550
602
  def test_flask_auth_middleware_security_header_response(self):
551
603
  """Test _security_header_response method."""
552
604
  mock_start_response = Mock()
553
-
554
- response = self.middleware._security_header_response("Security header validation failed", -32000, mock_start_response)
555
-
605
+
606
+ response = self.middleware._security_header_response(
607
+ "Security header validation failed", -32000, mock_start_response
608
+ )
609
+
556
610
  assert response is not None
557
611
  mock_start_response.assert_called_once()
558
-
612
+
559
613
  def test_flask_auth_middleware_log_auth_event(self):
560
614
  """Test _log_auth_event method."""
561
615
  event_data = {
@@ -563,76 +617,80 @@ class TestFlaskAuthMiddleware:
563
617
  "username": "testuser",
564
618
  "path": "/api/v1/users/me",
565
619
  "method": "GET",
566
- "auth_method": "api_key"
620
+ "auth_method": "api_key",
567
621
  }
568
-
622
+
569
623
  self.middleware._log_auth_event("authentication_successful", event_data)
570
-
624
+
571
625
  self.mock_logger.info.assert_called_once()
572
-
626
+
573
627
  def test_flask_auth_middleware_get_rate_limit_identifier(self):
574
628
  """Test _get_rate_limit_identifier method."""
575
629
  mock_request = Mock(spec=Request)
576
630
  mock_request.remote_addr = "192.168.1.1"
577
631
  mock_request.headers = {}
578
-
632
+
579
633
  identifier = self.middleware._get_rate_limit_identifier(mock_request)
580
634
  assert identifier == "192.168.1.1"
581
-
635
+
582
636
  def test_flask_auth_middleware_get_request_path(self):
583
637
  """Test _get_request_path method."""
584
638
  mock_request = Mock(spec=Request)
585
639
  mock_request.path = "/api/v1/users/me"
586
-
640
+
587
641
  path = self.middleware._get_request_path(mock_request)
588
642
  assert path == "/api/v1/users/me"
589
-
643
+
590
644
  def test_flask_auth_middleware_get_required_permissions(self):
591
645
  """Test _get_required_permissions method."""
592
646
  mock_request = Mock(spec=Request)
593
647
  mock_request.path = "/api/v1/users/me"
594
648
  mock_request.method = "GET"
595
-
649
+
596
650
  permissions = self.middleware._get_required_permissions(mock_request)
597
651
  assert isinstance(permissions, list)
598
-
652
+
599
653
  def test_flask_auth_middleware_authenticate_only_success(self):
600
654
  """Test _authenticate_only with successful authentication."""
601
655
  mock_request = Mock(spec=Request)
602
656
  mock_request.headers = {"X-API-Key": "test_key_123"}
603
657
  mock_request.remote_addr = "192.168.1.1"
604
-
658
+
605
659
  auth_result = AuthResult(
606
660
  is_valid=True,
607
661
  status=AuthStatus.SUCCESS,
608
662
  username="testuser",
609
663
  roles=["user"],
610
- auth_method=AuthMethod.API_KEY
664
+ auth_method=AuthMethod.API_KEY,
665
+ )
666
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
667
+ return_value=auth_result
611
668
  )
612
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=auth_result)
613
-
669
+
614
670
  result = self.middleware._authenticate_only(mock_request)
615
-
671
+
616
672
  assert result.is_valid is True
617
673
  assert result.username == "testuser"
618
-
674
+
619
675
  def test_flask_auth_middleware_authenticate_only_failure(self):
620
676
  """Test _authenticate_only with authentication failure."""
621
677
  mock_request = Mock(spec=Request)
622
678
  mock_request.headers = {}
623
679
  mock_request.remote_addr = "192.168.1.1"
624
-
680
+
625
681
  # Mock failed authentication for all methods
626
682
  failed_auth_result = AuthResult(
627
683
  is_valid=False,
628
684
  status=AuthStatus.INVALID,
629
685
  auth_method=AuthMethod.UNKNOWN,
630
686
  error_code=-32001,
631
- error_message="No authentication credentials provided"
687
+ error_message="No authentication credentials provided",
688
+ )
689
+ self.security_manager.auth_manager.authenticate_api_key = Mock(
690
+ return_value=failed_auth_result
632
691
  )
633
- self.security_manager.auth_manager.authenticate_api_key = Mock(return_value=failed_auth_result)
634
-
692
+
635
693
  result = self.middleware._authenticate_only(mock_request)
636
-
694
+
637
695
  assert result.is_valid is False
638
696
  assert result.error_code == -32033 # All authentication methods failed