mcp-security-framework 0.1.0__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 +96 -0
- mcp_security_framework/cli/__init__.py +18 -0
- mcp_security_framework/cli/cert_cli.py +511 -0
- mcp_security_framework/cli/security_cli.py +791 -0
- mcp_security_framework/constants.py +209 -0
- mcp_security_framework/core/__init__.py +61 -0
- mcp_security_framework/core/auth_manager.py +1011 -0
- mcp_security_framework/core/cert_manager.py +1663 -0
- mcp_security_framework/core/permission_manager.py +735 -0
- mcp_security_framework/core/rate_limiter.py +602 -0
- mcp_security_framework/core/security_manager.py +943 -0
- mcp_security_framework/core/ssl_manager.py +735 -0
- mcp_security_framework/examples/__init__.py +75 -0
- mcp_security_framework/examples/django_example.py +615 -0
- mcp_security_framework/examples/fastapi_example.py +472 -0
- mcp_security_framework/examples/flask_example.py +506 -0
- mcp_security_framework/examples/gateway_example.py +803 -0
- mcp_security_framework/examples/microservice_example.py +690 -0
- mcp_security_framework/examples/standalone_example.py +576 -0
- mcp_security_framework/middleware/__init__.py +250 -0
- mcp_security_framework/middleware/auth_middleware.py +292 -0
- mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
- mcp_security_framework/middleware/fastapi_middleware.py +757 -0
- mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
- mcp_security_framework/middleware/flask_middleware.py +591 -0
- mcp_security_framework/middleware/mtls_middleware.py +439 -0
- mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
- mcp_security_framework/middleware/security_middleware.py +507 -0
- mcp_security_framework/schemas/__init__.py +109 -0
- mcp_security_framework/schemas/config.py +694 -0
- mcp_security_framework/schemas/models.py +709 -0
- mcp_security_framework/schemas/responses.py +686 -0
- mcp_security_framework/tests/__init__.py +0 -0
- mcp_security_framework/utils/__init__.py +121 -0
- mcp_security_framework/utils/cert_utils.py +525 -0
- mcp_security_framework/utils/crypto_utils.py +475 -0
- mcp_security_framework/utils/validation_utils.py +571 -0
- mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
- mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
- mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
- mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
- mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_cli/__init__.py +0 -0
- tests/test_cli/test_cert_cli.py +379 -0
- tests/test_cli/test_security_cli.py +657 -0
- tests/test_core/__init__.py +0 -0
- tests/test_core/test_auth_manager.py +582 -0
- tests/test_core/test_cert_manager.py +795 -0
- tests/test_core/test_permission_manager.py +395 -0
- tests/test_core/test_rate_limiter.py +626 -0
- tests/test_core/test_security_manager.py +841 -0
- tests/test_core/test_ssl_manager.py +532 -0
- tests/test_examples/__init__.py +8 -0
- tests/test_examples/test_fastapi_example.py +264 -0
- tests/test_examples/test_flask_example.py +238 -0
- tests/test_examples/test_standalone_example.py +292 -0
- tests/test_integration/__init__.py +0 -0
- tests/test_integration/test_auth_flow.py +502 -0
- tests/test_integration/test_certificate_flow.py +527 -0
- tests/test_integration/test_fastapi_integration.py +341 -0
- tests/test_integration/test_flask_integration.py +398 -0
- tests/test_integration/test_standalone_integration.py +493 -0
- tests/test_middleware/__init__.py +0 -0
- tests/test_middleware/test_fastapi_middleware.py +523 -0
- tests/test_middleware/test_flask_middleware.py +582 -0
- tests/test_middleware/test_security_middleware.py +493 -0
- tests/test_schemas/__init__.py +0 -0
- tests/test_schemas/test_config.py +811 -0
- tests/test_schemas/test_models.py +879 -0
- tests/test_schemas/test_responses.py +1054 -0
- tests/test_schemas/test_serialization.py +493 -0
- tests/test_utils/__init__.py +0 -0
- tests/test_utils/test_cert_utils.py +510 -0
- tests/test_utils/test_crypto_utils.py +603 -0
- tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,510 @@
|
|
1
|
+
"""
|
2
|
+
Certificate Utilities Test Module
|
3
|
+
|
4
|
+
This module provides comprehensive unit tests for all certificate
|
5
|
+
utilities in the MCP Security Framework.
|
6
|
+
|
7
|
+
Test Classes:
|
8
|
+
TestCertificateParsing: Tests for certificate parsing
|
9
|
+
TestCertificateInfo: Tests for certificate information extraction
|
10
|
+
TestCertificateValidation: Tests for certificate validation
|
11
|
+
TestCertificateExtensions: Tests for certificate extensions
|
12
|
+
|
13
|
+
Author: MCP Security Team
|
14
|
+
Version: 1.0.0
|
15
|
+
License: MIT
|
16
|
+
"""
|
17
|
+
|
18
|
+
from datetime import datetime, timedelta, timezone
|
19
|
+
|
20
|
+
import pytest
|
21
|
+
from cryptography import x509
|
22
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
23
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
24
|
+
from cryptography.x509.oid import NameOID
|
25
|
+
|
26
|
+
from mcp_security_framework.utils.cert_utils import (
|
27
|
+
CertificateError,
|
28
|
+
convert_certificate_format,
|
29
|
+
extract_certificate_info,
|
30
|
+
extract_permissions_from_certificate,
|
31
|
+
extract_public_key,
|
32
|
+
extract_roles_from_certificate,
|
33
|
+
get_certificate_expiry,
|
34
|
+
get_certificate_serial_number,
|
35
|
+
is_certificate_self_signed,
|
36
|
+
parse_certificate,
|
37
|
+
validate_certificate_chain,
|
38
|
+
validate_certificate_format,
|
39
|
+
validate_certificate_purpose,
|
40
|
+
)
|
41
|
+
|
42
|
+
|
43
|
+
class TestCertificateCreation:
|
44
|
+
"""Test suite for creating test certificates."""
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def create_test_certificate():
|
48
|
+
"""Create a test certificate for testing."""
|
49
|
+
# Generate private key
|
50
|
+
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
51
|
+
|
52
|
+
# Create certificate
|
53
|
+
subject = issuer = x509.Name(
|
54
|
+
[
|
55
|
+
x509.NameAttribute(NameOID.COMMON_NAME, "test.example.com"),
|
56
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Organization"),
|
57
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
58
|
+
]
|
59
|
+
)
|
60
|
+
|
61
|
+
cert = (
|
62
|
+
x509.CertificateBuilder()
|
63
|
+
.subject_name(subject)
|
64
|
+
.issuer_name(issuer)
|
65
|
+
.public_key(private_key.public_key())
|
66
|
+
.serial_number(x509.random_serial_number())
|
67
|
+
.not_valid_before(datetime.now(timezone.utc))
|
68
|
+
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=365))
|
69
|
+
.add_extension(
|
70
|
+
x509.SubjectAlternativeName(
|
71
|
+
[
|
72
|
+
x509.DNSName("test.example.com"),
|
73
|
+
x509.DNSName("role=admin,permission=read"),
|
74
|
+
]
|
75
|
+
),
|
76
|
+
critical=False,
|
77
|
+
)
|
78
|
+
.sign(private_key, hashes.SHA256())
|
79
|
+
)
|
80
|
+
|
81
|
+
return cert, private_key
|
82
|
+
|
83
|
+
@staticmethod
|
84
|
+
def create_test_ca_certificate():
|
85
|
+
"""Create a test CA certificate for testing."""
|
86
|
+
# Generate private key
|
87
|
+
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
88
|
+
|
89
|
+
# Create CA certificate
|
90
|
+
subject = issuer = x509.Name(
|
91
|
+
[
|
92
|
+
x509.NameAttribute(NameOID.COMMON_NAME, "Test CA"),
|
93
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test CA Organization"),
|
94
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
95
|
+
]
|
96
|
+
)
|
97
|
+
|
98
|
+
cert = (
|
99
|
+
x509.CertificateBuilder()
|
100
|
+
.subject_name(subject)
|
101
|
+
.issuer_name(issuer)
|
102
|
+
.public_key(private_key.public_key())
|
103
|
+
.serial_number(x509.random_serial_number())
|
104
|
+
.not_valid_before(datetime.now(timezone.utc))
|
105
|
+
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=3650))
|
106
|
+
.add_extension(
|
107
|
+
x509.BasicConstraints(ca=True, path_length=None),
|
108
|
+
critical=True,
|
109
|
+
)
|
110
|
+
.sign(private_key, hashes.SHA256())
|
111
|
+
)
|
112
|
+
|
113
|
+
return cert, private_key
|
114
|
+
|
115
|
+
|
116
|
+
class TestCertificateParsing:
|
117
|
+
"""Test suite for certificate parsing functions."""
|
118
|
+
|
119
|
+
def test_parse_certificate_invalid_data(self):
|
120
|
+
"""Test certificate parsing with invalid data."""
|
121
|
+
with pytest.raises(CertificateError) as exc_info:
|
122
|
+
parse_certificate("invalid_certificate_data")
|
123
|
+
|
124
|
+
assert "Certificate parsing failed" in str(exc_info.value)
|
125
|
+
|
126
|
+
def test_parse_certificate_valid_pem(self):
|
127
|
+
"""Test certificate parsing with valid PEM data."""
|
128
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
129
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
130
|
+
|
131
|
+
parsed_cert = parse_certificate(cert_pem)
|
132
|
+
assert isinstance(parsed_cert, x509.Certificate)
|
133
|
+
assert parsed_cert.subject == cert.subject
|
134
|
+
|
135
|
+
def test_parse_certificate_valid_der(self):
|
136
|
+
"""Test certificate parsing with valid DER data."""
|
137
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
138
|
+
cert_der = cert.public_bytes(serialization.Encoding.DER)
|
139
|
+
|
140
|
+
parsed_cert = parse_certificate(cert_der)
|
141
|
+
assert isinstance(parsed_cert, x509.Certificate)
|
142
|
+
assert parsed_cert.subject == cert.subject
|
143
|
+
|
144
|
+
def test_parse_certificate_with_headers(self):
|
145
|
+
"""Test certificate parsing with PEM headers."""
|
146
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
147
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
148
|
+
|
149
|
+
parsed_cert = parse_certificate(cert_pem)
|
150
|
+
assert isinstance(parsed_cert, x509.Certificate)
|
151
|
+
|
152
|
+
def test_validate_certificate_format_invalid(self):
|
153
|
+
"""Test certificate format validation with invalid format."""
|
154
|
+
result = validate_certificate_format("invalid_format")
|
155
|
+
assert result is False
|
156
|
+
|
157
|
+
def test_validate_certificate_format_valid(self):
|
158
|
+
"""Test certificate format validation with valid format."""
|
159
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
160
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
161
|
+
|
162
|
+
result = validate_certificate_format(cert_pem)
|
163
|
+
assert result is True
|
164
|
+
|
165
|
+
def test_parse_certificate_file_not_found(self):
|
166
|
+
"""Test certificate parsing with non-existent file path."""
|
167
|
+
with pytest.raises(CertificateError) as exc_info:
|
168
|
+
parse_certificate("nonexistent_file.crt")
|
169
|
+
|
170
|
+
assert "Certificate parsing failed" in str(exc_info.value)
|
171
|
+
|
172
|
+
def test_parse_certificate_path_object_not_found(self):
|
173
|
+
"""Test certificate parsing with Path object pointing to non-existent file."""
|
174
|
+
from pathlib import Path
|
175
|
+
|
176
|
+
with pytest.raises(CertificateError) as exc_info:
|
177
|
+
parse_certificate(Path("nonexistent_file.crt"))
|
178
|
+
|
179
|
+
assert "Certificate file not found" in str(exc_info.value)
|
180
|
+
|
181
|
+
def test_parse_certificate_string_not_pem_not_file(self):
|
182
|
+
"""Test certificate parsing with string that's neither PEM nor file path."""
|
183
|
+
with pytest.raises(CertificateError) as exc_info:
|
184
|
+
parse_certificate("not_a_pem_string_and_not_a_file")
|
185
|
+
|
186
|
+
assert "Certificate parsing failed" in str(exc_info.value)
|
187
|
+
|
188
|
+
|
189
|
+
class TestCertificateInfo:
|
190
|
+
"""Test suite for certificate information extraction."""
|
191
|
+
|
192
|
+
def test_extract_certificate_info_invalid_cert(self):
|
193
|
+
"""Test certificate info extraction with invalid certificate."""
|
194
|
+
with pytest.raises(CertificateError) as exc_info:
|
195
|
+
extract_certificate_info("invalid_cert")
|
196
|
+
|
197
|
+
assert "Certificate information extraction failed" in str(exc_info.value)
|
198
|
+
|
199
|
+
def test_extract_certificate_info_valid_cert(self):
|
200
|
+
"""Test certificate info extraction with valid certificate."""
|
201
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
202
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
203
|
+
|
204
|
+
info = extract_certificate_info(cert_pem)
|
205
|
+
|
206
|
+
assert "subject" in info
|
207
|
+
assert "issuer" in info
|
208
|
+
assert "serial_number" in info
|
209
|
+
assert "version" in info
|
210
|
+
assert "not_before" in info
|
211
|
+
assert "not_after" in info
|
212
|
+
assert "signature_algorithm" in info
|
213
|
+
assert "public_key_algorithm" in info
|
214
|
+
assert "key_size" in info
|
215
|
+
assert "extensions" in info
|
216
|
+
assert "fingerprint_sha1" in info
|
217
|
+
assert "fingerprint_sha256" in info
|
218
|
+
assert "common_name" in info
|
219
|
+
assert "organization" in info
|
220
|
+
assert "country" in info
|
221
|
+
|
222
|
+
def test_get_certificate_expiry_invalid_cert(self):
|
223
|
+
"""Test certificate expiry extraction with invalid certificate."""
|
224
|
+
with pytest.raises(CertificateError) as exc_info:
|
225
|
+
get_certificate_expiry("invalid_cert")
|
226
|
+
|
227
|
+
assert "Certificate expiry information extraction failed" in str(exc_info.value)
|
228
|
+
|
229
|
+
def test_get_certificate_expiry_exception(self):
|
230
|
+
"""Test certificate expiry extraction with exception."""
|
231
|
+
with pytest.raises(CertificateError) as exc_info:
|
232
|
+
get_certificate_expiry("invalid_cert_data")
|
233
|
+
|
234
|
+
assert "Certificate expiry information extraction failed" in str(exc_info.value)
|
235
|
+
|
236
|
+
def test_get_certificate_expiry_valid_cert(self):
|
237
|
+
"""Test certificate expiry extraction with valid certificate."""
|
238
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
239
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
240
|
+
|
241
|
+
expiry_info = get_certificate_expiry(cert_pem)
|
242
|
+
|
243
|
+
assert "not_after" in expiry_info
|
244
|
+
assert "not_before" in expiry_info
|
245
|
+
assert "days_until_expiry" in expiry_info
|
246
|
+
assert "is_expired" in expiry_info
|
247
|
+
assert "expires_soon" in expiry_info
|
248
|
+
assert "status" in expiry_info
|
249
|
+
assert "total_seconds_until_expiry" in expiry_info
|
250
|
+
|
251
|
+
def test_get_certificate_serial_number_invalid_cert(self):
|
252
|
+
"""Test serial number extraction with invalid certificate."""
|
253
|
+
with pytest.raises(CertificateError) as exc_info:
|
254
|
+
get_certificate_serial_number("invalid_cert")
|
255
|
+
|
256
|
+
assert "Serial number extraction failed" in str(exc_info.value)
|
257
|
+
|
258
|
+
def test_get_certificate_serial_number_valid_cert(self):
|
259
|
+
"""Test serial number extraction with valid certificate."""
|
260
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
261
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
262
|
+
|
263
|
+
serial_number = get_certificate_serial_number(cert_pem)
|
264
|
+
assert isinstance(serial_number, str)
|
265
|
+
assert len(serial_number) > 0
|
266
|
+
|
267
|
+
def test_extract_public_key_invalid_cert(self):
|
268
|
+
"""Test public key extraction with invalid certificate."""
|
269
|
+
with pytest.raises(CertificateError) as exc_info:
|
270
|
+
extract_public_key("invalid_cert")
|
271
|
+
|
272
|
+
assert "Public key extraction failed" in str(exc_info.value)
|
273
|
+
|
274
|
+
def test_extract_public_key_valid_cert(self):
|
275
|
+
"""Test public key extraction with valid certificate."""
|
276
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
277
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
278
|
+
|
279
|
+
public_key = extract_public_key(cert_pem)
|
280
|
+
assert isinstance(public_key, str)
|
281
|
+
assert "-----BEGIN PUBLIC KEY-----" in public_key
|
282
|
+
|
283
|
+
|
284
|
+
class TestCertificateValidation:
|
285
|
+
"""Test suite for certificate validation functions."""
|
286
|
+
|
287
|
+
def test_validate_certificate_chain_invalid_certs(self):
|
288
|
+
"""Test certificate chain validation with invalid certificates."""
|
289
|
+
result = validate_certificate_chain("invalid_cert", "invalid_ca_cert")
|
290
|
+
assert result is False
|
291
|
+
|
292
|
+
def test_validate_certificate_chain_valid_certs(self):
|
293
|
+
"""Test certificate chain validation with valid certificates."""
|
294
|
+
ca_cert, ca_key = TestCertificateCreation.create_test_ca_certificate()
|
295
|
+
|
296
|
+
# Create a certificate signed by the CA
|
297
|
+
cert, cert_key = TestCertificateCreation.create_test_certificate()
|
298
|
+
|
299
|
+
ca_pem = ca_cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
300
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
301
|
+
|
302
|
+
# This should fail because the cert is not signed by the CA
|
303
|
+
result = validate_certificate_chain(cert_pem, ca_pem)
|
304
|
+
assert result is False
|
305
|
+
|
306
|
+
def test_validate_certificate_chain_exception(self):
|
307
|
+
"""Test certificate chain validation with exception."""
|
308
|
+
# Test with invalid certificate data that will cause an exception
|
309
|
+
result = validate_certificate_chain("invalid_cert", "invalid_ca_cert")
|
310
|
+
assert result is False
|
311
|
+
|
312
|
+
def test_validate_certificate_purpose_invalid_cert(self):
|
313
|
+
"""Test certificate purpose validation with invalid certificate."""
|
314
|
+
with pytest.raises(CertificateError):
|
315
|
+
validate_certificate_purpose("invalid_cert", "server")
|
316
|
+
|
317
|
+
def test_validate_certificate_purpose_valid_cert(self):
|
318
|
+
"""Test certificate purpose validation with valid certificate."""
|
319
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
320
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
321
|
+
|
322
|
+
# Test with server purpose (should return False for this test cert)
|
323
|
+
result = validate_certificate_purpose(cert_pem, "server")
|
324
|
+
assert isinstance(result, bool)
|
325
|
+
|
326
|
+
def test_is_certificate_self_signed_invalid_cert(self):
|
327
|
+
"""Test self-signed check with invalid certificate."""
|
328
|
+
with pytest.raises(CertificateError) as exc_info:
|
329
|
+
is_certificate_self_signed("invalid_cert")
|
330
|
+
|
331
|
+
assert "Self-signed check failed" in str(exc_info.value)
|
332
|
+
|
333
|
+
def test_is_certificate_self_signed_valid_cert(self):
|
334
|
+
"""Test self-signed check with valid certificate."""
|
335
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
336
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
337
|
+
|
338
|
+
result = is_certificate_self_signed(cert_pem)
|
339
|
+
assert isinstance(result, bool)
|
340
|
+
|
341
|
+
def test_get_certificate_serial_number_exception(self):
|
342
|
+
"""Test serial number extraction with exception."""
|
343
|
+
with pytest.raises(CertificateError) as exc_info:
|
344
|
+
get_certificate_serial_number("invalid_cert_data")
|
345
|
+
|
346
|
+
assert "Serial number extraction failed" in str(exc_info.value)
|
347
|
+
|
348
|
+
def test_is_certificate_self_signed_exception(self):
|
349
|
+
"""Test self-signed check with exception."""
|
350
|
+
with pytest.raises(CertificateError) as exc_info:
|
351
|
+
is_certificate_self_signed("invalid_cert_data")
|
352
|
+
|
353
|
+
assert "Self-signed check failed" in str(exc_info.value)
|
354
|
+
|
355
|
+
|
356
|
+
class TestCertificateExtensions:
|
357
|
+
"""Test suite for certificate extension functions."""
|
358
|
+
|
359
|
+
def test_extract_roles_from_certificate_invalid_cert(self):
|
360
|
+
"""Test role extraction with invalid certificate."""
|
361
|
+
with pytest.raises(CertificateError) as exc_info:
|
362
|
+
extract_roles_from_certificate("invalid_cert")
|
363
|
+
|
364
|
+
assert "Role extraction failed" in str(exc_info.value)
|
365
|
+
|
366
|
+
def test_extract_roles_from_certificate_valid_cert(self):
|
367
|
+
"""Test role extraction with valid certificate."""
|
368
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
369
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
370
|
+
|
371
|
+
roles = extract_roles_from_certificate(cert_pem)
|
372
|
+
assert isinstance(roles, list)
|
373
|
+
# Should extract role from SAN extension
|
374
|
+
assert "admin" in roles
|
375
|
+
|
376
|
+
def test_extract_permissions_from_certificate_invalid_cert(self):
|
377
|
+
"""Test permission extraction with invalid certificate."""
|
378
|
+
with pytest.raises(CertificateError) as exc_info:
|
379
|
+
extract_permissions_from_certificate("invalid_cert")
|
380
|
+
|
381
|
+
assert "Permission extraction failed" in str(exc_info.value)
|
382
|
+
|
383
|
+
def test_extract_permissions_from_certificate_valid_cert(self):
|
384
|
+
"""Test permission extraction with valid certificate."""
|
385
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
386
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
387
|
+
|
388
|
+
permissions = extract_permissions_from_certificate(cert_pem)
|
389
|
+
assert isinstance(permissions, list)
|
390
|
+
# Should return empty list since no custom permissions extension exists
|
391
|
+
assert permissions == []
|
392
|
+
|
393
|
+
def test_extract_roles_from_certificate_no_extensions(self):
|
394
|
+
"""Test role extraction with certificate that has no relevant extensions."""
|
395
|
+
# Create certificate without SAN or custom extensions
|
396
|
+
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
397
|
+
|
398
|
+
subject = issuer = x509.Name(
|
399
|
+
[
|
400
|
+
x509.NameAttribute(NameOID.COMMON_NAME, "test.example.com"),
|
401
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Organization"),
|
402
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
403
|
+
]
|
404
|
+
)
|
405
|
+
|
406
|
+
cert = (
|
407
|
+
x509.CertificateBuilder()
|
408
|
+
.subject_name(subject)
|
409
|
+
.issuer_name(issuer)
|
410
|
+
.public_key(private_key.public_key())
|
411
|
+
.serial_number(x509.random_serial_number())
|
412
|
+
.not_valid_before(datetime.now(timezone.utc))
|
413
|
+
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=365))
|
414
|
+
.sign(private_key, hashes.SHA256())
|
415
|
+
)
|
416
|
+
|
417
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
418
|
+
roles = extract_roles_from_certificate(cert_pem)
|
419
|
+
|
420
|
+
assert isinstance(roles, list)
|
421
|
+
assert roles == []
|
422
|
+
|
423
|
+
def test_extract_permissions_from_certificate_no_extensions(self):
|
424
|
+
"""Test permission extraction with certificate that has no permissions extension."""
|
425
|
+
# Create certificate without custom permissions extension
|
426
|
+
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
427
|
+
|
428
|
+
subject = issuer = x509.Name(
|
429
|
+
[
|
430
|
+
x509.NameAttribute(NameOID.COMMON_NAME, "test.example.com"),
|
431
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Organization"),
|
432
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
433
|
+
]
|
434
|
+
)
|
435
|
+
|
436
|
+
cert = (
|
437
|
+
x509.CertificateBuilder()
|
438
|
+
.subject_name(subject)
|
439
|
+
.issuer_name(issuer)
|
440
|
+
.public_key(private_key.public_key())
|
441
|
+
.serial_number(x509.random_serial_number())
|
442
|
+
.not_valid_before(datetime.now(timezone.utc))
|
443
|
+
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=365))
|
444
|
+
.sign(private_key, hashes.SHA256())
|
445
|
+
)
|
446
|
+
|
447
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
448
|
+
permissions = extract_permissions_from_certificate(cert_pem)
|
449
|
+
|
450
|
+
assert isinstance(permissions, list)
|
451
|
+
assert permissions == []
|
452
|
+
|
453
|
+
def test_extract_permissions_from_certificate_non_bytes_data(self):
|
454
|
+
"""Test permission extraction with non-bytes extension data."""
|
455
|
+
# This test would require creating a certificate with a custom extension
|
456
|
+
# that has non-bytes data, which is complex. Instead, we'll test the
|
457
|
+
# exception handling path by passing invalid data.
|
458
|
+
with pytest.raises(CertificateError):
|
459
|
+
extract_permissions_from_certificate("invalid_cert_data")
|
460
|
+
|
461
|
+
|
462
|
+
class TestCertificateFormatConversion:
|
463
|
+
"""Test suite for certificate format conversion."""
|
464
|
+
|
465
|
+
def test_convert_certificate_format_invalid_cert(self):
|
466
|
+
"""Test certificate format conversion with invalid certificate."""
|
467
|
+
with pytest.raises(CertificateError) as exc_info:
|
468
|
+
convert_certificate_format("invalid_cert", "PEM")
|
469
|
+
|
470
|
+
assert "Certificate format conversion failed" in str(exc_info.value)
|
471
|
+
|
472
|
+
def test_convert_certificate_format_unsupported_format(self):
|
473
|
+
"""Test certificate format conversion with unsupported format."""
|
474
|
+
with pytest.raises(CertificateError) as exc_info:
|
475
|
+
convert_certificate_format("invalid_cert", "UNSUPPORTED")
|
476
|
+
|
477
|
+
assert "Certificate format conversion failed" in str(exc_info.value)
|
478
|
+
|
479
|
+
def test_convert_certificate_format_pem_to_pem(self):
|
480
|
+
"""Test certificate format conversion from PEM to PEM."""
|
481
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
482
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
483
|
+
|
484
|
+
result = convert_certificate_format(cert_pem, "PEM")
|
485
|
+
assert isinstance(result, str)
|
486
|
+
assert "-----BEGIN CERTIFICATE-----" in result
|
487
|
+
|
488
|
+
def test_convert_certificate_format_pem_to_der(self):
|
489
|
+
"""Test certificate format conversion from PEM to DER."""
|
490
|
+
cert, _ = TestCertificateCreation.create_test_certificate()
|
491
|
+
cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
492
|
+
|
493
|
+
result = convert_certificate_format(cert_pem, "DER")
|
494
|
+
assert isinstance(result, str)
|
495
|
+
# Should be base64 encoded DER
|
496
|
+
assert len(result) > 0
|
497
|
+
|
498
|
+
def test_convert_certificate_format_exception(self):
|
499
|
+
"""Test certificate format conversion with exception."""
|
500
|
+
with pytest.raises(CertificateError) as exc_info:
|
501
|
+
convert_certificate_format("invalid_cert_data")
|
502
|
+
|
503
|
+
assert "Certificate format conversion failed" in str(exc_info.value)
|
504
|
+
|
505
|
+
def test_extract_public_key_exception(self):
|
506
|
+
"""Test public key extraction with exception."""
|
507
|
+
with pytest.raises(CertificateError) as exc_info:
|
508
|
+
extract_public_key("invalid_cert_data")
|
509
|
+
|
510
|
+
assert "Public key extraction failed" in str(exc_info.value)
|