mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +120 -91
- 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.6.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
- mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +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__(
|
33
|
-
|
34
|
-
|
35
|
-
|
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": {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
"
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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(
|
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(
|
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(
|
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(
|
186
|
-
|
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
|
-
|
206
|
-
)
|
207
|
-
|
243
|
+
return ErrorResult(message="Common name cannot be empty")
|
244
|
+
|
208
245
|
if validity_days <= 0:
|
209
|
-
return ErrorResult(
|
210
|
-
|
211
|
-
)
|
212
|
-
|
246
|
+
return ErrorResult(message="Validity days must be positive")
|
247
|
+
|
213
248
|
if key_size < 1024:
|
214
|
-
return ErrorResult(
|
215
|
-
|
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,
|
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
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
284
|
-
)
|
285
|
-
|
318
|
+
return ErrorResult(message="Common name cannot be empty")
|
319
|
+
|
286
320
|
if not roles:
|
287
|
-
return ErrorResult(
|
288
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
368
|
-
)
|
369
|
-
|
395
|
+
return ErrorResult(message="Common name cannot be empty")
|
396
|
+
|
370
397
|
if not roles:
|
371
|
-
return ErrorResult(
|
372
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 = [
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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)}")
|