mcp-security-framework 0.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 (76) hide show
  1. mcp_security_framework/__init__.py +96 -0
  2. mcp_security_framework/cli/__init__.py +18 -0
  3. mcp_security_framework/cli/cert_cli.py +511 -0
  4. mcp_security_framework/cli/security_cli.py +791 -0
  5. mcp_security_framework/constants.py +209 -0
  6. mcp_security_framework/core/__init__.py +61 -0
  7. mcp_security_framework/core/auth_manager.py +1011 -0
  8. mcp_security_framework/core/cert_manager.py +1663 -0
  9. mcp_security_framework/core/permission_manager.py +735 -0
  10. mcp_security_framework/core/rate_limiter.py +602 -0
  11. mcp_security_framework/core/security_manager.py +943 -0
  12. mcp_security_framework/core/ssl_manager.py +735 -0
  13. mcp_security_framework/examples/__init__.py +75 -0
  14. mcp_security_framework/examples/django_example.py +615 -0
  15. mcp_security_framework/examples/fastapi_example.py +472 -0
  16. mcp_security_framework/examples/flask_example.py +506 -0
  17. mcp_security_framework/examples/gateway_example.py +803 -0
  18. mcp_security_framework/examples/microservice_example.py +690 -0
  19. mcp_security_framework/examples/standalone_example.py +576 -0
  20. mcp_security_framework/middleware/__init__.py +250 -0
  21. mcp_security_framework/middleware/auth_middleware.py +292 -0
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
  23. mcp_security_framework/middleware/fastapi_middleware.py +757 -0
  24. mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
  25. mcp_security_framework/middleware/flask_middleware.py +591 -0
  26. mcp_security_framework/middleware/mtls_middleware.py +439 -0
  27. mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
  28. mcp_security_framework/middleware/security_middleware.py +507 -0
  29. mcp_security_framework/schemas/__init__.py +109 -0
  30. mcp_security_framework/schemas/config.py +694 -0
  31. mcp_security_framework/schemas/models.py +709 -0
  32. mcp_security_framework/schemas/responses.py +686 -0
  33. mcp_security_framework/tests/__init__.py +0 -0
  34. mcp_security_framework/utils/__init__.py +121 -0
  35. mcp_security_framework/utils/cert_utils.py +525 -0
  36. mcp_security_framework/utils/crypto_utils.py +475 -0
  37. mcp_security_framework/utils/validation_utils.py +571 -0
  38. mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
  39. mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
  40. mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
  41. mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
  42. mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
  43. tests/__init__.py +0 -0
  44. tests/test_cli/__init__.py +0 -0
  45. tests/test_cli/test_cert_cli.py +379 -0
  46. tests/test_cli/test_security_cli.py +657 -0
  47. tests/test_core/__init__.py +0 -0
  48. tests/test_core/test_auth_manager.py +582 -0
  49. tests/test_core/test_cert_manager.py +795 -0
  50. tests/test_core/test_permission_manager.py +395 -0
  51. tests/test_core/test_rate_limiter.py +626 -0
  52. tests/test_core/test_security_manager.py +841 -0
  53. tests/test_core/test_ssl_manager.py +532 -0
  54. tests/test_examples/__init__.py +8 -0
  55. tests/test_examples/test_fastapi_example.py +264 -0
  56. tests/test_examples/test_flask_example.py +238 -0
  57. tests/test_examples/test_standalone_example.py +292 -0
  58. tests/test_integration/__init__.py +0 -0
  59. tests/test_integration/test_auth_flow.py +502 -0
  60. tests/test_integration/test_certificate_flow.py +527 -0
  61. tests/test_integration/test_fastapi_integration.py +341 -0
  62. tests/test_integration/test_flask_integration.py +398 -0
  63. tests/test_integration/test_standalone_integration.py +493 -0
  64. tests/test_middleware/__init__.py +0 -0
  65. tests/test_middleware/test_fastapi_middleware.py +523 -0
  66. tests/test_middleware/test_flask_middleware.py +582 -0
  67. tests/test_middleware/test_security_middleware.py +493 -0
  68. tests/test_schemas/__init__.py +0 -0
  69. tests/test_schemas/test_config.py +811 -0
  70. tests/test_schemas/test_models.py +879 -0
  71. tests/test_schemas/test_responses.py +1054 -0
  72. tests/test_schemas/test_serialization.py +493 -0
  73. tests/test_utils/__init__.py +0 -0
  74. tests/test_utils/test_cert_utils.py +510 -0
  75. tests/test_utils/test_crypto_utils.py +603 -0
  76. tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,465 @@
1
+ """
2
+ Flask Authentication Middleware Module
3
+
4
+ This module provides Flask-specific authentication middleware implementation
5
+ for the MCP Security Framework. It handles authentication-only processing
6
+ for Flask applications.
7
+
8
+ Key Features:
9
+ - Flask-specific request/response handling
10
+ - Authentication caching and optimization
11
+ - Framework-specific error responses
12
+ - Security event logging
13
+
14
+ Classes:
15
+ FlaskAuthMiddleware: Flask authentication middleware implementation
16
+
17
+ Dependencies:
18
+ - flask: For Flask request/response handling
19
+ - werkzeug: For WSGI utilities
20
+
21
+ Author: MCP Security Team
22
+ Version: 1.0.0
23
+ License: MIT
24
+ Created: 2024-01-15
25
+ Last Modified: 2024-01-20
26
+ """
27
+
28
+ import json
29
+ import logging
30
+ from typing import Any, Dict, Optional
31
+
32
+ from flask import Request, Response, jsonify, current_app
33
+ from werkzeug.exceptions import Unauthorized
34
+
35
+ from mcp_security_framework.middleware.auth_middleware import AuthMiddleware
36
+ from mcp_security_framework.schemas.models import AuthResult, AuthStatus, AuthMethod
37
+ from mcp_security_framework.schemas.config import SecurityConfig
38
+
39
+
40
+ class FlaskAuthMiddleware(AuthMiddleware):
41
+ """
42
+ Flask Authentication Middleware Implementation
43
+
44
+ This class provides Flask-specific authentication middleware that
45
+ handles authentication-only processing for Flask applications.
46
+
47
+ The middleware implements:
48
+ - Flask request/response handling
49
+ - Authentication caching and optimization
50
+ - Framework-specific error responses
51
+ - Security event logging
52
+
53
+ Attributes:
54
+ config (SecurityConfig): Security configuration settings
55
+ security_manager: Security manager instance
56
+ logger (Logger): Logger instance for authentication operations
57
+ _auth_cache (Dict): Authentication result cache
58
+
59
+ Example:
60
+ >>> from mcp_security_framework.middleware import create_auth_middleware
61
+ >>> middleware = create_auth_middleware(config, framework="flask")
62
+ >>> app.wsgi_app = middleware(app.wsgi_app)
63
+
64
+ Raises:
65
+ AuthMiddlewareError: When authentication processing fails
66
+ """
67
+
68
+ def __init__(self, config: SecurityConfig, security_manager: Any):
69
+ """
70
+ Initialize Flask Authentication Middleware.
71
+
72
+ Args:
73
+ config (SecurityConfig): Security configuration settings
74
+ security_manager: Security manager instance
75
+ """
76
+ super().__init__(config, security_manager)
77
+ self.logger = logging.getLogger(__name__)
78
+
79
+ def __call__(self, environ: Dict[str, Any], start_response: Any) -> Any:
80
+ """
81
+ Process Flask request through authentication middleware.
82
+
83
+ This method implements the authentication-only processing
84
+ pipeline for Flask requests, focusing solely on user
85
+ authentication without authorization checks.
86
+
87
+ Args:
88
+ environ (Dict[str, Any]): WSGI environment
89
+ start_response: WSGI start_response function
90
+
91
+ Returns:
92
+ Any: WSGI response
93
+
94
+ Raises:
95
+ AuthMiddlewareError: If authentication processing fails
96
+ """
97
+ try:
98
+ # Create Flask request object
99
+ request = Request(environ)
100
+
101
+ # Check if path is public (bypasses authentication)
102
+ if self._is_public_path(request):
103
+ return self._call_next(environ, start_response)
104
+
105
+ # Perform authentication
106
+ auth_result = self._authenticate_only(request)
107
+
108
+ if not auth_result.is_valid:
109
+ return self._auth_error_response(auth_result, start_response)
110
+
111
+ # Add authentication info to request context
112
+ request.auth_result = auth_result
113
+ request.username = auth_result.username
114
+ request.user_roles = auth_result.roles
115
+ request.auth_method = auth_result.auth_method
116
+
117
+ # Process request
118
+ response = self._call_next(environ, start_response)
119
+
120
+ # Log successful authentication
121
+ self._log_auth_event("authentication_successful", {
122
+ "ip_address": self._get_client_ip(request),
123
+ "username": auth_result.username,
124
+ "path": request.path,
125
+ "method": request.method,
126
+ "auth_method": auth_result.auth_method
127
+ })
128
+
129
+ return response
130
+
131
+ except Exception as e:
132
+ self.logger.error(
133
+ "Flask authentication middleware processing failed",
134
+ extra={"error": str(e)},
135
+ exc_info=True
136
+ )
137
+ raise AuthMiddlewareError(
138
+ f"Authentication processing failed: {str(e)}",
139
+ error_code=-32035
140
+ )
141
+
142
+ def _call_next(self, environ: Dict[str, Any], start_response: Any) -> Any:
143
+ """
144
+ Call the next WSGI application in the chain.
145
+
146
+ Args:
147
+ environ (Dict[str, Any]): WSGI environment
148
+ start_response: WSGI start_response function
149
+
150
+ Returns:
151
+ Any: WSGI response
152
+ """
153
+ # This would typically call the Flask app
154
+ # For now, return a simple response
155
+ status = '200 OK'
156
+ headers = [('Content-Type', 'application/json')]
157
+ start_response(status, headers)
158
+ return [json.dumps({"message": "OK"}).encode()]
159
+
160
+ def _is_public_path(self, request: Request) -> bool:
161
+ """
162
+ Check if the request path is public (bypasses authentication).
163
+
164
+ Args:
165
+ request (Request): Flask request object
166
+
167
+ Returns:
168
+ bool: True if path is public, False otherwise
169
+ """
170
+ path = request.path
171
+
172
+ # Check configured public paths
173
+ if hasattr(self.config.auth, 'public_paths'):
174
+ for public_path in self.config.auth.public_paths:
175
+ if path.startswith(public_path):
176
+ return True
177
+
178
+ # Check common public paths
179
+ public_paths = ["/health", "/status", "/metrics", "/docs", "/openapi.json"]
180
+ return any(path.startswith(public_path) for public_path in public_paths)
181
+
182
+ def _get_client_ip(self, request: Request) -> str:
183
+ """
184
+ Get client IP address from Flask request.
185
+
186
+ Args:
187
+ request (Request): Flask request object
188
+
189
+ Returns:
190
+ str: Client IP address
191
+ """
192
+ # Try X-Forwarded-For header
193
+ forwarded_for = request.headers.get("X-Forwarded-For")
194
+ if forwarded_for:
195
+ return forwarded_for.split(",")[0].strip()
196
+
197
+ # Try X-Real-IP header
198
+ real_ip = request.headers.get("X-Real-IP")
199
+ if real_ip:
200
+ return real_ip
201
+
202
+ # Use remote address
203
+ if request.remote_addr:
204
+ return request.remote_addr
205
+
206
+ # Fallback to default IP from config or environment
207
+ default_ip = getattr(self.config, 'default_client_ip', None)
208
+ if default_ip:
209
+ return default_ip
210
+
211
+ # Use environment variable or default
212
+ import os
213
+ from ..constants import DEFAULT_CLIENT_IP
214
+ return os.environ.get('DEFAULT_CLIENT_IP', DEFAULT_CLIENT_IP)
215
+
216
+ def _get_cache_key(self, request: Request) -> str:
217
+ """
218
+ Generate cache key for authentication result.
219
+
220
+ Args:
221
+ request (Request): Flask request object
222
+
223
+ Returns:
224
+ str: Cache key
225
+ """
226
+ # Use combination of IP and user agent for cache key
227
+ ip = self._get_client_ip(request)
228
+ user_agent = request.headers.get("User-Agent", "")
229
+ return f"auth:{ip}:{hash(user_agent)}"
230
+
231
+ def _try_auth_method(self, request: Request, method: str) -> AuthResult:
232
+ """
233
+ Try authentication using specific method with Flask request.
234
+
235
+ Args:
236
+ request (Request): Flask request object
237
+ method (str): Authentication method to try
238
+
239
+ Returns:
240
+ AuthResult: Authentication result
241
+ """
242
+ try:
243
+ if method == "api_key":
244
+ return self._try_api_key_auth(request)
245
+ elif method == "jwt":
246
+ return self._try_jwt_auth(request)
247
+ elif method == "certificate":
248
+ return self._try_certificate_auth(request)
249
+ elif method == "basic":
250
+ return self._try_basic_auth(request)
251
+ else:
252
+ return AuthResult(
253
+ is_valid=False,
254
+ status=AuthStatus.FAILED,
255
+ username=None,
256
+ roles=[],
257
+ auth_method=None,
258
+ error_code=-32022,
259
+ error_message=f"Unsupported authentication method: {method}"
260
+ )
261
+ except Exception as e:
262
+ self.logger.error(
263
+ f"Authentication method {method} failed",
264
+ extra={"error": str(e)},
265
+ exc_info=True
266
+ )
267
+ return AuthResult(
268
+ is_valid=False,
269
+ status=AuthStatus.FAILED,
270
+ username=None,
271
+ roles=[],
272
+ auth_method=None,
273
+ error_code=-32023,
274
+ error_message=f"Authentication method {method} failed: {str(e)}"
275
+ )
276
+
277
+ def _try_api_key_auth(self, request: Request) -> AuthResult:
278
+ """
279
+ Try API key authentication with Flask request.
280
+
281
+ Args:
282
+ request (Request): Flask request object
283
+
284
+ Returns:
285
+ AuthResult: Authentication result
286
+ """
287
+ # Try to get API key from headers
288
+ api_key = request.headers.get("X-API-Key")
289
+ if not api_key:
290
+ # Try Authorization header
291
+ auth_header = request.headers.get("Authorization")
292
+ if auth_header and auth_header.startswith("Bearer "):
293
+ api_key = auth_header[7:] # Remove "Bearer " prefix
294
+
295
+ if not api_key:
296
+ return AuthResult(
297
+ is_valid=False,
298
+ status=AuthStatus.FAILED,
299
+ username=None,
300
+ roles=[],
301
+ auth_method=AuthMethod.API_KEY,
302
+ error_code=-32012,
303
+ error_message="API key not found in request"
304
+ )
305
+
306
+ # Authenticate using security manager
307
+ return self.security_manager.auth_manager.authenticate_api_key(api_key)
308
+
309
+ def _try_jwt_auth(self, request: Request) -> AuthResult:
310
+ """
311
+ Try JWT authentication with Flask request.
312
+
313
+ Args:
314
+ request (Request): Flask request object
315
+
316
+ Returns:
317
+ AuthResult: Authentication result
318
+ """
319
+ # Try to get JWT token from Authorization header
320
+ auth_header = request.headers.get("Authorization")
321
+ if not auth_header or not auth_header.startswith("Bearer "):
322
+ return AuthResult(
323
+ is_valid=False,
324
+ status=AuthStatus.FAILED,
325
+ username=None,
326
+ roles=[],
327
+ auth_method=AuthMethod.JWT,
328
+ error_code=-32013,
329
+ error_message="JWT token not found in Authorization header"
330
+ )
331
+
332
+ token = auth_header[7:] # Remove "Bearer " prefix
333
+
334
+ # Authenticate using security manager
335
+ return self.security_manager.auth_manager.authenticate_jwt_token(token)
336
+
337
+ def _try_certificate_auth(self, request: Request) -> AuthResult:
338
+ """
339
+ Try certificate authentication with Flask request.
340
+
341
+ Args:
342
+ request (Request): Flask request object
343
+
344
+ Returns:
345
+ AuthResult: Authentication result
346
+ """
347
+ # Certificate authentication is typically handled at the SSL/TLS level
348
+ # This method would extract certificate information from the request
349
+ # For now, return not implemented
350
+ return AuthResult(
351
+ is_valid=False,
352
+ status=AuthStatus.FAILED,
353
+ username=None,
354
+ roles=[],
355
+ auth_method=AuthMethod.CERTIFICATE,
356
+ error_code=-32014,
357
+ error_message="Certificate authentication not implemented"
358
+ )
359
+
360
+ def _try_basic_auth(self, request: Request) -> AuthResult:
361
+ """
362
+ Try basic authentication with Flask request.
363
+
364
+ Args:
365
+ request (Request): Flask request object
366
+
367
+ Returns:
368
+ AuthResult: Authentication result
369
+ """
370
+ # Try to get basic auth credentials from Authorization header
371
+ auth_header = request.headers.get("Authorization")
372
+ if not auth_header or not auth_header.startswith("Basic "):
373
+ return AuthResult(
374
+ is_valid=False,
375
+ status=AuthStatus.FAILED,
376
+ username=None,
377
+ roles=[],
378
+ auth_method=AuthMethod.BASIC,
379
+ error_code=-32015,
380
+ error_message="Basic authentication credentials not found"
381
+ )
382
+
383
+ # Basic auth is not implemented in this version
384
+ return AuthResult(
385
+ is_valid=False,
386
+ status=AuthStatus.FAILED,
387
+ username=None,
388
+ roles=[],
389
+ auth_method=AuthMethod.BASIC,
390
+ error_code=-32016,
391
+ error_message="Basic authentication not implemented"
392
+ )
393
+
394
+ def _auth_error_response(self, auth_result: AuthResult, start_response: Any) -> Any:
395
+ """
396
+ Create authentication error response for Flask.
397
+
398
+ Args:
399
+ auth_result (AuthResult): Authentication result with error
400
+ start_response: WSGI start_response function
401
+
402
+ Returns:
403
+ Any: WSGI error response
404
+ """
405
+ error_data = {
406
+ "error": "Authentication failed",
407
+ "error_code": auth_result.error_code,
408
+ "error_message": auth_result.error_message,
409
+ "auth_method": auth_result.auth_method.value if auth_result.auth_method else None
410
+ }
411
+
412
+ status = '401 Unauthorized'
413
+ headers = [
414
+ ('Content-Type', 'application/json'),
415
+ ('WWW-Authenticate', 'Bearer')
416
+ ]
417
+
418
+ start_response(status, headers)
419
+ return [json.dumps(error_data).encode()]
420
+
421
+ def _log_auth_event(self, event_type: str, details: Dict[str, Any]) -> None:
422
+ """
423
+ Log authentication event.
424
+
425
+ Args:
426
+ event_type (str): Type of authentication event
427
+ details (Dict[str, Any]): Event details
428
+ """
429
+ try:
430
+ self.logger.info(
431
+ f"Authentication event: {event_type}",
432
+ extra={
433
+ "event_type": event_type,
434
+ "timestamp": details.get("timestamp"),
435
+ "ip_address": details.get("ip_address"),
436
+ "username": details.get("username"),
437
+ "path": details.get("path"),
438
+ "method": details.get("method"),
439
+ "auth_method": details.get("auth_method"),
440
+ **details
441
+ }
442
+ )
443
+ except Exception as e:
444
+ self.logger.error(
445
+ "Failed to log authentication event",
446
+ extra={"error": str(e)},
447
+ exc_info=True
448
+ )
449
+
450
+
451
+ class AuthMiddlewareError(Exception):
452
+ """
453
+ Authentication Middleware Error
454
+
455
+ This exception is raised when authentication middleware processing fails.
456
+
457
+ Attributes:
458
+ message (str): Error message
459
+ error_code (int): Error code for programmatic handling
460
+ """
461
+
462
+ def __init__(self, message: str, error_code: int = -32030):
463
+ self.message = message
464
+ self.error_code = error_code
465
+ super().__init__(self.message)