mcp-proxy-adapter 6.3.3__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.
Files changed (129) 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 +108 -88
  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 +12 -2
  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.3.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
  125. mcp_proxy_adapter-6.3.3.dist-info/RECORD +0 -143
  126. {mcp_proxy_adapter-6.3.3.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
  127. {mcp_proxy_adapter-6.3.3.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
  128. {mcp_proxy_adapter-6.3.3.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
  129. {mcp_proxy_adapter-6.3.3.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -26,17 +26,25 @@ logger = logging.getLogger(__name__)
26
26
  class CertMonitorResult:
27
27
  """
28
28
  Result class for certificate monitoring operations.
29
-
29
+
30
30
  Contains monitoring information and operation status.
31
31
  """
32
-
33
- def __init__(self, cert_path: str, check_type: str, status: str,
34
- expiry_date: Optional[str] = None, days_until_expiry: Optional[int] = None,
35
- health_score: Optional[int] = None, alerts: Optional[List[str]] = None,
36
- auto_renewal_status: Optional[str] = None, error: Optional[str] = None):
32
+
33
+ def __init__(
34
+ self,
35
+ cert_path: str,
36
+ check_type: str,
37
+ status: str,
38
+ expiry_date: Optional[str] = None,
39
+ days_until_expiry: Optional[int] = None,
40
+ health_score: Optional[int] = None,
41
+ alerts: Optional[List[str]] = None,
42
+ auto_renewal_status: Optional[str] = None,
43
+ error: Optional[str] = None,
44
+ ):
37
45
  """
38
46
  Initialize certificate monitor result.
39
-
47
+
40
48
  Args:
41
49
  cert_path: Path to certificate file
42
50
  check_type: Type of check performed (expiry, health, alert, auto_renewal)
@@ -57,11 +65,11 @@ class CertMonitorResult:
57
65
  self.alerts = alerts or []
58
66
  self.auto_renewal_status = auto_renewal_status
59
67
  self.error = error
60
-
68
+
61
69
  def to_dict(self) -> Dict[str, Any]:
62
70
  """
63
71
  Convert to dictionary format.
64
-
72
+
65
73
  Returns:
66
74
  Dictionary representation
67
75
  """
@@ -74,9 +82,9 @@ class CertMonitorResult:
74
82
  "health_score": self.health_score,
75
83
  "alerts": self.alerts,
76
84
  "auto_renewal_status": self.auto_renewal_status,
77
- "error": self.error
85
+ "error": self.error,
78
86
  }
79
-
87
+
80
88
  @classmethod
81
89
  def get_schema(cls) -> Dict[str, Any]:
82
90
  """
@@ -88,31 +96,56 @@ class CertMonitorResult:
88
96
  return {
89
97
  "type": "object",
90
98
  "properties": {
91
- "cert_path": {"type": "string", "description": "Path to certificate file"},
92
- "check_type": {"type": "string", "enum": ["expiry", "health", "alert", "auto_renewal"],
93
- "description": "Type of check performed"},
94
- "status": {"type": "string", "enum": ["healthy", "warning", "critical", "error"],
95
- "description": "Overall status"},
96
- "expiry_date": {"type": "string", "description": "Certificate expiry date"},
97
- "days_until_expiry": {"type": "integer", "description": "Days until certificate expires"},
98
- "health_score": {"type": "integer", "minimum": 0, "maximum": 100,
99
- "description": "Health score (0-100)"},
100
- "alerts": {"type": "array", "items": {"type": "string"},
101
- "description": "List of alert messages"},
102
- "auto_renewal_status": {"type": "string", "description": "Auto-renewal status"},
103
- "error": {"type": "string", "description": "Error message if any"}
99
+ "cert_path": {
100
+ "type": "string",
101
+ "description": "Path to certificate file",
102
+ },
103
+ "check_type": {
104
+ "type": "string",
105
+ "enum": ["expiry", "health", "alert", "auto_renewal"],
106
+ "description": "Type of check performed",
107
+ },
108
+ "status": {
109
+ "type": "string",
110
+ "enum": ["healthy", "warning", "critical", "error"],
111
+ "description": "Overall status",
112
+ },
113
+ "expiry_date": {
114
+ "type": "string",
115
+ "description": "Certificate expiry date",
116
+ },
117
+ "days_until_expiry": {
118
+ "type": "integer",
119
+ "description": "Days until certificate expires",
120
+ },
121
+ "health_score": {
122
+ "type": "integer",
123
+ "minimum": 0,
124
+ "maximum": 100,
125
+ "description": "Health score (0-100)",
126
+ },
127
+ "alerts": {
128
+ "type": "array",
129
+ "items": {"type": "string"},
130
+ "description": "List of alert messages",
131
+ },
132
+ "auto_renewal_status": {
133
+ "type": "string",
134
+ "description": "Auto-renewal status",
135
+ },
136
+ "error": {"type": "string", "description": "Error message if any"},
104
137
  },
105
- "required": ["cert_path", "check_type", "status"]
138
+ "required": ["cert_path", "check_type", "status"],
106
139
  }
107
140
 
108
141
 
109
142
  class CertMonitorCommand(Command):
110
143
  """
111
144
  Command for certificate monitoring.
112
-
145
+
113
146
  Provides methods for monitoring certificate expiry, health, alerts, and auto-renewal.
114
147
  """
115
-
148
+
116
149
  # Command metadata
117
150
  name = "cert_monitor"
118
151
  version = "1.0.0"
@@ -122,17 +155,17 @@ class CertMonitorCommand(Command):
122
155
  email = "team@mcp-proxy-adapter.com"
123
156
  source_url = "https://github.com/mcp-proxy-adapter"
124
157
  result_class = CertMonitorResult
125
-
158
+
126
159
  def __init__(self):
127
160
  """Initialize certificate monitor command."""
128
161
  super().__init__()
129
162
  self.certificate_utils = CertificateUtils()
130
163
  self.auth_validator = AuthValidator()
131
-
164
+
132
165
  async def execute(self, **kwargs) -> CommandResult:
133
166
  """
134
167
  Execute certificate monitor command.
135
-
168
+
136
169
  Args:
137
170
  **kwargs: Command parameters including:
138
171
  - action: Action to perform (cert_expiry_check, cert_health_check, cert_alert_setup, cert_auto_renew)
@@ -141,12 +174,12 @@ class CertMonitorCommand(Command):
141
174
  - critical_days: Days before expiry for critical status
142
175
  - alert_config: Alert configuration for setup
143
176
  - auto_renew_config: Auto-renewal configuration
144
-
177
+
145
178
  Returns:
146
179
  CommandResult with monitoring operation status
147
180
  """
148
181
  action = kwargs.get("action", "cert_expiry_check")
149
-
182
+
150
183
  if action == "cert_expiry_check":
151
184
  cert_path = kwargs.get("cert_path")
152
185
  warning_days = kwargs.get("warning_days", 30)
@@ -167,50 +200,50 @@ class CertMonitorCommand(Command):
167
200
  return ErrorResult(
168
201
  message=f"Unknown action: {action}. Supported actions: cert_expiry_check, cert_health_check, cert_alert_setup, cert_auto_renew"
169
202
  )
170
-
171
- async def cert_expiry_check(self, cert_path: str, warning_days: int = 30, critical_days: int = 7) -> CommandResult:
203
+
204
+ async def cert_expiry_check(
205
+ self, cert_path: str, warning_days: int = 30, critical_days: int = 7
206
+ ) -> CommandResult:
172
207
  """
173
208
  Check certificate expiry date.
174
-
209
+
175
210
  Args:
176
211
  cert_path: Path to certificate file
177
212
  warning_days: Days before expiry to start warning
178
213
  critical_days: Days before expiry for critical status
179
-
214
+
180
215
  Returns:
181
216
  CommandResult with expiry check results
182
217
  """
183
218
  try:
184
219
  logger.info(f"Performing certificate expiry check for {cert_path}")
185
-
220
+
186
221
  # Check if certificate file exists
187
222
  if not os.path.exists(cert_path):
188
- return ErrorResult(
189
- message=f"Certificate file not found: {cert_path}"
190
- )
191
-
223
+ return ErrorResult(message=f"Certificate file not found: {cert_path}")
224
+
192
225
  # Get certificate info
193
226
  cert_info = self.certificate_utils.get_certificate_info(cert_path)
194
227
  if not cert_info:
195
- return ErrorResult(
196
- message="Could not read certificate information"
197
- )
198
-
228
+ return ErrorResult(message="Could not read certificate information")
229
+
199
230
  expiry_date = cert_info.get("expiry_date")
200
231
  if not expiry_date:
201
232
  return ErrorResult(
202
233
  message="Could not determine certificate expiry date"
203
234
  )
204
-
235
+
205
236
  try:
206
237
  # Calculate days until expiry
207
- expiry_datetime = datetime.fromisoformat(expiry_date.replace('Z', '+00:00'))
208
- days_until_expiry = (expiry_datetime - datetime.now(expiry_datetime.tzinfo)).days
209
- except ValueError:
210
- return ErrorResult(
211
- message="Invalid expiry date format"
238
+ expiry_datetime = datetime.fromisoformat(
239
+ expiry_date.replace("Z", "+00:00")
212
240
  )
213
-
241
+ days_until_expiry = (
242
+ expiry_datetime - datetime.now(expiry_datetime.tzinfo)
243
+ ).days
244
+ except ValueError:
245
+ return ErrorResult(message="Invalid expiry date format")
246
+
214
247
  # Determine status
215
248
  is_expired = days_until_expiry < 0
216
249
  if is_expired:
@@ -221,9 +254,11 @@ class CertMonitorCommand(Command):
221
254
  health_status = "warning"
222
255
  else:
223
256
  health_status = "healthy"
224
-
225
- logger.info(f"Certificate expiry check completed: {health_status} ({days_until_expiry} days)")
226
-
257
+
258
+ logger.info(
259
+ f"Certificate expiry check completed: {health_status} ({days_until_expiry} days)"
260
+ )
261
+
227
262
  return SuccessResult(
228
263
  data={
229
264
  "monitor_result": {
@@ -232,81 +267,87 @@ class CertMonitorCommand(Command):
232
267
  "days_until_expiry": days_until_expiry,
233
268
  "expiry_date": expiry_date,
234
269
  "warning_days": warning_days,
235
- "critical_days": critical_days
270
+ "critical_days": critical_days,
236
271
  }
237
272
  }
238
273
  )
239
-
274
+
240
275
  except Exception as e:
241
276
  logger.error(f"Certificate expiry check failed: {e}")
242
- return ErrorResult(
243
- message=f"Certificate expiry check failed: {str(e)}"
244
- )
245
-
277
+ return ErrorResult(message=f"Certificate expiry check failed: {str(e)}")
278
+
246
279
  async def cert_health_check(self, cert_path: str) -> CommandResult:
247
280
  """
248
281
  Perform comprehensive health check on certificate.
249
-
282
+
250
283
  Args:
251
284
  cert_path: Path to certificate file
252
-
285
+
253
286
  Returns:
254
287
  CommandResult with health check results
255
288
  """
256
289
  try:
257
290
  logger.info(f"Performing certificate health check for {cert_path}")
258
-
291
+
259
292
  # Check if certificate file exists
260
293
  if not os.path.exists(cert_path):
261
- return ErrorResult(
262
- message=f"Certificate file not found: {cert_path}"
263
- )
264
-
294
+ return ErrorResult(message=f"Certificate file not found: {cert_path}")
295
+
265
296
  # Get certificate info
266
297
  cert_info = self.certificate_utils.get_certificate_info(cert_path)
267
298
  if not cert_info:
268
- return ErrorResult(
269
- message="Could not read certificate information"
270
- )
271
-
299
+ return ErrorResult(message="Could not read certificate information")
300
+
272
301
  # Validate certificate
273
302
  validation = self.auth_validator.validate_certificate(cert_path)
274
-
303
+
275
304
  # Calculate health score
276
305
  health_score = 100
277
306
  alerts = []
278
-
307
+
279
308
  # Check if certificate is valid
280
309
  if not validation.is_valid:
281
310
  health_score -= 50
282
- alerts.append(f"Certificate validation failed: {validation.error_message}")
283
-
311
+ alerts.append(
312
+ f"Certificate validation failed: {validation.error_message}"
313
+ )
314
+
284
315
  # Check expiry
285
316
  expiry_date = cert_info.get("expiry_date")
286
317
  if expiry_date:
287
318
  try:
288
- expiry_datetime = datetime.fromisoformat(expiry_date.replace('Z', '+00:00'))
289
- days_until_expiry = (expiry_datetime - datetime.now(expiry_datetime.tzinfo)).days
290
-
319
+ expiry_datetime = datetime.fromisoformat(
320
+ expiry_date.replace("Z", "+00:00")
321
+ )
322
+ days_until_expiry = (
323
+ expiry_datetime - datetime.now(expiry_datetime.tzinfo)
324
+ ).days
325
+
291
326
  if days_until_expiry < 0:
292
327
  health_score -= 30
293
328
  alerts.append("Certificate has expired")
294
329
  elif days_until_expiry <= 7:
295
330
  health_score -= 20
296
- alerts.append(f"Certificate expires in {days_until_expiry} days")
331
+ alerts.append(
332
+ f"Certificate expires in {days_until_expiry} days"
333
+ )
297
334
  elif days_until_expiry <= 30:
298
335
  health_score -= 10
299
- alerts.append(f"Certificate expires in {days_until_expiry} days")
336
+ alerts.append(
337
+ f"Certificate expires in {days_until_expiry} days"
338
+ )
300
339
  except ValueError:
301
340
  health_score -= 10
302
341
  alerts.append("Invalid expiry date format")
303
-
342
+
304
343
  # Check key strength
305
344
  key_size = cert_info.get("key_size", 0)
306
345
  if key_size < 2048:
307
346
  health_score -= 15
308
- alerts.append(f"Key size {key_size} bits is below recommended 2048 bits")
309
-
347
+ alerts.append(
348
+ f"Key size {key_size} bits is below recommended 2048 bits"
349
+ )
350
+
310
351
  # Determine overall status
311
352
  if health_score >= 80:
312
353
  overall_status = "healthy"
@@ -314,68 +355,60 @@ class CertMonitorCommand(Command):
314
355
  overall_status = "warning"
315
356
  else:
316
357
  overall_status = "critical"
317
-
318
- logger.info(f"Certificate health check completed: {overall_status} (score: {health_score})")
319
-
358
+
359
+ logger.info(
360
+ f"Certificate health check completed: {overall_status} (score: {health_score})"
361
+ )
362
+
320
363
  return SuccessResult(
321
364
  data={
322
365
  "monitor_result": {
323
366
  "health_score": health_score,
324
367
  "alerts": alerts,
325
- "expiry_date": expiry_date
326
- },
327
- "health_checks": {
328
- "validation": {
329
- "passed": validation.is_valid
330
- }
368
+ "expiry_date": expiry_date,
331
369
  },
332
- "overall_status": overall_status
370
+ "health_checks": {"validation": {"passed": validation.is_valid}},
371
+ "overall_status": overall_status,
333
372
  }
334
373
  )
335
-
374
+
336
375
  except Exception as e:
337
376
  logger.error(f"Certificate health check failed: {e}")
338
- return ErrorResult(
339
- message=f"Certificate health check failed: {str(e)}"
340
- )
341
-
342
- async def cert_alert_setup(self, cert_path: str, alert_config: Dict[str, Any]) -> CommandResult:
377
+ return ErrorResult(message=f"Certificate health check failed: {str(e)}")
378
+
379
+ async def cert_alert_setup(
380
+ self, cert_path: str, alert_config: Dict[str, Any]
381
+ ) -> CommandResult:
343
382
  """
344
383
  Set up certificate monitoring alerts.
345
-
384
+
346
385
  Args:
347
386
  cert_path: Path to certificate file
348
387
  alert_config: Alert configuration dictionary
349
-
388
+
350
389
  Returns:
351
390
  CommandResult with alert setup status
352
391
  """
353
392
  try:
354
393
  logger.info(f"Setting up certificate monitoring alerts for {cert_path}")
355
-
394
+
356
395
  # Check if certificate file exists
357
396
  if not os.path.exists(cert_path):
358
- return ErrorResult(
359
- message=f"Certificate file not found: {cert_path}"
360
- )
361
-
397
+ return ErrorResult(message=f"Certificate file not found: {cert_path}")
398
+
362
399
  # Validate alert configuration
363
400
  if not isinstance(alert_config, dict):
364
- return ErrorResult(
365
- message="Alert configuration must be a dictionary"
366
- )
367
-
401
+ return ErrorResult(message="Alert configuration must be a dictionary")
402
+
368
403
  # Check if alerts are disabled
369
404
  if not alert_config.get("enabled", True):
370
405
  return SuccessResult(
371
406
  data={
372
- "monitor_result": {
373
- "alerts_enabled": False
374
- },
375
- "message": "Alerts disabled"
407
+ "monitor_result": {"alerts_enabled": False},
408
+ "message": "Alerts disabled",
376
409
  }
377
410
  )
378
-
411
+
379
412
  # Validate required fields
380
413
  required_fields = ["warning_days", "critical_days"]
381
414
  for field in required_fields:
@@ -383,93 +416,85 @@ class CertMonitorCommand(Command):
383
416
  return ErrorResult(
384
417
  message=f"Missing required field in alert config: {field}"
385
418
  )
386
-
419
+
387
420
  if alert_config["warning_days"] <= 0 or alert_config["critical_days"] <= 0:
388
- return ErrorResult(
389
- message="Warning and critical days must be positive"
390
- )
391
-
421
+ return ErrorResult(message="Warning and critical days must be positive")
422
+
392
423
  if alert_config["warning_days"] <= alert_config["critical_days"]:
393
424
  return ErrorResult(
394
425
  message="Warning days must be greater than critical days"
395
426
  )
396
-
427
+
397
428
  # Check notification channels
398
429
  notification_channels = alert_config.get("notification_channels", [])
399
430
  if not notification_channels:
400
431
  return ErrorResult(
401
432
  message="At least one notification channel must be specified"
402
433
  )
403
-
434
+
404
435
  # Test alert configuration
405
436
  test_result = await self._test_alert_config(alert_config)
406
437
  if not test_result["success"]:
407
438
  return ErrorResult(
408
439
  message=f"Alert configuration test failed: {test_result['error']}"
409
440
  )
410
-
441
+
411
442
  # Save alert configuration
412
443
  config_path = "/tmp/cert_alert_config.json"
413
- with open(config_path, 'w') as f:
444
+ with open(config_path, "w") as f:
414
445
  json.dump(alert_config, f, indent=2)
415
-
446
+
416
447
  logger.info(f"Alert configuration saved to {config_path}")
417
-
448
+
418
449
  return SuccessResult(
419
450
  data={
420
- "monitor_result": {
421
- "alerts_enabled": True
422
- },
451
+ "monitor_result": {"alerts_enabled": True},
423
452
  "alert_config": alert_config,
424
453
  "config_path": config_path,
425
454
  "setup_date": datetime.now().isoformat(),
426
- "message": "Alerts configured successfully"
455
+ "message": "Alerts configured successfully",
427
456
  }
428
457
  )
429
-
458
+
430
459
  except Exception as e:
431
460
  logger.error(f"Alert setup failed: {e}")
432
- return ErrorResult(
433
- message=f"Alert setup failed: {str(e)}"
434
- )
435
-
436
- async def cert_auto_renew(self, cert_path: str, auto_renew_config: Dict[str, Any]) -> CommandResult:
461
+ return ErrorResult(message=f"Alert setup failed: {str(e)}")
462
+
463
+ async def cert_auto_renew(
464
+ self, cert_path: str, auto_renew_config: Dict[str, Any]
465
+ ) -> CommandResult:
437
466
  """
438
467
  Set up certificate auto-renewal.
439
-
468
+
440
469
  Args:
441
470
  cert_path: Path to certificate file
442
471
  auto_renew_config: Auto-renewal configuration dictionary
443
-
472
+
444
473
  Returns:
445
474
  CommandResult with auto-renewal setup status
446
475
  """
447
476
  try:
448
477
  logger.info(f"Setting up certificate auto-renewal for {cert_path}")
449
-
478
+
450
479
  # Check if certificate file exists
451
480
  if not os.path.exists(cert_path):
452
- return ErrorResult(
453
- message=f"Certificate file not found: {cert_path}"
454
- )
455
-
481
+ return ErrorResult(message=f"Certificate file not found: {cert_path}")
482
+
456
483
  # Validate auto-renewal configuration
457
484
  if not isinstance(auto_renew_config, dict):
458
485
  return ErrorResult(
459
486
  message="Auto-renewal configuration must be a dictionary"
460
487
  )
461
-
488
+
462
489
  # Check if auto-renewal is disabled
463
490
  if not auto_renew_config.get("enabled", True):
464
491
  return SuccessResult(
465
492
  data={
466
- "monitor_result": {
467
- "auto_renewal_enabled": False
468
- },
469
- "message": "Auto-renewal disabled"
493
+ "monitor_result": {"auto_renewal_enabled": False},
494
+ "message": "Auto-renewal disabled",
470
495
  }
471
496
  )
472
-
497
+
473
498
  # Validate required fields
474
499
  required_fields = ["renew_before_days", "ca_cert_path", "ca_key_path"]
475
500
  for field in required_fields:
@@ -477,91 +502,79 @@ class CertMonitorCommand(Command):
477
502
  return ErrorResult(
478
503
  message=f"Missing required field in auto-renewal config: {field}"
479
504
  )
480
-
505
+
481
506
  if auto_renew_config["renew_before_days"] <= 0:
482
- return ErrorResult(
483
- message="Renew before days must be positive"
484
- )
485
-
507
+ return ErrorResult(message="Renew before days must be positive")
508
+
486
509
  # Check CA files
487
510
  ca_cert_path = auto_renew_config["ca_cert_path"]
488
511
  ca_key_path = auto_renew_config["ca_key_path"]
489
-
512
+
490
513
  if not os.path.exists(ca_cert_path):
491
- return ErrorResult(
492
- message=f"CA certificate not found: {ca_cert_path}"
493
- )
494
-
514
+ return ErrorResult(message=f"CA certificate not found: {ca_cert_path}")
515
+
495
516
  if not os.path.exists(ca_key_path):
496
- return ErrorResult(
497
- message=f"CA private key not found: {ca_key_path}"
498
- )
499
-
517
+ return ErrorResult(message=f"CA private key not found: {ca_key_path}")
518
+
500
519
  # Check output directory
501
520
  output_dir = auto_renew_config.get("output_dir")
502
521
  if not output_dir:
503
- return ErrorResult(
504
- message="Output directory must be specified"
505
- )
506
-
522
+ return ErrorResult(message="Output directory must be specified")
523
+
507
524
  # Test renewal configuration
508
525
  test_result = await self._test_renewal_config(cert_path, auto_renew_config)
509
526
  if not test_result["success"]:
510
527
  return ErrorResult(
511
528
  message=f"Renewal configuration test failed: {test_result['error']}"
512
529
  )
513
-
530
+
514
531
  # Save auto-renewal configuration
515
532
  config_path = "/tmp/cert_auto_renew_config.json"
516
- with open(config_path, 'w') as f:
533
+ with open(config_path, "w") as f:
517
534
  json.dump(auto_renew_config, f, indent=2)
518
-
535
+
519
536
  logger.info(f"Auto-renewal configuration saved to {config_path}")
520
-
537
+
521
538
  return SuccessResult(
522
539
  data={
523
- "monitor_result": {
524
- "auto_renewal_enabled": True
525
- },
540
+ "monitor_result": {"auto_renewal_enabled": True},
526
541
  "auto_renew_config": auto_renew_config,
527
542
  "config_path": config_path,
528
543
  "setup_date": datetime.now().isoformat(),
529
- "message": "Auto-renewal configured successfully"
544
+ "message": "Auto-renewal configured successfully",
530
545
  }
531
546
  )
532
-
547
+
533
548
  except Exception as e:
534
549
  logger.error(f"Auto-renewal setup failed: {e}")
535
- return ErrorResult(
536
- message=f"Auto-renewal setup failed: {str(e)}"
537
- )
538
-
550
+ return ErrorResult(message=f"Auto-renewal setup failed: {str(e)}")
551
+
539
552
  def _find_certificates(self, directory: str) -> List[str]:
540
553
  """
541
554
  Find all certificate files in a directory.
542
-
555
+
543
556
  Args:
544
557
  directory: Directory to scan
545
-
558
+
546
559
  Returns:
547
560
  List of certificate file paths
548
561
  """
549
562
  certificates = []
550
- cert_extensions = ['.crt', '.pem', '.cer', '.der']
551
-
552
- for file_path in Path(directory).rglob('*'):
563
+ cert_extensions = [".crt", ".pem", ".cer", ".der"]
564
+
565
+ for file_path in Path(directory).rglob("*"):
553
566
  if file_path.is_file() and file_path.suffix.lower() in cert_extensions:
554
567
  certificates.append(str(file_path))
555
-
568
+
556
569
  return certificates
557
-
570
+
558
571
  async def _test_alert_config(self, alert_config: Dict[str, Any]) -> Dict[str, Any]:
559
572
  """
560
573
  Test alert configuration.
561
-
574
+
562
575
  Args:
563
576
  alert_config: Alert configuration to test
564
-
577
+
565
578
  Returns:
566
579
  Test result dictionary
567
580
  """
@@ -571,26 +584,28 @@ class CertMonitorCommand(Command):
571
584
  recipients = alert_config["email_recipients"]
572
585
  if not isinstance(recipients, list) or not recipients:
573
586
  return {"success": False, "error": "Invalid email recipients"}
574
-
587
+
575
588
  # Test webhook configuration if present
576
589
  if "webhook_url" in alert_config:
577
590
  webhook_url = alert_config["webhook_url"]
578
591
  if not isinstance(webhook_url, str) or not webhook_url:
579
592
  return {"success": False, "error": "Invalid webhook URL"}
580
-
593
+
581
594
  return {"success": True}
582
-
595
+
583
596
  except Exception as e:
584
597
  return {"success": False, "error": str(e)}
585
-
586
- async def _test_renewal_config(self, cert_path: str, renewal_config: Dict[str, Any]) -> Dict[str, Any]:
598
+
599
+ async def _test_renewal_config(
600
+ self, cert_path: str, renewal_config: Dict[str, Any]
601
+ ) -> Dict[str, Any]:
587
602
  """
588
603
  Test renewal configuration.
589
-
604
+
590
605
  Args:
591
606
  cert_path: Path to certificate file
592
607
  renewal_config: Renewal configuration to test
593
-
608
+
594
609
  Returns:
595
610
  Test result dictionary
596
611
  """
@@ -598,24 +613,27 @@ class CertMonitorCommand(Command):
598
613
  # Get certificate info
599
614
  cert_info = self.certificate_utils.get_certificate_info(cert_path)
600
615
  if not cert_info:
601
- return {"success": False, "error": "Could not read certificate information"}
602
-
616
+ return {
617
+ "success": False,
618
+ "error": "Could not read certificate information",
619
+ }
620
+
603
621
  # Check CA certificate
604
622
  ca_cert_path = renewal_config.get("ca_cert_path")
605
623
  if not ca_cert_path or not os.path.exists(ca_cert_path):
606
624
  return {"success": False, "error": "CA certificate not found"}
607
-
625
+
608
626
  # Check CA key
609
627
  ca_key_path = renewal_config.get("ca_key_path")
610
628
  if not ca_key_path or not os.path.exists(ca_key_path):
611
629
  return {"success": False, "error": "CA private key not found"}
612
-
630
+
613
631
  # Check output directory
614
632
  output_dir = renewal_config.get("output_dir")
615
633
  if not output_dir or not os.path.exists(output_dir):
616
634
  return {"success": False, "error": "Output directory not found"}
617
-
635
+
618
636
  return {"success": True}
619
-
637
+
620
638
  except Exception as e:
621
- return {"success": False, "error": str(e)}
639
+ return {"success": False, "error": str(e)}