mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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 (131) hide show
  1. mcp_proxy_adapter/__init__.py +9 -5
  2. mcp_proxy_adapter/__main__.py +1 -1
  3. mcp_proxy_adapter/api/app.py +227 -176
  4. mcp_proxy_adapter/api/handlers.py +68 -60
  5. mcp_proxy_adapter/api/middleware/__init__.py +7 -5
  6. mcp_proxy_adapter/api/middleware/base.py +19 -16
  7. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
  8. mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
  9. mcp_proxy_adapter/api/middleware/factory.py +50 -52
  10. mcp_proxy_adapter/api/middleware/logging.py +46 -30
  11. mcp_proxy_adapter/api/middleware/performance.py +19 -16
  12. mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
  13. mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
  14. mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
  15. mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
  16. mcp_proxy_adapter/api/schemas.py +69 -43
  17. mcp_proxy_adapter/api/tool_integration.py +83 -63
  18. mcp_proxy_adapter/api/tools.py +60 -50
  19. mcp_proxy_adapter/commands/__init__.py +15 -6
  20. mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
  21. mcp_proxy_adapter/commands/base.py +108 -112
  22. mcp_proxy_adapter/commands/builtin_commands.py +28 -18
  23. mcp_proxy_adapter/commands/catalog_manager.py +394 -265
  24. mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
  25. mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
  26. mcp_proxy_adapter/commands/command_registry.py +275 -226
  27. mcp_proxy_adapter/commands/config_command.py +48 -33
  28. mcp_proxy_adapter/commands/dependency_container.py +22 -23
  29. mcp_proxy_adapter/commands/dependency_manager.py +65 -56
  30. mcp_proxy_adapter/commands/echo_command.py +15 -15
  31. mcp_proxy_adapter/commands/health_command.py +31 -29
  32. mcp_proxy_adapter/commands/help_command.py +97 -61
  33. mcp_proxy_adapter/commands/hooks.py +65 -49
  34. mcp_proxy_adapter/commands/key_management_command.py +148 -147
  35. mcp_proxy_adapter/commands/load_command.py +58 -40
  36. mcp_proxy_adapter/commands/plugins_command.py +80 -54
  37. mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
  38. mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
  39. mcp_proxy_adapter/commands/reload_command.py +43 -37
  40. mcp_proxy_adapter/commands/result.py +26 -33
  41. mcp_proxy_adapter/commands/role_test_command.py +26 -26
  42. mcp_proxy_adapter/commands/roles_management_command.py +176 -173
  43. mcp_proxy_adapter/commands/security_command.py +134 -122
  44. mcp_proxy_adapter/commands/settings_command.py +47 -56
  45. mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
  46. mcp_proxy_adapter/commands/token_management_command.py +129 -158
  47. mcp_proxy_adapter/commands/transport_management_command.py +41 -36
  48. mcp_proxy_adapter/commands/unload_command.py +42 -37
  49. mcp_proxy_adapter/config.py +36 -35
  50. mcp_proxy_adapter/core/__init__.py +19 -21
  51. mcp_proxy_adapter/core/app_factory.py +30 -9
  52. mcp_proxy_adapter/core/app_runner.py +81 -64
  53. mcp_proxy_adapter/core/auth_validator.py +176 -182
  54. mcp_proxy_adapter/core/certificate_utils.py +469 -426
  55. mcp_proxy_adapter/core/client.py +155 -126
  56. mcp_proxy_adapter/core/client_manager.py +60 -54
  57. mcp_proxy_adapter/core/client_security.py +120 -91
  58. mcp_proxy_adapter/core/config_converter.py +176 -143
  59. mcp_proxy_adapter/core/config_validator.py +12 -4
  60. mcp_proxy_adapter/core/crl_utils.py +21 -7
  61. mcp_proxy_adapter/core/errors.py +64 -20
  62. mcp_proxy_adapter/core/logging.py +34 -29
  63. mcp_proxy_adapter/core/mtls_asgi.py +29 -25
  64. mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
  65. mcp_proxy_adapter/core/protocol_manager.py +154 -104
  66. mcp_proxy_adapter/core/proxy_client.py +202 -144
  67. mcp_proxy_adapter/core/proxy_registration.py +7 -3
  68. mcp_proxy_adapter/core/role_utils.py +139 -125
  69. mcp_proxy_adapter/core/security_adapter.py +88 -77
  70. mcp_proxy_adapter/core/security_factory.py +50 -44
  71. mcp_proxy_adapter/core/security_integration.py +72 -24
  72. mcp_proxy_adapter/core/server_adapter.py +68 -64
  73. mcp_proxy_adapter/core/server_engine.py +71 -53
  74. mcp_proxy_adapter/core/settings.py +68 -58
  75. mcp_proxy_adapter/core/ssl_utils.py +69 -56
  76. mcp_proxy_adapter/core/transport_manager.py +72 -60
  77. mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
  78. mcp_proxy_adapter/core/utils.py +4 -2
  79. mcp_proxy_adapter/custom_openapi.py +107 -99
  80. mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
  81. mcp_proxy_adapter/examples/commands/__init__.py +1 -1
  82. mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
  83. mcp_proxy_adapter/examples/debug_request_state.py +38 -19
  84. mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
  85. mcp_proxy_adapter/examples/demo_client.py +48 -36
  86. mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
  87. mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
  88. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
  89. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  90. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
  91. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  92. mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
  93. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
  94. mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
  95. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
  96. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  99. mcp_proxy_adapter/examples/full_application/main.py +27 -2
  100. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
  102. mcp_proxy_adapter/examples/generate_certificates.py +31 -16
  103. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
  104. mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
  105. mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
  106. mcp_proxy_adapter/examples/run_example.py +23 -5
  107. mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
  108. mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
  109. mcp_proxy_adapter/examples/run_security_tests.py +103 -41
  110. mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
  111. mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
  112. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
  113. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
  114. mcp_proxy_adapter/examples/security_test_client.py +196 -127
  115. mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
  116. mcp_proxy_adapter/examples/test_config.py +19 -4
  117. mcp_proxy_adapter/examples/test_config_generator.py +23 -7
  118. mcp_proxy_adapter/examples/test_examples.py +84 -56
  119. mcp_proxy_adapter/examples/universal_client.py +119 -62
  120. mcp_proxy_adapter/openapi.py +108 -115
  121. mcp_proxy_adapter/utils/config_generator.py +429 -274
  122. mcp_proxy_adapter/version.py +1 -2
  123. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
  125. mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
  126. mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
  127. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  128. mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
  130. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
  131. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -16,9 +16,12 @@ from starlette.middleware.base import BaseHTTPMiddleware
16
16
 
17
17
  # Direct import from framework
18
18
  try:
19
- from mcp_security_framework.middleware.fastapi_middleware import FastAPISecurityMiddleware
19
+ from mcp_security_framework.middleware.fastapi_middleware import (
20
+ FastAPISecurityMiddleware,
21
+ )
20
22
  from mcp_security_framework import SecurityManager
21
23
  from mcp_security_framework.schemas.config import SecurityConfig
24
+
22
25
  SECURITY_FRAMEWORK_AVAILABLE = True
23
26
  except ImportError:
24
27
  SECURITY_FRAMEWORK_AVAILABLE = False
@@ -32,7 +35,7 @@ from mcp_proxy_adapter.core.security_integration import create_security_integrat
32
35
 
33
36
  class SecurityValidationError(Exception):
34
37
  """Security validation error."""
35
-
38
+
36
39
  def __init__(self, message: str, error_code: int):
37
40
  self.message = message
38
41
  self.error_code = error_code
@@ -42,28 +45,30 @@ class SecurityValidationError(Exception):
42
45
  class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
43
46
  """
44
47
  Unified security middleware using mcp_security_framework.
45
-
48
+
46
49
  This middleware now directly uses the security framework's FastAPI middleware
47
50
  and components instead of custom implementations.
48
51
  """
49
-
52
+
50
53
  def __init__(self, app, config: Dict[str, Any]):
51
54
  """
52
55
  Initialize unified security middleware.
53
-
56
+
54
57
  Args:
55
58
  app: FastAPI application
56
59
  config: mcp_proxy_adapter configuration dictionary
57
60
  """
58
61
  super().__init__(app)
59
62
  self.config = config
60
-
63
+
61
64
  # Create security integration
62
65
  try:
63
66
  security_config = config.get("security", {})
64
67
  self.security_integration = create_security_integration(security_config)
65
68
  # Use framework's FastAPI middleware
66
- self.framework_middleware = self.security_integration.security_manager.create_fastapi_middleware()
69
+ self.framework_middleware = (
70
+ self.security_integration.security_manager.create_fastapi_middleware()
71
+ )
67
72
  logger.info("Using mcp_security_framework FastAPI middleware")
68
73
  # IMPORTANT: Don't replace self.app! This breaks the middleware chain.
69
74
  # Instead, store the framework middleware for use in dispatch method.
@@ -71,31 +76,39 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
71
76
  except Exception as e:
72
77
  logger.error(f"Security framework integration failed: {e}")
73
78
  # Instead of raising error, log warning and continue without security
74
- logger.warning("Continuing without security framework - some security features will be disabled")
79
+ logger.warning(
80
+ "Continuing without security framework - some security features will be disabled"
81
+ )
75
82
  self.security_integration = None
76
83
  self.framework_middleware = None
77
84
  # Keep original app in place when framework middleware is unavailable
78
85
  # BaseHTTPMiddleware initialized it via super().__init__(app)
79
-
86
+
80
87
  logger.info("Unified security middleware initialized")
81
-
82
- async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
88
+
89
+ async def dispatch(
90
+ self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
91
+ ) -> Response:
83
92
  """
84
93
  Process request using framework middleware.
85
-
94
+
86
95
  Args:
87
96
  request: Request object
88
97
  call_next: Next handler
89
-
98
+
90
99
  Returns:
91
100
  Response object
92
101
  """
93
102
  try:
94
103
  # Simple built-in API key enforcement if configured
95
- security_cfg = self.config.get("security", {}) if isinstance(self.config, dict) else {}
104
+ security_cfg = (
105
+ self.config.get("security", {}) if isinstance(self.config, dict) else {}
106
+ )
96
107
  auth_cfg = security_cfg.get("auth", {})
97
108
  permissions_cfg = security_cfg.get("permissions", {})
98
- public_paths = set(auth_cfg.get("public_paths", ["/health", "/docs", "/openapi.json"]))
109
+ public_paths = set(
110
+ auth_cfg.get("public_paths", ["/health", "/docs", "/openapi.json"])
111
+ )
99
112
  # JSON-RPC endpoint must not be public when API key is required
100
113
  public_paths.discard("/api/jsonrpc")
101
114
  path = request.url.path
@@ -103,7 +116,11 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
103
116
  api_keys: Dict[str, str] = auth_cfg.get("api_keys", {}) or {}
104
117
 
105
118
  # Enforce only for non-public paths when api_key method configured
106
- if security_cfg.get("enabled", False) and ("api_key" in methods) and (path not in public_paths):
119
+ if (
120
+ security_cfg.get("enabled", False)
121
+ and ("api_key" in methods)
122
+ and (path not in public_paths)
123
+ ):
107
124
  # Accept either X-API-Key or Authorization: Bearer
108
125
  token = request.headers.get("X-API-Key")
109
126
  if not token:
@@ -112,19 +129,25 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
112
129
  token = authz[7:]
113
130
  if not token or (api_keys and token not in api_keys):
114
131
  from fastapi.responses import JSONResponse
115
- return JSONResponse(status_code=401, content={
116
- "error": {
117
- "code": 401,
118
- "message": "Unauthorized: invalid or missing API key",
119
- "type": "authentication_error"
120
- }
121
- })
132
+
133
+ return JSONResponse(
134
+ status_code=401,
135
+ content={
136
+ "error": {
137
+ "code": 401,
138
+ "message": "Unauthorized: invalid or missing API key",
139
+ "type": "authentication_error",
140
+ }
141
+ },
142
+ )
122
143
 
123
144
  # Continue with framework middleware or regular flow
124
145
  if self.framework_middleware:
125
146
  # If framework middleware exists, we need to call it manually
126
147
  # This is a workaround since we can't chain ASGI apps in BaseHTTPMiddleware
127
- logger.debug("Framework middleware exists, continuing with regular call_next")
148
+ logger.debug(
149
+ "Framework middleware exists, continuing with regular call_next"
150
+ )
128
151
  return await call_next(request)
129
152
  else:
130
153
  # No framework middleware, continue normally
@@ -137,61 +160,57 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
137
160
  # Handle other errors
138
161
  logger.error(f"Unexpected error in unified security middleware: {e}")
139
162
  return await self._handle_general_error(request, e)
140
-
141
163
 
142
-
143
- async def _handle_security_error(self, request: Request, error: SecurityValidationError) -> Response:
164
+ async def _handle_security_error(
165
+ self, request: Request, error: SecurityValidationError
166
+ ) -> Response:
144
167
  """
145
168
  Handle security validation errors.
146
-
169
+
147
170
  Args:
148
171
  request: Request object
149
172
  error: Security validation error
150
-
173
+
151
174
  Returns:
152
175
  Error response
153
176
  """
154
177
  from fastapi.responses import JSONResponse
155
-
178
+
156
179
  error_response = {
157
180
  "error": {
158
181
  "code": error.error_code,
159
182
  "message": error.message,
160
- "type": "security_validation_error"
183
+ "type": "security_validation_error",
161
184
  }
162
185
  }
163
-
186
+
164
187
  logger.warning(f"Security validation failed: {error.message}")
165
-
166
- return JSONResponse(
167
- status_code=error.error_code,
168
- content=error_response
169
- )
170
-
171
- async def _handle_general_error(self, request: Request, error: Exception) -> Response:
188
+
189
+ return JSONResponse(status_code=error.error_code, content=error_response)
190
+
191
+ async def _handle_general_error(
192
+ self, request: Request, error: Exception
193
+ ) -> Response:
172
194
  """
173
195
  Handle general errors.
174
-
196
+
175
197
  Args:
176
198
  request: Request object
177
199
  error: General error
178
-
200
+
179
201
  Returns:
180
202
  Error response
181
203
  """
182
204
  from fastapi.responses import JSONResponse
183
-
205
+
184
206
  error_response = {
185
- "error": {
207
+ "error": {
186
208
  "code": 500,
187
- "message": "Internal server error",
188
- "type": "general_error"
209
+ "message": "Internal server error",
210
+ "type": "general_error",
189
211
  }
190
212
  }
191
-
213
+
192
214
  logger.error(f"General error in security middleware: {error}")
193
-
194
- return JSONResponse(
195
- status_code=500,
196
- content=error_response
197
- )
215
+
216
+ return JSONResponse(status_code=500, content=error_response)
@@ -17,15 +17,13 @@ from mcp_proxy_adapter.core.logging import logger
17
17
  # Import mcp_security_framework components
18
18
  try:
19
19
  from mcp_security_framework import AuthManager, PermissionManager
20
- from mcp_security_framework.schemas.config import (
21
- AuthConfig, PermissionConfig
22
- )
20
+ from mcp_security_framework.schemas.config import AuthConfig, PermissionConfig
21
+
23
22
  _MCP_SECURITY_AVAILABLE = True
24
23
  print("✅ mcp_security_framework available in middleware")
25
24
  except ImportError:
26
25
  _MCP_SECURITY_AVAILABLE = False
27
- print("⚠️ mcp_security_framework not available in middleware, "
28
- "using basic auth")
26
+ print("⚠️ mcp_security_framework not available in middleware, " "using basic auth")
29
27
 
30
28
 
31
29
  class UserInfoMiddleware(BaseHTTPMiddleware):
@@ -35,6 +33,7 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
35
33
  This middleware extracts user information from authentication headers
36
34
  and sets it in request.state for use by commands.
37
35
  """
36
+
38
37
  def __init__(self, app, config: Dict[str, Any]):
39
38
  """
40
39
  Initialize user info middleware.
@@ -59,12 +58,12 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
59
58
 
60
59
  # Check if permissions are enabled
61
60
  permissions_enabled = permissions_config.get("enabled", False)
62
-
61
+
63
62
  # Create AuthConfig for mcp_security_framework
64
63
  mcp_auth_config = AuthConfig(
65
64
  enabled=True,
66
65
  methods=["api_key"],
67
- api_keys=auth_config.get("api_keys", {})
66
+ api_keys=auth_config.get("api_keys", {}),
68
67
  )
69
68
 
70
69
  # Initialize PermissionManager only if permissions are enabled
@@ -85,7 +84,7 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
85
84
  "wildcard_permissions", False
86
85
  ),
87
86
  strict_mode=permissions_config.get("strict_mode", True),
88
- roles=permissions_config.get("roles", {})
87
+ roles=permissions_config.get("roles", {}),
89
88
  )
90
89
 
91
90
  # Initialize PermissionManager first
@@ -101,7 +100,7 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
101
100
  permission_cache_ttl=300,
102
101
  wildcard_permissions=False,
103
102
  strict_mode=False,
104
- roles={}
103
+ roles={},
105
104
  )
106
105
  self.permission_manager = PermissionManager(mcp_permission_config)
107
106
 
@@ -109,8 +108,9 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
109
108
  self.auth_manager = AuthManager(
110
109
  mcp_auth_config, self.permission_manager
111
110
  )
112
- logger.info("✅ User info middleware initialized with "
113
- "mcp_security_framework")
111
+ logger.info(
112
+ "✅ User info middleware initialized with " "mcp_security_framework"
113
+ )
114
114
  except Exception as e:
115
115
  logger.warning(f"⚠️ Failed to initialize AuthManager: {e}")
116
116
  self._security_available = False
@@ -123,8 +123,7 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
123
123
  logger.info("ℹ️ User info middleware initialized with basic auth")
124
124
 
125
125
  async def dispatch(
126
- self, request: Request,
127
- call_next: Callable[[Request], Awaitable[Response]]
126
+ self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
128
127
  ) -> Response:
129
128
  """
130
129
  Process request and set user info in request.state.
@@ -148,41 +147,49 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
148
147
  # Set user info from AuthManager result
149
148
  request.state.user = {
150
149
  "id": api_key,
151
- "role": (auth_result.roles[0]
152
- if auth_result.roles else "guest"),
150
+ "role": (
151
+ auth_result.roles[0] if auth_result.roles else "guest"
152
+ ),
153
153
  "roles": auth_result.roles or ["guest"],
154
154
  "permissions": getattr(
155
- auth_result, 'permissions', ["read"]
156
- )
155
+ auth_result, "permissions", ["read"]
156
+ ),
157
157
  }
158
- logger.debug(f"✅ Authenticated user with "
159
- f"mcp_security_framework: "
160
- f"{request.state.user}")
158
+ logger.debug(
159
+ f" Authenticated user with "
160
+ f"mcp_security_framework: "
161
+ f"{request.state.user}"
162
+ )
161
163
  else:
162
164
  # Authentication failed
163
165
  request.state.user = {
164
166
  "id": None,
165
167
  "role": "guest",
166
168
  "roles": ["guest"],
167
- "permissions": ["read"]
169
+ "permissions": ["read"],
168
170
  }
169
- logger.debug(f"❌ Authentication failed for API key: "
170
- f"{api_key[:8]}...")
171
+ logger.debug(
172
+ f"❌ Authentication failed for API key: "
173
+ f"{api_key[:8]}..."
174
+ )
171
175
  except Exception as e:
172
- logger.warning(f"⚠️ AuthManager error: {e}, "
173
- f"falling back to basic auth")
176
+ logger.warning(
177
+ f"⚠️ AuthManager error: {e}, " f"falling back to basic auth"
178
+ )
174
179
  self._security_available = False
175
180
 
176
181
  if not self._security_available:
177
182
  # Fallback to basic API key handling
178
- if api_key in getattr(self, 'api_keys', {}):
183
+ if api_key in getattr(self, "api_keys", {}):
179
184
  user_role = self.api_keys[api_key]
180
185
 
181
186
  # Get permissions for this role from roles file if available
182
187
  role_permissions = ["read"] # default permissions
183
- if (hasattr(self, 'roles_config') and
184
- self.roles_config and
185
- user_role in self.roles_config):
188
+ if (
189
+ hasattr(self, "roles_config")
190
+ and self.roles_config
191
+ and user_role in self.roles_config
192
+ ):
186
193
  role_permissions = self.roles_config[user_role].get(
187
194
  "permissions", ["read"]
188
195
  )
@@ -192,18 +199,20 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
192
199
  "id": api_key,
193
200
  "role": user_role,
194
201
  "roles": [user_role],
195
- "permissions": role_permissions
202
+ "permissions": role_permissions,
196
203
  }
197
204
 
198
- logger.debug(f"✅ Authenticated user with basic auth: "
199
- f"{request.state.user}")
205
+ logger.debug(
206
+ f"✅ Authenticated user with basic auth: "
207
+ f"{request.state.user}"
208
+ )
200
209
  else:
201
210
  # API key not found
202
211
  request.state.user = {
203
212
  "id": None,
204
213
  "role": "guest",
205
214
  "roles": ["guest"],
206
- "permissions": ["read"]
215
+ "permissions": ["read"],
207
216
  }
208
217
  logger.debug(f"❌ API key not found: {api_key[:8]}...")
209
218
  else:
@@ -212,7 +221,7 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
212
221
  "id": None,
213
222
  "role": "guest",
214
223
  "roles": ["guest"],
215
- "permissions": ["read"]
224
+ "permissions": ["read"],
216
225
  }
217
226
  logger.debug("ℹ️ No API key provided, using guest access")
218
227