mcp-security-framework 2.1.0__tar.gz → 2.2.1__tar.gz
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-2.1.0 → mcp_security_framework-2.2.1}/PKG-INFO +1 -1
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/__init__.py +1 -1
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/cert_manager.py +109 -18
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/ssl_manager.py +273 -43
- mcp_security_framework-2.2.1/mcp_security_framework/examples/cert_inspection_example.py +391 -0
- mcp_security_framework-2.2.1/mcp_security_framework/examples/crl_retrieval_example.py +385 -0
- mcp_security_framework-2.2.1/mcp_security_framework/examples/protected_keys_example.py +314 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/schemas/config.py +45 -6
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/utils/__init__.py +27 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/utils/cert_utils.py +206 -7
- mcp_security_framework-2.2.1/mcp_security_framework/utils/crl_client.py +408 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework.egg-info/PKG-INFO +1 -1
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework.egg-info/SOURCES.txt +4 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/pyproject.toml +1 -1
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/LICENSE +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/README.md +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/cli/__init__.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/cli/cert_cli.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/cli/security_cli.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/constants.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/__init__.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/adapter_wrapper.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/audit_logger.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/auth_manager.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/permission_manager.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/rate_limiter.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/security_adapter.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/core/security_manager.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/__init__.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/adapter_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/comprehensive_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/fastapi_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/flask_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/gateway_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/microservice_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/examples/standalone_example.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/__init__.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/auth_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/fastapi_auth_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/fastapi_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/flask_auth_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/flask_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/mtls_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/rate_limit_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/middleware/security_middleware.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/schemas/__init__.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/schemas/models.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/schemas/operation_context.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/schemas/responses.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/utils/crypto_utils.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/utils/datetime_compat.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework/utils/validation_utils.py +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework.egg-info/dependency_links.txt +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework.egg-info/entry_points.txt +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework.egg-info/requires.txt +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/mcp_security_framework.egg-info/top_level.txt +0 -0
- {mcp_security_framework-2.1.0 → mcp_security_framework-2.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-security-framework
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Universal security framework for microservices with SSL/TLS, authentication, authorization, and rate limiting. Added extensible security adapter system for custom operation validation, structured audit logging, and operation context support. Comprehensive test coverage with 1104+ tests. Fully backward compatible. Requires cryptography>=42.0.0 for certificate operations.
|
|
5
5
|
Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
|
|
6
6
|
Maintainer-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
|
|
@@ -36,6 +36,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
|
|
36
36
|
from cryptography import x509
|
|
37
37
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
38
38
|
from cryptography.hazmat.primitives.asymmetric import ec, ed448, ed25519, rsa
|
|
39
|
+
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
|
|
39
40
|
from cryptography.x509.oid import ExtendedKeyUsageOID, ExtensionOID, NameOID
|
|
40
41
|
|
|
41
42
|
from mcp_security_framework.schemas.config import (
|
|
@@ -53,6 +54,7 @@ from mcp_security_framework.schemas.models import (
|
|
|
53
54
|
UnknownRoleError,
|
|
54
55
|
)
|
|
55
56
|
from mcp_security_framework.utils.cert_utils import (
|
|
57
|
+
CertificateError,
|
|
56
58
|
extract_permissions_from_certificate,
|
|
57
59
|
extract_roles_from_certificate,
|
|
58
60
|
get_certificate_expiry,
|
|
@@ -61,6 +63,7 @@ from mcp_security_framework.utils.cert_utils import (
|
|
|
61
63
|
is_certificate_revoked,
|
|
62
64
|
is_crl_valid,
|
|
63
65
|
parse_certificate,
|
|
66
|
+
parse_private_key,
|
|
64
67
|
validate_certificate_against_crl,
|
|
65
68
|
validate_certificate_chain,
|
|
66
69
|
)
|
|
@@ -123,6 +126,39 @@ def _get_signing_algorithm(private_key):
|
|
|
123
126
|
return hashes.SHA256()
|
|
124
127
|
|
|
125
128
|
|
|
129
|
+
def _load_private_key(
|
|
130
|
+
key_bytes: bytes,
|
|
131
|
+
password: Optional[bytes],
|
|
132
|
+
source: str,
|
|
133
|
+
) -> PrivateKeyTypes:
|
|
134
|
+
"""
|
|
135
|
+
Load a private key from PEM or DER bytes with optional passphrase.
|
|
136
|
+
|
|
137
|
+
This helper wraps parse_private_key to attach a source label and convert
|
|
138
|
+
from CertificateError (raised by parse_private_key) to CertificateValidationError
|
|
139
|
+
(expected by callers in this module). The classification of whether the key
|
|
140
|
+
is encrypted, whether a password was supplied, and whether the password is
|
|
141
|
+
correct all lives in parse_private_key; this wrapper's only job is to add
|
|
142
|
+
the source context and bridge the exception type.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
key_bytes: The PEM or DER-encoded private key
|
|
146
|
+
password: Optional passphrase as bytes, or None
|
|
147
|
+
source: Human-readable label for error messages (e.g. "CA private key",
|
|
148
|
+
"Parent CA private key"). Included in all error messages.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Loaded private key object
|
|
152
|
+
|
|
153
|
+
Raises:
|
|
154
|
+
CertificateValidationError: When the key cannot be loaded or decrypted
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
return parse_private_key(key_bytes, password=password)
|
|
158
|
+
except CertificateError as exc:
|
|
159
|
+
raise CertificateValidationError(f"{source}: {exc}", error_code=-32003) from exc
|
|
160
|
+
|
|
161
|
+
|
|
126
162
|
def _subject_attribute(certificate, oid) -> str:
|
|
127
163
|
"""
|
|
128
164
|
Return one subject attribute of a certificate, or "" when it carries none.
|
|
@@ -616,8 +652,15 @@ class CertificateManager:
|
|
|
616
652
|
parent_ca_cert = x509.load_pem_x509_certificate(f.read())
|
|
617
653
|
|
|
618
654
|
with open(intermediate_config.parent_ca_key, "rb") as f:
|
|
619
|
-
|
|
620
|
-
|
|
655
|
+
password = (
|
|
656
|
+
intermediate_config.parent_ca_key_password.encode()
|
|
657
|
+
if intermediate_config.parent_ca_key_password
|
|
658
|
+
else None
|
|
659
|
+
)
|
|
660
|
+
parent_ca_key = _load_private_key(
|
|
661
|
+
f.read(),
|
|
662
|
+
password=password,
|
|
663
|
+
source="Parent CA private key",
|
|
621
664
|
)
|
|
622
665
|
|
|
623
666
|
# Generate intermediate CA private key
|
|
@@ -878,7 +921,16 @@ class CertificateManager:
|
|
|
878
921
|
ca_cert = x509.load_pem_x509_certificate(f.read())
|
|
879
922
|
|
|
880
923
|
with open(client_config.ca_key_path, "rb") as f:
|
|
881
|
-
|
|
924
|
+
password = (
|
|
925
|
+
client_config.ca_key_password.encode()
|
|
926
|
+
if client_config.ca_key_password
|
|
927
|
+
else None
|
|
928
|
+
)
|
|
929
|
+
ca_key = _load_private_key(
|
|
930
|
+
f.read(),
|
|
931
|
+
password=password,
|
|
932
|
+
source="CA private key",
|
|
933
|
+
)
|
|
882
934
|
|
|
883
935
|
# Verify that CA private key matches CA certificate
|
|
884
936
|
_verify_ca_key_matches_cert(
|
|
@@ -1104,8 +1156,8 @@ class CertificateManager:
|
|
|
1104
1156
|
)
|
|
1105
1157
|
except (FileNotFoundError, PermissionError, ValueError):
|
|
1106
1158
|
raise
|
|
1107
|
-
except CertificateGenerationError:
|
|
1108
|
-
# Re-raise
|
|
1159
|
+
except (CertificateGenerationError, CertificateValidationError):
|
|
1160
|
+
# Re-raise these without wrapping
|
|
1109
1161
|
raise
|
|
1110
1162
|
except Exception as e:
|
|
1111
1163
|
self.logger.error(
|
|
@@ -1189,7 +1241,16 @@ class CertificateManager:
|
|
|
1189
1241
|
ca_cert = x509.load_pem_x509_certificate(f.read())
|
|
1190
1242
|
|
|
1191
1243
|
with open(server_config.ca_key_path, "rb") as f:
|
|
1192
|
-
|
|
1244
|
+
password = (
|
|
1245
|
+
server_config.ca_key_password.encode()
|
|
1246
|
+
if server_config.ca_key_password
|
|
1247
|
+
else None
|
|
1248
|
+
)
|
|
1249
|
+
ca_key = _load_private_key(
|
|
1250
|
+
f.read(),
|
|
1251
|
+
password=password,
|
|
1252
|
+
source="CA private key",
|
|
1253
|
+
)
|
|
1193
1254
|
|
|
1194
1255
|
# Verify that CA private key matches CA certificate
|
|
1195
1256
|
_verify_ca_key_matches_cert(
|
|
@@ -1406,8 +1467,8 @@ class CertificateManager:
|
|
|
1406
1467
|
)
|
|
1407
1468
|
except (FileNotFoundError, PermissionError, ValueError):
|
|
1408
1469
|
raise
|
|
1409
|
-
except CertificateGenerationError:
|
|
1410
|
-
# Re-raise
|
|
1470
|
+
except (CertificateGenerationError, CertificateValidationError):
|
|
1471
|
+
# Re-raise these without wrapping
|
|
1411
1472
|
raise
|
|
1412
1473
|
except Exception as e:
|
|
1413
1474
|
self.logger.error(
|
|
@@ -1493,7 +1554,16 @@ class CertificateManager:
|
|
|
1493
1554
|
ca_cert = x509.load_pem_x509_certificate(f.read())
|
|
1494
1555
|
|
|
1495
1556
|
with open(ca_key_file, "rb") as f:
|
|
1496
|
-
|
|
1557
|
+
password = (
|
|
1558
|
+
self.config.ca_key_password.encode()
|
|
1559
|
+
if self.config.ca_key_password
|
|
1560
|
+
else None
|
|
1561
|
+
)
|
|
1562
|
+
ca_key = _load_private_key(
|
|
1563
|
+
f.read(),
|
|
1564
|
+
password=password,
|
|
1565
|
+
source="CA private key",
|
|
1566
|
+
)
|
|
1497
1567
|
|
|
1498
1568
|
# Create new certificate with extended validity
|
|
1499
1569
|
builder = x509.CertificateBuilder()
|
|
@@ -1651,7 +1721,16 @@ class CertificateManager:
|
|
|
1651
1721
|
ca_cert = x509.load_pem_x509_certificate(f.read())
|
|
1652
1722
|
|
|
1653
1723
|
with open(ca_key_file, "rb") as f:
|
|
1654
|
-
|
|
1724
|
+
password = (
|
|
1725
|
+
self.config.ca_key_password.encode()
|
|
1726
|
+
if self.config.ca_key_password
|
|
1727
|
+
else None
|
|
1728
|
+
)
|
|
1729
|
+
ca_key = _load_private_key(
|
|
1730
|
+
f.read(),
|
|
1731
|
+
password=password,
|
|
1732
|
+
source="CA private key",
|
|
1733
|
+
)
|
|
1655
1734
|
|
|
1656
1735
|
# Create CRL builder
|
|
1657
1736
|
builder = x509.CertificateRevocationListBuilder()
|
|
@@ -2165,8 +2244,15 @@ class CertificateManager:
|
|
|
2165
2244
|
# Load CA private key
|
|
2166
2245
|
with open(ca_key_path, "rb") as f:
|
|
2167
2246
|
ca_key_data = f.read()
|
|
2168
|
-
|
|
2169
|
-
|
|
2247
|
+
password = (
|
|
2248
|
+
self.config.ca_key_password.encode()
|
|
2249
|
+
if self.config.ca_key_password
|
|
2250
|
+
else None
|
|
2251
|
+
)
|
|
2252
|
+
ca_private_key = _load_private_key(
|
|
2253
|
+
ca_key_data,
|
|
2254
|
+
password=password,
|
|
2255
|
+
source="CA private key",
|
|
2170
2256
|
)
|
|
2171
2257
|
|
|
2172
2258
|
# Verify that CA private key matches CA certificate
|
|
@@ -2249,8 +2335,8 @@ class CertificateManager:
|
|
|
2249
2335
|
|
|
2250
2336
|
except (FileNotFoundError, PermissionError, ValueError):
|
|
2251
2337
|
raise
|
|
2252
|
-
except CertificateGenerationError:
|
|
2253
|
-
# Re-raise
|
|
2338
|
+
except (CertificateGenerationError, CertificateValidationError):
|
|
2339
|
+
# Re-raise these without wrapping
|
|
2254
2340
|
raise
|
|
2255
2341
|
except Exception as e:
|
|
2256
2342
|
self.logger.error(
|
|
@@ -2495,11 +2581,16 @@ class CertificateManager:
|
|
|
2495
2581
|
return key_data.decode("utf-8")
|
|
2496
2582
|
elif format.lower() == "der":
|
|
2497
2583
|
# Convert PEM to DER
|
|
2498
|
-
|
|
2499
|
-
|
|
2584
|
+
password = (
|
|
2585
|
+
self.config.ca_key_password.encode()
|
|
2586
|
+
if self.config.ca_key_password
|
|
2587
|
+
else None
|
|
2588
|
+
)
|
|
2589
|
+
key = _load_private_key(
|
|
2590
|
+
key_data,
|
|
2591
|
+
password=password,
|
|
2592
|
+
source="Private key",
|
|
2500
2593
|
)
|
|
2501
|
-
|
|
2502
|
-
key = load_pem_private_key(key_data, password=None)
|
|
2503
2594
|
return key.private_bytes(
|
|
2504
2595
|
encoding=serialization.Encoding.DER,
|
|
2505
2596
|
format=serialization.PrivateFormat.PKCS8,
|