mcp-security-framework 1.1.2__py3-none-any.whl → 1.2.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 +1 -1
- mcp_security_framework/cli/cert_cli.py +167 -3
- mcp_security_framework/core/auth_manager.py +32 -10
- mcp_security_framework/core/cert_manager.py +261 -6
- mcp_security_framework/core/ssl_manager.py +41 -9
- mcp_security_framework/middleware/mtls_middleware.py +10 -2
- mcp_security_framework/schemas/config.py +31 -0
- mcp_security_framework/schemas/models.py +46 -0
- mcp_security_framework/utils/cert_utils.py +309 -8
- {mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/METADATA +1 -1
- {mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/RECORD +17 -16
- tests/test_core/test_auth_manager.py +6 -6
- tests/test_utils/test_cert_utils.py +168 -0
- tests/test_utils/test_unitid_compat.py +550 -0
- {mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,550 @@
|
|
1
|
+
"""
|
2
|
+
Tests for UnitID Compatibility Module
|
3
|
+
|
4
|
+
This module contains tests for the unitid functionality that
|
5
|
+
handles unique unit identifiers in certificates.
|
6
|
+
|
7
|
+
Test Coverage:
|
8
|
+
- UnitID extraction from certificates
|
9
|
+
- UnitID validation
|
10
|
+
- UnitID integration with certificate creation
|
11
|
+
- UnitID integration with authentication
|
12
|
+
|
13
|
+
Author: Vasiliy Zdanovskiy
|
14
|
+
email: vasilyvz@gmail.com
|
15
|
+
"""
|
16
|
+
|
17
|
+
import pytest
|
18
|
+
import uuid
|
19
|
+
from unittest.mock import Mock, patch
|
20
|
+
from cryptography import x509
|
21
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
22
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
23
|
+
|
24
|
+
from mcp_security_framework.utils.cert_utils import extract_unitid_from_certificate
|
25
|
+
|
26
|
+
|
27
|
+
class TestUnitIDCompatibility:
|
28
|
+
"""Test suite for unitid compatibility functions."""
|
29
|
+
|
30
|
+
def test_extract_unitid_from_certificate_with_unitid_extension(self):
|
31
|
+
"""Test extracting unitid from certificate with unitid extension."""
|
32
|
+
# Create a mock certificate with unitid extension
|
33
|
+
mock_cert = Mock()
|
34
|
+
mock_extension = Mock()
|
35
|
+
mock_extension.value.value = b"550e8400-e29b-41d4-a716-446655440000"
|
36
|
+
mock_cert.extensions.get_extension_for_oid.return_value = mock_extension
|
37
|
+
|
38
|
+
# Mock the parse_certificate function to return our mock certificate
|
39
|
+
with patch('mcp_security_framework.utils.cert_utils.parse_certificate') as mock_parse:
|
40
|
+
mock_parse.return_value = mock_cert
|
41
|
+
|
42
|
+
result = extract_unitid_from_certificate("mock_cert_data")
|
43
|
+
|
44
|
+
assert result == "550e8400-e29b-41d4-a716-446655440000"
|
45
|
+
|
46
|
+
def test_extract_unitid_from_certificate_without_unitid_extension(self):
|
47
|
+
"""Test extracting unitid from certificate without unitid extension."""
|
48
|
+
# Create a mock certificate without unitid extension
|
49
|
+
mock_cert = Mock()
|
50
|
+
mock_cert.extensions.get_extension_for_oid.side_effect = x509.ExtensionNotFound(
|
51
|
+
"Extension not found", "1.3.6.1.4.1.99999.1.3"
|
52
|
+
)
|
53
|
+
|
54
|
+
# Mock the parse_certificate function to return our mock certificate
|
55
|
+
with patch('mcp_security_framework.utils.cert_utils.parse_certificate') as mock_parse:
|
56
|
+
mock_parse.return_value = mock_cert
|
57
|
+
|
58
|
+
result = extract_unitid_from_certificate("mock_cert_data")
|
59
|
+
|
60
|
+
assert result is None
|
61
|
+
|
62
|
+
def test_extract_unitid_from_certificate_with_invalid_unitid(self):
|
63
|
+
"""Test extracting unitid from certificate with invalid unitid format."""
|
64
|
+
# Create a mock certificate with invalid unitid
|
65
|
+
mock_cert = Mock()
|
66
|
+
mock_extension = Mock()
|
67
|
+
mock_extension.value.value = b"invalid-unitid"
|
68
|
+
mock_cert.extensions.get_extension_for_oid.return_value = mock_extension
|
69
|
+
|
70
|
+
# Mock the parse_certificate function to return our mock certificate
|
71
|
+
with patch('mcp_security_framework.utils.cert_utils.parse_certificate') as mock_parse:
|
72
|
+
mock_parse.return_value = mock_cert
|
73
|
+
|
74
|
+
result = extract_unitid_from_certificate("mock_cert_data")
|
75
|
+
|
76
|
+
# Should return None for invalid unitid
|
77
|
+
assert result is None
|
78
|
+
|
79
|
+
def test_extract_unitid_from_certificate_with_real_certificate(self):
|
80
|
+
"""Test extracting unitid from a real certificate."""
|
81
|
+
# Generate a valid UUID4
|
82
|
+
test_unitid = str(uuid.uuid4())
|
83
|
+
|
84
|
+
# Create a real certificate with unitid extension
|
85
|
+
private_key = rsa.generate_private_key(
|
86
|
+
public_exponent=65537,
|
87
|
+
key_size=2048,
|
88
|
+
)
|
89
|
+
|
90
|
+
subject = issuer = x509.Name([
|
91
|
+
x509.NameAttribute(x509.NameOID.COMMON_NAME, 'Test UnitID'),
|
92
|
+
])
|
93
|
+
|
94
|
+
# Create certificate builder
|
95
|
+
from datetime import datetime, timezone, timedelta
|
96
|
+
builder = x509.CertificateBuilder()
|
97
|
+
builder = builder.subject_name(subject)
|
98
|
+
builder = builder.issuer_name(issuer)
|
99
|
+
builder = builder.public_key(private_key.public_key())
|
100
|
+
builder = builder.serial_number(x509.random_serial_number())
|
101
|
+
builder = builder.not_valid_before(datetime.now(timezone.utc))
|
102
|
+
builder = builder.not_valid_after(
|
103
|
+
datetime.now(timezone.utc) + timedelta(days=10)
|
104
|
+
)
|
105
|
+
|
106
|
+
# Add unitid extension
|
107
|
+
unitid_extension = x509.UnrecognizedExtension(
|
108
|
+
oid=x509.ObjectIdentifier("1.3.6.1.4.1.99999.1.3"),
|
109
|
+
value=test_unitid.encode(),
|
110
|
+
)
|
111
|
+
builder = builder.add_extension(unitid_extension, critical=False)
|
112
|
+
|
113
|
+
# Sign the certificate
|
114
|
+
certificate = builder.sign(private_key, hashes.SHA256())
|
115
|
+
|
116
|
+
# Test extraction
|
117
|
+
cert_pem = certificate.public_bytes(serialization.Encoding.PEM)
|
118
|
+
result = extract_unitid_from_certificate(cert_pem)
|
119
|
+
|
120
|
+
assert result == test_unitid
|
121
|
+
|
122
|
+
def test_unitid_validation_in_certificate_info(self):
|
123
|
+
"""Test unitid validation in CertificateInfo model."""
|
124
|
+
from mcp_security_framework.schemas.models import CertificateInfo, CertificateType
|
125
|
+
from datetime import datetime, timezone
|
126
|
+
|
127
|
+
# Test with valid UUID4
|
128
|
+
valid_unitid = str(uuid.uuid4())
|
129
|
+
cert_info = CertificateInfo(
|
130
|
+
subject={"CN": "Test"},
|
131
|
+
issuer={"CN": "Test CA"},
|
132
|
+
serial_number="123456789",
|
133
|
+
not_before=datetime.now(timezone.utc),
|
134
|
+
not_after=datetime.now(timezone.utc),
|
135
|
+
certificate_type=CertificateType.CLIENT,
|
136
|
+
key_size=2048,
|
137
|
+
signature_algorithm="sha256",
|
138
|
+
unitid=valid_unitid
|
139
|
+
)
|
140
|
+
|
141
|
+
assert cert_info.unitid == valid_unitid
|
142
|
+
|
143
|
+
# Test with invalid UUID4
|
144
|
+
with pytest.raises(ValueError, match="unitid must be a valid UUID4 string"):
|
145
|
+
CertificateInfo(
|
146
|
+
subject={"CN": "Test"},
|
147
|
+
issuer={"CN": "Test CA"},
|
148
|
+
serial_number="123456789",
|
149
|
+
not_before=datetime.now(timezone.utc),
|
150
|
+
not_after=datetime.now(timezone.utc),
|
151
|
+
certificate_type=CertificateType.CLIENT,
|
152
|
+
key_size=2048,
|
153
|
+
signature_algorithm="sha256",
|
154
|
+
unitid="invalid-unitid"
|
155
|
+
)
|
156
|
+
|
157
|
+
def test_unitid_validation_in_certificate_pair(self):
|
158
|
+
"""Test unitid validation in CertificatePair model."""
|
159
|
+
from mcp_security_framework.schemas.models import CertificatePair, CertificateType
|
160
|
+
from datetime import datetime, timezone
|
161
|
+
|
162
|
+
# Test with valid UUID4
|
163
|
+
valid_unitid = str(uuid.uuid4())
|
164
|
+
cert_pair = CertificatePair(
|
165
|
+
certificate_path="/path/to/cert.crt",
|
166
|
+
private_key_path="/path/to/key.key",
|
167
|
+
certificate_pem="-----BEGIN CERTIFICATE-----\nMOCK\n-----END CERTIFICATE-----",
|
168
|
+
private_key_pem="-----BEGIN PRIVATE KEY-----\nMOCK\n-----END PRIVATE KEY-----",
|
169
|
+
serial_number="123456789",
|
170
|
+
common_name="Test",
|
171
|
+
organization="Test Org",
|
172
|
+
not_before=datetime.now(timezone.utc),
|
173
|
+
not_after=datetime.now(timezone.utc),
|
174
|
+
certificate_type=CertificateType.CLIENT,
|
175
|
+
key_size=2048,
|
176
|
+
unitid=valid_unitid
|
177
|
+
)
|
178
|
+
|
179
|
+
assert cert_pair.unitid == valid_unitid
|
180
|
+
|
181
|
+
# Test with invalid UUID4
|
182
|
+
with pytest.raises(ValueError, match="unitid must be a valid UUID4 string"):
|
183
|
+
CertificatePair(
|
184
|
+
certificate_path="/path/to/cert.crt",
|
185
|
+
private_key_path="/path/to/key.key",
|
186
|
+
certificate_pem="-----BEGIN CERTIFICATE-----\nMOCK\n-----END CERTIFICATE-----",
|
187
|
+
private_key_pem="-----BEGIN PRIVATE KEY-----\nMOCK\n-----END PRIVATE KEY-----",
|
188
|
+
serial_number="123456789",
|
189
|
+
common_name="Test",
|
190
|
+
organization="Test Org",
|
191
|
+
not_before=datetime.now(timezone.utc),
|
192
|
+
not_after=datetime.now(timezone.utc),
|
193
|
+
certificate_type=CertificateType.CLIENT,
|
194
|
+
key_size=2048,
|
195
|
+
unitid="invalid-unitid"
|
196
|
+
)
|
197
|
+
|
198
|
+
def test_unitid_validation_in_auth_result(self):
|
199
|
+
"""Test unitid validation in AuthResult model."""
|
200
|
+
from mcp_security_framework.schemas.models import AuthResult, AuthStatus
|
201
|
+
from datetime import datetime, timezone
|
202
|
+
|
203
|
+
# Test with valid UUID4
|
204
|
+
valid_unitid = str(uuid.uuid4())
|
205
|
+
auth_result = AuthResult(
|
206
|
+
is_valid=True,
|
207
|
+
status=AuthStatus.SUCCESS,
|
208
|
+
username="test_user",
|
209
|
+
unitid=valid_unitid
|
210
|
+
)
|
211
|
+
|
212
|
+
assert auth_result.unitid == valid_unitid
|
213
|
+
|
214
|
+
# Test with invalid UUID4
|
215
|
+
with pytest.raises(ValueError, match="unitid must be a valid UUID4 string"):
|
216
|
+
AuthResult(
|
217
|
+
is_valid=True,
|
218
|
+
status=AuthStatus.SUCCESS,
|
219
|
+
username="test_user",
|
220
|
+
unitid="invalid-unitid"
|
221
|
+
)
|
222
|
+
|
223
|
+
def test_unitid_in_certificate_creation_config(self):
|
224
|
+
"""Test unitid in certificate creation configuration."""
|
225
|
+
from mcp_security_framework.schemas.config import CAConfig, ClientCertConfig
|
226
|
+
|
227
|
+
# Test with valid UUID4
|
228
|
+
valid_unitid = str(uuid.uuid4())
|
229
|
+
|
230
|
+
ca_config = CAConfig(
|
231
|
+
common_name="Test CA",
|
232
|
+
organization="Test Org",
|
233
|
+
country="US",
|
234
|
+
unitid=valid_unitid
|
235
|
+
)
|
236
|
+
assert ca_config.unitid == valid_unitid
|
237
|
+
|
238
|
+
client_config = ClientCertConfig(
|
239
|
+
common_name="test.example.com",
|
240
|
+
organization="Test Org",
|
241
|
+
country="US",
|
242
|
+
ca_cert_path="/path/to/ca.crt",
|
243
|
+
ca_key_path="/path/to/ca.key",
|
244
|
+
unitid=valid_unitid
|
245
|
+
)
|
246
|
+
assert client_config.unitid == valid_unitid
|
247
|
+
|
248
|
+
# Test with invalid UUID4
|
249
|
+
with pytest.raises(ValueError, match="unitid must be a valid UUID4 string"):
|
250
|
+
CAConfig(
|
251
|
+
common_name="Test CA",
|
252
|
+
organization="Test Org",
|
253
|
+
country="US",
|
254
|
+
unitid="invalid-unitid"
|
255
|
+
)
|
256
|
+
|
257
|
+
def test_unitid_integration_with_certificate_creation(self):
|
258
|
+
"""Test unitid integration with certificate creation."""
|
259
|
+
from mcp_security_framework.core.cert_manager import CertificateManager
|
260
|
+
from mcp_security_framework.schemas.config import CertificateConfig, CAConfig
|
261
|
+
from datetime import datetime, timezone
|
262
|
+
import tempfile
|
263
|
+
import os
|
264
|
+
|
265
|
+
# Create temporary directory for certificates
|
266
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
267
|
+
cert_config = CertificateConfig(
|
268
|
+
enabled=True,
|
269
|
+
ca_cert_path=os.path.join(temp_dir, "ca.crt"),
|
270
|
+
ca_key_path=os.path.join(temp_dir, "ca.key"),
|
271
|
+
cert_storage_path=temp_dir,
|
272
|
+
key_storage_path=temp_dir
|
273
|
+
)
|
274
|
+
|
275
|
+
# Mock the configuration validation to avoid file system checks
|
276
|
+
with patch.object(CertificateManager, '_validate_configuration'):
|
277
|
+
cert_manager = CertificateManager(cert_config)
|
278
|
+
|
279
|
+
# Test unitid in CA creation
|
280
|
+
valid_unitid = str(uuid.uuid4())
|
281
|
+
ca_config = CAConfig(
|
282
|
+
common_name="Test CA",
|
283
|
+
organization="Test Org",
|
284
|
+
country="US",
|
285
|
+
unitid=valid_unitid
|
286
|
+
)
|
287
|
+
|
288
|
+
# Mock the certificate creation to avoid actual file operations
|
289
|
+
with patch.object(cert_manager, '_validate_configuration'):
|
290
|
+
with patch('builtins.open', create=True):
|
291
|
+
with patch('os.makedirs'):
|
292
|
+
with patch('os.chmod'):
|
293
|
+
# Mock the certificate building process
|
294
|
+
mock_cert = Mock()
|
295
|
+
mock_cert.serial_number = 123456789
|
296
|
+
mock_cert.not_valid_before = datetime.now(timezone.utc)
|
297
|
+
mock_cert.not_valid_after = datetime.now(timezone.utc)
|
298
|
+
mock_cert.public_bytes.return_value = b"-----BEGIN CERTIFICATE-----\nMOCK\n-----END CERTIFICATE-----"
|
299
|
+
|
300
|
+
with patch('mcp_security_framework.core.cert_manager.rsa.generate_private_key') as mock_key:
|
301
|
+
mock_private_key = Mock()
|
302
|
+
mock_private_key.public_key.return_value = Mock()
|
303
|
+
mock_private_key.private_bytes.return_value = b"-----BEGIN PRIVATE KEY-----\nMOCK\n-----END PRIVATE KEY-----"
|
304
|
+
mock_key.return_value = mock_private_key
|
305
|
+
|
306
|
+
with patch('mcp_security_framework.core.cert_manager.x509.CertificateBuilder') as mock_builder:
|
307
|
+
mock_builder_instance = Mock()
|
308
|
+
mock_builder_instance.subject_name.return_value = mock_builder_instance
|
309
|
+
mock_builder_instance.issuer_name.return_value = mock_builder_instance
|
310
|
+
mock_builder_instance.public_key.return_value = mock_builder_instance
|
311
|
+
mock_builder_instance.serial_number.return_value = mock_builder_instance
|
312
|
+
mock_builder_instance.not_valid_before.return_value = mock_builder_instance
|
313
|
+
mock_builder_instance.not_valid_after.return_value = mock_builder_instance
|
314
|
+
mock_builder_instance.add_extension.return_value = mock_builder_instance
|
315
|
+
mock_builder_instance.sign.return_value = mock_cert
|
316
|
+
mock_builder.return_value = mock_builder_instance
|
317
|
+
|
318
|
+
# Mock the datetime compatibility functions
|
319
|
+
with patch('mcp_security_framework.core.cert_manager.get_not_valid_before_utc') as mock_before:
|
320
|
+
with patch('mcp_security_framework.core.cert_manager.get_not_valid_after_utc') as mock_after:
|
321
|
+
mock_before.return_value = datetime.now(timezone.utc)
|
322
|
+
mock_after.return_value = datetime.now(timezone.utc)
|
323
|
+
|
324
|
+
result = cert_manager.create_root_ca(ca_config)
|
325
|
+
|
326
|
+
# Verify unitid was added to the certificate pair
|
327
|
+
assert result.unitid == valid_unitid
|
328
|
+
|
329
|
+
def test_unitid_integration_with_authentication(self):
|
330
|
+
"""Test unitid integration with authentication."""
|
331
|
+
from mcp_security_framework.core.auth_manager import AuthManager
|
332
|
+
from mcp_security_framework.schemas.config import AuthConfig
|
333
|
+
from mcp_security_framework.core.permission_manager import PermissionManager
|
334
|
+
from mcp_security_framework.schemas.config import PermissionConfig
|
335
|
+
from unittest.mock import Mock
|
336
|
+
import tempfile
|
337
|
+
import json
|
338
|
+
import os
|
339
|
+
|
340
|
+
# Create test configuration
|
341
|
+
auth_config = AuthConfig(
|
342
|
+
enabled=True,
|
343
|
+
api_keys={"test_key": "test_user"},
|
344
|
+
jwt_secret="test_secret_key_for_jwt_signing_12345"
|
345
|
+
)
|
346
|
+
|
347
|
+
# Create temporary roles file
|
348
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
349
|
+
roles_file = os.path.join(temp_dir, "test_roles.json")
|
350
|
+
with open(roles_file, "w") as f:
|
351
|
+
json.dump({"roles": {}}, f)
|
352
|
+
|
353
|
+
perm_config = PermissionConfig(
|
354
|
+
enabled=True,
|
355
|
+
roles_file=roles_file
|
356
|
+
)
|
357
|
+
|
358
|
+
permission_manager = PermissionManager(perm_config)
|
359
|
+
auth_manager = AuthManager(auth_config, permission_manager)
|
360
|
+
|
361
|
+
# Test unitid in certificate authentication
|
362
|
+
valid_unitid = str(uuid.uuid4())
|
363
|
+
|
364
|
+
# Mock certificate with unitid
|
365
|
+
mock_cert_pem = "-----BEGIN CERTIFICATE-----\nMOCK\n-----END CERTIFICATE-----"
|
366
|
+
|
367
|
+
# Mock the authenticate_certificate method to return a successful result with unitid
|
368
|
+
from mcp_security_framework.schemas.models import AuthResult, AuthStatus
|
369
|
+
from datetime import datetime, timezone, timedelta
|
370
|
+
|
371
|
+
expected_result = AuthResult(
|
372
|
+
is_valid=True,
|
373
|
+
status=AuthStatus.SUCCESS,
|
374
|
+
username="test_user",
|
375
|
+
roles=[],
|
376
|
+
auth_method="certificate",
|
377
|
+
auth_timestamp=datetime.now(timezone.utc),
|
378
|
+
token_expiry=datetime.now(timezone.utc) + timedelta(days=30),
|
379
|
+
unitid=valid_unitid,
|
380
|
+
)
|
381
|
+
|
382
|
+
with patch.object(auth_manager, 'authenticate_certificate', return_value=expected_result):
|
383
|
+
result = auth_manager.authenticate_certificate(mock_cert_pem)
|
384
|
+
|
385
|
+
# Verify unitid was included in authentication result
|
386
|
+
assert result.unitid == valid_unitid
|
387
|
+
|
388
|
+
def test_unitid_optional_in_certificate_creation(self):
|
389
|
+
"""Test that unitid is optional in certificate creation."""
|
390
|
+
from mcp_security_framework.schemas.config import CAConfig, ClientCertConfig
|
391
|
+
|
392
|
+
# Test CA config without unitid
|
393
|
+
ca_config = CAConfig(
|
394
|
+
common_name="Test CA",
|
395
|
+
organization="Test Org",
|
396
|
+
country="US"
|
397
|
+
# unitid not specified
|
398
|
+
)
|
399
|
+
assert ca_config.unitid is None
|
400
|
+
|
401
|
+
# Test client config without unitid
|
402
|
+
client_config = ClientCertConfig(
|
403
|
+
common_name="test.example.com",
|
404
|
+
organization="Test Org",
|
405
|
+
country="US",
|
406
|
+
ca_cert_path="/path/to/ca.crt",
|
407
|
+
ca_key_path="/path/to/ca.key"
|
408
|
+
# unitid not specified
|
409
|
+
)
|
410
|
+
assert client_config.unitid is None
|
411
|
+
|
412
|
+
def test_unitid_optional_in_certificate_info(self):
|
413
|
+
"""Test that unitid is optional in CertificateInfo."""
|
414
|
+
from mcp_security_framework.schemas.models import CertificateInfo, CertificateType
|
415
|
+
from datetime import datetime, timezone
|
416
|
+
|
417
|
+
# Test CertificateInfo without unitid
|
418
|
+
cert_info = CertificateInfo(
|
419
|
+
subject={"CN": "Test"},
|
420
|
+
issuer={"CN": "Test CA"},
|
421
|
+
serial_number="123456789",
|
422
|
+
not_before=datetime.now(timezone.utc),
|
423
|
+
not_after=datetime.now(timezone.utc),
|
424
|
+
certificate_type=CertificateType.CLIENT,
|
425
|
+
key_size=2048,
|
426
|
+
signature_algorithm="sha256"
|
427
|
+
# unitid not specified
|
428
|
+
)
|
429
|
+
assert cert_info.unitid is None
|
430
|
+
|
431
|
+
def test_unitid_optional_in_certificate_pair(self):
|
432
|
+
"""Test that unitid is optional in CertificatePair."""
|
433
|
+
from mcp_security_framework.schemas.models import CertificatePair, CertificateType
|
434
|
+
from datetime import datetime, timezone
|
435
|
+
|
436
|
+
# Test CertificatePair without unitid
|
437
|
+
cert_pair = CertificatePair(
|
438
|
+
certificate_path="/path/to/cert.crt",
|
439
|
+
private_key_path="/path/to/key.key",
|
440
|
+
certificate_pem="-----BEGIN CERTIFICATE-----\nMOCK\n-----END CERTIFICATE-----",
|
441
|
+
private_key_pem="-----BEGIN PRIVATE KEY-----\nMOCK\n-----END PRIVATE KEY-----",
|
442
|
+
serial_number="123456789",
|
443
|
+
common_name="Test",
|
444
|
+
organization="Test Org",
|
445
|
+
not_before=datetime.now(timezone.utc),
|
446
|
+
not_after=datetime.now(timezone.utc),
|
447
|
+
certificate_type=CertificateType.CLIENT,
|
448
|
+
key_size=2048
|
449
|
+
# unitid not specified
|
450
|
+
)
|
451
|
+
assert cert_pair.unitid is None
|
452
|
+
|
453
|
+
def test_unitid_optional_in_auth_result(self):
|
454
|
+
"""Test that unitid is optional in AuthResult."""
|
455
|
+
from mcp_security_framework.schemas.models import AuthResult, AuthStatus
|
456
|
+
|
457
|
+
# Test AuthResult without unitid
|
458
|
+
auth_result = AuthResult(
|
459
|
+
is_valid=True,
|
460
|
+
status=AuthStatus.SUCCESS,
|
461
|
+
username="test_user"
|
462
|
+
# unitid not specified
|
463
|
+
)
|
464
|
+
assert auth_result.unitid is None
|
465
|
+
|
466
|
+
def test_extract_unitid_from_certificate_without_unitid_returns_none(self):
|
467
|
+
"""Test that extract_unitid_from_certificate returns None when unitid is not present."""
|
468
|
+
# Create a mock certificate without unitid extension
|
469
|
+
mock_cert = Mock()
|
470
|
+
mock_cert.extensions.get_extension_for_oid.side_effect = x509.ExtensionNotFound(
|
471
|
+
"Extension not found", "1.3.6.1.4.1.99999.1.3"
|
472
|
+
)
|
473
|
+
|
474
|
+
# Mock the parse_certificate function to return our mock certificate
|
475
|
+
with patch('mcp_security_framework.utils.cert_utils.parse_certificate') as mock_parse:
|
476
|
+
mock_parse.return_value = mock_cert
|
477
|
+
|
478
|
+
result = extract_unitid_from_certificate("mock_cert_data")
|
479
|
+
|
480
|
+
# Should return None when unitid is not present
|
481
|
+
assert result is None
|
482
|
+
|
483
|
+
def test_certificate_creation_without_unitid(self):
|
484
|
+
"""Test certificate creation when unitid is not specified."""
|
485
|
+
from mcp_security_framework.core.cert_manager import CertificateManager
|
486
|
+
from mcp_security_framework.schemas.config import CertificateConfig, CAConfig
|
487
|
+
from datetime import datetime, timezone
|
488
|
+
import tempfile
|
489
|
+
import os
|
490
|
+
|
491
|
+
# Create temporary directory for certificates
|
492
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
493
|
+
cert_config = CertificateConfig(
|
494
|
+
enabled=True,
|
495
|
+
ca_cert_path=os.path.join(temp_dir, "ca.crt"),
|
496
|
+
ca_key_path=os.path.join(temp_dir, "ca.key"),
|
497
|
+
cert_storage_path=temp_dir,
|
498
|
+
key_storage_path=temp_dir
|
499
|
+
)
|
500
|
+
|
501
|
+
# Mock the configuration validation to avoid file system checks
|
502
|
+
with patch.object(CertificateManager, '_validate_configuration'):
|
503
|
+
cert_manager = CertificateManager(cert_config)
|
504
|
+
|
505
|
+
# Test CA creation without unitid
|
506
|
+
ca_config = CAConfig(
|
507
|
+
common_name="Test CA",
|
508
|
+
organization="Test Org",
|
509
|
+
country="US"
|
510
|
+
# unitid not specified
|
511
|
+
)
|
512
|
+
|
513
|
+
# Mock the certificate building process
|
514
|
+
with patch('builtins.open', create=True):
|
515
|
+
with patch('os.makedirs'):
|
516
|
+
with patch('os.chmod'):
|
517
|
+
mock_cert = Mock()
|
518
|
+
mock_cert.serial_number = 123456789
|
519
|
+
mock_cert.not_valid_before = datetime.now(timezone.utc)
|
520
|
+
mock_cert.not_valid_after = datetime.now(timezone.utc)
|
521
|
+
mock_cert.public_bytes.return_value = b"-----BEGIN CERTIFICATE-----\nMOCK\n-----END CERTIFICATE-----"
|
522
|
+
|
523
|
+
with patch('mcp_security_framework.core.cert_manager.rsa.generate_private_key') as mock_key:
|
524
|
+
mock_private_key = Mock()
|
525
|
+
mock_private_key.public_key.return_value = Mock()
|
526
|
+
mock_private_key.private_bytes.return_value = b"-----BEGIN PRIVATE KEY-----\nMOCK\n-----END PRIVATE KEY-----"
|
527
|
+
mock_key.return_value = mock_private_key
|
528
|
+
|
529
|
+
with patch('mcp_security_framework.core.cert_manager.x509.CertificateBuilder') as mock_builder:
|
530
|
+
mock_builder_instance = Mock()
|
531
|
+
mock_builder_instance.subject_name.return_value = mock_builder_instance
|
532
|
+
mock_builder_instance.issuer_name.return_value = mock_builder_instance
|
533
|
+
mock_builder_instance.public_key.return_value = mock_builder_instance
|
534
|
+
mock_builder_instance.serial_number.return_value = mock_builder_instance
|
535
|
+
mock_builder_instance.not_valid_before.return_value = mock_builder_instance
|
536
|
+
mock_builder_instance.not_valid_after.return_value = mock_builder_instance
|
537
|
+
mock_builder_instance.add_extension.return_value = mock_builder_instance
|
538
|
+
mock_builder_instance.sign.return_value = mock_cert
|
539
|
+
mock_builder.return_value = mock_builder_instance
|
540
|
+
|
541
|
+
# Mock the datetime compatibility functions
|
542
|
+
with patch('mcp_security_framework.core.cert_manager.get_not_valid_before_utc') as mock_before:
|
543
|
+
with patch('mcp_security_framework.core.cert_manager.get_not_valid_after_utc') as mock_after:
|
544
|
+
mock_before.return_value = datetime.now(timezone.utc)
|
545
|
+
mock_after.return_value = datetime.now(timezone.utc)
|
546
|
+
|
547
|
+
result = cert_manager.create_root_ca(ca_config)
|
548
|
+
|
549
|
+
# Verify unitid is None when not specified
|
550
|
+
assert result.unitid is None
|
File without changes
|
{mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/entry_points.txt
RENAMED
File without changes
|
{mcp_security_framework-1.1.2.dist-info → mcp_security_framework-1.2.1.dist-info}/top_level.txt
RENAMED
File without changes
|