mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.5__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/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +108 -88
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -25,16 +25,23 @@ logger = logging.getLogger(__name__)
|
|
25
25
|
class KeyResult:
|
26
26
|
"""
|
27
27
|
Result class for key operations.
|
28
|
-
|
28
|
+
|
29
29
|
Contains key information and operation status.
|
30
30
|
"""
|
31
|
-
|
32
|
-
def __init__(
|
33
|
-
|
34
|
-
|
31
|
+
|
32
|
+
def __init__(
|
33
|
+
self,
|
34
|
+
key_path: str,
|
35
|
+
key_type: str,
|
36
|
+
key_size: int,
|
37
|
+
created_date: Optional[str] = None,
|
38
|
+
expiry_date: Optional[str] = None,
|
39
|
+
status: str = "valid",
|
40
|
+
error: Optional[str] = None,
|
41
|
+
):
|
35
42
|
"""
|
36
43
|
Initialize key result.
|
37
|
-
|
44
|
+
|
38
45
|
Args:
|
39
46
|
key_path: Path to key file
|
40
47
|
key_type: Type of key (RSA, ECDSA, etc.)
|
@@ -51,11 +58,11 @@ class KeyResult:
|
|
51
58
|
self.expiry_date = expiry_date
|
52
59
|
self.status = status
|
53
60
|
self.error = error
|
54
|
-
|
61
|
+
|
55
62
|
def to_dict(self) -> Dict[str, Any]:
|
56
63
|
"""
|
57
64
|
Convert to dictionary format.
|
58
|
-
|
65
|
+
|
59
66
|
Returns:
|
60
67
|
Dictionary representation
|
61
68
|
"""
|
@@ -66,9 +73,9 @@ class KeyResult:
|
|
66
73
|
"created_date": self.created_date,
|
67
74
|
"expiry_date": self.expiry_date,
|
68
75
|
"status": self.status,
|
69
|
-
"error": self.error
|
76
|
+
"error": self.error,
|
70
77
|
}
|
71
|
-
|
78
|
+
|
72
79
|
@classmethod
|
73
80
|
def get_schema(cls) -> Dict[str, Any]:
|
74
81
|
"""
|
@@ -85,21 +92,24 @@ class KeyResult:
|
|
85
92
|
"key_size": {"type": "integer", "description": "Key size in bits"},
|
86
93
|
"created_date": {"type": "string", "description": "Key creation date"},
|
87
94
|
"expiry_date": {"type": "string", "description": "Key expiry date"},
|
88
|
-
"status": {
|
89
|
-
|
90
|
-
|
95
|
+
"status": {
|
96
|
+
"type": "string",
|
97
|
+
"enum": ["valid", "expired", "error"],
|
98
|
+
"description": "Key status",
|
99
|
+
},
|
100
|
+
"error": {"type": "string", "description": "Error message if any"},
|
91
101
|
},
|
92
|
-
"required": ["key_path", "key_type", "key_size", "status"]
|
102
|
+
"required": ["key_path", "key_type", "key_size", "status"],
|
93
103
|
}
|
94
104
|
|
95
105
|
|
96
106
|
class KeyManagementCommand(Command):
|
97
107
|
"""
|
98
108
|
Command for key management.
|
99
|
-
|
109
|
+
|
100
110
|
Provides methods for generating, validating, rotating, backing up, and restoring keys.
|
101
111
|
"""
|
102
|
-
|
112
|
+
|
103
113
|
# Command metadata
|
104
114
|
name = "key_management"
|
105
115
|
version = "1.0.0"
|
@@ -109,16 +119,16 @@ class KeyManagementCommand(Command):
|
|
109
119
|
email = "team@mcp-proxy-adapter.com"
|
110
120
|
source_url = "https://github.com/mcp-proxy-adapter"
|
111
121
|
result_class = KeyResult
|
112
|
-
|
122
|
+
|
113
123
|
def __init__(self):
|
114
124
|
"""Initialize key management command."""
|
115
125
|
super().__init__()
|
116
126
|
self.certificate_utils = CertificateUtils()
|
117
|
-
|
127
|
+
|
118
128
|
async def execute(self, **kwargs) -> CommandResult:
|
119
129
|
"""
|
120
130
|
Execute key management command.
|
121
|
-
|
131
|
+
|
122
132
|
Args:
|
123
133
|
**kwargs: Command parameters including:
|
124
134
|
- action: Action to perform (key_generate, key_validate, key_rotate, key_backup, key_restore)
|
@@ -133,12 +143,12 @@ class KeyManagementCommand(Command):
|
|
133
143
|
- backup_old: Whether to backup old key during rotation
|
134
144
|
- backup_path: Backup path for key backup/restore
|
135
145
|
- encrypt_backup: Whether to encrypt backup
|
136
|
-
|
146
|
+
|
137
147
|
Returns:
|
138
148
|
CommandResult with key operation status
|
139
149
|
"""
|
140
150
|
action = kwargs.get("action", "key_validate")
|
141
|
-
|
151
|
+
|
142
152
|
if action == "key_generate":
|
143
153
|
key_type = kwargs.get("key_type")
|
144
154
|
key_size = kwargs.get("key_size")
|
@@ -154,13 +164,17 @@ class KeyManagementCommand(Command):
|
|
154
164
|
new_key_path = kwargs.get("new_key_path")
|
155
165
|
cert_path = kwargs.get("cert_path")
|
156
166
|
backup_old = kwargs.get("backup_old", True)
|
157
|
-
return await self.key_rotate(
|
167
|
+
return await self.key_rotate(
|
168
|
+
old_key_path, new_key_path, cert_path, backup_old
|
169
|
+
)
|
158
170
|
elif action == "key_backup":
|
159
171
|
key_path = kwargs.get("key_path")
|
160
172
|
backup_path = kwargs.get("backup_path")
|
161
173
|
encrypt_backup = kwargs.get("encrypt_backup", True)
|
162
174
|
password = kwargs.get("password")
|
163
|
-
return await self.key_backup(
|
175
|
+
return await self.key_backup(
|
176
|
+
key_path, backup_path, encrypt_backup, password
|
177
|
+
)
|
164
178
|
elif action == "key_restore":
|
165
179
|
backup_path = kwargs.get("backup_path")
|
166
180
|
key_path = kwargs.get("key_path")
|
@@ -170,175 +184,162 @@ class KeyManagementCommand(Command):
|
|
170
184
|
return ErrorResult(
|
171
185
|
message=f"Unknown action: {action}. Supported actions: key_generate, key_validate, key_rotate, key_backup, key_restore"
|
172
186
|
)
|
173
|
-
|
174
|
-
async def key_generate(
|
175
|
-
|
187
|
+
|
188
|
+
async def key_generate(
|
189
|
+
self,
|
190
|
+
key_type: str,
|
191
|
+
key_size: int,
|
192
|
+
output_path: str,
|
193
|
+
password: Optional[str] = None,
|
194
|
+
) -> CommandResult:
|
176
195
|
"""
|
177
196
|
Generate a new private key.
|
178
|
-
|
197
|
+
|
179
198
|
Args:
|
180
199
|
key_type: Type of key to generate (RSA, ECDSA)
|
181
200
|
key_size: Key size in bits
|
182
201
|
output_path: Path to save the generated key
|
183
202
|
password: Optional password to encrypt the key
|
184
|
-
|
203
|
+
|
185
204
|
Returns:
|
186
205
|
CommandResult with key generation status
|
187
206
|
"""
|
188
207
|
try:
|
189
208
|
logger.info(f"Generating {key_type} key with size {key_size} bits")
|
190
|
-
|
209
|
+
|
191
210
|
# Validate parameters
|
192
211
|
if key_type not in ["RSA", "ECDSA"]:
|
193
|
-
return ErrorResult(
|
194
|
-
|
195
|
-
)
|
196
|
-
|
212
|
+
return ErrorResult(message="Key type must be RSA or ECDSA")
|
213
|
+
|
197
214
|
if key_type == "ECDSA":
|
198
215
|
if key_size not in [256, 384, 521]:
|
199
216
|
return ErrorResult(
|
200
217
|
message="ECDSA key size must be 256, 384, or 521 bits"
|
201
218
|
)
|
202
219
|
elif key_size < 1024:
|
203
|
-
return ErrorResult(
|
204
|
-
|
205
|
-
)
|
206
|
-
|
220
|
+
return ErrorResult(message="Key size must be at least 1024 bits")
|
221
|
+
|
207
222
|
# Create output directory if it doesn't exist
|
208
223
|
output_dir = os.path.dirname(output_path)
|
209
224
|
if output_dir:
|
210
225
|
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
211
|
-
|
226
|
+
|
212
227
|
# Generate key
|
213
228
|
result = self.certificate_utils.generate_private_key(
|
214
229
|
key_type, key_size, output_path
|
215
230
|
)
|
216
|
-
|
231
|
+
|
217
232
|
if not result["success"]:
|
218
|
-
return ErrorResult(
|
219
|
-
|
220
|
-
)
|
221
|
-
|
233
|
+
return ErrorResult(message=f"Key generation failed: {result['error']}")
|
234
|
+
|
222
235
|
key_result = KeyResult(
|
223
236
|
key_path=output_path,
|
224
237
|
key_type=key_type,
|
225
238
|
key_size=key_size,
|
226
239
|
created_date=datetime.now().isoformat(),
|
227
|
-
status="valid"
|
240
|
+
status="valid",
|
228
241
|
)
|
229
|
-
|
242
|
+
|
230
243
|
logger.info(f"Key generated successfully: {output_path}")
|
231
|
-
return SuccessResult(
|
232
|
-
|
233
|
-
"key": key_result.to_dict(),
|
234
|
-
"details": result
|
235
|
-
}
|
236
|
-
)
|
237
|
-
|
244
|
+
return SuccessResult(data={"key": key_result.to_dict(), "details": result})
|
245
|
+
|
238
246
|
except Exception as e:
|
239
247
|
logger.error(f"Key generation failed: {e}")
|
240
|
-
return ErrorResult(
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
248
|
+
return ErrorResult(message=f"Key generation failed: {str(e)}")
|
249
|
+
|
250
|
+
async def key_validate(
|
251
|
+
self, key_path: str, password: Optional[str] = None
|
252
|
+
) -> CommandResult:
|
245
253
|
"""
|
246
254
|
Validate a private key.
|
247
|
-
|
255
|
+
|
248
256
|
Args:
|
249
257
|
key_path: Path to key file to validate
|
250
258
|
password: Optional password if key is encrypted
|
251
|
-
|
259
|
+
|
252
260
|
Returns:
|
253
261
|
CommandResult with key validation status
|
254
262
|
"""
|
255
263
|
try:
|
256
264
|
logger.info(f"Validating key: {key_path}")
|
257
|
-
|
265
|
+
|
258
266
|
# Validate parameters
|
259
267
|
if not key_path or not os.path.exists(key_path):
|
260
|
-
return ErrorResult(
|
261
|
-
|
262
|
-
)
|
263
|
-
|
268
|
+
return ErrorResult(message=f"Key file not found: {key_path}")
|
269
|
+
|
264
270
|
# Validate key
|
265
271
|
result = self.certificate_utils.validate_private_key(key_path)
|
266
|
-
|
272
|
+
|
267
273
|
if not result["success"]:
|
268
|
-
return ErrorResult(
|
269
|
-
|
270
|
-
)
|
271
|
-
|
274
|
+
return ErrorResult(message=f"Key validation failed: {result['error']}")
|
275
|
+
|
272
276
|
key_result = KeyResult(
|
273
277
|
key_path=key_path,
|
274
278
|
key_type=result.get("key_type", "unknown"),
|
275
279
|
key_size=result.get("key_size", 0),
|
276
280
|
created_date=result.get("created_date"),
|
277
|
-
status="valid"
|
281
|
+
status="valid",
|
278
282
|
)
|
279
|
-
|
283
|
+
|
280
284
|
logger.info(f"Key validation completed: {key_path}")
|
281
|
-
return SuccessResult(
|
282
|
-
|
283
|
-
"key": key_result.to_dict()
|
284
|
-
}
|
285
|
-
)
|
286
|
-
|
285
|
+
return SuccessResult(data={"key": key_result.to_dict()})
|
286
|
+
|
287
287
|
except Exception as e:
|
288
288
|
logger.error(f"Key validation failed: {e}")
|
289
|
-
return ErrorResult(
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
289
|
+
return ErrorResult(message=f"Key validation failed: {str(e)}")
|
290
|
+
|
291
|
+
async def key_rotate(
|
292
|
+
self,
|
293
|
+
old_key_path: str,
|
294
|
+
new_key_path: str,
|
295
|
+
cert_path: Optional[str] = None,
|
296
|
+
backup_old: bool = True,
|
297
|
+
) -> CommandResult:
|
295
298
|
"""
|
296
299
|
Rotate a private key.
|
297
|
-
|
300
|
+
|
298
301
|
Args:
|
299
302
|
old_key_path: Path to old key file
|
300
303
|
new_key_path: Path to new key file
|
301
304
|
cert_path: Optional certificate path to update with new key
|
302
305
|
backup_old: Whether to backup the old key
|
303
|
-
|
306
|
+
|
304
307
|
Returns:
|
305
308
|
CommandResult with key rotation status
|
306
309
|
"""
|
307
310
|
try:
|
308
311
|
logger.info(f"Rotating key from {old_key_path} to {new_key_path}")
|
309
|
-
|
312
|
+
|
310
313
|
# Validate parameters
|
311
314
|
if not old_key_path or not os.path.exists(old_key_path):
|
312
|
-
return ErrorResult(
|
313
|
-
|
314
|
-
)
|
315
|
-
|
315
|
+
return ErrorResult(message=f"Old key file not found: {old_key_path}")
|
316
|
+
|
316
317
|
if not new_key_path or not os.path.exists(new_key_path):
|
317
|
-
return ErrorResult(
|
318
|
-
|
319
|
-
)
|
320
|
-
|
318
|
+
return ErrorResult(message=f"New key file not found: {new_key_path}")
|
319
|
+
|
321
320
|
# Validate both keys
|
322
321
|
old_key_validation = await self.key_validate(old_key_path)
|
323
322
|
new_key_validation = await self.key_validate(new_key_path)
|
324
|
-
|
323
|
+
|
325
324
|
if not old_key_validation.to_dict()["success"]:
|
326
325
|
return ErrorResult(
|
327
326
|
message=f"Old key validation failed: {old_key_validation.to_dict()['error']['message']}"
|
328
327
|
)
|
329
|
-
|
328
|
+
|
330
329
|
if not new_key_validation.to_dict()["success"]:
|
331
330
|
return ErrorResult(
|
332
331
|
message=f"New key validation failed: {new_key_validation.to_dict()['error']['message']}"
|
333
332
|
)
|
334
|
-
|
333
|
+
|
335
334
|
# Backup old key if requested
|
336
335
|
backup_path = None
|
337
336
|
if backup_old:
|
338
|
-
backup_path =
|
337
|
+
backup_path = (
|
338
|
+
f"{old_key_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
339
|
+
)
|
339
340
|
shutil.copy2(old_key_path, backup_path)
|
340
341
|
logger.info(f"Old key backed up to: {backup_path}")
|
341
|
-
|
342
|
+
|
342
343
|
# Update certificate if provided
|
343
344
|
cert_updated = False
|
344
345
|
if cert_path and os.path.exists(cert_path):
|
@@ -346,13 +347,15 @@ class KeyManagementCommand(Command):
|
|
346
347
|
# This would require implementing certificate re-signing
|
347
348
|
# For now, we'll just note that it needs to be done
|
348
349
|
cert_updated = True
|
349
|
-
logger.info(
|
350
|
+
logger.info(
|
351
|
+
f"Certificate {cert_path} needs to be re-signed with new key"
|
352
|
+
)
|
350
353
|
except Exception as e:
|
351
354
|
logger.warning(f"Could not update certificate {cert_path}: {e}")
|
352
|
-
|
355
|
+
|
353
356
|
# Replace old key with new key
|
354
357
|
shutil.copy2(new_key_path, old_key_path)
|
355
|
-
|
358
|
+
|
356
359
|
logger.info(f"Key rotation completed successfully")
|
357
360
|
return SuccessResult(
|
358
361
|
data={
|
@@ -360,51 +363,52 @@ class KeyManagementCommand(Command):
|
|
360
363
|
"new_key_path": new_key_path,
|
361
364
|
"backup_path": backup_path,
|
362
365
|
"cert_updated": cert_updated,
|
363
|
-
"message": "Key rotation completed successfully"
|
366
|
+
"message": "Key rotation completed successfully",
|
364
367
|
}
|
365
368
|
)
|
366
|
-
|
369
|
+
|
367
370
|
except Exception as e:
|
368
371
|
logger.error(f"Key rotation failed: {e}")
|
369
|
-
return ErrorResult(
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
372
|
+
return ErrorResult(message=f"Key rotation failed: {str(e)}")
|
373
|
+
|
374
|
+
async def key_backup(
|
375
|
+
self,
|
376
|
+
key_path: str,
|
377
|
+
backup_path: str,
|
378
|
+
encrypt_backup: bool = True,
|
379
|
+
password: Optional[str] = None,
|
380
|
+
) -> CommandResult:
|
375
381
|
"""
|
376
382
|
Backup a private key.
|
377
|
-
|
383
|
+
|
378
384
|
Args:
|
379
385
|
key_path: Path to key file to backup
|
380
386
|
backup_path: Path to save the backup
|
381
387
|
encrypt_backup: Whether to encrypt the backup
|
382
388
|
password: Password for backup encryption
|
383
|
-
|
389
|
+
|
384
390
|
Returns:
|
385
391
|
CommandResult with backup status
|
386
392
|
"""
|
387
393
|
try:
|
388
394
|
logger.info(f"Backing up key: {key_path}")
|
389
|
-
|
395
|
+
|
390
396
|
# Validate parameters
|
391
397
|
if not key_path or not os.path.exists(key_path):
|
392
|
-
return ErrorResult(
|
393
|
-
|
394
|
-
)
|
395
|
-
|
398
|
+
return ErrorResult(message=f"Key file not found: {key_path}")
|
399
|
+
|
396
400
|
# Validate key before backup
|
397
401
|
key_validation = await self.key_validate(key_path)
|
398
402
|
if not key_validation.to_dict()["success"]:
|
399
403
|
return ErrorResult(
|
400
404
|
message=f"Key validation failed before backup: {key_validation.to_dict()['error']['message']}"
|
401
405
|
)
|
402
|
-
|
406
|
+
|
403
407
|
# Create backup directory if it doesn't exist
|
404
408
|
backup_dir = os.path.dirname(backup_path)
|
405
409
|
if backup_dir:
|
406
410
|
Path(backup_dir).mkdir(parents=True, exist_ok=True)
|
407
|
-
|
411
|
+
|
408
412
|
# Create backup
|
409
413
|
if encrypt_backup and password:
|
410
414
|
# Encrypted backup
|
@@ -418,13 +422,11 @@ class KeyManagementCommand(Command):
|
|
418
422
|
else:
|
419
423
|
# Simple file copy
|
420
424
|
shutil.copy2(key_path, backup_path)
|
421
|
-
|
425
|
+
|
422
426
|
# Verify backup
|
423
427
|
if not os.path.exists(backup_path):
|
424
|
-
return ErrorResult(
|
425
|
-
|
426
|
-
)
|
427
|
-
|
428
|
+
return ErrorResult(message="Backup file was not created")
|
429
|
+
|
428
430
|
logger.info(f"Key backup completed successfully: {backup_path}")
|
429
431
|
return SuccessResult(
|
430
432
|
data={
|
@@ -432,43 +434,40 @@ class KeyManagementCommand(Command):
|
|
432
434
|
"backup_path": backup_path,
|
433
435
|
"encrypted": encrypt_backup,
|
434
436
|
"backup_size": os.path.getsize(backup_path),
|
435
|
-
"backup_date": datetime.now().isoformat()
|
437
|
+
"backup_date": datetime.now().isoformat(),
|
436
438
|
}
|
437
439
|
)
|
438
|
-
|
440
|
+
|
439
441
|
except Exception as e:
|
440
442
|
logger.error(f"Key backup failed: {e}")
|
441
|
-
return ErrorResult(
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
password: Optional[str] = None) -> CommandResult:
|
443
|
+
return ErrorResult(message=f"Key backup failed: {str(e)}")
|
444
|
+
|
445
|
+
async def key_restore(
|
446
|
+
self, backup_path: str, key_path: str, password: Optional[str] = None
|
447
|
+
) -> CommandResult:
|
447
448
|
"""
|
448
449
|
Restore a private key from backup.
|
449
|
-
|
450
|
+
|
450
451
|
Args:
|
451
452
|
backup_path: Path to backup file
|
452
453
|
key_path: Path to restore the key to
|
453
454
|
password: Password if backup is encrypted
|
454
|
-
|
455
|
+
|
455
456
|
Returns:
|
456
457
|
CommandResult with restore status
|
457
458
|
"""
|
458
459
|
try:
|
459
460
|
logger.info(f"Restoring key from backup: {backup_path}")
|
460
|
-
|
461
|
+
|
461
462
|
# Validate parameters
|
462
463
|
if not backup_path or not os.path.exists(backup_path):
|
463
|
-
return ErrorResult(
|
464
|
-
|
465
|
-
)
|
466
|
-
|
464
|
+
return ErrorResult(message=f"Backup file not found: {backup_path}")
|
465
|
+
|
467
466
|
# Create target directory if it doesn't exist
|
468
467
|
key_dir = os.path.dirname(key_path)
|
469
468
|
if key_dir:
|
470
469
|
Path(key_dir).mkdir(parents=True, exist_ok=True)
|
471
|
-
|
470
|
+
|
472
471
|
# Restore key
|
473
472
|
if password:
|
474
473
|
# Try encrypted restore first
|
@@ -482,26 +481,28 @@ class KeyManagementCommand(Command):
|
|
482
481
|
else:
|
483
482
|
# Simple file copy
|
484
483
|
shutil.copy2(backup_path, key_path)
|
485
|
-
|
484
|
+
|
486
485
|
# Validate restored key
|
487
486
|
key_validation = await self.key_validate(key_path)
|
488
487
|
if not key_validation.to_dict()["success"]:
|
489
488
|
return ErrorResult(
|
490
489
|
message=f"Restored key validation failed: {key_validation.to_dict()['error']['message']}"
|
491
490
|
)
|
492
|
-
|
491
|
+
|
493
492
|
logger.info(f"Key restore completed successfully: {key_path}")
|
494
493
|
return SuccessResult(
|
495
494
|
data={
|
496
495
|
"backup_path": backup_path,
|
497
496
|
"key_path": key_path,
|
498
497
|
"restore_date": datetime.now().isoformat(),
|
499
|
-
"key_info":
|
498
|
+
"key_info": (
|
499
|
+
key_validation.to_dict().get("data", {}).get("key")
|
500
|
+
if key_validation.to_dict().get("success")
|
501
|
+
else None
|
502
|
+
),
|
500
503
|
}
|
501
504
|
)
|
502
|
-
|
505
|
+
|
503
506
|
except Exception as e:
|
504
507
|
logger.error(f"Key restore failed: {e}")
|
505
|
-
return ErrorResult(
|
506
|
-
message=f"Key restore failed: {str(e)}"
|
507
|
-
)
|
508
|
+
return ErrorResult(message=f"Key restore failed: {str(e)}")
|