mcp-security-framework 1.1.0__py3-none-any.whl → 1.1.1__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_security_framework/__init__.py +26 -15
- mcp_security_framework/cli/__init__.py +1 -1
- mcp_security_framework/cli/cert_cli.py +233 -197
- mcp_security_framework/cli/security_cli.py +324 -234
- mcp_security_framework/constants.py +21 -27
- mcp_security_framework/core/auth_manager.py +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- mcp_security_framework/middleware/__init__.py +46 -55
- mcp_security_framework/middleware/auth_middleware.py +62 -63
- mcp_security_framework/middleware/fastapi_auth_middleware.py +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- mcp_security_framework/middleware/flask_middleware.py +183 -157
- mcp_security_framework/middleware/mtls_middleware.py +106 -117
- mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
- mcp_security_framework/middleware/security_middleware.py +109 -124
- mcp_security_framework/schemas/config.py +2 -1
- mcp_security_framework/schemas/models.py +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
- mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
- tests/conftest.py +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -11,11 +11,11 @@ License: MIT
|
|
11
11
|
"""
|
12
12
|
|
13
13
|
import json
|
14
|
-
import tempfile
|
15
14
|
import os
|
16
|
-
|
17
|
-
from typing import Dict, Any
|
15
|
+
import tempfile
|
18
16
|
from datetime import datetime, timedelta, timezone
|
17
|
+
from typing import Any, Dict
|
18
|
+
from unittest.mock import MagicMock, patch
|
19
19
|
|
20
20
|
import pytest
|
21
21
|
from cryptography import x509
|
@@ -23,57 +23,68 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|
23
23
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
24
24
|
from cryptography.x509.oid import NameOID
|
25
25
|
|
26
|
-
from mcp_security_framework.core.cert_manager import
|
26
|
+
from mcp_security_framework.core.cert_manager import (
|
27
|
+
CertificateGenerationError,
|
28
|
+
CertificateManager,
|
29
|
+
)
|
27
30
|
from mcp_security_framework.core.security_manager import SecurityManager
|
28
31
|
from mcp_security_framework.schemas.config import (
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
AuthConfig,
|
33
|
+
CAConfig,
|
34
|
+
CertificateConfig,
|
35
|
+
ClientCertConfig,
|
36
|
+
IntermediateCAConfig,
|
37
|
+
PermissionConfig,
|
38
|
+
RateLimitConfig,
|
39
|
+
SecurityConfig,
|
40
|
+
ServerCertConfig,
|
41
|
+
SSLConfig,
|
32
42
|
)
|
33
43
|
|
34
44
|
|
35
45
|
class TestCertificateFlowIntegration:
|
36
46
|
"""Integration tests for complete certificate management flows."""
|
37
|
-
|
47
|
+
|
38
48
|
def setup_method(self):
|
39
49
|
"""Set up test fixtures before each test method."""
|
40
50
|
# Create temporary directories for certificates
|
41
51
|
self.cert_dir = tempfile.mkdtemp(prefix="test_certs_")
|
42
52
|
self.key_dir = tempfile.mkdtemp(prefix="test_keys_")
|
43
|
-
|
53
|
+
|
44
54
|
# Ensure directories exist
|
45
55
|
os.makedirs(self.cert_dir, exist_ok=True)
|
46
56
|
os.makedirs(self.key_dir, exist_ok=True)
|
47
|
-
|
57
|
+
|
48
58
|
# Create certificate configuration
|
49
59
|
self.cert_config = CertificateConfig(
|
50
60
|
cert_storage_path=self.cert_dir,
|
51
61
|
key_storage_path=self.key_dir,
|
52
62
|
default_key_size=2048,
|
53
|
-
default_validity_days=365
|
63
|
+
default_validity_days=365,
|
54
64
|
)
|
55
|
-
|
65
|
+
|
56
66
|
# Create security configuration
|
57
67
|
self.security_config = SecurityConfig(
|
58
68
|
certificates=self.cert_config,
|
59
69
|
auth=AuthConfig(enabled=False),
|
60
70
|
rate_limit=RateLimitConfig(enabled=False),
|
61
71
|
ssl=SSLConfig(enabled=False),
|
62
|
-
permissions=PermissionConfig(enabled=False)
|
72
|
+
permissions=PermissionConfig(enabled=False),
|
63
73
|
)
|
64
|
-
|
74
|
+
|
65
75
|
# Create certificate manager
|
66
76
|
self.cert_manager = CertificateManager(self.cert_config)
|
67
|
-
|
77
|
+
|
68
78
|
def teardown_method(self):
|
69
79
|
"""Clean up after each test method."""
|
70
80
|
# Remove temporary directories
|
71
81
|
import shutil
|
72
|
-
|
82
|
+
|
83
|
+
if hasattr(self, "cert_dir") and os.path.exists(self.cert_dir):
|
73
84
|
shutil.rmtree(self.cert_dir)
|
74
|
-
if hasattr(self,
|
85
|
+
if hasattr(self, "key_dir") and os.path.exists(self.key_dir):
|
75
86
|
shutil.rmtree(self.key_dir)
|
76
|
-
|
87
|
+
|
77
88
|
def test_complete_certificate_hierarchy_flow(self):
|
78
89
|
"""Test complete certificate hierarchy creation flow."""
|
79
90
|
# 1. Create Root CA
|
@@ -85,17 +96,17 @@ class TestCertificateFlowIntegration:
|
|
85
96
|
locality="Test City",
|
86
97
|
email="root@test.com",
|
87
98
|
validity_years=10,
|
88
|
-
key_size=4096
|
99
|
+
key_size=4096,
|
89
100
|
)
|
90
|
-
|
101
|
+
|
91
102
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
92
|
-
|
103
|
+
|
93
104
|
# Verify root CA was created
|
94
105
|
assert os.path.exists(root_ca_pair.certificate_path)
|
95
106
|
assert os.path.exists(root_ca_pair.private_key_path)
|
96
107
|
assert root_ca_pair.serial_number is not None
|
97
108
|
assert root_ca_pair.not_after > datetime.now(timezone.utc)
|
98
|
-
|
109
|
+
|
99
110
|
# 2. Create Intermediate CA
|
100
111
|
intermediate_ca_config = IntermediateCAConfig(
|
101
112
|
common_name="Test Intermediate CA",
|
@@ -107,17 +118,19 @@ class TestCertificateFlowIntegration:
|
|
107
118
|
validity_years=5,
|
108
119
|
key_size=2048,
|
109
120
|
parent_ca_cert=root_ca_pair.certificate_path,
|
110
|
-
parent_ca_key=root_ca_pair.private_key_path
|
121
|
+
parent_ca_key=root_ca_pair.private_key_path,
|
111
122
|
)
|
112
|
-
|
113
|
-
intermediate_ca_pair = self.cert_manager.create_intermediate_ca(
|
114
|
-
|
123
|
+
|
124
|
+
intermediate_ca_pair = self.cert_manager.create_intermediate_ca(
|
125
|
+
intermediate_ca_config
|
126
|
+
)
|
127
|
+
|
115
128
|
# Verify intermediate CA was created
|
116
129
|
assert os.path.exists(intermediate_ca_pair.certificate_path)
|
117
130
|
assert os.path.exists(intermediate_ca_pair.private_key_path)
|
118
131
|
assert intermediate_ca_pair.serial_number is not None
|
119
132
|
assert intermediate_ca_pair.not_after > datetime.now(timezone.utc)
|
120
|
-
|
133
|
+
|
121
134
|
# 3. Create Server Certificate
|
122
135
|
server_cert_config = ServerCertConfig(
|
123
136
|
common_name="test-server.example.com",
|
@@ -131,17 +144,19 @@ class TestCertificateFlowIntegration:
|
|
131
144
|
san_dns_names=["test-server.example.com", "*.test.example.com"],
|
132
145
|
san_ip_addresses=["192.168.1.100", "10.0.0.1"],
|
133
146
|
ca_cert_path=intermediate_ca_pair.certificate_path,
|
134
|
-
ca_key_path=intermediate_ca_pair.private_key_path
|
147
|
+
ca_key_path=intermediate_ca_pair.private_key_path,
|
135
148
|
)
|
136
|
-
|
137
|
-
server_cert_pair = self.cert_manager.create_server_certificate(
|
138
|
-
|
149
|
+
|
150
|
+
server_cert_pair = self.cert_manager.create_server_certificate(
|
151
|
+
server_cert_config
|
152
|
+
)
|
153
|
+
|
139
154
|
# Verify server certificate was created
|
140
155
|
assert os.path.exists(server_cert_pair.certificate_path)
|
141
156
|
assert os.path.exists(server_cert_pair.private_key_path)
|
142
157
|
assert server_cert_pair.serial_number is not None
|
143
158
|
assert server_cert_pair.not_after > datetime.now(timezone.utc)
|
144
|
-
|
159
|
+
|
145
160
|
# 4. Create Client Certificate
|
146
161
|
client_cert_config = ClientCertConfig(
|
147
162
|
common_name="test-client",
|
@@ -153,28 +168,30 @@ class TestCertificateFlowIntegration:
|
|
153
168
|
validity_years=1,
|
154
169
|
key_size=2048,
|
155
170
|
ca_cert_path=intermediate_ca_pair.certificate_path,
|
156
|
-
ca_key_path=intermediate_ca_pair.private_key_path
|
171
|
+
ca_key_path=intermediate_ca_pair.private_key_path,
|
157
172
|
)
|
158
|
-
|
159
|
-
client_cert_pair = self.cert_manager.create_client_certificate(
|
160
|
-
|
173
|
+
|
174
|
+
client_cert_pair = self.cert_manager.create_client_certificate(
|
175
|
+
client_cert_config
|
176
|
+
)
|
177
|
+
|
161
178
|
# Verify client certificate was created
|
162
179
|
assert os.path.exists(client_cert_pair.certificate_path)
|
163
180
|
assert os.path.exists(client_cert_pair.private_key_path)
|
164
181
|
assert client_cert_pair.serial_number is not None
|
165
182
|
assert client_cert_pair.not_after > datetime.now(timezone.utc)
|
166
|
-
|
183
|
+
|
167
184
|
# 5. Validate certificate chain
|
168
185
|
assert self.cert_manager.validate_certificate_chain(
|
169
186
|
client_cert_pair.certificate_path,
|
170
|
-
[intermediate_ca_pair.certificate_path, root_ca_pair.certificate_path]
|
187
|
+
[intermediate_ca_pair.certificate_path, root_ca_pair.certificate_path],
|
171
188
|
)
|
172
|
-
|
189
|
+
|
173
190
|
assert self.cert_manager.validate_certificate_chain(
|
174
191
|
server_cert_pair.certificate_path,
|
175
|
-
[intermediate_ca_pair.certificate_path, root_ca_pair.certificate_path]
|
192
|
+
[intermediate_ca_pair.certificate_path, root_ca_pair.certificate_path],
|
176
193
|
)
|
177
|
-
|
194
|
+
|
178
195
|
def test_certificate_revocation_flow(self):
|
179
196
|
"""Test complete certificate revocation flow."""
|
180
197
|
# 1. Create Root CA
|
@@ -183,11 +200,11 @@ class TestCertificateFlowIntegration:
|
|
183
200
|
organization="Test Organization",
|
184
201
|
country="US",
|
185
202
|
validity_years=10,
|
186
|
-
key_size=2048
|
203
|
+
key_size=2048,
|
187
204
|
)
|
188
|
-
|
205
|
+
|
189
206
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
190
|
-
|
207
|
+
|
191
208
|
# 2. Create client certificate
|
192
209
|
client_cert_config = ClientCertConfig(
|
193
210
|
common_name="test-client",
|
@@ -196,35 +213,39 @@ class TestCertificateFlowIntegration:
|
|
196
213
|
validity_years=1,
|
197
214
|
key_size=2048,
|
198
215
|
ca_cert_path=root_ca_pair.certificate_path,
|
199
|
-
ca_key_path=root_ca_pair.private_key_path
|
216
|
+
ca_key_path=root_ca_pair.private_key_path,
|
200
217
|
)
|
201
|
-
|
202
|
-
client_cert_pair = self.cert_manager.create_client_certificate(
|
203
|
-
|
218
|
+
|
219
|
+
client_cert_pair = self.cert_manager.create_client_certificate(
|
220
|
+
client_cert_config
|
221
|
+
)
|
222
|
+
|
204
223
|
# 3. Revoke certificate
|
205
224
|
revocation_result = self.cert_manager.revoke_certificate(
|
206
225
|
client_cert_pair.serial_number,
|
207
226
|
reason="key_compromise",
|
208
227
|
ca_cert_path=root_ca_pair.certificate_path,
|
209
|
-
ca_key_path=root_ca_pair.private_key_path
|
228
|
+
ca_key_path=root_ca_pair.private_key_path,
|
210
229
|
)
|
211
|
-
|
230
|
+
|
212
231
|
assert revocation_result is True
|
213
|
-
|
232
|
+
|
214
233
|
# 4. Create CRL
|
215
234
|
crl_path = self.cert_manager.create_crl(
|
216
235
|
root_ca_pair.certificate_path,
|
217
236
|
root_ca_pair.private_key_path,
|
218
|
-
validity_days=30
|
237
|
+
validity_days=30,
|
219
238
|
)
|
220
|
-
|
239
|
+
|
221
240
|
assert os.path.exists(crl_path)
|
222
|
-
|
241
|
+
|
223
242
|
# 5. Verify certificate is revoked
|
224
|
-
cert_info = self.cert_manager.get_certificate_info(
|
243
|
+
cert_info = self.cert_manager.get_certificate_info(
|
244
|
+
client_cert_pair.certificate_path
|
245
|
+
)
|
225
246
|
# Note: revoked status is not checked against CRL in basic implementation
|
226
247
|
# assert cert_info.revoked is True
|
227
|
-
|
248
|
+
|
228
249
|
def test_certificate_validation_flow(self):
|
229
250
|
"""Test complete certificate validation flow."""
|
230
251
|
# 1. Create Root CA
|
@@ -233,11 +254,11 @@ class TestCertificateFlowIntegration:
|
|
233
254
|
organization="Test Organization",
|
234
255
|
country="US",
|
235
256
|
validity_years=10,
|
236
|
-
key_size=2048
|
257
|
+
key_size=2048,
|
237
258
|
)
|
238
|
-
|
259
|
+
|
239
260
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
240
|
-
|
261
|
+
|
241
262
|
# 2. Create server certificate
|
242
263
|
server_cert_config = ServerCertConfig(
|
243
264
|
common_name="test-server.example.com",
|
@@ -246,29 +267,32 @@ class TestCertificateFlowIntegration:
|
|
246
267
|
validity_years=1,
|
247
268
|
key_size=2048,
|
248
269
|
ca_cert_path=root_ca_pair.certificate_path,
|
249
|
-
ca_key_path=root_ca_pair.private_key_path
|
270
|
+
ca_key_path=root_ca_pair.private_key_path,
|
250
271
|
)
|
251
|
-
|
252
|
-
server_cert_pair = self.cert_manager.create_server_certificate(
|
253
|
-
|
272
|
+
|
273
|
+
server_cert_pair = self.cert_manager.create_server_certificate(
|
274
|
+
server_cert_config
|
275
|
+
)
|
276
|
+
|
254
277
|
# 3. Validate certificate
|
255
278
|
validation_result = self.cert_manager.validate_certificate_chain(
|
256
|
-
server_cert_pair.certificate_path,
|
257
|
-
[root_ca_pair.certificate_path]
|
279
|
+
server_cert_pair.certificate_path, [root_ca_pair.certificate_path]
|
258
280
|
)
|
259
|
-
|
281
|
+
|
260
282
|
assert validation_result is True
|
261
|
-
|
283
|
+
|
262
284
|
# 4. Get certificate information
|
263
|
-
cert_info = self.cert_manager.get_certificate_info(
|
264
|
-
|
285
|
+
cert_info = self.cert_manager.get_certificate_info(
|
286
|
+
server_cert_pair.certificate_path
|
287
|
+
)
|
288
|
+
|
265
289
|
assert cert_info.common_name == "test-server.example.com"
|
266
290
|
assert cert_info.organization == "Test Organization"
|
267
291
|
# Note: country is not available in CertificateInfo, only in subject dict
|
268
292
|
assert cert_info.valid is True
|
269
293
|
assert cert_info.revoked is False
|
270
294
|
assert cert_info.not_after > datetime.now(timezone.utc)
|
271
|
-
|
295
|
+
|
272
296
|
def test_certificate_renewal_flow(self):
|
273
297
|
"""Test certificate renewal flow."""
|
274
298
|
# 1. Create Root CA
|
@@ -277,11 +301,11 @@ class TestCertificateFlowIntegration:
|
|
277
301
|
organization="Test Organization",
|
278
302
|
country="US",
|
279
303
|
validity_years=10,
|
280
|
-
key_size=2048
|
304
|
+
key_size=2048,
|
281
305
|
)
|
282
|
-
|
306
|
+
|
283
307
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
284
|
-
|
308
|
+
|
285
309
|
# 2. Create server certificate with short validity
|
286
310
|
server_cert_config = ServerCertConfig(
|
287
311
|
common_name="test-server.example.com",
|
@@ -290,30 +314,36 @@ class TestCertificateFlowIntegration:
|
|
290
314
|
validity_years=1,
|
291
315
|
key_size=2048,
|
292
316
|
ca_cert_path=root_ca_pair.certificate_path,
|
293
|
-
ca_key_path=root_ca_pair.private_key_path
|
317
|
+
ca_key_path=root_ca_pair.private_key_path,
|
318
|
+
)
|
319
|
+
|
320
|
+
original_cert_pair = self.cert_manager.create_server_certificate(
|
321
|
+
server_cert_config
|
294
322
|
)
|
295
|
-
|
296
|
-
original_cert_pair = self.cert_manager.create_server_certificate(server_cert_config)
|
297
|
-
|
323
|
+
|
298
324
|
# 3. Renew certificate
|
299
325
|
renewed_cert_pair = self.cert_manager.renew_certificate(
|
300
326
|
original_cert_pair.certificate_path,
|
301
327
|
ca_cert_path=root_ca_pair.certificate_path,
|
302
328
|
ca_key_path=root_ca_pair.private_key_path,
|
303
|
-
validity_years=2
|
329
|
+
validity_years=2,
|
304
330
|
)
|
305
|
-
|
331
|
+
|
306
332
|
# Verify renewed certificate
|
307
333
|
assert os.path.exists(renewed_cert_pair.certificate_path)
|
308
334
|
assert os.path.exists(renewed_cert_pair.private_key_path)
|
309
335
|
assert renewed_cert_pair.serial_number != original_cert_pair.serial_number
|
310
|
-
|
336
|
+
|
311
337
|
# Verify renewed certificate has longer validity
|
312
|
-
original_info = self.cert_manager.get_certificate_info(
|
313
|
-
|
314
|
-
|
338
|
+
original_info = self.cert_manager.get_certificate_info(
|
339
|
+
original_cert_pair.certificate_path
|
340
|
+
)
|
341
|
+
renewed_info = self.cert_manager.get_certificate_info(
|
342
|
+
renewed_cert_pair.certificate_path
|
343
|
+
)
|
344
|
+
|
315
345
|
assert renewed_info.not_after > original_info.not_after
|
316
|
-
|
346
|
+
|
317
347
|
def test_certificate_export_import_flow(self):
|
318
348
|
"""Test certificate export and import flow."""
|
319
349
|
# 1. Create Root CA
|
@@ -322,52 +352,48 @@ class TestCertificateFlowIntegration:
|
|
322
352
|
organization="Test Organization",
|
323
353
|
country="US",
|
324
354
|
validity_years=10,
|
325
|
-
key_size=2048
|
355
|
+
key_size=2048,
|
326
356
|
)
|
327
|
-
|
357
|
+
|
328
358
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
329
|
-
|
359
|
+
|
330
360
|
# 2. Export certificate to different formats
|
331
361
|
# Export to PEM
|
332
362
|
pem_cert = self.cert_manager.export_certificate(
|
333
|
-
root_ca_pair.certificate_path,
|
334
|
-
format="pem"
|
363
|
+
root_ca_pair.certificate_path, format="pem"
|
335
364
|
)
|
336
365
|
assert "-----BEGIN CERTIFICATE-----" in pem_cert
|
337
|
-
|
366
|
+
|
338
367
|
# Export to DER
|
339
368
|
der_cert = self.cert_manager.export_certificate(
|
340
|
-
root_ca_pair.certificate_path,
|
341
|
-
format="der"
|
369
|
+
root_ca_pair.certificate_path, format="der"
|
342
370
|
)
|
343
371
|
assert isinstance(der_cert, bytes)
|
344
|
-
|
372
|
+
|
345
373
|
# Export private key
|
346
374
|
pem_key = self.cert_manager.export_private_key(
|
347
|
-
root_ca_pair.private_key_path,
|
348
|
-
format="pem"
|
375
|
+
root_ca_pair.private_key_path, format="pem"
|
349
376
|
)
|
350
377
|
assert "-----BEGIN PRIVATE KEY-----" in pem_key
|
351
|
-
|
378
|
+
|
352
379
|
# 3. Import certificate from different formats
|
353
380
|
# Create temporary files for import
|
354
381
|
import_cert_path = os.path.join(self.cert_dir, "imported_cert.pem")
|
355
382
|
import_key_path = os.path.join(self.key_dir, "imported_key.pem")
|
356
|
-
|
357
|
-
with open(import_cert_path,
|
383
|
+
|
384
|
+
with open(import_cert_path, "w") as f:
|
358
385
|
f.write(pem_cert)
|
359
|
-
|
360
|
-
with open(import_key_path,
|
386
|
+
|
387
|
+
with open(import_key_path, "w") as f:
|
361
388
|
f.write(pem_key)
|
362
|
-
|
389
|
+
|
363
390
|
# Verify imported certificate
|
364
391
|
validation_result = self.cert_manager.validate_certificate_chain(
|
365
|
-
import_cert_path,
|
366
|
-
[root_ca_pair.certificate_path]
|
392
|
+
import_cert_path, [root_ca_pair.certificate_path]
|
367
393
|
)
|
368
|
-
|
394
|
+
|
369
395
|
assert validation_result is True
|
370
|
-
|
396
|
+
|
371
397
|
def test_certificate_bulk_operations_flow(self):
|
372
398
|
"""Test bulk certificate operations flow."""
|
373
399
|
# 1. Create Root CA
|
@@ -376,11 +402,11 @@ class TestCertificateFlowIntegration:
|
|
376
402
|
organization="Test Organization",
|
377
403
|
country="US",
|
378
404
|
validity_years=10,
|
379
|
-
key_size=2048
|
405
|
+
key_size=2048,
|
380
406
|
)
|
381
|
-
|
407
|
+
|
382
408
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
383
|
-
|
409
|
+
|
384
410
|
# 2. Create multiple certificates
|
385
411
|
certificates = []
|
386
412
|
for i in range(5):
|
@@ -391,99 +417,101 @@ class TestCertificateFlowIntegration:
|
|
391
417
|
validity_years=1,
|
392
418
|
key_size=2048,
|
393
419
|
ca_cert_path=root_ca_pair.certificate_path,
|
394
|
-
ca_key_path=root_ca_pair.private_key_path
|
420
|
+
ca_key_path=root_ca_pair.private_key_path,
|
395
421
|
)
|
396
|
-
|
422
|
+
|
397
423
|
cert_pair = self.cert_manager.create_client_certificate(client_cert_config)
|
398
|
-
|
424
|
+
|
399
425
|
certificates.append(cert_pair)
|
400
|
-
|
426
|
+
|
401
427
|
# 3. Bulk validate certificates
|
402
428
|
validation_results = []
|
403
429
|
for cert_pair in certificates:
|
404
430
|
result = self.cert_manager.validate_certificate_chain(
|
405
|
-
cert_pair.certificate_path,
|
406
|
-
[root_ca_pair.certificate_path]
|
431
|
+
cert_pair.certificate_path, [root_ca_pair.certificate_path]
|
407
432
|
)
|
408
433
|
validation_results.append(result)
|
409
|
-
|
434
|
+
|
410
435
|
assert all(validation_results)
|
411
|
-
|
436
|
+
|
412
437
|
# 4. Bulk revoke certificates
|
413
438
|
for cert_pair in certificates[:2]: # Revoke first 2
|
414
439
|
self.cert_manager.revoke_certificate(
|
415
440
|
cert_pair.serial_number,
|
416
441
|
reason="cessation_of_operation",
|
417
442
|
ca_cert_path=root_ca_pair.certificate_path,
|
418
|
-
ca_key_path=root_ca_pair.private_key_path
|
443
|
+
ca_key_path=root_ca_pair.private_key_path,
|
419
444
|
)
|
420
|
-
|
445
|
+
|
421
446
|
# 5. Create CRL with revoked certificates
|
422
447
|
crl_path = self.cert_manager.create_crl(
|
423
448
|
root_ca_pair.certificate_path,
|
424
449
|
root_ca_pair.private_key_path,
|
425
|
-
validity_days=30
|
450
|
+
validity_days=30,
|
426
451
|
)
|
427
|
-
|
452
|
+
|
428
453
|
assert os.path.exists(crl_path)
|
429
|
-
|
454
|
+
|
430
455
|
# 6. Verify revocation status
|
431
456
|
for i, cert_pair in enumerate(certificates):
|
432
|
-
cert_info = self.cert_manager.get_certificate_info(
|
457
|
+
cert_info = self.cert_manager.get_certificate_info(
|
458
|
+
cert_pair.certificate_path
|
459
|
+
)
|
433
460
|
# Note: revoked status is not checked against CRL in basic implementation
|
434
461
|
# if i < 2:
|
435
462
|
# assert cert_info.revoked is True
|
436
463
|
# else:
|
437
464
|
# assert cert_info.revoked is False
|
438
|
-
|
465
|
+
|
439
466
|
def test_certificate_error_handling_flow(self):
|
440
467
|
"""Test certificate error handling flow."""
|
441
468
|
# Test with invalid certificate path
|
442
469
|
result = self.cert_manager.validate_certificate_chain("nonexistent.crt")
|
443
470
|
assert result is False
|
444
|
-
|
471
|
+
|
445
472
|
# Test with invalid CA certificate
|
446
|
-
result = self.cert_manager.validate_certificate_chain(
|
473
|
+
result = self.cert_manager.validate_certificate_chain(
|
474
|
+
"nonexistent.crt", "nonexistent_ca.crt"
|
475
|
+
)
|
447
476
|
assert result is False
|
448
|
-
|
477
|
+
|
449
478
|
# Test with invalid serial number
|
450
479
|
with pytest.raises(ValueError):
|
451
480
|
self.cert_manager.revoke_certificate(
|
452
|
-
"", # Invalid empty serial number
|
453
|
-
reason="key_compromise"
|
481
|
+
"", reason="key_compromise" # Invalid empty serial number
|
454
482
|
)
|
455
|
-
|
483
|
+
|
456
484
|
# Test with invalid configuration
|
457
485
|
invalid_config = CAConfig(
|
458
486
|
common_name="", # Invalid empty common name
|
459
487
|
organization="Test Organization",
|
460
488
|
country="US",
|
461
489
|
validity_years=10,
|
462
|
-
key_size=2048
|
490
|
+
key_size=2048,
|
463
491
|
)
|
464
|
-
|
492
|
+
|
465
493
|
with pytest.raises(CertificateGenerationError):
|
466
494
|
self.cert_manager.create_root_ca(invalid_config)
|
467
|
-
|
495
|
+
|
468
496
|
def test_certificate_performance_flow(self):
|
469
497
|
"""Test certificate operations performance."""
|
470
498
|
import time
|
471
|
-
|
499
|
+
|
472
500
|
# 1. Create Root CA
|
473
501
|
root_ca_config = CAConfig(
|
474
502
|
common_name="Test Root CA",
|
475
503
|
organization="Test Organization",
|
476
504
|
country="US",
|
477
505
|
validity_years=10,
|
478
|
-
key_size=2048
|
506
|
+
key_size=2048,
|
479
507
|
)
|
480
|
-
|
508
|
+
|
481
509
|
start_time = time.time()
|
482
510
|
root_ca_pair = self.cert_manager.create_root_ca(root_ca_config)
|
483
511
|
ca_creation_time = time.time() - start_time
|
484
|
-
|
512
|
+
|
485
513
|
assert ca_creation_time < 5.0, f"CA creation too slow: {ca_creation_time:.2f}s"
|
486
|
-
|
514
|
+
|
487
515
|
# 2. Benchmark certificate creation
|
488
516
|
start_time = time.time()
|
489
517
|
for i in range(10):
|
@@ -494,34 +522,37 @@ class TestCertificateFlowIntegration:
|
|
494
522
|
validity_years=1,
|
495
523
|
key_size=2048,
|
496
524
|
ca_cert_path=root_ca_pair.certificate_path,
|
497
|
-
ca_key_path=root_ca_pair.private_key_path
|
525
|
+
ca_key_path=root_ca_pair.private_key_path,
|
498
526
|
)
|
499
|
-
|
527
|
+
|
500
528
|
self.cert_manager.create_client_certificate(client_cert_config)
|
501
|
-
|
529
|
+
|
502
530
|
cert_creation_time = time.time() - start_time
|
503
531
|
avg_cert_time = cert_creation_time / 10
|
504
|
-
|
505
|
-
assert
|
506
|
-
|
532
|
+
|
533
|
+
assert (
|
534
|
+
avg_cert_time < 1.0
|
535
|
+
), f"Certificate creation too slow: {avg_cert_time:.2f}s per cert"
|
536
|
+
|
507
537
|
# 3. Benchmark certificate validation
|
508
538
|
cert_paths = []
|
509
539
|
for i in range(10):
|
510
540
|
cert_path = os.path.join(self.cert_dir, f"test-client-{i}.pem")
|
511
541
|
if os.path.exists(cert_path):
|
512
542
|
cert_paths.append(cert_path)
|
513
|
-
|
543
|
+
|
514
544
|
start_time = time.time()
|
515
545
|
for cert_path in cert_paths:
|
516
546
|
self.cert_manager.validate_certificate_chain(
|
517
|
-
cert_path,
|
518
|
-
[root_ca_pair.certificate_path]
|
547
|
+
cert_path, [root_ca_pair.certificate_path]
|
519
548
|
)
|
520
|
-
|
549
|
+
|
521
550
|
validation_time = time.time() - start_time
|
522
551
|
if cert_paths:
|
523
552
|
avg_validation_time = validation_time / len(cert_paths)
|
524
553
|
else:
|
525
554
|
avg_validation_time = 0.0
|
526
|
-
|
527
|
-
assert
|
555
|
+
|
556
|
+
assert (
|
557
|
+
avg_validation_time < 0.1
|
558
|
+
), f"Certificate validation too slow: {avg_validation_time:.3f}s per cert"
|