mcp-security-framework 1.1.0__py3-none-any.whl → 1.1.2__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 +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- 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 +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- 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 +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/METADATA +4 -3
- mcp_security_framework-1.1.2.dist-info/RECORD +84 -0
- tests/conftest.py +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.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,
|
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
|
-
|
67
|
+
|
68
68
|
def __init__(self, security_manager: Any):
|
69
69
|
"""
|
70
70
|
Initialize Flask Authentication Middleware.
|
71
|
-
|
71
|
+
|
72
72
|
Args:
|
73
73
|
security_manager: Security manager instance containing
|
74
74
|
all security components and configuration.
|
75
75
|
"""
|
76
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,175 +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
|
+
|
450
453
|
def _apply_security_headers(self, response: Any) -> Any:
|
451
454
|
"""
|
452
455
|
Apply security headers to response.
|
453
|
-
|
456
|
+
|
454
457
|
Args:
|
455
458
|
response: WSGI response
|
456
|
-
|
459
|
+
|
457
460
|
Returns:
|
458
461
|
Any: Response with security headers
|
459
462
|
"""
|
460
463
|
# This would be implemented for actual Flask responses
|
461
464
|
# For now, return the response as-is
|
462
465
|
return response
|
463
|
-
|
464
|
-
def _create_error_response(
|
466
|
+
|
467
|
+
def _create_error_response(
|
468
|
+
self, error_message: str, error_code: int, start_response: Any
|
469
|
+
) -> Any:
|
465
470
|
"""
|
466
471
|
Create error response for Flask.
|
467
|
-
|
472
|
+
|
468
473
|
Args:
|
469
474
|
error_message (str): Error message
|
470
475
|
error_code (int): Error code
|
471
476
|
start_response: WSGI start_response function
|
472
|
-
|
477
|
+
|
473
478
|
Returns:
|
474
479
|
Any: WSGI error response
|
475
480
|
"""
|
476
481
|
error_data = {
|
477
482
|
"error": "Security error",
|
478
483
|
"error_code": error_code,
|
479
|
-
"error_message": error_message
|
484
|
+
"error_message": error_message,
|
480
485
|
}
|
481
|
-
|
482
|
-
status =
|
483
|
-
headers = [(
|
484
|
-
|
486
|
+
|
487
|
+
status = "400 Bad Request"
|
488
|
+
headers = [("Content-Type", "application/json")]
|
489
|
+
|
485
490
|
start_response(status, headers)
|
486
491
|
return [json.dumps(error_data).encode()]
|
487
|
-
|
492
|
+
|
488
493
|
def _get_rate_limit_identifier(self, request: Request) -> str:
|
489
494
|
"""
|
490
495
|
Get rate limit identifier from request.
|
491
|
-
|
496
|
+
|
492
497
|
Args:
|
493
498
|
request (Request): Flask request object
|
494
|
-
|
499
|
+
|
495
500
|
Returns:
|
496
501
|
str: Rate limit identifier (IP address)
|
497
502
|
"""
|
498
503
|
return self._get_client_ip(request)
|
499
|
-
|
504
|
+
|
500
505
|
def _get_request_path(self, request: Request) -> str:
|
501
506
|
"""
|
502
507
|
Get request path from request.
|
503
|
-
|
508
|
+
|
504
509
|
Args:
|
505
510
|
request (Request): Flask request object
|
506
|
-
|
511
|
+
|
507
512
|
Returns:
|
508
513
|
str: Request path
|
509
514
|
"""
|
510
515
|
return request.path
|
511
|
-
|
516
|
+
|
512
517
|
def _get_required_permissions(self, request: Request) -> List[str]:
|
513
518
|
"""
|
514
519
|
Get required permissions for request.
|
515
|
-
|
520
|
+
|
516
521
|
Args:
|
517
522
|
request (Request): Flask request object
|
518
|
-
|
523
|
+
|
519
524
|
Returns:
|
520
525
|
List[str]: Required permissions
|
521
526
|
"""
|
522
527
|
# This would be implemented based on your application's authorization logic
|
523
528
|
# For now, return an empty list
|
524
529
|
return []
|
525
|
-
|
526
|
-
def _validation_error_response(
|
530
|
+
|
531
|
+
def _validation_error_response(
|
532
|
+
self, error_message: str, error_code: int = -32000, start_response: Any = None
|
533
|
+
) -> Any:
|
527
534
|
"""
|
528
535
|
Create validation error response for Flask.
|
529
|
-
|
536
|
+
|
530
537
|
Args:
|
531
538
|
error_message (str): Error message
|
532
539
|
error_code (int): Error code
|
533
540
|
start_response: WSGI start_response function
|
534
|
-
|
541
|
+
|
535
542
|
Returns:
|
536
543
|
Any: WSGI error response
|
537
544
|
"""
|
538
545
|
if start_response is None:
|
539
546
|
start_response = Mock()
|
540
547
|
return self._create_error_response(error_message, error_code, start_response)
|
541
|
-
|
542
|
-
def _rate_limit_error_response(
|
548
|
+
|
549
|
+
def _rate_limit_error_response(
|
550
|
+
self, error_message: str, error_code: int = -32000, start_response: Any = None
|
551
|
+
) -> Any:
|
543
552
|
"""
|
544
553
|
Create rate limit error response for Flask.
|
545
|
-
|
554
|
+
|
546
555
|
Args:
|
547
556
|
error_message (str): Error message
|
548
557
|
error_code (int): Error code
|
549
558
|
start_response: WSGI start_response function
|
550
|
-
|
559
|
+
|
551
560
|
Returns:
|
552
561
|
Any: WSGI error response
|
553
562
|
"""
|
554
563
|
if start_response is None:
|
555
564
|
start_response = Mock()
|
556
565
|
return self._create_error_response(error_message, error_code, start_response)
|
557
|
-
|
558
|
-
def _security_header_response(
|
566
|
+
|
567
|
+
def _security_header_response(
|
568
|
+
self, error_message: str, error_code: int = -32000, start_response: Any = None
|
569
|
+
) -> Any:
|
559
570
|
"""
|
560
571
|
Create security header error response for Flask.
|
561
|
-
|
572
|
+
|
562
573
|
Args:
|
563
574
|
error_message (str): Error message
|
564
575
|
error_code (int): Error code
|
565
576
|
start_response: WSGI start_response function
|
566
|
-
|
577
|
+
|
567
578
|
Returns:
|
568
579
|
Any: WSGI error response
|
569
580
|
"""
|
570
581
|
if start_response is None:
|
571
582
|
start_response = Mock()
|
572
583
|
return self._create_error_response(error_message, error_code, start_response)
|
573
|
-
|
574
|
-
def _authz_error_response(
|
584
|
+
|
585
|
+
def _authz_error_response(
|
586
|
+
self, auth_result: AuthResult, start_response: Any
|
587
|
+
) -> Any:
|
575
588
|
"""
|
576
589
|
Create authorization error response for Flask.
|
577
|
-
|
590
|
+
|
578
591
|
Args:
|
579
592
|
auth_result (AuthResult): Authentication result
|
580
593
|
start_response: WSGI start_response function
|
581
|
-
|
594
|
+
|
582
595
|
Returns:
|
583
596
|
Any: WSGI error response
|
584
597
|
"""
|
585
598
|
error_data = {
|
586
599
|
"error": "Authorization failed",
|
587
600
|
"error_code": auth_result.error_code,
|
588
|
-
"error_message": auth_result.error_message
|
601
|
+
"error_message": auth_result.error_message,
|
589
602
|
}
|
590
|
-
|
591
|
-
status =
|
592
|
-
headers = [(
|
593
|
-
|
603
|
+
|
604
|
+
status = "403 Forbidden"
|
605
|
+
headers = [("Content-Type", "application/json")]
|
606
|
+
|
594
607
|
start_response(status, headers)
|
595
608
|
return [json.dumps(error_data).encode()]
|
596
|
-
|
597
|
-
|
609
|
+
|
610
|
+
|
598
611
|
class AuthMiddlewareError(Exception):
|
599
612
|
"""
|
600
613
|
Authentication Middleware Error
|
601
|
-
|
614
|
+
|
602
615
|
This exception is raised when authentication middleware processing fails.
|
603
|
-
|
616
|
+
|
604
617
|
Attributes:
|
605
618
|
message (str): Error message
|
606
619
|
error_code (int): Error code for programmatic handling
|
607
620
|
"""
|
608
|
-
|
621
|
+
|
609
622
|
def __init__(self, message: str, error_code: int = -32030):
|
610
623
|
self.message = message
|
611
624
|
self.error_code = error_code
|