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
@@ -27,165 +27,167 @@ Last Modified: 2024-01-20
|
|
27
27
|
|
28
28
|
import json
|
29
29
|
import logging
|
30
|
-
from typing import Any, Dict, Optional
|
30
|
+
from typing import Any, Dict, List, Optional
|
31
31
|
|
32
|
-
from flask import Request, Response,
|
32
|
+
from flask import Request, Response, current_app, jsonify
|
33
33
|
from werkzeug.exceptions import Unauthorized
|
34
34
|
|
35
35
|
from mcp_security_framework.middleware.auth_middleware import AuthMiddleware
|
36
|
-
from mcp_security_framework.schemas.models import AuthResult, AuthStatus, AuthMethod
|
37
36
|
from mcp_security_framework.schemas.config import SecurityConfig
|
37
|
+
from mcp_security_framework.schemas.models import AuthMethod, AuthResult, AuthStatus
|
38
38
|
|
39
39
|
|
40
40
|
class FlaskAuthMiddleware(AuthMiddleware):
|
41
41
|
"""
|
42
42
|
Flask Authentication Middleware Implementation
|
43
|
-
|
43
|
+
|
44
44
|
This class provides Flask-specific authentication middleware that
|
45
45
|
handles authentication-only processing for Flask applications.
|
46
|
-
|
46
|
+
|
47
47
|
The middleware implements:
|
48
48
|
- Flask request/response handling
|
49
49
|
- Authentication caching and optimization
|
50
50
|
- Framework-specific error responses
|
51
51
|
- Security event logging
|
52
|
-
|
52
|
+
|
53
53
|
Attributes:
|
54
54
|
config (SecurityConfig): Security configuration settings
|
55
55
|
security_manager: Security manager instance
|
56
56
|
logger (Logger): Logger instance for authentication operations
|
57
57
|
_auth_cache (Dict): Authentication result cache
|
58
|
-
|
58
|
+
|
59
59
|
Example:
|
60
60
|
>>> from mcp_security_framework.middleware import create_auth_middleware
|
61
61
|
>>> middleware = create_auth_middleware(config, framework="flask")
|
62
62
|
>>> app.wsgi_app = middleware(app.wsgi_app)
|
63
|
-
|
63
|
+
|
64
64
|
Raises:
|
65
65
|
AuthMiddlewareError: When authentication processing fails
|
66
66
|
"""
|
67
|
-
|
68
|
-
def __init__(self,
|
67
|
+
|
68
|
+
def __init__(self, security_manager: Any):
|
69
69
|
"""
|
70
70
|
Initialize Flask Authentication Middleware.
|
71
|
-
|
71
|
+
|
72
72
|
Args:
|
73
|
-
|
74
|
-
|
73
|
+
security_manager: Security manager instance containing
|
74
|
+
all security components and configuration.
|
75
75
|
"""
|
76
|
-
super().__init__(
|
76
|
+
super().__init__(security_manager)
|
77
77
|
self.logger = logging.getLogger(__name__)
|
78
|
-
|
78
|
+
|
79
79
|
def __call__(self, environ: Dict[str, Any], start_response: Any) -> Any:
|
80
80
|
"""
|
81
81
|
Process Flask request through authentication middleware.
|
82
|
-
|
82
|
+
|
83
83
|
This method implements the authentication-only processing
|
84
84
|
pipeline for Flask requests, focusing solely on user
|
85
85
|
authentication without authorization checks.
|
86
|
-
|
86
|
+
|
87
87
|
Args:
|
88
88
|
environ (Dict[str, Any]): WSGI environment
|
89
89
|
start_response: WSGI start_response function
|
90
|
-
|
90
|
+
|
91
91
|
Returns:
|
92
92
|
Any: WSGI response
|
93
|
-
|
93
|
+
|
94
94
|
Raises:
|
95
95
|
AuthMiddlewareError: If authentication processing fails
|
96
96
|
"""
|
97
97
|
try:
|
98
98
|
# Create Flask request object
|
99
99
|
request = Request(environ)
|
100
|
-
|
100
|
+
|
101
101
|
# Check if path is public (bypasses authentication)
|
102
102
|
if self._is_public_path(request):
|
103
103
|
return self._call_next(environ, start_response)
|
104
|
-
|
104
|
+
|
105
105
|
# Perform authentication
|
106
106
|
auth_result = self._authenticate_only(request)
|
107
|
-
|
107
|
+
|
108
108
|
if not auth_result.is_valid:
|
109
109
|
return self._auth_error_response(auth_result, start_response)
|
110
|
-
|
110
|
+
|
111
111
|
# Add authentication info to request context
|
112
112
|
request.auth_result = auth_result
|
113
113
|
request.username = auth_result.username
|
114
114
|
request.user_roles = auth_result.roles
|
115
115
|
request.auth_method = auth_result.auth_method
|
116
|
-
|
116
|
+
|
117
117
|
# Process request
|
118
118
|
response = self._call_next(environ, start_response)
|
119
|
-
|
119
|
+
|
120
120
|
# Log successful authentication
|
121
|
-
self._log_auth_event(
|
122
|
-
"
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
121
|
+
self._log_auth_event(
|
122
|
+
"authentication_successful",
|
123
|
+
{
|
124
|
+
"ip_address": self._get_client_ip(request),
|
125
|
+
"username": auth_result.username,
|
126
|
+
"path": request.path,
|
127
|
+
"method": request.method,
|
128
|
+
"auth_method": auth_result.auth_method,
|
129
|
+
},
|
130
|
+
)
|
131
|
+
|
129
132
|
return response
|
130
|
-
|
133
|
+
|
131
134
|
except Exception as e:
|
132
135
|
self.logger.error(
|
133
136
|
"Flask authentication middleware processing failed",
|
134
137
|
extra={"error": str(e)},
|
135
|
-
exc_info=True
|
138
|
+
exc_info=True,
|
136
139
|
)
|
137
140
|
raise AuthMiddlewareError(
|
138
|
-
f"Authentication processing failed: {str(e)}",
|
139
|
-
error_code=-32035
|
141
|
+
f"Authentication processing failed: {str(e)}", error_code=-32035
|
140
142
|
)
|
141
|
-
|
143
|
+
|
142
144
|
def _call_next(self, environ: Dict[str, Any], start_response: Any) -> Any:
|
143
145
|
"""
|
144
146
|
Call the next WSGI application in the chain.
|
145
|
-
|
147
|
+
|
146
148
|
Args:
|
147
149
|
environ (Dict[str, Any]): WSGI environment
|
148
150
|
start_response: WSGI start_response function
|
149
|
-
|
151
|
+
|
150
152
|
Returns:
|
151
153
|
Any: WSGI response
|
152
154
|
"""
|
153
155
|
# This would typically call the Flask app
|
154
156
|
# For now, return a simple response
|
155
|
-
status =
|
156
|
-
headers = [(
|
157
|
+
status = "200 OK"
|
158
|
+
headers = [("Content-Type", "application/json")]
|
157
159
|
start_response(status, headers)
|
158
160
|
return [json.dumps({"message": "OK"}).encode()]
|
159
|
-
|
161
|
+
|
160
162
|
def _is_public_path(self, request: Request) -> bool:
|
161
163
|
"""
|
162
164
|
Check if the request path is public (bypasses authentication).
|
163
|
-
|
165
|
+
|
164
166
|
Args:
|
165
167
|
request (Request): Flask request object
|
166
|
-
|
168
|
+
|
167
169
|
Returns:
|
168
170
|
bool: True if path is public, False otherwise
|
169
171
|
"""
|
170
172
|
path = request.path
|
171
|
-
|
173
|
+
|
172
174
|
# Check configured public paths
|
173
|
-
if hasattr(self.config.auth,
|
175
|
+
if hasattr(self.config.auth, "public_paths"):
|
174
176
|
for public_path in self.config.auth.public_paths:
|
175
177
|
if path.startswith(public_path):
|
176
178
|
return True
|
177
|
-
|
179
|
+
|
178
180
|
# Check common public paths
|
179
181
|
public_paths = ["/health", "/status", "/metrics", "/docs", "/openapi.json"]
|
180
182
|
return any(path.startswith(public_path) for public_path in public_paths)
|
181
|
-
|
183
|
+
|
182
184
|
def _get_client_ip(self, request: Request) -> str:
|
183
185
|
"""
|
184
186
|
Get client IP address from Flask request.
|
185
|
-
|
187
|
+
|
186
188
|
Args:
|
187
189
|
request (Request): Flask request object
|
188
|
-
|
190
|
+
|
189
191
|
Returns:
|
190
192
|
str: Client IP address
|
191
193
|
"""
|
@@ -193,33 +195,35 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
193
195
|
forwarded_for = request.headers.get("X-Forwarded-For")
|
194
196
|
if forwarded_for:
|
195
197
|
return forwarded_for.split(",")[0].strip()
|
196
|
-
|
198
|
+
|
197
199
|
# Try X-Real-IP header
|
198
200
|
real_ip = request.headers.get("X-Real-IP")
|
199
201
|
if real_ip:
|
200
202
|
return real_ip
|
201
|
-
|
203
|
+
|
202
204
|
# Use remote address
|
203
205
|
if request.remote_addr:
|
204
206
|
return request.remote_addr
|
205
|
-
|
207
|
+
|
206
208
|
# Fallback to default IP from config or environment
|
207
|
-
default_ip = getattr(self.config,
|
209
|
+
default_ip = getattr(self.config, "default_client_ip", None)
|
208
210
|
if default_ip:
|
209
211
|
return default_ip
|
210
|
-
|
212
|
+
|
211
213
|
# Use environment variable or default
|
212
214
|
import os
|
215
|
+
|
213
216
|
from ..constants import DEFAULT_CLIENT_IP
|
214
|
-
|
215
|
-
|
217
|
+
|
218
|
+
return os.environ.get("DEFAULT_CLIENT_IP", DEFAULT_CLIENT_IP)
|
219
|
+
|
216
220
|
def _get_cache_key(self, request: Request) -> str:
|
217
221
|
"""
|
218
222
|
Generate cache key for authentication result.
|
219
|
-
|
223
|
+
|
220
224
|
Args:
|
221
225
|
request (Request): Flask request object
|
222
|
-
|
226
|
+
|
223
227
|
Returns:
|
224
228
|
str: Cache key
|
225
229
|
"""
|
@@ -227,15 +231,15 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
227
231
|
ip = self._get_client_ip(request)
|
228
232
|
user_agent = request.headers.get("User-Agent", "")
|
229
233
|
return f"auth:{ip}:{hash(user_agent)}"
|
230
|
-
|
234
|
+
|
231
235
|
def _try_auth_method(self, request: Request, method: str) -> AuthResult:
|
232
236
|
"""
|
233
237
|
Try authentication using specific method with Flask request.
|
234
|
-
|
238
|
+
|
235
239
|
Args:
|
236
240
|
request (Request): Flask request object
|
237
241
|
method (str): Authentication method to try
|
238
|
-
|
242
|
+
|
239
243
|
Returns:
|
240
244
|
AuthResult: Authentication result
|
241
245
|
"""
|
@@ -256,13 +260,13 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
256
260
|
roles=[],
|
257
261
|
auth_method=None,
|
258
262
|
error_code=-32022,
|
259
|
-
error_message=f"Unsupported authentication method: {method}"
|
263
|
+
error_message=f"Unsupported authentication method: {method}",
|
260
264
|
)
|
261
265
|
except Exception as e:
|
262
266
|
self.logger.error(
|
263
267
|
f"Authentication method {method} failed",
|
264
268
|
extra={"error": str(e)},
|
265
|
-
exc_info=True
|
269
|
+
exc_info=True,
|
266
270
|
)
|
267
271
|
return AuthResult(
|
268
272
|
is_valid=False,
|
@@ -271,16 +275,16 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
271
275
|
roles=[],
|
272
276
|
auth_method=None,
|
273
277
|
error_code=-32023,
|
274
|
-
error_message=f"Authentication method {method} failed: {str(e)}"
|
278
|
+
error_message=f"Authentication method {method} failed: {str(e)}",
|
275
279
|
)
|
276
|
-
|
280
|
+
|
277
281
|
def _try_api_key_auth(self, request: Request) -> AuthResult:
|
278
282
|
"""
|
279
283
|
Try API key authentication with Flask request.
|
280
|
-
|
284
|
+
|
281
285
|
Args:
|
282
286
|
request (Request): Flask request object
|
283
|
-
|
287
|
+
|
284
288
|
Returns:
|
285
289
|
AuthResult: Authentication result
|
286
290
|
"""
|
@@ -291,7 +295,7 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
291
295
|
auth_header = request.headers.get("Authorization")
|
292
296
|
if auth_header and auth_header.startswith("Bearer "):
|
293
297
|
api_key = auth_header[7:] # Remove "Bearer " prefix
|
294
|
-
|
298
|
+
|
295
299
|
if not api_key:
|
296
300
|
return AuthResult(
|
297
301
|
is_valid=False,
|
@@ -300,19 +304,19 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
300
304
|
roles=[],
|
301
305
|
auth_method=AuthMethod.API_KEY,
|
302
306
|
error_code=-32012,
|
303
|
-
error_message="API key not found in request"
|
307
|
+
error_message="API key not found in request",
|
304
308
|
)
|
305
|
-
|
309
|
+
|
306
310
|
# Authenticate using security manager
|
307
311
|
return self.security_manager.auth_manager.authenticate_api_key(api_key)
|
308
|
-
|
312
|
+
|
309
313
|
def _try_jwt_auth(self, request: Request) -> AuthResult:
|
310
314
|
"""
|
311
315
|
Try JWT authentication with Flask request.
|
312
|
-
|
316
|
+
|
313
317
|
Args:
|
314
318
|
request (Request): Flask request object
|
315
|
-
|
319
|
+
|
316
320
|
Returns:
|
317
321
|
AuthResult: Authentication result
|
318
322
|
"""
|
@@ -326,21 +330,21 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
326
330
|
roles=[],
|
327
331
|
auth_method=AuthMethod.JWT,
|
328
332
|
error_code=-32013,
|
329
|
-
error_message="JWT token not found in Authorization header"
|
333
|
+
error_message="JWT token not found in Authorization header",
|
330
334
|
)
|
331
|
-
|
335
|
+
|
332
336
|
token = auth_header[7:] # Remove "Bearer " prefix
|
333
|
-
|
337
|
+
|
334
338
|
# Authenticate using security manager
|
335
339
|
return self.security_manager.auth_manager.authenticate_jwt_token(token)
|
336
|
-
|
340
|
+
|
337
341
|
def _try_certificate_auth(self, request: Request) -> AuthResult:
|
338
342
|
"""
|
339
343
|
Try certificate authentication with Flask request.
|
340
|
-
|
344
|
+
|
341
345
|
Args:
|
342
346
|
request (Request): Flask request object
|
343
|
-
|
347
|
+
|
344
348
|
Returns:
|
345
349
|
AuthResult: Authentication result
|
346
350
|
"""
|
@@ -354,16 +358,16 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
354
358
|
roles=[],
|
355
359
|
auth_method=AuthMethod.CERTIFICATE,
|
356
360
|
error_code=-32014,
|
357
|
-
error_message="Certificate authentication not implemented"
|
361
|
+
error_message="Certificate authentication not implemented",
|
358
362
|
)
|
359
|
-
|
363
|
+
|
360
364
|
def _try_basic_auth(self, request: Request) -> AuthResult:
|
361
365
|
"""
|
362
366
|
Try basic authentication with Flask request.
|
363
|
-
|
367
|
+
|
364
368
|
Args:
|
365
369
|
request (Request): Flask request object
|
366
|
-
|
370
|
+
|
367
371
|
Returns:
|
368
372
|
AuthResult: Authentication result
|
369
373
|
"""
|
@@ -377,9 +381,9 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
377
381
|
roles=[],
|
378
382
|
auth_method=AuthMethod.BASIC,
|
379
383
|
error_code=-32015,
|
380
|
-
error_message="Basic authentication credentials not found"
|
384
|
+
error_message="Basic authentication credentials not found",
|
381
385
|
)
|
382
|
-
|
386
|
+
|
383
387
|
# Basic auth is not implemented in this version
|
384
388
|
return AuthResult(
|
385
389
|
is_valid=False,
|
@@ -388,17 +392,17 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
388
392
|
roles=[],
|
389
393
|
auth_method=AuthMethod.BASIC,
|
390
394
|
error_code=-32016,
|
391
|
-
error_message="Basic authentication not implemented"
|
395
|
+
error_message="Basic authentication not implemented",
|
392
396
|
)
|
393
|
-
|
397
|
+
|
394
398
|
def _auth_error_response(self, auth_result: AuthResult, start_response: Any) -> Any:
|
395
399
|
"""
|
396
400
|
Create authentication error response for Flask.
|
397
|
-
|
401
|
+
|
398
402
|
Args:
|
399
403
|
auth_result (AuthResult): Authentication result with error
|
400
404
|
start_response: WSGI start_response function
|
401
|
-
|
405
|
+
|
402
406
|
Returns:
|
403
407
|
Any: WSGI error response
|
404
408
|
"""
|
@@ -406,22 +410,21 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
406
410
|
"error": "Authentication failed",
|
407
411
|
"error_code": auth_result.error_code,
|
408
412
|
"error_message": auth_result.error_message,
|
409
|
-
"auth_method":
|
413
|
+
"auth_method": (
|
414
|
+
auth_result.auth_method.value if auth_result.auth_method else None
|
415
|
+
),
|
410
416
|
}
|
411
|
-
|
412
|
-
status =
|
413
|
-
headers = [
|
414
|
-
|
415
|
-
('WWW-Authenticate', 'Bearer')
|
416
|
-
]
|
417
|
-
|
417
|
+
|
418
|
+
status = "401 Unauthorized"
|
419
|
+
headers = [("Content-Type", "application/json"), ("WWW-Authenticate", "Bearer")]
|
420
|
+
|
418
421
|
start_response(status, headers)
|
419
422
|
return [json.dumps(error_data).encode()]
|
420
|
-
|
423
|
+
|
421
424
|
def _log_auth_event(self, event_type: str, details: Dict[str, Any]) -> None:
|
422
425
|
"""
|
423
426
|
Log authentication event.
|
424
|
-
|
427
|
+
|
425
428
|
Args:
|
426
429
|
event_type (str): Type of authentication event
|
427
430
|
details (Dict[str, Any]): Event details
|
@@ -437,28 +440,185 @@ class FlaskAuthMiddleware(AuthMiddleware):
|
|
437
440
|
"path": details.get("path"),
|
438
441
|
"method": details.get("method"),
|
439
442
|
"auth_method": details.get("auth_method"),
|
440
|
-
**details
|
441
|
-
}
|
443
|
+
**details,
|
444
|
+
},
|
442
445
|
)
|
443
446
|
except Exception as e:
|
444
447
|
self.logger.error(
|
445
448
|
"Failed to log authentication event",
|
446
449
|
extra={"error": str(e)},
|
447
|
-
exc_info=True
|
450
|
+
exc_info=True,
|
448
451
|
)
|
449
452
|
|
453
|
+
def _apply_security_headers(self, response: Any) -> Any:
|
454
|
+
"""
|
455
|
+
Apply security headers to response.
|
456
|
+
|
457
|
+
Args:
|
458
|
+
response: WSGI response
|
459
|
+
|
460
|
+
Returns:
|
461
|
+
Any: Response with security headers
|
462
|
+
"""
|
463
|
+
# This would be implemented for actual Flask responses
|
464
|
+
# For now, return the response as-is
|
465
|
+
return response
|
466
|
+
|
467
|
+
def _create_error_response(
|
468
|
+
self, error_message: str, error_code: int, start_response: Any
|
469
|
+
) -> Any:
|
470
|
+
"""
|
471
|
+
Create error response for Flask.
|
472
|
+
|
473
|
+
Args:
|
474
|
+
error_message (str): Error message
|
475
|
+
error_code (int): Error code
|
476
|
+
start_response: WSGI start_response function
|
477
|
+
|
478
|
+
Returns:
|
479
|
+
Any: WSGI error response
|
480
|
+
"""
|
481
|
+
error_data = {
|
482
|
+
"error": "Security error",
|
483
|
+
"error_code": error_code,
|
484
|
+
"error_message": error_message,
|
485
|
+
}
|
486
|
+
|
487
|
+
status = "400 Bad Request"
|
488
|
+
headers = [("Content-Type", "application/json")]
|
489
|
+
|
490
|
+
start_response(status, headers)
|
491
|
+
return [json.dumps(error_data).encode()]
|
492
|
+
|
493
|
+
def _get_rate_limit_identifier(self, request: Request) -> str:
|
494
|
+
"""
|
495
|
+
Get rate limit identifier from request.
|
496
|
+
|
497
|
+
Args:
|
498
|
+
request (Request): Flask request object
|
499
|
+
|
500
|
+
Returns:
|
501
|
+
str: Rate limit identifier (IP address)
|
502
|
+
"""
|
503
|
+
return self._get_client_ip(request)
|
504
|
+
|
505
|
+
def _get_request_path(self, request: Request) -> str:
|
506
|
+
"""
|
507
|
+
Get request path from request.
|
508
|
+
|
509
|
+
Args:
|
510
|
+
request (Request): Flask request object
|
511
|
+
|
512
|
+
Returns:
|
513
|
+
str: Request path
|
514
|
+
"""
|
515
|
+
return request.path
|
516
|
+
|
517
|
+
def _get_required_permissions(self, request: Request) -> List[str]:
|
518
|
+
"""
|
519
|
+
Get required permissions for request.
|
520
|
+
|
521
|
+
Args:
|
522
|
+
request (Request): Flask request object
|
523
|
+
|
524
|
+
Returns:
|
525
|
+
List[str]: Required permissions
|
526
|
+
"""
|
527
|
+
# This would be implemented based on your application's authorization logic
|
528
|
+
# For now, return an empty list
|
529
|
+
return []
|
530
|
+
|
531
|
+
def _validation_error_response(
|
532
|
+
self, error_message: str, error_code: int = -32000, start_response: Any = None
|
533
|
+
) -> Any:
|
534
|
+
"""
|
535
|
+
Create validation error response for Flask.
|
536
|
+
|
537
|
+
Args:
|
538
|
+
error_message (str): Error message
|
539
|
+
error_code (int): Error code
|
540
|
+
start_response: WSGI start_response function
|
541
|
+
|
542
|
+
Returns:
|
543
|
+
Any: WSGI error response
|
544
|
+
"""
|
545
|
+
if start_response is None:
|
546
|
+
start_response = Mock()
|
547
|
+
return self._create_error_response(error_message, error_code, start_response)
|
548
|
+
|
549
|
+
def _rate_limit_error_response(
|
550
|
+
self, error_message: str, error_code: int = -32000, start_response: Any = None
|
551
|
+
) -> Any:
|
552
|
+
"""
|
553
|
+
Create rate limit error response for Flask.
|
554
|
+
|
555
|
+
Args:
|
556
|
+
error_message (str): Error message
|
557
|
+
error_code (int): Error code
|
558
|
+
start_response: WSGI start_response function
|
559
|
+
|
560
|
+
Returns:
|
561
|
+
Any: WSGI error response
|
562
|
+
"""
|
563
|
+
if start_response is None:
|
564
|
+
start_response = Mock()
|
565
|
+
return self._create_error_response(error_message, error_code, start_response)
|
566
|
+
|
567
|
+
def _security_header_response(
|
568
|
+
self, error_message: str, error_code: int = -32000, start_response: Any = None
|
569
|
+
) -> Any:
|
570
|
+
"""
|
571
|
+
Create security header error response for Flask.
|
572
|
+
|
573
|
+
Args:
|
574
|
+
error_message (str): Error message
|
575
|
+
error_code (int): Error code
|
576
|
+
start_response: WSGI start_response function
|
577
|
+
|
578
|
+
Returns:
|
579
|
+
Any: WSGI error response
|
580
|
+
"""
|
581
|
+
if start_response is None:
|
582
|
+
start_response = Mock()
|
583
|
+
return self._create_error_response(error_message, error_code, start_response)
|
584
|
+
|
585
|
+
def _authz_error_response(
|
586
|
+
self, auth_result: AuthResult, start_response: Any
|
587
|
+
) -> Any:
|
588
|
+
"""
|
589
|
+
Create authorization error response for Flask.
|
590
|
+
|
591
|
+
Args:
|
592
|
+
auth_result (AuthResult): Authentication result
|
593
|
+
start_response: WSGI start_response function
|
594
|
+
|
595
|
+
Returns:
|
596
|
+
Any: WSGI error response
|
597
|
+
"""
|
598
|
+
error_data = {
|
599
|
+
"error": "Authorization failed",
|
600
|
+
"error_code": auth_result.error_code,
|
601
|
+
"error_message": auth_result.error_message,
|
602
|
+
}
|
603
|
+
|
604
|
+
status = "403 Forbidden"
|
605
|
+
headers = [("Content-Type", "application/json")]
|
606
|
+
|
607
|
+
start_response(status, headers)
|
608
|
+
return [json.dumps(error_data).encode()]
|
609
|
+
|
450
610
|
|
451
611
|
class AuthMiddlewareError(Exception):
|
452
612
|
"""
|
453
613
|
Authentication Middleware Error
|
454
|
-
|
614
|
+
|
455
615
|
This exception is raised when authentication middleware processing fails.
|
456
|
-
|
616
|
+
|
457
617
|
Attributes:
|
458
618
|
message (str): Error message
|
459
619
|
error_code (int): Error code for programmatic handling
|
460
620
|
"""
|
461
|
-
|
621
|
+
|
462
622
|
def __init__(self, message: str, error_code: int = -32030):
|
463
623
|
self.message = message
|
464
624
|
self.error_code = error_code
|