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.
Files changed (58) hide show
  1. mcp_security_framework/__init__.py +26 -15
  2. mcp_security_framework/cli/__init__.py +1 -1
  3. mcp_security_framework/cli/cert_cli.py +233 -197
  4. mcp_security_framework/cli/security_cli.py +324 -234
  5. mcp_security_framework/constants.py +21 -27
  6. mcp_security_framework/core/auth_manager.py +41 -22
  7. mcp_security_framework/core/cert_manager.py +210 -147
  8. mcp_security_framework/core/permission_manager.py +9 -9
  9. mcp_security_framework/core/rate_limiter.py +2 -2
  10. mcp_security_framework/core/security_manager.py +284 -229
  11. mcp_security_framework/examples/__init__.py +6 -0
  12. mcp_security_framework/examples/comprehensive_example.py +349 -279
  13. mcp_security_framework/examples/django_example.py +247 -206
  14. mcp_security_framework/examples/fastapi_example.py +315 -283
  15. mcp_security_framework/examples/flask_example.py +274 -203
  16. mcp_security_framework/examples/gateway_example.py +304 -237
  17. mcp_security_framework/examples/microservice_example.py +258 -189
  18. mcp_security_framework/examples/standalone_example.py +255 -230
  19. mcp_security_framework/examples/test_all_examples.py +151 -135
  20. mcp_security_framework/middleware/__init__.py +46 -55
  21. mcp_security_framework/middleware/auth_middleware.py +62 -63
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +119 -118
  23. mcp_security_framework/middleware/fastapi_middleware.py +156 -148
  24. mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
  25. mcp_security_framework/middleware/flask_middleware.py +183 -157
  26. mcp_security_framework/middleware/mtls_middleware.py +106 -117
  27. mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
  28. mcp_security_framework/middleware/security_middleware.py +109 -124
  29. mcp_security_framework/schemas/config.py +2 -1
  30. mcp_security_framework/schemas/models.py +18 -6
  31. mcp_security_framework/utils/cert_utils.py +14 -8
  32. mcp_security_framework/utils/datetime_compat.py +116 -0
  33. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/METADATA +4 -3
  34. mcp_security_framework-1.1.2.dist-info/RECORD +84 -0
  35. tests/conftest.py +63 -66
  36. tests/test_cli/test_cert_cli.py +184 -146
  37. tests/test_cli/test_security_cli.py +274 -247
  38. tests/test_core/test_cert_manager.py +24 -10
  39. tests/test_core/test_security_manager.py +2 -2
  40. tests/test_examples/test_comprehensive_example.py +190 -137
  41. tests/test_examples/test_fastapi_example.py +124 -101
  42. tests/test_examples/test_flask_example.py +124 -101
  43. tests/test_examples/test_standalone_example.py +73 -80
  44. tests/test_integration/test_auth_flow.py +213 -197
  45. tests/test_integration/test_certificate_flow.py +180 -149
  46. tests/test_integration/test_fastapi_integration.py +108 -111
  47. tests/test_integration/test_flask_integration.py +141 -140
  48. tests/test_integration/test_standalone_integration.py +290 -259
  49. tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
  50. tests/test_middleware/test_fastapi_middleware.py +147 -132
  51. tests/test_middleware/test_flask_auth_middleware.py +260 -202
  52. tests/test_middleware/test_flask_middleware.py +201 -179
  53. tests/test_middleware/test_security_middleware.py +145 -130
  54. tests/test_utils/test_datetime_compat.py +147 -0
  55. mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
  56. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/WHEEL +0 -0
  57. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/entry_points.txt +0 -0
  58. {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, Optional, List
30
+ from typing import Any, Dict, List, Optional
31
31
 
32
- from flask import Request, Response, jsonify, current_app
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("authentication_successful", {
122
- "ip_address": self._get_client_ip(request),
123
- "username": auth_result.username,
124
- "path": request.path,
125
- "method": request.method,
126
- "auth_method": auth_result.auth_method
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 = '200 OK'
156
- headers = [('Content-Type', 'application/json')]
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, 'public_paths'):
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, 'default_client_ip', None)
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
- return os.environ.get('DEFAULT_CLIENT_IP', DEFAULT_CLIENT_IP)
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": auth_result.auth_method.value if auth_result.auth_method else None
413
+ "auth_method": (
414
+ auth_result.auth_method.value if auth_result.auth_method else None
415
+ ),
410
416
  }
411
-
412
- status = '401 Unauthorized'
413
- headers = [
414
- ('Content-Type', 'application/json'),
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(self, error_message: str, error_code: int, start_response: Any) -> Any:
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 = '400 Bad Request'
483
- headers = [('Content-Type', 'application/json')]
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(self, error_message: str, error_code: int = -32000, start_response: Any = None) -> Any:
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(self, error_message: str, error_code: int = -32000, start_response: Any = None) -> Any:
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(self, error_message: str, error_code: int = -32000, start_response: Any = None) -> Any:
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(self, auth_result: AuthResult, start_response: Any) -> Any:
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 = '403 Forbidden'
592
- headers = [('Content-Type', 'application/json')]
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