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