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.
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 +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.5.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
  125. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  126. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
  127. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
  128. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -25,17 +25,24 @@ logger = logging.getLogger(__name__)
25
25
  class CertificateResult:
26
26
  """
27
27
  Result class for certificate operations.
28
-
28
+
29
29
  Contains certificate information and operation status.
30
30
  """
31
-
32
- def __init__(self, cert_path: str, cert_type: str, common_name: str,
33
- roles: Optional[List[str]] = None, expiry_date: Optional[str] = None,
34
- serial_number: Optional[str] = None, status: str = "valid",
35
- error: Optional[str] = None):
31
+
32
+ def __init__(
33
+ self,
34
+ cert_path: str,
35
+ cert_type: str,
36
+ common_name: str,
37
+ roles: Optional[List[str]] = None,
38
+ expiry_date: Optional[str] = None,
39
+ serial_number: Optional[str] = None,
40
+ status: str = "valid",
41
+ error: Optional[str] = None,
42
+ ):
36
43
  """
37
44
  Initialize certificate result.
38
-
45
+
39
46
  Args:
40
47
  cert_path: Path to certificate file
41
48
  cert_type: Type of certificate (CA, server, client)
@@ -54,11 +61,11 @@ class CertificateResult:
54
61
  self.serial_number = serial_number
55
62
  self.status = status
56
63
  self.error = error
57
-
64
+
58
65
  def to_dict(self) -> Dict[str, Any]:
59
66
  """
60
67
  Convert to dictionary format.
61
-
68
+
62
69
  Returns:
63
70
  Dictionary representation
64
71
  """
@@ -70,9 +77,9 @@ class CertificateResult:
70
77
  "expiry_date": self.expiry_date,
71
78
  "serial_number": self.serial_number,
72
79
  "status": self.status,
73
- "error": self.error
80
+ "error": self.error,
74
81
  }
75
-
82
+
76
83
  @classmethod
77
84
  def get_schema(cls) -> Dict[str, Any]:
78
85
  """
@@ -84,29 +91,50 @@ class CertificateResult:
84
91
  return {
85
92
  "type": "object",
86
93
  "properties": {
87
- "cert_path": {"type": "string", "description": "Path to certificate file"},
88
- "cert_type": {"type": "string", "enum": ["CA", "server", "client"],
89
- "description": "Type of certificate"},
90
- "common_name": {"type": "string", "description": "Common name of certificate"},
91
- "roles": {"type": "array", "items": {"type": "string"},
92
- "description": "List of roles assigned to certificate"},
93
- "expiry_date": {"type": "string", "description": "Certificate expiry date"},
94
- "serial_number": {"type": "string", "description": "Certificate serial number"},
95
- "status": {"type": "string", "enum": ["valid", "expired", "revoked", "error"],
96
- "description": "Certificate status"},
97
- "error": {"type": "string", "description": "Error message if any"}
94
+ "cert_path": {
95
+ "type": "string",
96
+ "description": "Path to certificate file",
97
+ },
98
+ "cert_type": {
99
+ "type": "string",
100
+ "enum": ["CA", "server", "client"],
101
+ "description": "Type of certificate",
102
+ },
103
+ "common_name": {
104
+ "type": "string",
105
+ "description": "Common name of certificate",
106
+ },
107
+ "roles": {
108
+ "type": "array",
109
+ "items": {"type": "string"},
110
+ "description": "List of roles assigned to certificate",
111
+ },
112
+ "expiry_date": {
113
+ "type": "string",
114
+ "description": "Certificate expiry date",
115
+ },
116
+ "serial_number": {
117
+ "type": "string",
118
+ "description": "Certificate serial number",
119
+ },
120
+ "status": {
121
+ "type": "string",
122
+ "enum": ["valid", "expired", "revoked", "error"],
123
+ "description": "Certificate status",
124
+ },
125
+ "error": {"type": "string", "description": "Error message if any"},
98
126
  },
99
- "required": ["cert_path", "cert_type", "common_name", "status"]
127
+ "required": ["cert_path", "cert_type", "common_name", "status"],
100
128
  }
101
129
 
102
130
 
103
131
  class CertificateManagementCommand(Command):
104
132
  """
105
133
  Command for certificate management.
106
-
134
+
107
135
  Provides methods for creating, managing, and validating certificates.
108
136
  """
109
-
137
+
110
138
  # Command metadata
111
139
  name = "certificate_management"
112
140
  version = "1.0.0"
@@ -116,18 +144,18 @@ class CertificateManagementCommand(Command):
116
144
  email = "team@mcp-proxy-adapter.com"
117
145
  source_url = "https://github.com/mcp-proxy-adapter"
118
146
  result_class = CertificateResult
119
-
147
+
120
148
  def __init__(self):
121
149
  """Initialize certificate management command."""
122
150
  super().__init__()
123
151
  self.certificate_utils = CertificateUtils()
124
152
  self.auth_validator = AuthValidator()
125
153
  self.role_utils = RoleUtils()
126
-
154
+
127
155
  async def execute(self, **kwargs) -> CommandResult:
128
156
  """
129
157
  Execute certificate management command.
130
-
158
+
131
159
  Args:
132
160
  **kwargs: Command parameters including:
133
161
  - action: Action to perform (cert_create_ca, cert_create_server, cert_create_client, cert_revoke, cert_list, cert_info)
@@ -140,18 +168,20 @@ class CertificateManagementCommand(Command):
140
168
  - key_size: Key size in bits for CA certificate creation
141
169
  - cert_path: Certificate path for revocation and info
142
170
  - cert_dir: Directory for certificate listing
143
-
171
+
144
172
  Returns:
145
173
  CommandResult with certificate operation status
146
174
  """
147
175
  action = kwargs.get("action", "cert_list")
148
-
176
+
149
177
  if action == "cert_create_ca":
150
178
  common_name = kwargs.get("common_name")
151
179
  output_dir = kwargs.get("output_dir")
152
180
  validity_days = kwargs.get("validity_days", 365)
153
181
  key_size = kwargs.get("key_size", 2048)
154
- return await self.cert_create_ca(common_name, output_dir, validity_days, key_size)
182
+ return await self.cert_create_ca(
183
+ common_name, output_dir, validity_days, key_size
184
+ )
155
185
  elif action == "cert_create_server":
156
186
  common_name = kwargs.get("common_name")
157
187
  roles = kwargs.get("roles", [])
@@ -159,7 +189,9 @@ class CertificateManagementCommand(Command):
159
189
  ca_key_path = kwargs.get("ca_key_path")
160
190
  output_dir = kwargs.get("output_dir")
161
191
  validity_days = kwargs.get("validity_days", 365)
162
- return await self.cert_create_server(common_name, roles, ca_cert_path, ca_key_path, output_dir, validity_days)
192
+ return await self.cert_create_server(
193
+ common_name, roles, ca_cert_path, ca_key_path, output_dir, validity_days
194
+ )
163
195
  elif action == "cert_create_client":
164
196
  common_name = kwargs.get("common_name")
165
197
  roles = kwargs.get("roles", [])
@@ -167,7 +199,9 @@ class CertificateManagementCommand(Command):
167
199
  ca_key_path = kwargs.get("ca_key_path")
168
200
  output_dir = kwargs.get("output_dir")
169
201
  validity_days = kwargs.get("validity_days", 365)
170
- return await self.cert_create_client(common_name, roles, ca_cert_path, ca_key_path, output_dir, validity_days)
202
+ return await self.cert_create_client(
203
+ common_name, roles, ca_cert_path, ca_key_path, output_dir, validity_days
204
+ )
171
205
  elif action == "cert_revoke":
172
206
  cert_path = kwargs.get("cert_path")
173
207
  return await self.cert_revoke(cert_path)
@@ -181,51 +215,50 @@ class CertificateManagementCommand(Command):
181
215
  return ErrorResult(
182
216
  message=f"Unknown action: {action}. Supported actions: cert_create_ca, cert_create_server, cert_create_client, cert_revoke, cert_list, cert_info"
183
217
  )
184
-
185
- async def cert_create_ca(self, common_name: str, output_dir: str,
186
- validity_days: int = 365, key_size: int = 2048) -> CommandResult:
218
+
219
+ async def cert_create_ca(
220
+ self,
221
+ common_name: str,
222
+ output_dir: str,
223
+ validity_days: int = 365,
224
+ key_size: int = 2048,
225
+ ) -> CommandResult:
187
226
  """
188
227
  Create a CA certificate and private key.
189
-
228
+
190
229
  Args:
191
230
  common_name: Common name for the CA certificate
192
231
  output_dir: Directory to save certificate and key files
193
232
  validity_days: Certificate validity period in days
194
233
  key_size: RSA key size in bits
195
-
234
+
196
235
  Returns:
197
236
  CommandResult with CA certificate creation status
198
237
  """
199
238
  try:
200
239
  logger.info(f"Creating CA certificate: {common_name}")
201
-
240
+
202
241
  # Validate parameters
203
242
  if not common_name or not common_name.strip():
204
- return ErrorResult(
205
- message="Common name cannot be empty"
206
- )
207
-
243
+ return ErrorResult(message="Common name cannot be empty")
244
+
208
245
  if validity_days <= 0:
209
- return ErrorResult(
210
- message="Validity days must be positive"
211
- )
212
-
246
+ return ErrorResult(message="Validity days must be positive")
247
+
213
248
  if key_size < 1024:
214
- return ErrorResult(
215
- message="Key size must be at least 1024 bits"
216
- )
217
-
249
+ return ErrorResult(message="Key size must be at least 1024 bits")
250
+
218
251
  # Create CA certificate
219
252
  result = self.certificate_utils.create_ca_certificate(
220
253
  common_name, output_dir, validity_days, key_size
221
254
  )
222
-
255
+
223
256
  # Validate created certificate (CA certificates don't need server validation)
224
257
  cert_path = result.get("cert_path")
225
258
  if cert_path and os.path.exists(cert_path):
226
259
  # For CA certificates, we only check if the file exists and is readable
227
260
  try:
228
- with open(cert_path, 'rb') as f:
261
+ with open(cert_path, "rb") as f:
229
262
  cert_data = f.read()
230
263
  if not cert_data:
231
264
  return ErrorResult(
@@ -235,34 +268,37 @@ class CertificateManagementCommand(Command):
235
268
  return ErrorResult(
236
269
  message=f"Created CA certificate file is not readable: {str(e)}"
237
270
  )
238
-
271
+
239
272
  cert_result = CertificateResult(
240
273
  cert_path=result.get("cert_path", ""),
241
274
  cert_type="CA",
242
275
  common_name=common_name,
243
- status="valid"
276
+ status="valid",
277
+ )
278
+
279
+ logger.info(
280
+ f"CA certificate created successfully: {result.get('cert_path')}"
244
281
  )
245
-
246
- logger.info(f"CA certificate created successfully: {result.get('cert_path')}")
247
282
  return SuccessResult(
248
- data={
249
- "certificate": cert_result.to_dict(),
250
- "files": result
251
- }
283
+ data={"certificate": cert_result.to_dict(), "files": result}
252
284
  )
253
-
285
+
254
286
  except Exception as e:
255
287
  logger.error(f"CA certificate creation failed: {e}")
256
- return ErrorResult(
257
- message=f"CA certificate creation failed: {str(e)}"
258
- )
259
-
260
- async def cert_create_server(self, common_name: str, roles: List[str],
261
- ca_cert_path: str, ca_key_path: str,
262
- output_dir: str, validity_days: int = 365) -> CommandResult:
288
+ return ErrorResult(message=f"CA certificate creation failed: {str(e)}")
289
+
290
+ async def cert_create_server(
291
+ self,
292
+ common_name: str,
293
+ roles: List[str],
294
+ ca_cert_path: str,
295
+ ca_key_path: str,
296
+ output_dir: str,
297
+ validity_days: int = 365,
298
+ ) -> CommandResult:
263
299
  """
264
300
  Create a server certificate signed by CA.
265
-
301
+
266
302
  Args:
267
303
  common_name: Common name for the server certificate
268
304
  roles: List of roles to assign to the certificate
@@ -270,46 +306,36 @@ class CertificateManagementCommand(Command):
270
306
  ca_key_path: Path to CA private key file
271
307
  output_dir: Directory to save certificate and key files
272
308
  validity_days: Certificate validity period in days
273
-
309
+
274
310
  Returns:
275
311
  CommandResult with server certificate creation status
276
312
  """
277
313
  try:
278
314
  logger.info(f"Creating server certificate: {common_name}")
279
-
315
+
280
316
  # Validate parameters
281
317
  if not common_name or not common_name.strip():
282
- return ErrorResult(
283
- message="Common name cannot be empty"
284
- )
285
-
318
+ return ErrorResult(message="Common name cannot be empty")
319
+
286
320
  if not roles:
287
- return ErrorResult(
288
- message="At least one role must be specified"
289
- )
290
-
321
+ return ErrorResult(message="At least one role must be specified")
322
+
291
323
  # Validate roles
292
324
  if not self.role_utils.validate_roles(roles):
293
- return ErrorResult(
294
- message="Invalid roles specified"
295
- )
296
-
325
+ return ErrorResult(message="Invalid roles specified")
326
+
297
327
  # Check CA files
298
328
  if not os.path.exists(ca_cert_path):
299
- return ErrorResult(
300
- message=f"CA certificate not found: {ca_cert_path}"
301
- )
302
-
329
+ return ErrorResult(message=f"CA certificate not found: {ca_cert_path}")
330
+
303
331
  if not os.path.exists(ca_key_path):
304
- return ErrorResult(
305
- message=f"CA private key not found: {ca_key_path}"
306
- )
307
-
332
+ return ErrorResult(message=f"CA private key not found: {ca_key_path}")
333
+
308
334
  # Create server certificate
309
335
  result = self.certificate_utils.create_server_certificate(
310
336
  common_name, roles, ca_cert_path, ca_key_path, output_dir, validity_days
311
337
  )
312
-
338
+
313
339
  # Validate created certificate
314
340
  cert_path = result.get("cert_path")
315
341
  if cert_path and os.path.exists(cert_path):
@@ -318,35 +344,38 @@ class CertificateManagementCommand(Command):
318
344
  return ErrorResult(
319
345
  message=f"Created server certificate validation failed: {validation.error_message}"
320
346
  )
321
-
347
+
322
348
  cert_result = CertificateResult(
323
349
  cert_path=result.get("cert_path", ""),
324
350
  cert_type="server",
325
351
  common_name=common_name,
326
352
  roles=roles,
327
- status="valid"
353
+ status="valid",
354
+ )
355
+
356
+ logger.info(
357
+ f"Server certificate created successfully: {result.get('cert_path')}"
328
358
  )
329
-
330
- logger.info(f"Server certificate created successfully: {result.get('cert_path')}")
331
359
  return SuccessResult(
332
- data={
333
- "certificate": cert_result.to_dict(),
334
- "files": result
335
- }
360
+ data={"certificate": cert_result.to_dict(), "files": result}
336
361
  )
337
-
362
+
338
363
  except Exception as e:
339
364
  logger.error(f"Server certificate creation failed: {e}")
340
- return ErrorResult(
341
- message=f"Server certificate creation failed: {str(e)}"
342
- )
343
-
344
- async def cert_create_client(self, common_name: str, roles: List[str],
345
- ca_cert_path: str, ca_key_path: str,
346
- output_dir: str, validity_days: int = 365) -> CommandResult:
365
+ return ErrorResult(message=f"Server certificate creation failed: {str(e)}")
366
+
367
+ async def cert_create_client(
368
+ self,
369
+ common_name: str,
370
+ roles: List[str],
371
+ ca_cert_path: str,
372
+ ca_key_path: str,
373
+ output_dir: str,
374
+ validity_days: int = 365,
375
+ ) -> CommandResult:
347
376
  """
348
377
  Create a client certificate signed by CA.
349
-
378
+
350
379
  Args:
351
380
  common_name: Common name for the client certificate
352
381
  roles: List of roles to assign to the certificate
@@ -354,46 +383,36 @@ class CertificateManagementCommand(Command):
354
383
  ca_key_path: Path to CA private key file
355
384
  output_dir: Directory to save certificate and key files
356
385
  validity_days: Certificate validity period in days
357
-
386
+
358
387
  Returns:
359
388
  CommandResult with client certificate creation status
360
389
  """
361
390
  try:
362
391
  logger.info(f"Creating client certificate: {common_name}")
363
-
392
+
364
393
  # Validate parameters
365
394
  if not common_name or not common_name.strip():
366
- return ErrorResult(
367
- message="Common name cannot be empty"
368
- )
369
-
395
+ return ErrorResult(message="Common name cannot be empty")
396
+
370
397
  if not roles:
371
- return ErrorResult(
372
- message="At least one role must be specified"
373
- )
374
-
398
+ return ErrorResult(message="At least one role must be specified")
399
+
375
400
  # Validate roles
376
401
  if not self.role_utils.validate_roles(roles):
377
- return ErrorResult(
378
- message="Invalid roles specified"
379
- )
380
-
402
+ return ErrorResult(message="Invalid roles specified")
403
+
381
404
  # Check CA files
382
405
  if not os.path.exists(ca_cert_path):
383
- return ErrorResult(
384
- message=f"CA certificate not found: {ca_cert_path}"
385
- )
386
-
406
+ return ErrorResult(message=f"CA certificate not found: {ca_cert_path}")
407
+
387
408
  if not os.path.exists(ca_key_path):
388
- return ErrorResult(
389
- message=f"CA private key not found: {ca_key_path}"
390
- )
391
-
409
+ return ErrorResult(message=f"CA private key not found: {ca_key_path}")
410
+
392
411
  # Create client certificate
393
412
  result = self.certificate_utils.create_client_certificate(
394
413
  common_name, roles, ca_cert_path, ca_key_path, output_dir, validity_days
395
414
  )
396
-
415
+
397
416
  # Validate created certificate
398
417
  cert_path = result.get("cert_path")
399
418
  if cert_path and os.path.exists(cert_path):
@@ -402,113 +421,99 @@ class CertificateManagementCommand(Command):
402
421
  return ErrorResult(
403
422
  message=f"Created client certificate validation failed: {validation.error_message}"
404
423
  )
405
-
424
+
406
425
  cert_result = CertificateResult(
407
426
  cert_path=result.get("cert_path", ""),
408
427
  cert_type="client",
409
428
  common_name=common_name,
410
429
  roles=roles,
411
- status="valid"
430
+ status="valid",
431
+ )
432
+
433
+ logger.info(
434
+ f"Client certificate created successfully: {result.get('cert_path')}"
412
435
  )
413
-
414
- logger.info(f"Client certificate created successfully: {result.get('cert_path')}")
415
436
  return SuccessResult(
416
- data={
417
- "certificate": cert_result.to_dict(),
418
- "files": result
419
- }
437
+ data={"certificate": cert_result.to_dict(), "files": result}
420
438
  )
421
-
439
+
422
440
  except Exception as e:
423
441
  logger.error(f"Client certificate creation failed: {e}")
424
- return ErrorResult(
425
- message=f"Client certificate creation failed: {str(e)}"
426
- )
427
-
442
+ return ErrorResult(message=f"Client certificate creation failed: {str(e)}")
443
+
428
444
  async def cert_revoke(self, cert_path: str) -> CommandResult:
429
445
  """
430
446
  Revoke a certificate.
431
-
447
+
432
448
  Args:
433
449
  cert_path: Path to certificate file to revoke
434
-
450
+
435
451
  Returns:
436
452
  CommandResult with revocation status
437
453
  """
438
454
  try:
439
455
  logger.info(f"Revoking certificate: {cert_path}")
440
-
456
+
441
457
  # Validate parameters
442
458
  if not cert_path or not os.path.exists(cert_path):
443
- return ErrorResult(
444
- message=f"Certificate file not found: {cert_path}"
445
- )
446
-
459
+ return ErrorResult(message=f"Certificate file not found: {cert_path}")
460
+
447
461
  # Get certificate info before revocation
448
462
  cert_info = self.certificate_utils.get_certificate_info(cert_path)
449
463
  if not cert_info:
450
- return ErrorResult(
451
- message="Could not read certificate information"
452
- )
453
-
464
+ return ErrorResult(message="Could not read certificate information")
465
+
454
466
  # Revoke certificate
455
467
  result = self.certificate_utils.revoke_certificate(cert_path)
456
-
468
+
457
469
  cert_result = CertificateResult(
458
470
  cert_path=cert_path,
459
471
  cert_type=cert_info.get("type", "unknown"),
460
472
  common_name=cert_info.get("common_name", ""),
461
473
  roles=cert_info.get("roles", []),
462
474
  serial_number=cert_info.get("serial_number"),
463
- status="revoked"
475
+ status="revoked",
464
476
  )
465
-
477
+
466
478
  logger.info(f"Certificate revoked successfully: {cert_path}")
467
479
  return SuccessResult(
468
- data={
469
- "certificate": cert_result.to_dict(),
470
- "revocation_result": result
471
- }
480
+ data={"certificate": cert_result.to_dict(), "revocation_result": result}
472
481
  )
473
-
482
+
474
483
  except Exception as e:
475
484
  logger.error(f"Certificate revocation failed: {e}")
476
- return ErrorResult(
477
- message=f"Certificate revocation failed: {str(e)}"
478
- )
479
-
485
+ return ErrorResult(message=f"Certificate revocation failed: {str(e)}")
486
+
480
487
  async def cert_list(self, cert_dir: str) -> CommandResult:
481
488
  """
482
489
  List all certificates in a directory.
483
-
490
+
484
491
  Args:
485
492
  cert_dir: Directory to scan for certificates
486
-
493
+
487
494
  Returns:
488
495
  CommandResult with list of certificates
489
496
  """
490
497
  try:
491
498
  logger.info(f"Listing certificates in directory: {cert_dir}")
492
-
499
+
493
500
  # Validate parameters
494
501
  if not cert_dir or not os.path.exists(cert_dir):
495
- return ErrorResult(
496
- message=f"Directory not found: {cert_dir}"
497
- )
498
-
502
+ return ErrorResult(message=f"Directory not found: {cert_dir}")
503
+
499
504
  if not os.path.isdir(cert_dir):
500
- return ErrorResult(
501
- message=f"Path is not a directory: {cert_dir}"
502
- )
503
-
505
+ return ErrorResult(message=f"Path is not a directory: {cert_dir}")
506
+
504
507
  # List certificates
505
508
  certificates = []
506
- cert_extensions = ['.crt', '.pem', '.cer', '.der']
507
-
508
- for file_path in Path(cert_dir).rglob('*'):
509
+ cert_extensions = [".crt", ".pem", ".cer", ".der"]
510
+
511
+ for file_path in Path(cert_dir).rglob("*"):
509
512
  if file_path.is_file() and file_path.suffix.lower() in cert_extensions:
510
513
  try:
511
- cert_info = self.certificate_utils.get_certificate_info(str(file_path))
514
+ cert_info = self.certificate_utils.get_certificate_info(
515
+ str(file_path)
516
+ )
512
517
  if cert_info:
513
518
  cert_result = CertificateResult(
514
519
  cert_path=str(file_path),
@@ -517,7 +522,7 @@ class CertificateManagementCommand(Command):
517
522
  roles=cert_info.get("roles", []),
518
523
  expiry_date=cert_info.get("expiry_date"),
519
524
  serial_number=cert_info.get("serial_number"),
520
- status=cert_info.get("status", "valid")
525
+ status=cert_info.get("status", "valid"),
521
526
  )
522
527
  certificates.append(cert_result.to_dict())
523
528
  except Exception as e:
@@ -528,55 +533,49 @@ class CertificateManagementCommand(Command):
528
533
  cert_type="unknown",
529
534
  common_name="",
530
535
  status="error",
531
- error=str(e)
536
+ error=str(e),
532
537
  )
533
538
  certificates.append(cert_result.to_dict())
534
-
539
+
535
540
  logger.info(f"Found {len(certificates)} certificates in {cert_dir}")
536
541
  return SuccessResult(
537
542
  data={
538
543
  "certificates": certificates,
539
544
  "total_count": len(certificates),
540
- "directory": cert_dir
545
+ "directory": cert_dir,
541
546
  }
542
547
  )
543
-
548
+
544
549
  except Exception as e:
545
550
  logger.error(f"Certificate listing failed: {e}")
546
- return ErrorResult(
547
- message=f"Certificate listing failed: {str(e)}"
548
- )
549
-
551
+ return ErrorResult(message=f"Certificate listing failed: {str(e)}")
552
+
550
553
  async def cert_info(self, cert_path: str) -> CommandResult:
551
554
  """
552
555
  Get detailed information about a certificate.
553
-
556
+
554
557
  Args:
555
558
  cert_path: Path to certificate file
556
-
559
+
557
560
  Returns:
558
561
  CommandResult with certificate information
559
562
  """
560
563
  try:
561
564
  logger.info(f"Getting certificate info: {cert_path}")
562
-
565
+
563
566
  # Validate parameters
564
567
  if not cert_path or not os.path.exists(cert_path):
565
- return ErrorResult(
566
- message=f"Certificate file not found: {cert_path}"
567
- )
568
-
568
+ return ErrorResult(message=f"Certificate file not found: {cert_path}")
569
+
569
570
  # Get certificate information
570
571
  cert_info = self.certificate_utils.get_certificate_info(cert_path)
571
572
  if not cert_info:
572
- return ErrorResult(
573
- message="Could not read certificate information"
574
- )
575
-
573
+ return ErrorResult(message="Could not read certificate information")
574
+
576
575
  # Validate certificate
577
576
  validation = self.auth_validator.validate_certificate(cert_path)
578
577
  status = "valid" if validation.is_valid else "error"
579
-
578
+
580
579
  cert_result = CertificateResult(
581
580
  cert_path=cert_path,
582
581
  cert_type=cert_info.get("type", "unknown"),
@@ -585,9 +584,9 @@ class CertificateManagementCommand(Command):
585
584
  expiry_date=cert_info.get("expiry_date"),
586
585
  serial_number=cert_info.get("serial_number"),
587
586
  status=status,
588
- error=None if validation.is_valid else validation.error_message
587
+ error=None if validation.is_valid else validation.error_message,
589
588
  )
590
-
589
+
591
590
  logger.info(f"Certificate info retrieved successfully: {cert_path}")
592
591
  return SuccessResult(
593
592
  data={
@@ -596,14 +595,12 @@ class CertificateManagementCommand(Command):
596
595
  "is_valid": validation.is_valid,
597
596
  "error_code": validation.error_code,
598
597
  "error_message": validation.error_message,
599
- "roles": validation.roles
598
+ "roles": validation.roles,
600
599
  },
601
- "details": cert_info
600
+ "details": cert_info,
602
601
  }
603
602
  )
604
-
603
+
605
604
  except Exception as e:
606
605
  logger.error(f"Certificate info retrieval failed: {e}")
607
- return ErrorResult(
608
- message=f"Certificate info retrieval failed: {str(e)}"
609
- )
606
+ return ErrorResult(message=f"Certificate info retrieval failed: {str(e)}")