mcp-proxy-adapter 6.2.21__py3-none-any.whl → 6.2.23__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_proxy_adapter/api/app.py +3 -0
- mcp_proxy_adapter/api/middleware/__init__.py +7 -7
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +59 -12
- mcp_proxy_adapter/api/middleware/unified_security.py +42 -6
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +99 -24
- mcp_proxy_adapter/core/protocol_manager.py +33 -5
- mcp_proxy_adapter/examples/create_certificates_simple.py +77 -9
- mcp_proxy_adapter/examples/generate_test_configs.py +44 -23
- mcp_proxy_adapter/examples/run_full_test_suite.py +125 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +9 -7
- mcp_proxy_adapter/examples/run_security_tests.py +187 -36
- mcp_proxy_adapter/examples/security_test_client.py +182 -38
- mcp_proxy_adapter/examples/setup_test_environment.py +12 -0
- mcp_proxy_adapter/main.py +12 -2
- mcp_proxy_adapter/utils/config_generator.py +740 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.2.21.dist-info → mcp_proxy_adapter-6.2.23.dist-info}/METADATA +9 -11
- {mcp_proxy_adapter-6.2.21.dist-info → mcp_proxy_adapter-6.2.23.dist-info}/RECORD +22 -19
- mcp_proxy_adapter-6.2.23.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.2.21.dist-info → mcp_proxy_adapter-6.2.23.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.2.21.dist-info → mcp_proxy_adapter-6.2.23.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.2.21.dist-info → mcp_proxy_adapter-6.2.23.dist-info}/top_level.txt +0 -0
mcp_proxy_adapter/api/app.py
CHANGED
@@ -350,6 +350,9 @@ def create_app(title: Optional[str] = None, description: Optional[str] = None, v
|
|
350
350
|
)
|
351
351
|
|
352
352
|
# Setup middleware using the new middleware package
|
353
|
+
print(f"DEBUG create_app: calling setup_middleware with config type: {type(current_config)}")
|
354
|
+
if hasattr(current_config, 'keys'):
|
355
|
+
print(f"DEBUG create_app: current_config keys: {list(current_config.keys())}")
|
353
356
|
setup_middleware(app, current_config)
|
354
357
|
|
355
358
|
# Use custom OpenAPI schema
|
@@ -23,18 +23,21 @@ def setup_middleware(app: FastAPI, app_config: Optional[Dict[str, Any]] = None)
|
|
23
23
|
# Use provided configuration or fallback to global config
|
24
24
|
current_config = app_config if app_config is not None else config.get_all()
|
25
25
|
|
26
|
+
# Add protocol middleware FIRST (before other middleware)
|
27
|
+
setup_protocol_middleware(app, current_config)
|
28
|
+
|
26
29
|
# Create middleware factory
|
27
30
|
factory = MiddlewareFactory(app, current_config)
|
28
|
-
|
31
|
+
|
29
32
|
# Validate middleware configuration
|
30
33
|
if not factory.validate_middleware_config():
|
31
34
|
logger.error("Middleware configuration validation failed")
|
32
35
|
raise SystemExit(1)
|
33
|
-
|
36
|
+
|
34
37
|
logger.info("Using unified security middleware")
|
35
38
|
middleware_list = factory.create_all_middleware()
|
36
|
-
|
37
|
-
# Add middleware to application
|
39
|
+
|
40
|
+
# Add middleware to application AFTER protocol middleware
|
38
41
|
for middleware in middleware_list:
|
39
42
|
# For ASGI middleware, we need to wrap the application
|
40
43
|
if hasattr(middleware, 'dispatch'):
|
@@ -43,9 +46,6 @@ def setup_middleware(app: FastAPI, app_config: Optional[Dict[str, Any]] = None)
|
|
43
46
|
else:
|
44
47
|
logger.warning(f"Middleware {middleware.__class__.__name__} doesn't have dispatch method")
|
45
48
|
|
46
|
-
# Add protocol middleware with current configuration
|
47
|
-
setup_protocol_middleware(app, current_config)
|
48
|
-
|
49
49
|
# Log middleware information
|
50
50
|
middleware_info = factory.get_middleware_info()
|
51
51
|
logger.info(f"Middleware setup completed:")
|
@@ -24,15 +24,37 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
|
|
24
24
|
def __init__(self, app, app_config: Optional[Dict[str, Any]] = None):
|
25
25
|
"""
|
26
26
|
Initialize protocol middleware.
|
27
|
-
|
27
|
+
|
28
28
|
Args:
|
29
29
|
app: FastAPI application
|
30
30
|
app_config: Application configuration dictionary (optional)
|
31
31
|
"""
|
32
32
|
super().__init__(app)
|
33
|
-
|
33
|
+
# Normalize config to dictionary
|
34
|
+
normalized_config: Optional[Dict[str, Any]]
|
35
|
+
if app_config is None:
|
36
|
+
normalized_config = None
|
37
|
+
elif hasattr(app_config, 'get_all'):
|
38
|
+
try:
|
39
|
+
normalized_config = app_config.get_all()
|
40
|
+
except Exception as e:
|
41
|
+
logger.debug(f"ProtocolMiddleware - Error calling get_all(): {e}, type: {type(app_config)}")
|
42
|
+
normalized_config = None
|
43
|
+
elif hasattr(app_config, 'keys'):
|
44
|
+
normalized_config = app_config # Already dict-like
|
45
|
+
else:
|
46
|
+
logger.debug(f"ProtocolMiddleware - app_config is not dict-like, type: {type(app_config)}, value: {repr(app_config)}")
|
47
|
+
normalized_config = None
|
48
|
+
|
49
|
+
logger.debug(f"ProtocolMiddleware - normalized_config type: {type(normalized_config)}")
|
50
|
+
if normalized_config:
|
51
|
+
logger.debug(f"ProtocolMiddleware - protocols in config: {'protocols' in normalized_config}")
|
52
|
+
if 'protocols' in normalized_config:
|
53
|
+
logger.debug(f"ProtocolMiddleware - protocols type: {type(normalized_config['protocols'])}")
|
54
|
+
|
55
|
+
self.app_config = normalized_config
|
34
56
|
# Get protocol manager with current configuration
|
35
|
-
self.protocol_manager = get_protocol_manager(
|
57
|
+
self.protocol_manager = get_protocol_manager(normalized_config)
|
36
58
|
|
37
59
|
def update_config(self, new_config: Dict[str, Any]):
|
38
60
|
"""
|
@@ -41,21 +63,31 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
|
|
41
63
|
Args:
|
42
64
|
new_config: New configuration dictionary
|
43
65
|
"""
|
44
|
-
|
45
|
-
|
66
|
+
# Normalize new config
|
67
|
+
if hasattr(new_config, 'get_all'):
|
68
|
+
try:
|
69
|
+
self.app_config = new_config.get_all()
|
70
|
+
except Exception:
|
71
|
+
self.app_config = None
|
72
|
+
elif hasattr(new_config, 'keys'):
|
73
|
+
self.app_config = new_config
|
74
|
+
else:
|
75
|
+
self.app_config = None
|
76
|
+
self.protocol_manager = get_protocol_manager(self.app_config)
|
46
77
|
logger.info("Protocol middleware configuration updated")
|
47
78
|
|
48
79
|
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
49
80
|
"""
|
50
81
|
Process request through protocol middleware.
|
51
|
-
|
82
|
+
|
52
83
|
Args:
|
53
84
|
request: Incoming request
|
54
85
|
call_next: Next middleware/endpoint function
|
55
|
-
|
86
|
+
|
56
87
|
Returns:
|
57
88
|
Response object
|
58
89
|
"""
|
90
|
+
logger.info(f"ProtocolMiddleware.dispatch called for {request.method} {request.url.path}")
|
59
91
|
try:
|
60
92
|
# Get protocol from request
|
61
93
|
protocol = self._get_request_protocol(request)
|
@@ -132,22 +164,37 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
|
|
132
164
|
def setup_protocol_middleware(app, app_config: Optional[Dict[str, Any]] = None):
|
133
165
|
"""
|
134
166
|
Setup protocol middleware for FastAPI application.
|
135
|
-
|
167
|
+
|
136
168
|
Args:
|
137
169
|
app: FastAPI application
|
138
170
|
app_config: Application configuration dictionary (optional)
|
139
171
|
"""
|
172
|
+
logger.info(f"setup_protocol_middleware - app_config type: {type(app_config)}")
|
173
|
+
|
140
174
|
# Check if protocol management is enabled
|
141
175
|
if app_config is None:
|
142
176
|
from mcp_proxy_adapter.config import config
|
143
177
|
app_config = config.get_all()
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
178
|
+
logger.info(f"setup_protocol_middleware - loaded from global config, type: {type(app_config)}")
|
179
|
+
|
180
|
+
logger.info(f"setup_protocol_middleware - final app_config type: {type(app_config)}")
|
181
|
+
|
182
|
+
if hasattr(app_config, 'get'):
|
183
|
+
logger.info(f"setup_protocol_middleware - app_config keys: {list(app_config.keys()) if hasattr(app_config, 'keys') else 'no keys'}")
|
184
|
+
protocols_config = app_config.get("protocols", {})
|
185
|
+
logger.info(f"setup_protocol_middleware - protocols_config type: {type(protocols_config)}")
|
186
|
+
enabled = protocols_config.get("enabled", True) if hasattr(protocols_config, 'get') else True
|
187
|
+
else:
|
188
|
+
logger.info(f"setup_protocol_middleware - app_config is not dict-like: {repr(app_config)}")
|
189
|
+
enabled = True
|
190
|
+
|
191
|
+
logger.info(f"setup_protocol_middleware - protocol management enabled: {enabled}")
|
192
|
+
|
148
193
|
if enabled:
|
149
194
|
# Create protocol middleware with current configuration
|
195
|
+
logger.info(f"setup_protocol_middleware - creating ProtocolMiddleware with config type: {type(app_config)}")
|
150
196
|
middleware = ProtocolMiddleware(app, app_config)
|
197
|
+
logger.info(f"setup_protocol_middleware - adding middleware to app")
|
151
198
|
app.add_middleware(ProtocolMiddleware, app_config=app_config)
|
152
199
|
logger.info("Protocol middleware added to application")
|
153
200
|
else:
|
@@ -65,12 +65,17 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
|
|
65
65
|
# Use framework's FastAPI middleware
|
66
66
|
self.framework_middleware = self.security_integration.security_manager.create_fastapi_middleware()
|
67
67
|
logger.info("Using mcp_security_framework FastAPI middleware")
|
68
|
+
# IMPORTANT: Don't replace self.app! This breaks the middleware chain.
|
69
|
+
# Instead, store the framework middleware for use in dispatch method.
|
70
|
+
logger.info("Framework middleware will be used in dispatch method")
|
68
71
|
except Exception as e:
|
69
72
|
logger.error(f"Security framework integration failed: {e}")
|
70
73
|
# Instead of raising error, log warning and continue without security
|
71
74
|
logger.warning("Continuing without security framework - some security features will be disabled")
|
72
75
|
self.security_integration = None
|
73
76
|
self.framework_middleware = None
|
77
|
+
# Keep original app in place when framework middleware is unavailable
|
78
|
+
# BaseHTTPMiddleware initialized it via super().__init__(app)
|
74
79
|
|
75
80
|
logger.info("Unified security middleware initialized")
|
76
81
|
|
@@ -86,14 +91,45 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
|
|
86
91
|
Response object
|
87
92
|
"""
|
88
93
|
try:
|
89
|
-
#
|
90
|
-
if self.
|
91
|
-
|
94
|
+
# Simple built-in API key enforcement if configured
|
95
|
+
security_cfg = self.config.get("security", {}) if isinstance(self.config, dict) else {}
|
96
|
+
auth_cfg = security_cfg.get("auth", {})
|
97
|
+
permissions_cfg = security_cfg.get("permissions", {})
|
98
|
+
public_paths = set(auth_cfg.get("public_paths", ["/health", "/docs", "/openapi.json"]))
|
99
|
+
# JSON-RPC endpoint must not be public when API key is required
|
100
|
+
public_paths.discard("/api/jsonrpc")
|
101
|
+
path = request.url.path
|
102
|
+
methods = set(auth_cfg.get("methods", []))
|
103
|
+
api_keys: Dict[str, str] = auth_cfg.get("api_keys", {}) or {}
|
104
|
+
|
105
|
+
# 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):
|
107
|
+
# Accept either X-API-Key or Authorization: Bearer
|
108
|
+
token = request.headers.get("X-API-Key")
|
109
|
+
if not token:
|
110
|
+
authz = request.headers.get("Authorization", "")
|
111
|
+
if authz.startswith("Bearer "):
|
112
|
+
token = authz[7:]
|
113
|
+
if not token or (api_keys and token not in api_keys):
|
114
|
+
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
|
+
})
|
122
|
+
|
123
|
+
# Continue with framework middleware or regular flow
|
124
|
+
if self.framework_middleware:
|
125
|
+
# If framework middleware exists, we need to call it manually
|
126
|
+
# This is a workaround since we can't chain ASGI apps in BaseHTTPMiddleware
|
127
|
+
logger.debug("Framework middleware exists, continuing with regular call_next")
|
128
|
+
return await call_next(request)
|
92
129
|
else:
|
93
|
-
#
|
94
|
-
logger.debug("Security framework not available, continuing without security checks")
|
130
|
+
# No framework middleware, continue normally
|
95
131
|
return await call_next(request)
|
96
|
-
|
132
|
+
|
97
133
|
except SecurityValidationError as e:
|
98
134
|
# Handle security validation errors
|
99
135
|
return await self._handle_security_error(request, e)
|
@@ -15,6 +15,16 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
|
15
15
|
|
16
16
|
from mcp_proxy_adapter.core.logging import logger
|
17
17
|
|
18
|
+
# Import mcp_security_framework components
|
19
|
+
try:
|
20
|
+
from mcp_security_framework import AuthManager
|
21
|
+
from mcp_security_framework.schemas.config import AuthConfig
|
22
|
+
_MCP_SECURITY_AVAILABLE = True
|
23
|
+
print("✅ mcp_security_framework available in middleware")
|
24
|
+
except ImportError:
|
25
|
+
_MCP_SECURITY_AVAILABLE = False
|
26
|
+
print("⚠️ mcp_security_framework not available in middleware, using basic auth")
|
27
|
+
|
18
28
|
|
19
29
|
class UserInfoMiddleware(BaseHTTPMiddleware):
|
20
30
|
"""
|
@@ -27,20 +37,43 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
|
|
27
37
|
def __init__(self, app, config: Dict[str, Any]):
|
28
38
|
"""
|
29
39
|
Initialize user info middleware.
|
30
|
-
|
40
|
+
|
31
41
|
Args:
|
32
42
|
app: FastAPI application
|
33
43
|
config: Configuration dictionary
|
34
44
|
"""
|
35
45
|
super().__init__(app)
|
36
46
|
self.config = config
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
47
|
+
|
48
|
+
# Initialize AuthManager if available
|
49
|
+
self.auth_manager = None
|
50
|
+
self._security_available = _MCP_SECURITY_AVAILABLE
|
51
|
+
|
52
|
+
if self._security_available:
|
53
|
+
try:
|
54
|
+
# Get API keys configuration
|
55
|
+
security_config = config.get("security", {})
|
56
|
+
auth_config = security_config.get("auth", {})
|
57
|
+
|
58
|
+
# Create AuthConfig for mcp_security_framework
|
59
|
+
mcp_auth_config = AuthConfig(
|
60
|
+
enabled=True,
|
61
|
+
methods=["api_key"],
|
62
|
+
api_keys=auth_config.get("api_keys", {})
|
63
|
+
)
|
64
|
+
|
65
|
+
self.auth_manager = AuthManager(mcp_auth_config)
|
66
|
+
logger.info("✅ User info middleware initialized with mcp_security_framework")
|
67
|
+
except Exception as e:
|
68
|
+
logger.warning(f"⚠️ Failed to initialize AuthManager: {e}")
|
69
|
+
self._security_available = False
|
70
|
+
|
71
|
+
if not self._security_available:
|
72
|
+
# Fallback to basic API key handling
|
73
|
+
security_config = config.get("security", {})
|
74
|
+
auth_config = security_config.get("auth", {})
|
75
|
+
self.api_keys = auth_config.get("api_keys", {})
|
76
|
+
logger.info("ℹ️ User info middleware initialized with basic auth")
|
44
77
|
|
45
78
|
async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
|
46
79
|
"""
|
@@ -56,28 +89,70 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
|
|
56
89
|
# Extract API key from headers
|
57
90
|
api_key = request.headers.get("X-API-Key")
|
58
91
|
|
59
|
-
if api_key
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
92
|
+
if api_key:
|
93
|
+
if self.auth_manager and self._security_available:
|
94
|
+
try:
|
95
|
+
# Use mcp_security_framework AuthManager
|
96
|
+
auth_result = self.auth_manager.authenticate_api_key(api_key)
|
97
|
+
|
98
|
+
if auth_result.is_valid:
|
99
|
+
# Set user info from AuthManager result
|
100
|
+
request.state.user = {
|
101
|
+
"id": api_key,
|
102
|
+
"role": auth_result.roles[0] if auth_result.roles else "guest",
|
103
|
+
"roles": auth_result.roles or ["guest"],
|
104
|
+
"permissions": getattr(auth_result, 'permissions', ["read"])
|
105
|
+
}
|
106
|
+
logger.debug(f"✅ Authenticated user with mcp_security_framework: {request.state.user}")
|
107
|
+
else:
|
108
|
+
# Authentication failed
|
109
|
+
request.state.user = {
|
110
|
+
"id": None,
|
111
|
+
"role": "guest",
|
112
|
+
"roles": ["guest"],
|
113
|
+
"permissions": ["read"]
|
114
|
+
}
|
115
|
+
logger.debug(f"❌ Authentication failed for API key: {api_key[:8]}...")
|
116
|
+
except Exception as e:
|
117
|
+
logger.warning(f"⚠️ AuthManager error: {e}, falling back to basic auth")
|
118
|
+
self._security_available = False
|
119
|
+
|
120
|
+
if not self._security_available:
|
121
|
+
# Fallback to basic API key handling
|
122
|
+
if api_key in getattr(self, 'api_keys', {}):
|
123
|
+
user_role = self.api_keys[api_key]
|
124
|
+
|
125
|
+
# Get permissions for this role from roles file if available
|
126
|
+
role_permissions = ["read"] # default permissions
|
127
|
+
if hasattr(self, 'roles_config') and self.roles_config and user_role in self.roles_config:
|
128
|
+
role_permissions = self.roles_config[user_role].get("permissions", ["read"])
|
129
|
+
|
130
|
+
# Set user info in request.state
|
131
|
+
request.state.user = {
|
132
|
+
"id": api_key,
|
133
|
+
"role": user_role,
|
134
|
+
"roles": [user_role],
|
135
|
+
"permissions": role_permissions
|
136
|
+
}
|
137
|
+
|
138
|
+
logger.debug(f"✅ Authenticated user with basic auth: {request.state.user}")
|
139
|
+
else:
|
140
|
+
# API key not found
|
141
|
+
request.state.user = {
|
142
|
+
"id": None,
|
143
|
+
"role": "guest",
|
144
|
+
"roles": ["guest"],
|
145
|
+
"permissions": ["read"]
|
146
|
+
}
|
147
|
+
logger.debug(f"❌ API key not found: {api_key[:8]}...")
|
72
148
|
else:
|
73
|
-
#
|
149
|
+
# No API key provided - guest access
|
74
150
|
request.state.user = {
|
75
151
|
"id": None,
|
76
152
|
"role": "guest",
|
77
153
|
"roles": ["guest"],
|
78
154
|
"permissions": ["read"]
|
79
155
|
}
|
80
|
-
|
81
|
-
logger.debug("Set default guest user info")
|
156
|
+
logger.debug("ℹ️ No API key provided, using guest access")
|
82
157
|
|
83
158
|
return await call_next(request)
|
@@ -33,12 +33,33 @@ class ProtocolManager:
|
|
33
33
|
|
34
34
|
def _load_config(self):
|
35
35
|
"""Load protocol configuration from config."""
|
36
|
-
# Use provided config or fallback to global config
|
36
|
+
# Use provided config or fallback to global config; normalize types
|
37
37
|
current_config = self.app_config if self.app_config is not None else config.get_all()
|
38
|
-
|
38
|
+
logger.info(f"ProtocolManager._load_config - current_config type: {type(current_config)}")
|
39
|
+
|
40
|
+
if not hasattr(current_config, 'get'):
|
41
|
+
# Not a dict-like config, fallback to global
|
42
|
+
logger.info(f"ProtocolManager._load_config - current_config is not dict-like, falling back to global config")
|
43
|
+
current_config = config.get_all()
|
44
|
+
|
45
|
+
logger.info(f"ProtocolManager._load_config - final current_config type: {type(current_config)}")
|
46
|
+
if hasattr(current_config, 'get'):
|
47
|
+
logger.info(f"ProtocolManager._load_config - current_config keys: {list(current_config.keys()) if hasattr(current_config, 'keys') else 'no keys'}")
|
48
|
+
|
39
49
|
# Get protocols configuration
|
40
|
-
|
41
|
-
|
50
|
+
logger.info(f"ProtocolManager._load_config - before getting protocols")
|
51
|
+
try:
|
52
|
+
self.protocols_config = current_config.get("protocols", {})
|
53
|
+
logger.info(f"ProtocolManager._load_config - protocols_config type: {type(self.protocols_config)}")
|
54
|
+
if hasattr(self.protocols_config, 'get'):
|
55
|
+
logger.info(f"ProtocolManager._load_config - protocols_config is dict-like")
|
56
|
+
else:
|
57
|
+
logger.info(f"ProtocolManager._load_config - protocols_config is NOT dict-like: {repr(self.protocols_config)}")
|
58
|
+
except Exception as e:
|
59
|
+
logger.info(f"ProtocolManager._load_config - ERROR getting protocols: {e}")
|
60
|
+
self.protocols_config = {}
|
61
|
+
|
62
|
+
self.enabled = self.protocols_config.get("enabled", True) if hasattr(self.protocols_config, 'get') else True
|
42
63
|
|
43
64
|
# Get SSL configuration to determine allowed protocols
|
44
65
|
ssl_enabled = self._is_ssl_enabled(current_config)
|
@@ -162,7 +183,14 @@ class ProtocolManager:
|
|
162
183
|
Protocol configuration dictionary
|
163
184
|
"""
|
164
185
|
protocol_lower = protocol.lower()
|
165
|
-
|
186
|
+
cfg = self.protocols_config.get(protocol_lower, {})
|
187
|
+
# Ensure dict type
|
188
|
+
if isinstance(cfg, dict):
|
189
|
+
try:
|
190
|
+
return cfg.copy()
|
191
|
+
except Exception:
|
192
|
+
return {}
|
193
|
+
return {}
|
166
194
|
|
167
195
|
def validate_url_protocol(self, url: str) -> Tuple[bool, Optional[str]]:
|
168
196
|
"""
|
@@ -22,17 +22,18 @@ except ImportError:
|
|
22
22
|
class SimpleCertificateCreator:
|
23
23
|
"""Create certificates using OpenSSL directly."""
|
24
24
|
def __init__(self, certs_dir: str = None, keys_dir: str = None):
|
25
|
+
# Use current working directory as base
|
26
|
+
cwd = Path.cwd()
|
27
|
+
|
25
28
|
if certs_dir:
|
26
29
|
self.certs_dir = Path(certs_dir).resolve()
|
27
30
|
else:
|
28
|
-
self.
|
29
|
-
|
31
|
+
self.certs_dir = cwd / "certs"
|
32
|
+
|
30
33
|
if keys_dir:
|
31
34
|
self.keys_dir = Path(keys_dir).resolve()
|
32
35
|
else:
|
33
|
-
|
34
|
-
self.project_root = Path(__file__).parent.parent.parent
|
35
|
-
self.keys_dir = self.project_root / "mcp_proxy_adapter" / "examples" / "keys"
|
36
|
+
self.keys_dir = cwd / "keys"
|
36
37
|
# Create directories
|
37
38
|
self.certs_dir.mkdir(parents=True, exist_ok=True)
|
38
39
|
self.keys_dir.mkdir(parents=True, exist_ok=True)
|
@@ -450,6 +451,56 @@ class SimpleCertificateCreator:
|
|
450
451
|
# 5. Validate certificates
|
451
452
|
if not self.validate_certificates():
|
452
453
|
success = False
|
454
|
+
# Create compatibility symlinks
|
455
|
+
if success:
|
456
|
+
# CA certificate symlink
|
457
|
+
ca_cert = self.certs_dir / "ca_cert.pem"
|
458
|
+
expected_ca_cert = self.certs_dir / "mcp_proxy_adapter_ca_ca.crt"
|
459
|
+
if ca_cert.exists() and not expected_ca_cert.exists():
|
460
|
+
try:
|
461
|
+
expected_ca_cert.symlink_to(ca_cert)
|
462
|
+
print(f"✅ Created CA certificate symlink: {expected_ca_cert}")
|
463
|
+
except OSError:
|
464
|
+
# On Windows, symlink might require admin privileges, copy instead
|
465
|
+
import shutil
|
466
|
+
shutil.copy2(ca_cert, expected_ca_cert)
|
467
|
+
print(f"✅ Created CA certificate copy: {expected_ca_cert}")
|
468
|
+
|
469
|
+
# Server certificate symlink
|
470
|
+
server_cert = self.certs_dir / "server_cert.pem"
|
471
|
+
expected_server_cert = self.certs_dir / "localhost_server.crt"
|
472
|
+
if server_cert.exists() and not expected_server_cert.exists():
|
473
|
+
try:
|
474
|
+
expected_server_cert.symlink_to(server_cert)
|
475
|
+
print(f"✅ Created server certificate symlink: {expected_server_cert}")
|
476
|
+
except OSError:
|
477
|
+
# On Windows, symlink might require admin privileges, copy instead
|
478
|
+
import shutil
|
479
|
+
shutil.copy2(server_cert, expected_server_cert)
|
480
|
+
print(f"✅ Created server certificate copy: {expected_server_cert}")
|
481
|
+
|
482
|
+
# Server key symlink - check if it's in certs or keys directory
|
483
|
+
server_key_certs = self.certs_dir / "server_key.pem"
|
484
|
+
server_key_keys = self.keys_dir / "server_key.pem"
|
485
|
+
|
486
|
+
if server_key_certs.exists():
|
487
|
+
# Server key is in certs directory, move it to keys directory
|
488
|
+
import shutil
|
489
|
+
shutil.move(str(server_key_certs), str(server_key_keys))
|
490
|
+
print(f"✅ Moved server key to keys directory: {server_key_keys}")
|
491
|
+
|
492
|
+
if server_key_keys.exists():
|
493
|
+
expected_server_key = self.keys_dir / "localhost_server.key"
|
494
|
+
if not expected_server_key.exists():
|
495
|
+
try:
|
496
|
+
expected_server_key.symlink_to(server_key_keys)
|
497
|
+
print(f"✅ Created server key symlink: {expected_server_key}")
|
498
|
+
except OSError:
|
499
|
+
# On Windows, symlink might require admin privileges, copy instead
|
500
|
+
import shutil
|
501
|
+
shutil.copy2(server_key_keys, expected_server_key)
|
502
|
+
print(f"✅ Created server key copy: {expected_server_key}")
|
503
|
+
|
453
504
|
# Print summary
|
454
505
|
print("\n" + "=" * 60)
|
455
506
|
print("📊 CERTIFICATE CREATION SUMMARY")
|
@@ -472,12 +523,29 @@ class SimpleCertificateCreator:
|
|
472
523
|
def main():
|
473
524
|
"""Main function."""
|
474
525
|
parser = argparse.ArgumentParser(description="Create certificates for testing")
|
475
|
-
parser.add_argument("--certs-dir", help="Directory for certificates")
|
476
|
-
parser.add_argument("--keys-dir", help="Directory for keys")
|
526
|
+
parser.add_argument("--certs-dir", help="Directory for certificates (default: ./certs)")
|
527
|
+
parser.add_argument("--keys-dir", help="Directory for keys (default: ./keys)")
|
477
528
|
args = parser.parse_args()
|
529
|
+
|
530
|
+
# If no directories specified, check if we're in a test environment
|
531
|
+
if not args.certs_dir and not args.keys_dir:
|
532
|
+
cwd = Path.cwd()
|
533
|
+
# Check if we're in a test environment by looking for typical directories
|
534
|
+
if (cwd / "configs").exists() and (cwd / "examples").exists():
|
535
|
+
# We're in a test environment, use current directory
|
536
|
+
certs_dir = cwd / "certs"
|
537
|
+
keys_dir = cwd / "keys"
|
538
|
+
else:
|
539
|
+
# Use default project structure
|
540
|
+
certs_dir = None
|
541
|
+
keys_dir = None
|
542
|
+
else:
|
543
|
+
certs_dir = args.certs_dir
|
544
|
+
keys_dir = args.keys_dir
|
545
|
+
|
478
546
|
creator = SimpleCertificateCreator(
|
479
|
-
certs_dir=
|
480
|
-
keys_dir=
|
547
|
+
certs_dir=certs_dir,
|
548
|
+
keys_dir=keys_dir
|
481
549
|
)
|
482
550
|
try:
|
483
551
|
success = creator.create_all()
|