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.
Files changed (76) hide show
  1. mcp_security_framework/__init__.py +96 -0
  2. mcp_security_framework/cli/__init__.py +18 -0
  3. mcp_security_framework/cli/cert_cli.py +511 -0
  4. mcp_security_framework/cli/security_cli.py +791 -0
  5. mcp_security_framework/constants.py +209 -0
  6. mcp_security_framework/core/__init__.py +61 -0
  7. mcp_security_framework/core/auth_manager.py +1011 -0
  8. mcp_security_framework/core/cert_manager.py +1663 -0
  9. mcp_security_framework/core/permission_manager.py +735 -0
  10. mcp_security_framework/core/rate_limiter.py +602 -0
  11. mcp_security_framework/core/security_manager.py +943 -0
  12. mcp_security_framework/core/ssl_manager.py +735 -0
  13. mcp_security_framework/examples/__init__.py +75 -0
  14. mcp_security_framework/examples/django_example.py +615 -0
  15. mcp_security_framework/examples/fastapi_example.py +472 -0
  16. mcp_security_framework/examples/flask_example.py +506 -0
  17. mcp_security_framework/examples/gateway_example.py +803 -0
  18. mcp_security_framework/examples/microservice_example.py +690 -0
  19. mcp_security_framework/examples/standalone_example.py +576 -0
  20. mcp_security_framework/middleware/__init__.py +250 -0
  21. mcp_security_framework/middleware/auth_middleware.py +292 -0
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
  23. mcp_security_framework/middleware/fastapi_middleware.py +757 -0
  24. mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
  25. mcp_security_framework/middleware/flask_middleware.py +591 -0
  26. mcp_security_framework/middleware/mtls_middleware.py +439 -0
  27. mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
  28. mcp_security_framework/middleware/security_middleware.py +507 -0
  29. mcp_security_framework/schemas/__init__.py +109 -0
  30. mcp_security_framework/schemas/config.py +694 -0
  31. mcp_security_framework/schemas/models.py +709 -0
  32. mcp_security_framework/schemas/responses.py +686 -0
  33. mcp_security_framework/tests/__init__.py +0 -0
  34. mcp_security_framework/utils/__init__.py +121 -0
  35. mcp_security_framework/utils/cert_utils.py +525 -0
  36. mcp_security_framework/utils/crypto_utils.py +475 -0
  37. mcp_security_framework/utils/validation_utils.py +571 -0
  38. mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
  39. mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
  40. mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
  41. mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
  42. mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
  43. tests/__init__.py +0 -0
  44. tests/test_cli/__init__.py +0 -0
  45. tests/test_cli/test_cert_cli.py +379 -0
  46. tests/test_cli/test_security_cli.py +657 -0
  47. tests/test_core/__init__.py +0 -0
  48. tests/test_core/test_auth_manager.py +582 -0
  49. tests/test_core/test_cert_manager.py +795 -0
  50. tests/test_core/test_permission_manager.py +395 -0
  51. tests/test_core/test_rate_limiter.py +626 -0
  52. tests/test_core/test_security_manager.py +841 -0
  53. tests/test_core/test_ssl_manager.py +532 -0
  54. tests/test_examples/__init__.py +8 -0
  55. tests/test_examples/test_fastapi_example.py +264 -0
  56. tests/test_examples/test_flask_example.py +238 -0
  57. tests/test_examples/test_standalone_example.py +292 -0
  58. tests/test_integration/__init__.py +0 -0
  59. tests/test_integration/test_auth_flow.py +502 -0
  60. tests/test_integration/test_certificate_flow.py +527 -0
  61. tests/test_integration/test_fastapi_integration.py +341 -0
  62. tests/test_integration/test_flask_integration.py +398 -0
  63. tests/test_integration/test_standalone_integration.py +493 -0
  64. tests/test_middleware/__init__.py +0 -0
  65. tests/test_middleware/test_fastapi_middleware.py +523 -0
  66. tests/test_middleware/test_flask_middleware.py +582 -0
  67. tests/test_middleware/test_security_middleware.py +493 -0
  68. tests/test_schemas/__init__.py +0 -0
  69. tests/test_schemas/test_config.py +811 -0
  70. tests/test_schemas/test_models.py +879 -0
  71. tests/test_schemas/test_responses.py +1054 -0
  72. tests/test_schemas/test_serialization.py +493 -0
  73. tests/test_utils/__init__.py +0 -0
  74. tests/test_utils/test_cert_utils.py +510 -0
  75. tests/test_utils/test_crypto_utils.py +603 -0
  76. 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)