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