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.
@@ -1,15 +1,15 @@
1
- mcp_security_framework/__init__.py,sha256=TM8y71Navd_6woEab2cO07MefRsL0tRBItEYfVO7DS8,3172
1
+ mcp_security_framework/__init__.py,sha256=wRlT28hRsnSZCUVNGtU-KggRk66KzSQl4agKOxE5ZCA,3172
2
2
  mcp_security_framework/constants.py,sha256=k7NMSrgc83Cci8aoilybQxdC7jir7J-mVFE_EpqVrDk,5307
3
3
  mcp_security_framework/cli/__init__.py,sha256=plpWdiWMp2dcLvUuGwXynRg5CDjz8YKnNTBn7lcta08,369
4
- mcp_security_framework/cli/cert_cli.py,sha256=ME8RgTBrjNvOAMPVnXGLr3cbTpOZ2NN4Ei1SouGRPQs,18297
4
+ mcp_security_framework/cli/cert_cli.py,sha256=LdZ3SYKM3e3dP5LsVR5Y0OENtlG0ENu64aHefHjuiN8,23818
5
5
  mcp_security_framework/cli/security_cli.py,sha256=Thine_Zzfesz7j29y2k_XZFYUK5YSrhCc6w2FilgEiE,28486
6
6
  mcp_security_framework/core/__init__.py,sha256=LiX8_M5qWiTXccJFjSLxup9emhklp-poq57SvznsKEg,1729
7
- mcp_security_framework/core/auth_manager.py,sha256=vCa6YBlz7P_vmif-KGWrCCUE54bHO5F3jN-bDJF2o9Y,38909
8
- mcp_security_framework/core/cert_manager.py,sha256=s625nyMsmrglT7QaqRZtX4oAJVKla5zu0sptHmXJnh4,78750
7
+ mcp_security_framework/core/auth_manager.py,sha256=GqGAW83Qg1_z2HJ0-FEVTmlli_DBSOPOap2jJMEU1_k,39882
8
+ mcp_security_framework/core/cert_manager.py,sha256=RJgZxElPgMV15Lj_N2uCyO7Ofb9z-MclM1hf8wzFwSc,88882
9
9
  mcp_security_framework/core/permission_manager.py,sha256=SADS_oXpwp9MhXHKJMCsvjEq8KWcz7vPYL05Yr-zfio,26478
10
10
  mcp_security_framework/core/rate_limiter.py,sha256=6qjVBxK2YHouSxQuCcbr0PBpRqA5toQss_Ce178RElY,20682
11
11
  mcp_security_framework/core/security_manager.py,sha256=mAF-5znqxin-MSSgXISB7t1kTkqHltEqGzzmlLAhRGs,37766
12
- mcp_security_framework/core/ssl_manager.py,sha256=nCDrukaELONQs_uAfb30g69HDxRp2u4k0Bpq8eJH6Zo,27718
12
+ mcp_security_framework/core/ssl_manager.py,sha256=SXuN5PMTAnMNz04CEKzHbxRKjzF-VqvS-QCFhV-wFeo,29133
13
13
  mcp_security_framework/examples/__init__.py,sha256=nfYPVvIQ5wHuDkQvyCYDd1VjCsZMw9HnvjeUORqKyuQ,1915
14
14
  mcp_security_framework/examples/comprehensive_example.py,sha256=6CXkqLFjWIQ2rRMYPnDrBdBuWFKbeu2gd2GI_SFVjds,35421
15
15
  mcp_security_framework/examples/django_example.py,sha256=IHk-aHsah-cEHjvsngUx91lup1aRC8W9XHzK6jfOMdA,24628
@@ -25,16 +25,16 @@ mcp_security_framework/middleware/fastapi_auth_middleware.py,sha256=LWVEn90I1XpV
25
25
  mcp_security_framework/middleware/fastapi_middleware.py,sha256=Ye0qJsEMwgeUqVJpqXgbjJJbbf-ZU-6SysMQPmQba-s,26658
26
26
  mcp_security_framework/middleware/flask_auth_middleware.py,sha256=ubBlKO0ponOV_KuxkUK4xGcSoslXTaikrdsIZQtGeV0,20228
27
27
  mcp_security_framework/middleware/flask_middleware.py,sha256=Ag0zYDKwlvU78LBQ-7Za14IYOAlEVZaXJPD0Qc4MUvA,20666
28
- mcp_security_framework/middleware/mtls_middleware.py,sha256=iCOX3PVro49GMlTSUtYQFaWLrqtqoWPgI5DEa6Cnst4,13881
28
+ mcp_security_framework/middleware/mtls_middleware.py,sha256=WSyWIk1fCN96hkofODKj_kzLG0d7A0NmDnTr3HHZ5xw,14213
29
29
  mcp_security_framework/middleware/rate_limit_middleware.py,sha256=deCwwigI0Pt7pBUnk2jDurI9ZyjujWTsexEWWndXm3g,13177
30
30
  mcp_security_framework/middleware/security_middleware.py,sha256=PQ251Fr2UrYVPgGfhXq6QJyqK2tRk0WCIg9_FBvfVkg,16844
31
31
  mcp_security_framework/schemas/__init__.py,sha256=lefkbRlbj2ICfasSj51MQ04o3z1YycnbnknSJCFfXbU,2590
32
- mcp_security_framework/schemas/config.py,sha256=kpJJBupR4ZpQyouZyxiZqm7U7AHfLoGncuRltpo_QEM,25825
33
- mcp_security_framework/schemas/models.py,sha256=n-Ug8O1cpMeA0mIdOd9h1i3kkBZOJsCQMxj3IcNpBFY,26957
32
+ mcp_security_framework/schemas/config.py,sha256=SUUrpOz9ZTIhZG9Fu2Gsz86yxoGbUt00Qk-Qk_GaTus,26819
33
+ mcp_security_framework/schemas/models.py,sha256=Izjy3I55zjMVLsVZpXZ0M4aK3SCks9sC2U1cbxrXYeI,28439
34
34
  mcp_security_framework/schemas/responses.py,sha256=nVXaqF5GTSprXTa_wiUEu38nvSw9WAXtKViAJNbO-Xg,23206
35
35
  mcp_security_framework/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  mcp_security_framework/utils/__init__.py,sha256=wwwdmQYHTSz0Puvs9FD6aIKmWp3NFARe3JPWNH-b_wk,3098
37
- mcp_security_framework/utils/cert_utils.py,sha256=DNzCqFKbFgYvQxxUUJPgeWe1XCnM4DpUBDqQYRYtlOQ,17266
37
+ mcp_security_framework/utils/cert_utils.py,sha256=4AHNrfL0Oqp354aEq5H2_0XLT7v3mNOnTVDV-DLaJyI,27585
38
38
  mcp_security_framework/utils/crypto_utils.py,sha256=OH2V7_C3FjStxFTIXMUPfNXZuWG2-QjgoBrIH4Lv4p0,12392
39
39
  mcp_security_framework/utils/datetime_compat.py,sha256=ool-xs-EevhuYygdzhiAenLAacLuZwGwjPkF43i-9gg,3859
40
40
  mcp_security_framework/utils/validation_utils.py,sha256=e9BX3kw9gdXSmFsc7lmG-qnzSlK0-Ynn7Xs4uKHquF4,16279
@@ -44,7 +44,7 @@ tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
44
  tests/test_cli/test_cert_cli.py,sha256=Rm7z-20VAvnmYKY3sgxS-qVNks1vbniQJSpSxjsx_wo,14677
45
45
  tests/test_cli/test_security_cli.py,sha256=Bpd31IPJSUl_V1Xzy74ZCOvQpwlbj8Da83C46T8Jewg,25569
46
46
  tests/test_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- tests/test_core/test_auth_manager.py,sha256=tyJUe3yBP95SFwbhSr_IloKhEQw0BuEdvjAC1Hnwu4s,22540
47
+ tests/test_core/test_auth_manager.py,sha256=7Z2DLfJLqKtiwX5Q-lR85hN6NxHbE2Q_FT7IsoyKPQk,22568
48
48
  tests/test_core/test_cert_manager.py,sha256=4YMAkRedkAZW3PEZYEbo1PyrzMntUrKfl7arPsHXDCE,36356
49
49
  tests/test_core/test_permission_manager.py,sha256=0XeghWXZqVpKyyRuhuDu1dkLUSwuZaFWkRQxQhkkFVI,14966
50
50
  tests/test_core/test_rate_limiter.py,sha256=YzzlhlxZm-A7YGMiIV8LXDA0zmb_6uRF9GRx9s21Q0U,22544
@@ -73,12 +73,13 @@ tests/test_schemas/test_models.py,sha256=bBeZOPqveuVJuEi_BTVWdVsdj08JXJTEFwvBM4e
73
73
  tests/test_schemas/test_responses.py,sha256=ZSbO7A3ThPBovTXO8PFF-2ONWAjJx2dMOoV2lQIfd8s,40774
74
74
  tests/test_schemas/test_serialization.py,sha256=jCugAyrdD6Mw1U7Kxni9oTukarZmMMl6KUcl6cq_NTk,18599
75
75
  tests/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
- tests/test_utils/test_cert_utils.py,sha256=2k1kUCJy0T2HPBwOcU5USGfEBQZ_rGzumAGkDdywLak,21232
76
+ tests/test_utils/test_cert_utils.py,sha256=yZGHPuJcjgSHFeT7gnMdsw6UYXmlGUiuHkErukOm8II,28238
77
77
  tests/test_utils/test_crypto_utils.py,sha256=yEb4hzG6-irj2DPoXY0DUboJfbeR87ussgTuBpxLGz4,20737
78
78
  tests/test_utils/test_datetime_compat.py,sha256=n8S4X5HN-_ejSNpgymDXRyZkmxhnyxwwjxFPdX23I40,5656
79
+ tests/test_utils/test_unitid_compat.py,sha256=MWh03A4FwzQyZa20PKHEWz4W03YtARwBOd_1JbABznQ,25544
79
80
  tests/test_utils/test_validation_utils.py,sha256=lus_wHJ2WyVnBGQ28S7dSv78uWcCIuLhn5uflJw-uGw,18569
80
- mcp_security_framework-1.1.2.dist-info/METADATA,sha256=xuXJme6tsfv0HEsky6X-Nd-S7sULfScqr9y6aU6w-ZM,11771
81
- mcp_security_framework-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
- mcp_security_framework-1.1.2.dist-info/entry_points.txt,sha256=qBh92fVDmd1m2f3xeW0hTu3Ksg8QfGJyV8UEkdA2itg,142
83
- mcp_security_framework-1.1.2.dist-info/top_level.txt,sha256=ifUiGrTDcD574MXSOoAN2rp2wpUvWlb4jD9LTUgDWCA,29
84
- mcp_security_framework-1.1.2.dist-info/RECORD,,
81
+ mcp_security_framework-1.2.1.dist-info/METADATA,sha256=3HiQhhOc1aqdstjAhfUnT2gRmxg61pYutP_lVkn6cxw,11771
82
+ mcp_security_framework-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
+ mcp_security_framework-1.2.1.dist-info/entry_points.txt,sha256=qBh92fVDmd1m2f3xeW0hTu3Ksg8QfGJyV8UEkdA2itg,142
84
+ mcp_security_framework-1.2.1.dist-info/top_level.txt,sha256=ifUiGrTDcD574MXSOoAN2rp2wpUvWlb4jD9LTUgDWCA,29
85
+ mcp_security_framework-1.2.1.dist-info/RECORD,,
@@ -46,7 +46,7 @@ class TestAuthManager:
46
46
 
47
47
  # Create test configuration
48
48
  self.auth_config = AuthConfig(
49
- api_keys={"test_api_key_123": "test_user"},
49
+ api_keys={"test_user": "test_api_key_123"},
50
50
  jwt_secret="test_jwt_secret_key_32_chars_long",
51
51
  jwt_expiry_hours=1,
52
52
  ca_cert_file=None,
@@ -65,7 +65,7 @@ class TestAuthManager:
65
65
  """Test successful AuthManager initialization."""
66
66
  assert self.auth_manager.config == self.auth_config
67
67
  assert self.auth_manager.permission_manager == self.mock_permission_manager
68
- assert self.auth_manager._api_keys == {"test_user": "test_api_key_123"}
68
+ assert self.auth_manager._api_keys == {"test_api_key_123": "test_user"}
69
69
  assert self.auth_manager._jwt_secret == "test_jwt_secret_key_32_chars_long"
70
70
  assert isinstance(self.auth_manager._token_cache, dict)
71
71
  assert isinstance(self.auth_manager._session_store, dict)
@@ -429,8 +429,8 @@ class TestAuthManager:
429
429
  success = self.auth_manager.add_api_key("new_user", "new_api_key_456789")
430
430
 
431
431
  assert success is True
432
- assert "new_user" in self.auth_manager._api_keys
433
- assert self.auth_manager._api_keys["new_user"] == "new_api_key_456789"
432
+ assert "new_api_key_456789" in self.auth_manager._api_keys
433
+ assert self.auth_manager._api_keys["new_api_key_456789"] == "new_user"
434
434
 
435
435
  def test_add_api_key_invalid_input(self):
436
436
  """Test API key addition with invalid input."""
@@ -456,12 +456,12 @@ class TestAuthManager:
456
456
  """Test successful API key removal."""
457
457
  # Add a key first
458
458
  self.auth_manager.add_api_key("temp_user", "temp_key_123456789")
459
- assert "temp_user" in self.auth_manager._api_keys
459
+ assert "temp_key_123456789" in self.auth_manager._api_keys
460
460
 
461
461
  # Remove the key
462
462
  success = self.auth_manager.remove_api_key("temp_user")
463
463
  assert success is True
464
- assert "temp_user" not in self.auth_manager._api_keys
464
+ assert "temp_key_123456789" not in self.auth_manager._api_keys
465
465
 
466
466
  def test_remove_api_key_not_found(self):
467
467
  """Test API key removal for non-existent user."""
@@ -32,8 +32,13 @@ from mcp_security_framework.utils.cert_utils import (
32
32
  extract_roles_from_certificate,
33
33
  get_certificate_expiry,
34
34
  get_certificate_serial_number,
35
+ get_crl_info,
36
+ is_certificate_revoked,
35
37
  is_certificate_self_signed,
38
+ is_crl_valid,
36
39
  parse_certificate,
40
+ parse_crl,
41
+ validate_certificate_against_crl,
37
42
  validate_certificate_chain,
38
43
  validate_certificate_format,
39
44
  validate_certificate_purpose,
@@ -508,3 +513,166 @@ class TestCertificateFormatConversion:
508
513
  extract_public_key("invalid_cert_data")
509
514
 
510
515
  assert "Public key extraction failed" in str(exc_info.value)
516
+
517
+
518
+ class TestCRLFunctionality:
519
+ """Test suite for CRL (Certificate Revocation List) functionality."""
520
+
521
+ @staticmethod
522
+ def create_test_crl():
523
+ """Create a test CRL for testing."""
524
+ # Generate private key for CRL issuer
525
+ private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
526
+
527
+ # Create issuer name
528
+ issuer = x509.Name([
529
+ x509.NameAttribute(NameOID.COMMON_NAME, "Test CA"),
530
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Organization"),
531
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
532
+ ])
533
+
534
+ # Create CRL
535
+ now = datetime.now(timezone.utc)
536
+ crl = x509.CertificateRevocationListBuilder().issuer_name(
537
+ issuer
538
+ ).last_update(
539
+ now
540
+ ).next_update(
541
+ now + timedelta(days=30)
542
+ ).add_extension(
543
+ x509.CRLNumber(1),
544
+ critical=False,
545
+ ).sign(private_key, hashes.SHA256())
546
+
547
+ return crl, private_key
548
+
549
+ def test_parse_crl_success(self):
550
+ """Test successful CRL parsing."""
551
+ crl, _ = self.create_test_crl()
552
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
553
+
554
+ parsed_crl = parse_crl(crl_pem)
555
+
556
+ assert parsed_crl is not None
557
+ assert isinstance(parsed_crl, x509.CertificateRevocationList)
558
+ assert "Test CA" in str(parsed_crl.issuer)
559
+
560
+ def test_parse_crl_invalid_data(self):
561
+ """Test CRL parsing with invalid data."""
562
+ with pytest.raises(CertificateError) as exc_info:
563
+ parse_crl("invalid_crl_data")
564
+
565
+ assert "CRL parsing failed" in str(exc_info.value)
566
+
567
+ def test_is_certificate_revoked_not_revoked(self):
568
+ """Test certificate revocation check when certificate is not revoked."""
569
+ # Create test certificate and CRL
570
+ cert, _ = TestCertificateCreation.create_test_certificate()
571
+ cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
572
+ crl, _ = self.create_test_crl()
573
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
574
+
575
+ # Certificate should not be revoked since it's not in the CRL
576
+ is_revoked = is_certificate_revoked(cert_pem, crl_pem)
577
+ assert is_revoked is False
578
+
579
+ def test_is_certificate_revoked_invalid_cert(self):
580
+ """Test certificate revocation check with invalid certificate."""
581
+ crl, _ = self.create_test_crl()
582
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
583
+
584
+ with pytest.raises(CertificateError) as exc_info:
585
+ is_certificate_revoked("invalid_cert", crl_pem)
586
+
587
+ assert "Certificate revocation check failed" in str(exc_info.value)
588
+
589
+ def test_validate_certificate_against_crl_not_revoked(self):
590
+ """Test certificate validation against CRL when not revoked."""
591
+ # Create test certificate and CRL
592
+ cert, _ = TestCertificateCreation.create_test_certificate()
593
+ cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
594
+ crl, _ = self.create_test_crl()
595
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
596
+
597
+ result = validate_certificate_against_crl(cert_pem, crl_pem)
598
+
599
+ assert result["is_revoked"] is False
600
+ assert "serial_number" in result
601
+ assert "crl_issuer" in result
602
+ assert "crl_last_update" in result
603
+ assert "crl_next_update" in result
604
+
605
+ def test_validate_certificate_against_crl_invalid_data(self):
606
+ """Test certificate validation against CRL with invalid data."""
607
+ with pytest.raises(CertificateError) as exc_info:
608
+ validate_certificate_against_crl("invalid_cert", "invalid_crl")
609
+
610
+ assert "Certificate CRL validation failed" in str(exc_info.value)
611
+
612
+ def test_is_crl_valid_success(self):
613
+ """Test CRL validation success."""
614
+ crl, _ = self.create_test_crl()
615
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
616
+
617
+ is_valid = is_crl_valid(crl_pem)
618
+ assert is_valid is True
619
+
620
+ def test_is_crl_valid_invalid_data(self):
621
+ """Test CRL validation with invalid data."""
622
+ with pytest.raises(CertificateError) as exc_info:
623
+ is_crl_valid("invalid_crl_data")
624
+
625
+ assert "CRL validation failed" in str(exc_info.value)
626
+
627
+ def test_get_crl_info_success(self):
628
+ """Test CRL information extraction success."""
629
+ crl, _ = self.create_test_crl()
630
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
631
+
632
+ info = get_crl_info(crl_pem)
633
+
634
+ assert "issuer" in info
635
+ assert "last_update" in info
636
+ assert "next_update" in info
637
+ assert "revoked_certificates_count" in info
638
+ assert "status" in info
639
+ assert "version" in info
640
+ assert "signature_algorithm" in info
641
+ assert "signature" in info
642
+ assert info["revoked_certificates_count"] == 0
643
+ assert info["status"] == "valid"
644
+
645
+ def test_get_crl_info_invalid_data(self):
646
+ """Test CRL information extraction with invalid data."""
647
+ with pytest.raises(CertificateError) as exc_info:
648
+ get_crl_info("invalid_crl_data")
649
+
650
+ assert "CRL information extraction failed" in str(exc_info.value)
651
+
652
+ def test_validate_certificate_chain_with_crl(self):
653
+ """Test certificate chain validation with CRL."""
654
+ # Create test certificate and CA certificate
655
+ cert, _ = TestCertificateCreation.create_test_certificate()
656
+ cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
657
+ ca_cert, _ = TestCertificateCreation.create_test_certificate()
658
+ ca_cert_pem = ca_cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
659
+
660
+ # Create test CRL
661
+ crl, _ = self.create_test_crl()
662
+ crl_pem = crl.public_bytes(serialization.Encoding.PEM).decode('utf-8')
663
+
664
+ # Test validation with CRL (should pass since certificate is not revoked)
665
+ is_valid = validate_certificate_chain(cert_pem, ca_cert_pem, crl_pem)
666
+ assert is_valid is True
667
+
668
+ def test_validate_certificate_chain_without_crl(self):
669
+ """Test certificate chain validation without CRL."""
670
+ # Create test certificate and CA certificate
671
+ cert, _ = TestCertificateCreation.create_test_certificate()
672
+ cert_pem = cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
673
+ ca_cert, _ = TestCertificateCreation.create_test_certificate()
674
+ ca_cert_pem = ca_cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
675
+
676
+ # Test validation without CRL
677
+ is_valid = validate_certificate_chain(cert_pem, ca_cert_pem)
678
+ assert is_valid is True