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,757 @@
1
+ """
2
+ FastAPI Security Middleware Module
3
+
4
+ This module provides FastAPI-specific security middleware implementation
5
+ that integrates with FastAPI's middleware system and request/response
6
+ handling.
7
+
8
+ Key Features:
9
+ - FastAPI-specific request/response processing
10
+ - Integration with FastAPI middleware system
11
+ - FastAPI-specific authentication methods
12
+ - FastAPI-specific error responses
13
+ - FastAPI-specific header management
14
+ - FastAPI-specific rate limiting
15
+
16
+ Classes:
17
+ FastAPISecurityMiddleware: FastAPI-specific security middleware
18
+ FastAPIMiddlewareError: FastAPI middleware-specific error exception
19
+
20
+ Author: MCP Security Team
21
+ Version: 1.0.0
22
+ License: MIT
23
+ """
24
+
25
+ import json
26
+ import logging
27
+ from typing import Any, Dict, List, Optional, Union
28
+
29
+ from fastapi import Request, Response, HTTPException, status
30
+ from fastapi.responses import JSONResponse
31
+
32
+ from .security_middleware import SecurityMiddleware, SecurityMiddlewareError
33
+ from ..schemas.models import AuthResult, AuthStatus, AuthMethod, ValidationResult, ValidationStatus
34
+
35
+
36
+ class FastAPIMiddlewareError(SecurityMiddlewareError):
37
+ """Raised when FastAPI middleware encounters an error."""
38
+
39
+ def __init__(self, message: str, error_code: int = -32008):
40
+ self.message = message
41
+ self.error_code = error_code
42
+ super().__init__(self.message)
43
+
44
+
45
+ class FastAPISecurityMiddleware(SecurityMiddleware):
46
+ """
47
+ FastAPI Security Middleware Class
48
+
49
+ This class provides FastAPI-specific implementation of the security
50
+ middleware. It integrates with FastAPI's middleware system and
51
+ handles FastAPI Request/Response objects.
52
+
53
+ The FastAPISecurityMiddleware implements:
54
+ - FastAPI-specific request processing
55
+ - FastAPI authentication method handling
56
+ - FastAPI response creation and modification
57
+ - FastAPI-specific error handling
58
+ - FastAPI header management
59
+ - FastAPI rate limiting integration
60
+
61
+ Key Responsibilities:
62
+ - Process FastAPI requests through security pipeline
63
+ - Extract authentication credentials from FastAPI requests
64
+ - Create FastAPI-specific error responses
65
+ - Add security headers to FastAPI responses
66
+ - Handle FastAPI-specific request/response objects
67
+ - Integrate with FastAPI middleware system
68
+
69
+ Attributes:
70
+ Inherits all attributes from SecurityMiddleware
71
+ _fastapi_app: Reference to FastAPI application (if available)
72
+
73
+ Example:
74
+ >>> from fastapi import FastAPI
75
+ >>> from mcp_security_framework.middleware import FastAPISecurityMiddleware
76
+ >>>
77
+ >>> app = FastAPI()
78
+ >>> security_manager = SecurityManager(config)
79
+ >>> middleware = FastAPISecurityMiddleware(security_manager)
80
+ >>> app.add_middleware(middleware)
81
+
82
+ Note:
83
+ This middleware should be added to FastAPI applications using
84
+ the add_middleware method or as a dependency.
85
+ """
86
+
87
+ def __init__(self, security_manager=None, app=None):
88
+ """
89
+ Initialize FastAPI Security Middleware.
90
+
91
+ Args:
92
+ security_manager: Security manager instance containing
93
+ all security components and configuration.
94
+ app: FastAPI application instance (optional, for compatibility)
95
+
96
+ Raises:
97
+ FastAPIMiddlewareError: If initialization fails
98
+ """
99
+ if security_manager is None:
100
+ raise FastAPIMiddlewareError("Security manager is required")
101
+
102
+ super().__init__(security_manager)
103
+ self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
104
+ self.app = app
105
+
106
+ self.logger.info("FastAPI Security Middleware initialized")
107
+
108
+ async def __call__(self, request: Request, call_next):
109
+ """
110
+ Process FastAPI request through security middleware.
111
+
112
+ This method implements the security processing pipeline for
113
+ FastAPI requests, including rate limiting, authentication,
114
+ authorization, and security header management.
115
+
116
+ Args:
117
+ request (Request): FastAPI request object
118
+ call_next: FastAPI call_next function
119
+
120
+ Returns:
121
+ Response: FastAPI response object
122
+
123
+ Raises:
124
+ HTTPException: For security violations (rate limit, auth, permissions)
125
+ FastAPIMiddlewareError: For middleware processing errors
126
+ """
127
+ try:
128
+ # Check if public path first (no rate limiting for public paths)
129
+ if self._is_public_path(request):
130
+ response = await call_next(request)
131
+ self._add_security_headers(response)
132
+ return response
133
+
134
+ # Check rate limit
135
+ if not self._check_rate_limit(request):
136
+ return self._rate_limit_response()
137
+
138
+ # Authenticate request
139
+ auth_result = await self._authenticate_request(request)
140
+ if not auth_result.is_valid:
141
+ return self._auth_error_response(auth_result)
142
+
143
+ # Validate permissions
144
+ if not self._validate_permissions(request, auth_result):
145
+ return self._permission_error_response()
146
+
147
+ # Process request
148
+ response = await call_next(request)
149
+ self._add_security_headers(response)
150
+
151
+ # Log successful request
152
+ self._log_security_event("request_processed", {
153
+ "ip_address": self._get_client_ip(request),
154
+ "username": auth_result.username,
155
+ "path": str(request.url.path),
156
+ "method": request.method,
157
+ "status_code": response.status_code
158
+ })
159
+
160
+ return response
161
+
162
+ except HTTPException:
163
+ # Re-raise FastAPI HTTP exceptions
164
+ raise
165
+ except Exception as e:
166
+ self.logger.error(
167
+ "FastAPI middleware processing failed",
168
+ extra={"error": str(e)},
169
+ exc_info=True
170
+ )
171
+ raise FastAPIMiddlewareError(
172
+ f"Middleware processing failed: {str(e)}",
173
+ error_code=-32009
174
+ )
175
+
176
+ def _check_rate_limit(self, request: Request) -> bool:
177
+ """Check rate limit for request."""
178
+ identifier = self._get_rate_limit_identifier(request)
179
+ return self.security_manager.check_rate_limit(identifier)
180
+
181
+ def _is_public_path(self, request: Request) -> bool:
182
+ """Check if request path is public."""
183
+ path = str(request.url.path)
184
+ return path in self.config.auth.public_paths
185
+
186
+ async def _authenticate_request(self, request: Request) -> AuthResult:
187
+ """Authenticate request using configured methods."""
188
+ # Try API key authentication first
189
+ auth_result = self._try_api_key_auth(request)
190
+ if auth_result.is_valid:
191
+ return auth_result
192
+
193
+ # Try JWT authentication
194
+ auth_result = self._try_jwt_auth(request)
195
+ if auth_result.is_valid:
196
+ return auth_result
197
+
198
+ # Return failed authentication
199
+ return AuthResult(
200
+ is_valid=False,
201
+ status=AuthStatus.INVALID,
202
+ auth_method="unknown",
203
+ error_code=-32001,
204
+ error_message="Authentication required"
205
+ )
206
+
207
+ def _validate_permissions(self, request: Request, auth_result: AuthResult) -> bool:
208
+ """Validate permissions for request."""
209
+ # Get required permissions from request state or default
210
+ required_permissions = self._get_required_permissions_from_state(request)
211
+ if not required_permissions:
212
+ return True # No permissions required
213
+
214
+ # Check permissions
215
+ validation_result = self.security_manager.check_permissions(
216
+ auth_result.roles, required_permissions
217
+ )
218
+ return validation_result.is_valid
219
+
220
+ def _rate_limit_response(self) -> JSONResponse:
221
+ """Create rate limit exceeded response."""
222
+ return JSONResponse(
223
+ status_code=status.HTTP_429_TOO_MANY_REQUESTS,
224
+ content={
225
+ "error": "Rate limit exceeded",
226
+ "error_code": -32010,
227
+ "retry_after": 60
228
+ }
229
+ )
230
+
231
+ def _auth_error_response(self, auth_result: AuthResult) -> JSONResponse:
232
+ """Create authentication error response."""
233
+ return JSONResponse(
234
+ status_code=status.HTTP_401_UNAUTHORIZED,
235
+ content={
236
+ "error": "Authentication failed",
237
+ "error_code": auth_result.error_code,
238
+ "error_message": auth_result.error_message
239
+ }
240
+ )
241
+
242
+ def _permission_error_response(self) -> JSONResponse:
243
+ """Create permission denied response."""
244
+ return JSONResponse(
245
+ status_code=status.HTTP_403_FORBIDDEN,
246
+ content={
247
+ "error": "Permission denied",
248
+ "error_code": -32004,
249
+ "error_message": "Insufficient permissions"
250
+ }
251
+ )
252
+
253
+ def _add_security_headers(self, response: Response) -> None:
254
+ """Add security headers to response."""
255
+ response.headers["X-Content-Type-Options"] = "nosniff"
256
+ response.headers["X-Frame-Options"] = "DENY"
257
+ response.headers["X-XSS-Protection"] = "1; mode=block"
258
+ response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
259
+
260
+ def _log_security_event(self, event_type: str, data: Dict[str, Any]) -> None:
261
+ """Log security event."""
262
+ self.logger.info(
263
+ f"Security event: {event_type}",
264
+ extra=data
265
+ )
266
+
267
+ def _get_required_permissions_from_state(self, request: Request) -> List[str]:
268
+ """Get required permissions from request state."""
269
+ # This would typically be set by route decorators or middleware
270
+ # For now, return empty list (no permissions required)
271
+ return []
272
+
273
+ def _get_required_permissions_default(self, request: Request) -> List[str]:
274
+ """Get default required permissions for request."""
275
+ # Default permissions based on HTTP method
276
+ method_permissions = {
277
+ "GET": ["read"],
278
+ "POST": ["write"],
279
+ "PUT": ["write"],
280
+ "DELETE": ["delete"],
281
+ "PATCH": ["write"]
282
+ }
283
+ return method_permissions.get(request.method, [])
284
+
285
+ def _try_api_key_auth(self, request: Request) -> AuthResult:
286
+ """Try API key authentication."""
287
+ # Check for API key in headers
288
+ api_key = request.headers.get("X-API-Key")
289
+ if api_key:
290
+ return self.security_manager.auth_manager.authenticate_api_key(api_key)
291
+
292
+ # Check for API key in Authorization header
293
+ auth_header = request.headers.get("Authorization")
294
+ if auth_header and auth_header.startswith("Bearer "):
295
+ api_key = auth_header[7:] # Remove "Bearer " prefix
296
+ return self.security_manager.auth_manager.authenticate_api_key(api_key)
297
+
298
+ return AuthResult(
299
+ is_valid=False,
300
+ status=AuthStatus.INVALID,
301
+ auth_method="api_key",
302
+ error_code=-32001,
303
+ error_message="API key not provided"
304
+ )
305
+
306
+ def _try_jwt_auth(self, request: Request) -> AuthResult:
307
+ """Try JWT authentication."""
308
+ auth_header = request.headers.get("Authorization")
309
+ if not auth_header or not auth_header.startswith("Bearer "):
310
+ return AuthResult(
311
+ is_valid=False,
312
+ status=AuthStatus.INVALID,
313
+ auth_method="jwt",
314
+ error_code=-32001,
315
+ error_message="JWT token not provided"
316
+ )
317
+
318
+ token = auth_header[7:] # Remove "Bearer " prefix
319
+ return self.security_manager.auth_manager.authenticate_jwt_token(token)
320
+
321
+ def _get_client_ip(self, request: Request) -> str:
322
+ """Get client IP address from request."""
323
+ # Check for forwarded headers
324
+ forwarded_for = request.headers.get("X-Forwarded-For")
325
+ if forwarded_for:
326
+ return forwarded_for.split(",")[0].strip()
327
+
328
+ # Check for real IP header
329
+ real_ip = request.headers.get("X-Real-IP")
330
+ if real_ip:
331
+ return real_ip
332
+
333
+ # Check for client host
334
+ if request.client and request.client.host:
335
+ return request.client.host
336
+
337
+ # Fallback
338
+ return "unknown"
339
+
340
+ def _get_rate_limit_identifier(self, request: Request) -> str:
341
+ """
342
+ Get rate limit identifier from FastAPI request.
343
+
344
+ This method extracts the rate limit identifier from the FastAPI
345
+ request, typically using the client IP address.
346
+
347
+ Args:
348
+ request (Request): FastAPI request object
349
+
350
+ Returns:
351
+ str: Rate limit identifier (IP address)
352
+ """
353
+ return self._get_client_ip(request)
354
+
355
+ def _get_request_path(self, request: Request) -> str:
356
+ """
357
+ Get request path from FastAPI request.
358
+
359
+ Args:
360
+ request (Request): FastAPI request object
361
+
362
+ Returns:
363
+ str: Request path
364
+ """
365
+ return str(request.url.path)
366
+
367
+ def _get_required_permissions(self, request: Request) -> List[str]:
368
+ """
369
+ Get required permissions for FastAPI request.
370
+
371
+ This method extracts required permissions from the FastAPI request,
372
+ typically from route dependencies or request state.
373
+
374
+ Args:
375
+ request (Request): FastAPI request object
376
+
377
+ Returns:
378
+ List[str]: List of required permissions
379
+ """
380
+ # Try to get permissions from request state
381
+ if hasattr(request.state, 'required_permissions'):
382
+ return request.state.required_permissions
383
+
384
+ # Try to get permissions from route dependencies
385
+ if hasattr(request, 'route') and hasattr(request.route, 'dependencies'):
386
+ # Check if route has permission dependencies
387
+ dependencies = request.route.dependencies
388
+ for dep in dependencies:
389
+ if hasattr(dep, 'dependency') and hasattr(dep.dependency, 'required_permissions'):
390
+ return dep.dependency.required_permissions
391
+ # Check for permission decorators
392
+ if hasattr(dep, 'dependency') and hasattr(dep.dependency, '__permissions__'):
393
+ return dep.dependency.__permissions__
394
+
395
+ # Default: no specific permissions required
396
+ return []
397
+
398
+ async def _authenticate_request(self, request: Request) -> AuthResult:
399
+ """
400
+ Authenticate the request using configured authentication methods.
401
+
402
+ This method attempts to authenticate the request using all configured
403
+ authentication methods in order until one succeeds.
404
+
405
+ Args:
406
+ request (Request): FastAPI request object
407
+
408
+ Returns:
409
+ AuthResult: Authentication result
410
+
411
+ Raises:
412
+ SecurityMiddlewareError: If authentication process fails
413
+ """
414
+ try:
415
+ if not self.config.auth.enabled:
416
+ return AuthResult(
417
+ is_valid=True,
418
+ status=AuthStatus.SUCCESS,
419
+ username="anonymous",
420
+ roles=[],
421
+ auth_method=None
422
+ )
423
+
424
+ # Try each authentication method in order
425
+ for method in self.config.auth.methods:
426
+ auth_result = await self._try_auth_method(request, method)
427
+ if auth_result.is_valid:
428
+ self.logger.info(
429
+ "Authentication successful",
430
+ extra={
431
+ "username": auth_result.username,
432
+ "auth_method": auth_result.auth_method,
433
+ "user_roles": auth_result.roles
434
+ }
435
+ )
436
+ return auth_result
437
+
438
+ # All authentication methods failed
439
+ self.logger.warning(
440
+ "All authentication methods failed",
441
+ extra={"auth_methods": self.config.auth.methods}
442
+ )
443
+
444
+ return AuthResult(
445
+ is_valid=False,
446
+ status=AuthStatus.FAILED,
447
+ username=None,
448
+ roles=[],
449
+ auth_method=None,
450
+ error_code=-32005,
451
+ error_message="All authentication methods failed"
452
+ )
453
+
454
+ except Exception as e:
455
+ self.logger.error(
456
+ "Authentication process failed",
457
+ extra={"error": str(e)},
458
+ exc_info=True
459
+ )
460
+ raise SecurityMiddlewareError(
461
+ f"Authentication process failed: {str(e)}",
462
+ error_code=-32006
463
+ )
464
+
465
+ async def _try_auth_method(self, request: Request, method: str) -> AuthResult:
466
+ """
467
+ Try authentication using specific method with FastAPI request.
468
+
469
+ This method attempts to authenticate the FastAPI request using
470
+ the specified authentication method.
471
+
472
+ Args:
473
+ request (Request): FastAPI request object
474
+ method (str): Authentication method to try
475
+
476
+ Returns:
477
+ AuthResult: Authentication result
478
+ """
479
+ try:
480
+ if method == "api_key":
481
+ return await self._try_api_key_auth(request)
482
+ elif method == "jwt":
483
+ return await self._try_jwt_auth(request)
484
+ elif method == "certificate":
485
+ return await self._try_certificate_auth(request)
486
+ elif method == "basic":
487
+ return await self._try_basic_auth(request)
488
+ else:
489
+ return AuthResult(
490
+ is_valid=False,
491
+ status=AuthStatus.FAILED,
492
+ username=None,
493
+ roles=[],
494
+ auth_method=None,
495
+ error_code=-32010,
496
+ error_message=f"Unsupported authentication method: {method}"
497
+ )
498
+ except Exception as e:
499
+ self.logger.error(
500
+ f"Authentication method {method} failed",
501
+ extra={"error": str(e)},
502
+ exc_info=True
503
+ )
504
+ return AuthResult(
505
+ is_valid=False,
506
+ status=AuthStatus.FAILED,
507
+ username=None,
508
+ roles=[],
509
+ auth_method=None,
510
+ error_code=-32011,
511
+ error_message=f"Authentication method {method} failed: {str(e)}"
512
+ )
513
+
514
+ async def _try_api_key_auth(self, request: Request) -> AuthResult:
515
+ """
516
+ Try API key authentication with FastAPI request.
517
+
518
+ Args:
519
+ request (Request): FastAPI request object
520
+
521
+ Returns:
522
+ AuthResult: Authentication result
523
+ """
524
+ # Try to get API key from headers
525
+ api_key = request.headers.get("X-API-Key")
526
+ if not api_key:
527
+ # Try Authorization header
528
+ auth_header = request.headers.get("Authorization")
529
+ if auth_header and auth_header.startswith("Bearer "):
530
+ api_key = auth_header[7:] # Remove "Bearer " prefix
531
+
532
+ if not api_key:
533
+ return AuthResult(
534
+ is_valid=False,
535
+ status=AuthStatus.FAILED,
536
+ username=None,
537
+ roles=[],
538
+ auth_method=AuthMethod.API_KEY,
539
+ error_code=-32012,
540
+ error_message="API key not found in request"
541
+ )
542
+
543
+ # Authenticate using security manager
544
+ return self.security_manager.auth_manager.authenticate_api_key(api_key)
545
+
546
+ async def _try_jwt_auth(self, request: Request) -> AuthResult:
547
+ """
548
+ Try JWT authentication with FastAPI request.
549
+
550
+ Args:
551
+ request (Request): FastAPI request object
552
+
553
+ Returns:
554
+ AuthResult: Authentication result
555
+ """
556
+ # Try to get JWT token from Authorization header
557
+ auth_header = request.headers.get("Authorization")
558
+ if not auth_header or not auth_header.startswith("Bearer "):
559
+ return AuthResult(
560
+ is_valid=False,
561
+ status=AuthStatus.FAILED,
562
+ username=None,
563
+ roles=[],
564
+ auth_method=AuthMethod.JWT,
565
+ error_code=-32013,
566
+ error_message="JWT token not found in Authorization header"
567
+ )
568
+
569
+ token = auth_header[7:] # Remove "Bearer " prefix
570
+
571
+ # Authenticate using security manager
572
+ return self.security_manager.auth_manager.authenticate_jwt_token(token)
573
+
574
+ async def _try_certificate_auth(self, request: Request) -> AuthResult:
575
+ """
576
+ Try certificate authentication with FastAPI request.
577
+
578
+ Args:
579
+ request (Request): FastAPI request object
580
+
581
+ Returns:
582
+ AuthResult: Authentication result
583
+ """
584
+ # For certificate authentication, we would typically need
585
+ # to access the client certificate from the SSL context
586
+ # This is more complex and depends on the SSL configuration
587
+
588
+ # For now, return not implemented
589
+ return AuthResult(
590
+ is_valid=False,
591
+ status=AuthStatus.FAILED,
592
+ username=None,
593
+ roles=[],
594
+ auth_method=AuthMethod.CERTIFICATE,
595
+ error_code=-32014,
596
+ error_message="Certificate authentication not implemented"
597
+ )
598
+
599
+ async def _try_basic_auth(self, request: Request) -> AuthResult:
600
+ """
601
+ Try basic authentication with FastAPI request.
602
+
603
+ Args:
604
+ request (Request): FastAPI request object
605
+
606
+ Returns:
607
+ AuthResult: Authentication result
608
+ """
609
+ # Try to get basic auth from Authorization header
610
+ auth_header = request.headers.get("Authorization")
611
+ if not auth_header or not auth_header.startswith("Basic "):
612
+ return AuthResult(
613
+ is_valid=False,
614
+ status=AuthStatus.FAILED,
615
+ username=None,
616
+ roles=[],
617
+ auth_method=AuthMethod.BASIC,
618
+ error_code=-32015,
619
+ error_message="Basic authentication credentials not found"
620
+ )
621
+
622
+ # Basic auth implementation would go here
623
+ # For now, return not implemented
624
+ return AuthResult(
625
+ is_valid=False,
626
+ status=AuthStatus.FAILED,
627
+ username=None,
628
+ roles=[],
629
+ auth_method=AuthMethod.BASIC,
630
+ error_code=-32016,
631
+ error_message="Basic authentication not implemented"
632
+ )
633
+
634
+ def _apply_security_headers(self, response: Response, headers: Dict[str, str]) -> None:
635
+ """
636
+ Apply security headers to FastAPI response.
637
+
638
+ Args:
639
+ response (Response): FastAPI response object
640
+ headers (Dict[str, str]): Headers to apply
641
+ """
642
+ for header_name, header_value in headers.items():
643
+ response.headers[header_name] = header_value
644
+
645
+ def _create_error_response(self, status_code: int, message: str) -> Response:
646
+ """
647
+ Create error response for security violations.
648
+
649
+ Args:
650
+ status_code (int): HTTP status code
651
+ message (str): Error message
652
+
653
+ Returns:
654
+ Response: FastAPI error response
655
+ """
656
+ return JSONResponse(
657
+ status_code=status_code,
658
+ content={
659
+ "error": "Security violation",
660
+ "message": message,
661
+ "error_code": -32017
662
+ }
663
+ )
664
+
665
+ def _rate_limit_response(self) -> Response:
666
+ """
667
+ Create rate limit exceeded response.
668
+
669
+ Returns:
670
+ Response: FastAPI rate limit response
671
+ """
672
+ return JSONResponse(
673
+ status_code=status.HTTP_429_TOO_MANY_REQUESTS,
674
+ content={
675
+ "error": "Rate limit exceeded",
676
+ "message": "Too many requests, please try again later",
677
+ "error_code": -32018
678
+ },
679
+ headers={
680
+ "Retry-After": str(self.config.rate_limit.window_size_seconds)
681
+ }
682
+ )
683
+
684
+ def _auth_error_response(self, auth_result: AuthResult) -> Response:
685
+ """
686
+ Create authentication error response.
687
+
688
+ Args:
689
+ auth_result (AuthResult): Authentication result
690
+
691
+ Returns:
692
+ Response: FastAPI authentication error response
693
+ """
694
+ return JSONResponse(
695
+ status_code=status.HTTP_401_UNAUTHORIZED,
696
+ content={
697
+ "error": "Authentication failed",
698
+ "message": auth_result.error_message or "Invalid credentials",
699
+ "error_code": auth_result.error_code,
700
+ "auth_method": auth_result.auth_method
701
+ },
702
+ headers={
703
+ "WWW-Authenticate": "Bearer, ApiKey"
704
+ }
705
+ )
706
+
707
+ def _permission_error_response(self) -> Response:
708
+ """
709
+ Create permission denied response.
710
+
711
+ Returns:
712
+ Response: FastAPI permission error response
713
+ """
714
+ return JSONResponse(
715
+ status_code=status.HTTP_403_FORBIDDEN,
716
+ content={
717
+ "error": "Permission denied",
718
+ "message": "Insufficient permissions to access this resource",
719
+ "error_code": -32019
720
+ }
721
+ )
722
+
723
+ def _get_client_ip(self, request: Request) -> str:
724
+ """
725
+ Get client IP address from FastAPI request.
726
+
727
+ Args:
728
+ request (Request): FastAPI request object
729
+
730
+ Returns:
731
+ str: Client IP address
732
+ """
733
+ # Try to get IP from X-Forwarded-For header (for proxy scenarios)
734
+ forwarded_for = request.headers.get("X-Forwarded-For")
735
+ if forwarded_for:
736
+ # Take the first IP in the chain
737
+ return forwarded_for.split(",")[0].strip()
738
+
739
+ # Try to get IP from X-Real-IP header
740
+ real_ip = request.headers.get("X-Real-IP")
741
+ if real_ip:
742
+ return real_ip
743
+
744
+ # Fall back to client host
745
+ if request.client:
746
+ return request.client.host
747
+
748
+ # Default fallback
749
+ # Fallback to default IP from config or environment
750
+ default_ip = getattr(self.config, 'default_client_ip', None)
751
+ if default_ip:
752
+ return default_ip
753
+
754
+ # Use environment variable or default
755
+ import os
756
+ from ..constants import DEFAULT_CLIENT_IP
757
+ return os.environ.get('DEFAULT_CLIENT_IP', DEFAULT_CLIENT_IP)