provide-foundation 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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.
- provide/foundation/__init__.py +36 -10
- provide/foundation/archive/__init__.py +1 -1
- provide/foundation/archive/base.py +15 -14
- provide/foundation/archive/bzip2.py +40 -40
- provide/foundation/archive/gzip.py +42 -42
- provide/foundation/archive/operations.py +93 -96
- provide/foundation/archive/tar.py +33 -31
- provide/foundation/archive/zip.py +52 -50
- provide/foundation/asynctools/__init__.py +20 -0
- provide/foundation/asynctools/core.py +126 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +15 -9
- provide/foundation/cli/commands/logs/__init__.py +3 -3
- provide/foundation/cli/commands/logs/generate.py +2 -2
- provide/foundation/cli/commands/logs/query.py +4 -4
- provide/foundation/cli/commands/logs/send.py +3 -3
- provide/foundation/cli/commands/logs/tail.py +3 -3
- provide/foundation/cli/decorators.py +11 -11
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -40
- provide/foundation/cli/utils.py +21 -18
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +477 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +6 -20
- provide/foundation/config/loader.py +10 -4
- provide/foundation/config/sync.py +8 -6
- provide/foundation/config/types.py +5 -5
- provide/foundation/config/validators.py +4 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +36 -14
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +88 -110
- provide/foundation/crypto/certificates/__init__.py +9 -5
- provide/foundation/crypto/certificates/base.py +2 -2
- provide/foundation/crypto/certificates/certificate.py +48 -19
- provide/foundation/crypto/certificates/factory.py +26 -18
- provide/foundation/crypto/certificates/generator.py +24 -23
- provide/foundation/crypto/certificates/loader.py +24 -16
- provide/foundation/crypto/certificates/operations.py +17 -10
- provide/foundation/crypto/certificates/trust.py +21 -21
- provide/foundation/env/__init__.py +28 -0
- provide/foundation/env/core.py +218 -0
- provide/foundation/errors/__init__.py +3 -3
- provide/foundation/errors/decorators.py +0 -234
- provide/foundation/errors/types.py +0 -98
- provide/foundation/eventsets/display.py +13 -14
- provide/foundation/eventsets/registry.py +61 -31
- provide/foundation/eventsets/resolver.py +50 -46
- provide/foundation/eventsets/sets/das.py +8 -8
- provide/foundation/eventsets/sets/database.py +14 -14
- provide/foundation/eventsets/sets/http.py +21 -21
- provide/foundation/eventsets/sets/llm.py +16 -16
- provide/foundation/eventsets/sets/task_queue.py +13 -13
- provide/foundation/eventsets/types.py +7 -7
- provide/foundation/file/directory.py +14 -23
- provide/foundation/file/lock.py +4 -3
- provide/foundation/hub/components.py +75 -389
- provide/foundation/hub/config.py +157 -0
- provide/foundation/hub/discovery.py +63 -0
- provide/foundation/hub/handlers.py +89 -0
- provide/foundation/hub/lifecycle.py +195 -0
- provide/foundation/hub/manager.py +7 -4
- provide/foundation/hub/processors.py +49 -0
- provide/foundation/integrations/__init__.py +11 -0
- provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
- provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/client.py +14 -14
- provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
- provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
- provide/foundation/logger/__init__.py +0 -1
- provide/foundation/logger/config/base.py +1 -1
- provide/foundation/logger/config/logging.py +69 -299
- provide/foundation/logger/config/telemetry.py +39 -121
- provide/foundation/logger/factories.py +2 -2
- provide/foundation/logger/processors/main.py +12 -10
- provide/foundation/logger/ratelimit/limiters.py +4 -4
- provide/foundation/logger/ratelimit/processor.py +1 -1
- provide/foundation/logger/setup/coordinator.py +39 -25
- provide/foundation/logger/setup/processors.py +3 -3
- provide/foundation/logger/setup/testing.py +14 -0
- provide/foundation/logger/trace.py +5 -5
- provide/foundation/metrics/__init__.py +1 -1
- provide/foundation/metrics/otel.py +3 -1
- provide/foundation/observability/__init__.py +3 -3
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +48 -0
- provide/foundation/process/lifecycle.py +69 -46
- provide/foundation/resilience/__init__.py +36 -0
- provide/foundation/resilience/circuit.py +166 -0
- provide/foundation/resilience/decorators.py +236 -0
- provide/foundation/resilience/fallback.py +208 -0
- provide/foundation/resilience/retry.py +327 -0
- provide/foundation/serialization/__init__.py +16 -0
- provide/foundation/serialization/core.py +70 -0
- provide/foundation/streams/config.py +78 -0
- provide/foundation/streams/console.py +4 -5
- provide/foundation/streams/core.py +5 -2
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +29 -9
- provide/foundation/testing/archive/__init__.py +7 -7
- provide/foundation/testing/archive/fixtures.py +58 -54
- provide/foundation/testing/cli.py +30 -20
- provide/foundation/testing/common/__init__.py +13 -15
- provide/foundation/testing/common/fixtures.py +27 -57
- provide/foundation/testing/file/__init__.py +15 -15
- provide/foundation/testing/file/content_fixtures.py +289 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +42 -516
- provide/foundation/testing/file/special_fixtures.py +145 -0
- provide/foundation/testing/logger.py +89 -8
- provide/foundation/testing/mocking/__init__.py +21 -21
- provide/foundation/testing/mocking/fixtures.py +80 -67
- provide/foundation/testing/process/__init__.py +23 -23
- provide/foundation/testing/process/async_fixtures.py +414 -0
- provide/foundation/testing/process/fixtures.py +48 -571
- provide/foundation/testing/process/subprocess_fixtures.py +210 -0
- provide/foundation/testing/threading/__init__.py +17 -17
- provide/foundation/testing/threading/basic_fixtures.py +105 -0
- provide/foundation/testing/threading/data_fixtures.py +101 -0
- provide/foundation/testing/threading/execution_fixtures.py +278 -0
- provide/foundation/testing/threading/fixtures.py +32 -502
- provide/foundation/testing/threading/sync_fixtures.py +100 -0
- provide/foundation/testing/time/__init__.py +11 -11
- provide/foundation/testing/time/fixtures.py +95 -83
- provide/foundation/testing/transport/__init__.py +9 -9
- provide/foundation/testing/transport/fixtures.py +54 -54
- provide/foundation/time/__init__.py +18 -0
- provide/foundation/time/core.py +63 -0
- provide/foundation/tools/__init__.py +2 -2
- provide/foundation/tools/base.py +68 -67
- provide/foundation/tools/cache.py +69 -74
- provide/foundation/tools/downloader.py +68 -62
- provide/foundation/tools/installer.py +51 -57
- provide/foundation/tools/registry.py +38 -45
- provide/foundation/tools/resolver.py +70 -68
- provide/foundation/tools/verifier.py +39 -50
- provide/foundation/tracer/spans.py +2 -14
- provide/foundation/transport/__init__.py +26 -33
- provide/foundation/transport/base.py +32 -30
- provide/foundation/transport/client.py +44 -49
- provide/foundation/transport/config.py +36 -107
- provide/foundation/transport/errors.py +13 -27
- provide/foundation/transport/http.py +69 -55
- provide/foundation/transport/middleware.py +113 -114
- provide/foundation/transport/registry.py +29 -27
- provide/foundation/transport/types.py +6 -6
- provide/foundation/utils/deps.py +17 -14
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
- provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
- provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ try:
|
|
11
11
|
from cryptography.hazmat.primitives import serialization
|
12
12
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
13
13
|
from cryptography.x509 import Certificate as X509Certificate
|
14
|
-
|
14
|
+
|
15
15
|
_HAS_CRYPTO = True
|
16
16
|
except ImportError:
|
17
17
|
x509 = None
|
@@ -22,11 +22,17 @@ except ImportError:
|
|
22
22
|
_HAS_CRYPTO = False
|
23
23
|
|
24
24
|
from provide.foundation import logger
|
25
|
-
from provide.foundation.crypto.certificates.base import
|
25
|
+
from provide.foundation.crypto.certificates.base import (
|
26
|
+
CertificateBase,
|
27
|
+
CertificateError,
|
28
|
+
PublicKey,
|
29
|
+
)
|
26
30
|
from provide.foundation.crypto.certificates.generator import generate_certificate
|
27
31
|
from provide.foundation.crypto.certificates.loader import load_certificate_from_pem
|
28
32
|
from provide.foundation.crypto.certificates.operations import create_x509_certificate
|
29
|
-
from provide.foundation.crypto.certificates.trust import
|
33
|
+
from provide.foundation.crypto.certificates.trust import (
|
34
|
+
verify_trust as verify_trust_impl,
|
35
|
+
)
|
30
36
|
from provide.foundation.crypto.constants import (
|
31
37
|
DEFAULT_CERTIFICATE_CURVE,
|
32
38
|
DEFAULT_CERTIFICATE_KEY_TYPE,
|
@@ -83,13 +89,10 @@ class Certificate:
|
|
83
89
|
else:
|
84
90
|
# Load existing certificate
|
85
91
|
if not self.cert_pem_or_uri:
|
86
|
-
raise CertificateError(
|
87
|
-
|
88
|
-
)
|
89
|
-
|
92
|
+
raise CertificateError("cert_pem_or_uri required when not generating")
|
93
|
+
|
90
94
|
base, x509_cert, private_key, cert_pem, key_pem = load_certificate_from_pem(
|
91
|
-
self.cert_pem_or_uri,
|
92
|
-
self.key_pem_or_uri
|
95
|
+
self.cert_pem_or_uri, self.key_pem_or_uri
|
93
96
|
)
|
94
97
|
self._base = base
|
95
98
|
self._cert = x509_cert
|
@@ -174,9 +177,14 @@ class Certificate:
|
|
174
177
|
) -> "Certificate":
|
175
178
|
"""Creates a new self-signed CA certificate."""
|
176
179
|
from provide.foundation.crypto.certificates.factory import create_ca_certificate
|
180
|
+
|
177
181
|
return create_ca_certificate(
|
178
|
-
common_name,
|
179
|
-
|
182
|
+
common_name,
|
183
|
+
organization_name,
|
184
|
+
validity_days,
|
185
|
+
key_type,
|
186
|
+
key_size,
|
187
|
+
ecdsa_curve,
|
180
188
|
)
|
181
189
|
|
182
190
|
@classmethod
|
@@ -193,10 +201,20 @@ class Certificate:
|
|
193
201
|
is_client_cert: bool = False,
|
194
202
|
) -> "Certificate":
|
195
203
|
"""Creates a new certificate signed by the provided CA certificate."""
|
196
|
-
from provide.foundation.crypto.certificates.factory import
|
204
|
+
from provide.foundation.crypto.certificates.factory import (
|
205
|
+
create_signed_certificate,
|
206
|
+
)
|
207
|
+
|
197
208
|
return create_signed_certificate(
|
198
|
-
ca_certificate,
|
199
|
-
|
209
|
+
ca_certificate,
|
210
|
+
common_name,
|
211
|
+
organization_name,
|
212
|
+
validity_days,
|
213
|
+
alt_names,
|
214
|
+
key_type,
|
215
|
+
key_size,
|
216
|
+
ecdsa_curve,
|
217
|
+
is_client_cert,
|
200
218
|
)
|
201
219
|
|
202
220
|
@classmethod
|
@@ -211,10 +229,18 @@ class Certificate:
|
|
211
229
|
ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
|
212
230
|
) -> "Certificate":
|
213
231
|
"""Creates a new self-signed end-entity certificate suitable for a server."""
|
214
|
-
from provide.foundation.crypto.certificates.factory import
|
232
|
+
from provide.foundation.crypto.certificates.factory import (
|
233
|
+
create_self_signed_server_cert,
|
234
|
+
)
|
235
|
+
|
215
236
|
return create_self_signed_server_cert(
|
216
|
-
common_name,
|
217
|
-
|
237
|
+
common_name,
|
238
|
+
organization_name,
|
239
|
+
validity_days,
|
240
|
+
alt_names,
|
241
|
+
key_type,
|
242
|
+
key_size,
|
243
|
+
ecdsa_curve,
|
218
244
|
)
|
219
245
|
|
220
246
|
def verify_trust(self, other_cert: Self) -> bool:
|
@@ -243,7 +269,10 @@ class Certificate:
|
|
243
269
|
self, signed_cert: "Certificate", signing_cert: "Certificate"
|
244
270
|
) -> bool:
|
245
271
|
"""Internal helper: Validates signature and issuer/subject match."""
|
246
|
-
from provide.foundation.crypto.certificates.trust import
|
272
|
+
from provide.foundation.crypto.certificates.trust import (
|
273
|
+
validate_signature_wrapper,
|
274
|
+
)
|
275
|
+
|
247
276
|
return validate_signature_wrapper(signed_cert, signing_cert)
|
248
277
|
|
249
278
|
def __eq__(self, other: object) -> bool:
|
@@ -287,4 +316,4 @@ class Certificate:
|
|
287
316
|
|
288
317
|
|
289
318
|
# Type alias for backwards compatibility
|
290
|
-
KeyPair = rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None
|
319
|
+
KeyPair = rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None
|
@@ -1,9 +1,14 @@
|
|
1
1
|
"""Certificate factory methods."""
|
2
2
|
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from provide.foundation.crypto.certificates.certificate import Certificate
|
7
|
+
|
3
8
|
try:
|
4
9
|
from cryptography import x509
|
5
10
|
from cryptography.hazmat.primitives import serialization
|
6
|
-
|
11
|
+
|
7
12
|
_HAS_CRYPTO = True
|
8
13
|
except ImportError:
|
9
14
|
x509 = None
|
@@ -11,7 +16,10 @@ except ImportError:
|
|
11
16
|
_HAS_CRYPTO = False
|
12
17
|
|
13
18
|
from provide.foundation import logger
|
14
|
-
from provide.foundation.crypto.certificates.base import
|
19
|
+
from provide.foundation.crypto.certificates.base import (
|
20
|
+
CertificateError,
|
21
|
+
_require_crypto,
|
22
|
+
)
|
15
23
|
from provide.foundation.crypto.certificates.operations import create_x509_certificate
|
16
24
|
from provide.foundation.crypto.constants import (
|
17
25
|
DEFAULT_CERTIFICATE_CURVE,
|
@@ -32,7 +40,7 @@ def create_ca_certificate(
|
|
32
40
|
"""Creates a new self-signed CA certificate."""
|
33
41
|
# Import here to avoid circular dependency
|
34
42
|
from provide.foundation.crypto.certificates.certificate import Certificate
|
35
|
-
|
43
|
+
|
36
44
|
logger.info(
|
37
45
|
f"📜🔑🏭 Creating new CA certificate: CN={common_name}, Org={organization_name}"
|
38
46
|
)
|
@@ -76,7 +84,7 @@ def create_signed_certificate(
|
|
76
84
|
"""Creates a new certificate signed by the provided CA certificate."""
|
77
85
|
# Import here to avoid circular dependency
|
78
86
|
from provide.foundation.crypto.certificates.certificate import Certificate
|
79
|
-
|
87
|
+
|
80
88
|
logger.info(
|
81
89
|
f"📜🔑🏭 Creating new certificate signed by CA '{ca_certificate.subject}': "
|
82
90
|
f"CN={common_name}, Org={organization_name}, ClientCert={is_client_cert}"
|
@@ -91,7 +99,7 @@ def create_signed_certificate(
|
|
91
99
|
f"📜🔑⚠️ Signing certificate (Subject: {ca_certificate.subject}) "
|
92
100
|
"is not marked as a CA. This might lead to validation issues."
|
93
101
|
)
|
94
|
-
|
102
|
+
|
95
103
|
new_cert_obj = Certificate(
|
96
104
|
generate_keypair=True,
|
97
105
|
common_name=common_name,
|
@@ -102,7 +110,7 @@ def create_signed_certificate(
|
|
102
110
|
key_size=key_size,
|
103
111
|
ecdsa_curve=ecdsa_curve,
|
104
112
|
)
|
105
|
-
|
113
|
+
|
106
114
|
signed_x509_cert = create_x509_certificate(
|
107
115
|
base=new_cert_obj._base,
|
108
116
|
private_key=new_cert_obj._private_key,
|
@@ -112,12 +120,12 @@ def create_signed_certificate(
|
|
112
120
|
is_ca=False,
|
113
121
|
is_client_cert=is_client_cert,
|
114
122
|
)
|
115
|
-
|
123
|
+
|
116
124
|
new_cert_obj._cert = signed_x509_cert
|
117
125
|
new_cert_obj.cert = signed_x509_cert.public_bytes(
|
118
126
|
serialization.Encoding.PEM
|
119
127
|
).decode("utf-8")
|
120
|
-
|
128
|
+
|
121
129
|
logger.info(
|
122
130
|
f"📜🔑✅ Successfully created and signed certificate for "
|
123
131
|
f"CN={common_name} by CA='{ca_certificate.subject}'"
|
@@ -137,12 +145,12 @@ def create_self_signed_server_cert(
|
|
137
145
|
"""Creates a new self-signed end-entity certificate suitable for a server."""
|
138
146
|
# Import here to avoid circular dependency
|
139
147
|
from provide.foundation.crypto.certificates.certificate import Certificate
|
140
|
-
|
148
|
+
|
141
149
|
logger.info(
|
142
150
|
f"📜🔑🏭 Creating new self-signed SERVER certificate: "
|
143
151
|
f"CN={common_name}, Org={organization_name}"
|
144
152
|
)
|
145
|
-
|
153
|
+
|
146
154
|
cert_obj = Certificate(
|
147
155
|
generate_keypair=True,
|
148
156
|
common_name=common_name,
|
@@ -153,12 +161,12 @@ def create_self_signed_server_cert(
|
|
153
161
|
key_size=key_size,
|
154
162
|
ecdsa_curve=ecdsa_curve,
|
155
163
|
)
|
156
|
-
|
164
|
+
|
157
165
|
if not cert_obj._private_key:
|
158
166
|
raise CertificateError(
|
159
167
|
"Private key not generated for self-signed server certificate"
|
160
168
|
)
|
161
|
-
|
169
|
+
|
162
170
|
actual_x509_cert = create_x509_certificate(
|
163
171
|
base=cert_obj._base,
|
164
172
|
private_key=cert_obj._private_key,
|
@@ -166,12 +174,12 @@ def create_self_signed_server_cert(
|
|
166
174
|
is_ca=False,
|
167
175
|
is_client_cert=False,
|
168
176
|
)
|
169
|
-
|
177
|
+
|
170
178
|
cert_obj._cert = actual_x509_cert
|
171
|
-
cert_obj.cert = actual_x509_cert.public_bytes(
|
172
|
-
|
173
|
-
)
|
174
|
-
|
179
|
+
cert_obj.cert = actual_x509_cert.public_bytes(serialization.Encoding.PEM).decode(
|
180
|
+
"utf-8"
|
181
|
+
)
|
182
|
+
|
175
183
|
logger.info(
|
176
184
|
f"📜🔑✅ Successfully created self-signed SERVER certificate for CN={common_name}"
|
177
185
|
)
|
@@ -210,4 +218,4 @@ def create_ca(
|
|
210
218
|
organization_name=organization,
|
211
219
|
validity_days=validity_days,
|
212
220
|
key_type=key_type,
|
213
|
-
)
|
221
|
+
)
|
@@ -7,7 +7,7 @@ try:
|
|
7
7
|
from cryptography import x509
|
8
8
|
from cryptography.hazmat.primitives import serialization
|
9
9
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
10
|
-
|
10
|
+
|
11
11
|
_HAS_CRYPTO = True
|
12
12
|
except ImportError:
|
13
13
|
x509 = None
|
@@ -28,7 +28,6 @@ from provide.foundation.crypto.certificates.operations import create_x509_certif
|
|
28
28
|
from provide.foundation.crypto.constants import (
|
29
29
|
DEFAULT_CERTIFICATE_CURVE,
|
30
30
|
DEFAULT_CERTIFICATE_KEY_TYPE,
|
31
|
-
DEFAULT_CERTIFICATE_VALIDITY_DAYS,
|
32
31
|
DEFAULT_RSA_KEY_SIZE,
|
33
32
|
)
|
34
33
|
|
@@ -43,20 +42,26 @@ def generate_certificate(
|
|
43
42
|
alt_names: list[str] | None = None,
|
44
43
|
is_ca: bool = False,
|
45
44
|
is_client_cert: bool = False,
|
46
|
-
) -> tuple[
|
45
|
+
) -> tuple[
|
46
|
+
CertificateBase,
|
47
|
+
"x509.Certificate",
|
48
|
+
"rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey",
|
49
|
+
str,
|
50
|
+
str,
|
51
|
+
]:
|
47
52
|
"""
|
48
53
|
Generate a new certificate with a keypair.
|
49
|
-
|
54
|
+
|
50
55
|
Returns:
|
51
56
|
Tuple of (CertificateBase, X509Certificate, private_key, cert_pem, key_pem)
|
52
57
|
"""
|
53
58
|
try:
|
54
59
|
logger.debug("📜🔑🚀 Generating new keypair")
|
55
|
-
|
60
|
+
|
56
61
|
now = datetime.now(UTC)
|
57
62
|
not_valid_before = now - timedelta(days=1)
|
58
63
|
not_valid_after = now + timedelta(days=validity_days)
|
59
|
-
|
64
|
+
|
60
65
|
# Parse key type
|
61
66
|
normalized_key_type_str = key_type.lower()
|
62
67
|
match normalized_key_type_str:
|
@@ -69,21 +74,19 @@ def generate_certificate(
|
|
69
74
|
f"Unsupported key_type string: '{key_type}'. "
|
70
75
|
"Must be 'rsa' or 'ecdsa'."
|
71
76
|
)
|
72
|
-
|
77
|
+
|
73
78
|
# Configure key parameters
|
74
79
|
gen_curve: CurveType | None = None
|
75
80
|
gen_key_size = None
|
76
|
-
|
81
|
+
|
77
82
|
if gen_key_type == KeyType.ECDSA:
|
78
83
|
try:
|
79
84
|
gen_curve = CurveType[ecdsa_curve.upper()]
|
80
85
|
except KeyError as e_curve:
|
81
|
-
raise ValueError(
|
82
|
-
f"Unsupported ECDSA curve: {ecdsa_curve}"
|
83
|
-
) from e_curve
|
86
|
+
raise ValueError(f"Unsupported ECDSA curve: {ecdsa_curve}") from e_curve
|
84
87
|
else: # RSA
|
85
88
|
gen_key_size = key_size
|
86
|
-
|
89
|
+
|
87
90
|
# Build configuration
|
88
91
|
conf: CertificateConfig = {
|
89
92
|
"common_name": common_name,
|
@@ -98,10 +101,10 @@ def generate_certificate(
|
|
98
101
|
if gen_key_size is not None:
|
99
102
|
conf["key_size"] = gen_key_size
|
100
103
|
logger.debug(f"📜🔑🚀 Generation config: {conf}")
|
101
|
-
|
104
|
+
|
102
105
|
# Generate base certificate and private key
|
103
106
|
base, private_key = CertificateBase.create(conf)
|
104
|
-
|
107
|
+
|
105
108
|
# Create X.509 certificate
|
106
109
|
x509_cert = create_x509_certificate(
|
107
110
|
base=base,
|
@@ -110,12 +113,10 @@ def generate_certificate(
|
|
110
113
|
is_ca=is_ca,
|
111
114
|
is_client_cert=is_client_cert,
|
112
115
|
)
|
113
|
-
|
116
|
+
|
114
117
|
if x509_cert is None:
|
115
|
-
raise CertificateError(
|
116
|
-
|
117
|
-
)
|
118
|
-
|
118
|
+
raise CertificateError("Certificate object (_cert) is None after creation")
|
119
|
+
|
119
120
|
# Convert to PEM format
|
120
121
|
cert_pem = x509_cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
|
121
122
|
key_pem = private_key.private_bytes(
|
@@ -123,11 +124,11 @@ def generate_certificate(
|
|
123
124
|
format=serialization.PrivateFormat.PKCS8,
|
124
125
|
encryption_algorithm=serialization.NoEncryption(),
|
125
126
|
).decode("utf-8")
|
126
|
-
|
127
|
+
|
127
128
|
logger.debug("📜🔑✅ Generated cert and key")
|
128
|
-
|
129
|
+
|
129
130
|
return base, x509_cert, private_key, cert_pem, key_pem
|
130
|
-
|
131
|
+
|
131
132
|
except Exception as e:
|
132
133
|
logger.error(
|
133
134
|
f"📜❌ Failed to generate certificate. Error: {type(e).__name__}: {e}",
|
@@ -135,4 +136,4 @@ def generate_certificate(
|
|
135
136
|
)
|
136
137
|
raise CertificateError(
|
137
138
|
f"Failed to initialize certificate. Original error: {type(e).__name__}"
|
138
|
-
) from e
|
139
|
+
) from e
|
@@ -10,7 +10,7 @@ try:
|
|
10
10
|
from cryptography.hazmat.primitives import serialization
|
11
11
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
12
12
|
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
13
|
-
|
13
|
+
|
14
14
|
_HAS_CRYPTO = True
|
15
15
|
except ImportError:
|
16
16
|
x509 = None
|
@@ -21,7 +21,10 @@ except ImportError:
|
|
21
21
|
_HAS_CRYPTO = False
|
22
22
|
|
23
23
|
from provide.foundation import logger
|
24
|
-
from provide.foundation.crypto.certificates.base import
|
24
|
+
from provide.foundation.crypto.certificates.base import (
|
25
|
+
CertificateBase,
|
26
|
+
CertificateError,
|
27
|
+
)
|
25
28
|
|
26
29
|
|
27
30
|
def load_from_uri_or_pem(data: str) -> str:
|
@@ -53,30 +56,35 @@ def load_from_uri_or_pem(data: str) -> str:
|
|
53
56
|
|
54
57
|
|
55
58
|
def load_certificate_from_pem(
|
56
|
-
cert_pem_or_uri: str,
|
57
|
-
|
58
|
-
|
59
|
+
cert_pem_or_uri: str, key_pem_or_uri: str | None = None
|
60
|
+
) -> tuple[
|
61
|
+
CertificateBase,
|
62
|
+
"x509.Certificate",
|
63
|
+
"rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None",
|
64
|
+
str,
|
65
|
+
str | None,
|
66
|
+
]:
|
59
67
|
"""
|
60
68
|
Load a certificate and optionally its private key from PEM data or file URIs.
|
61
|
-
|
69
|
+
|
62
70
|
Returns:
|
63
71
|
Tuple of (CertificateBase, X509Certificate, private_key, cert_pem, key_pem)
|
64
72
|
"""
|
65
73
|
try:
|
66
74
|
logger.debug("📜🔑🚀 Loading certificate from provided data")
|
67
75
|
cert_data = load_from_uri_or_pem(cert_pem_or_uri)
|
68
|
-
|
76
|
+
|
69
77
|
logger.debug("📜🔑🔍 Loading X.509 certificate from PEM data")
|
70
78
|
x509_cert = x509.load_pem_x509_certificate(cert_data.encode("utf-8"))
|
71
79
|
logger.debug("📜🔑✅ X.509 certificate object loaded from PEM")
|
72
|
-
|
80
|
+
|
73
81
|
private_key = None
|
74
82
|
key_data = None
|
75
|
-
|
83
|
+
|
76
84
|
if key_pem_or_uri:
|
77
85
|
logger.debug("📜🔑🚀 Loading private key")
|
78
86
|
key_data = load_from_uri_or_pem(key_pem_or_uri)
|
79
|
-
|
87
|
+
|
80
88
|
loaded_priv_key = load_pem_private_key(
|
81
89
|
key_data.encode("utf-8"), password=None
|
82
90
|
)
|
@@ -90,7 +98,7 @@ def load_certificate_from_pem(
|
|
90
98
|
)
|
91
99
|
private_key = loaded_priv_key
|
92
100
|
logger.debug("📜🔑✅ Private key object loaded and type validated")
|
93
|
-
|
101
|
+
|
94
102
|
# Extract certificate details for CertificateBase
|
95
103
|
loaded_not_valid_before = x509_cert.not_valid_before_utc
|
96
104
|
loaded_not_valid_after = x509_cert.not_valid_after_utc
|
@@ -98,7 +106,7 @@ def load_certificate_from_pem(
|
|
98
106
|
loaded_not_valid_before = loaded_not_valid_before.replace(tzinfo=UTC)
|
99
107
|
if loaded_not_valid_after.tzinfo is None:
|
100
108
|
loaded_not_valid_after = loaded_not_valid_after.replace(tzinfo=UTC)
|
101
|
-
|
109
|
+
|
102
110
|
cert_public_key = x509_cert.public_key()
|
103
111
|
if not isinstance(
|
104
112
|
cert_public_key, rsa.RSAPublicKey | ec.EllipticCurvePublicKey
|
@@ -107,7 +115,7 @@ def load_certificate_from_pem(
|
|
107
115
|
f"Certificate's public key is of unsupported type: {type(cert_public_key)}. "
|
108
116
|
"Expected RSA or ECDSA public key."
|
109
117
|
)
|
110
|
-
|
118
|
+
|
111
119
|
base = CertificateBase(
|
112
120
|
subject=x509_cert.subject,
|
113
121
|
issuer=x509_cert.issuer,
|
@@ -117,9 +125,9 @@ def load_certificate_from_pem(
|
|
117
125
|
serial_number=x509_cert.serial_number,
|
118
126
|
)
|
119
127
|
logger.debug("📜🔑✅ Reconstructed CertificateBase from loaded cert")
|
120
|
-
|
128
|
+
|
121
129
|
return base, x509_cert, private_key, cert_data, key_data
|
122
|
-
|
130
|
+
|
123
131
|
except Exception as e:
|
124
132
|
logger.error(
|
125
133
|
f"📜❌ Failed to load certificate. Error: {type(e).__name__}: {e}",
|
@@ -127,4 +135,4 @@ def load_certificate_from_pem(
|
|
127
135
|
)
|
128
136
|
raise CertificateError(
|
129
137
|
f"Failed to initialize certificate. Original error: {type(e).__name__}"
|
130
|
-
) from e
|
138
|
+
) from e
|
@@ -24,13 +24,12 @@ except ImportError:
|
|
24
24
|
_HAS_CRYPTO = False
|
25
25
|
|
26
26
|
from provide.foundation import logger
|
27
|
-
from provide.foundation.crypto.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
from provide.foundation.crypto.certificates.base import (
|
28
|
+
CertificateBase,
|
29
|
+
CertificateError,
|
30
|
+
KeyPair,
|
31
|
+
PublicKey,
|
32
32
|
)
|
33
|
-
from .base import CertificateBase, CertificateError, KeyPair, PublicKey
|
34
33
|
|
35
34
|
|
36
35
|
def create_x509_certificate(
|
@@ -46,8 +45,12 @@ def create_x509_certificate(
|
|
46
45
|
try:
|
47
46
|
logger.debug("📜📝🚀 create_x509_certificate: Building certificate")
|
48
47
|
|
49
|
-
actual_issuer_name =
|
50
|
-
|
48
|
+
actual_issuer_name = (
|
49
|
+
issuer_name_override if issuer_name_override else base.issuer
|
50
|
+
)
|
51
|
+
actual_signing_key = (
|
52
|
+
signing_key_override if signing_key_override else private_key
|
53
|
+
)
|
51
54
|
|
52
55
|
if not actual_signing_key:
|
53
56
|
raise CertificateError(
|
@@ -147,7 +150,11 @@ def create_x509_certificate(
|
|
147
150
|
raise CertificateError("Failed to create X.509 certificate object") from e
|
148
151
|
|
149
152
|
|
150
|
-
def validate_signature(
|
153
|
+
def validate_signature(
|
154
|
+
signed_cert_obj: "X509Certificate",
|
155
|
+
signing_cert_obj: "X509Certificate",
|
156
|
+
signing_public_key: "PublicKey",
|
157
|
+
) -> bool:
|
151
158
|
"""Internal helper: Validates signature and issuer/subject match."""
|
152
159
|
if signed_cert_obj.issuer != signing_cert_obj.subject:
|
153
160
|
logger.debug(
|
@@ -195,4 +202,4 @@ def validate_signature(signed_cert_obj: "X509Certificate", signing_cert_obj: "X5
|
|
195
202
|
|
196
203
|
except Exception as e:
|
197
204
|
logger.debug(f"📜🔍❌ Signature validation failed: {type(e).__name__}: {e}")
|
198
|
-
return False
|
205
|
+
return False
|
@@ -1,9 +1,14 @@
|
|
1
1
|
"""Certificate trust chain and verification utilities."""
|
2
2
|
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from provide.foundation.crypto.certificates.certificate import Certificate
|
7
|
+
|
3
8
|
try:
|
4
9
|
from cryptography import x509
|
5
10
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
6
|
-
|
11
|
+
|
7
12
|
_HAS_CRYPTO = True
|
8
13
|
except ImportError:
|
9
14
|
x509 = None
|
@@ -23,45 +28,41 @@ def verify_trust(
|
|
23
28
|
) -> bool:
|
24
29
|
"""
|
25
30
|
Verifies if the other_cert is trusted based on this certificate's trust chain.
|
26
|
-
|
31
|
+
|
27
32
|
Args:
|
28
33
|
cert: The certificate doing the verification
|
29
34
|
other_cert: The certificate to verify
|
30
35
|
trust_chain: List of trusted certificates
|
31
|
-
|
36
|
+
|
32
37
|
Returns:
|
33
38
|
True if the certificate is trusted, False otherwise
|
34
39
|
"""
|
35
40
|
if other_cert is None:
|
36
41
|
raise CertificateError("Cannot verify trust: other_cert is None")
|
37
|
-
|
42
|
+
|
38
43
|
logger.debug(
|
39
44
|
f"📜🔍🚀 Verifying trust for cert S/N {other_cert.serial_number} "
|
40
45
|
f"against chain of S/N {cert.serial_number}"
|
41
46
|
)
|
42
|
-
|
47
|
+
|
43
48
|
if not other_cert.is_valid:
|
44
|
-
logger.debug(
|
45
|
-
"📜🔍⚠️ Trust verification failed: Other certificate is not valid"
|
46
|
-
)
|
49
|
+
logger.debug("📜🔍⚠️ Trust verification failed: Other certificate is not valid")
|
47
50
|
return False
|
48
51
|
if not other_cert.public_key:
|
49
52
|
raise CertificateError(
|
50
53
|
"Cannot verify trust: Other certificate has no public key"
|
51
54
|
)
|
52
|
-
|
55
|
+
|
53
56
|
if cert == other_cert:
|
54
57
|
logger.debug(
|
55
58
|
"📜🔍✅ Trust verified: Certificates are identical (based on subject/serial)"
|
56
59
|
)
|
57
60
|
return True
|
58
|
-
|
61
|
+
|
59
62
|
if other_cert in trust_chain:
|
60
|
-
logger.debug(
|
61
|
-
"📜🔍✅ Trust verified: Other certificate found in trust chain"
|
62
|
-
)
|
63
|
+
logger.debug("📜🔍✅ Trust verified: Other certificate found in trust chain")
|
63
64
|
return True
|
64
|
-
|
65
|
+
|
65
66
|
for trusted_cert in trust_chain:
|
66
67
|
logger.debug(
|
67
68
|
f"📜🔍🔁 Checking signature against trusted cert S/N {trusted_cert.serial_number}"
|
@@ -74,7 +75,7 @@ def verify_trust(
|
|
74
75
|
f"{trusted_cert.serial_number}"
|
75
76
|
)
|
76
77
|
return True
|
77
|
-
|
78
|
+
|
78
79
|
logger.debug(
|
79
80
|
"📜🔍❌ Trust verification failed: Other certificate not identical, "
|
80
81
|
"not in chain, and not signed by any cert in chain"
|
@@ -83,16 +84,15 @@ def verify_trust(
|
|
83
84
|
|
84
85
|
|
85
86
|
def validate_signature_wrapper(
|
86
|
-
signed_cert: "Certificate",
|
87
|
-
signing_cert: "Certificate"
|
87
|
+
signed_cert: "Certificate", signing_cert: "Certificate"
|
88
88
|
) -> bool:
|
89
89
|
"""
|
90
90
|
Internal helper: Validates signature and issuer/subject match.
|
91
|
-
|
91
|
+
|
92
92
|
Args:
|
93
93
|
signed_cert: The certificate that was signed
|
94
94
|
signing_cert: The certificate that did the signing
|
95
|
-
|
95
|
+
|
96
96
|
Returns:
|
97
97
|
True if signature is valid, False otherwise
|
98
98
|
"""
|
@@ -101,7 +101,7 @@ def validate_signature_wrapper(
|
|
101
101
|
"📜🔍❌ Cannot validate signature: Certificate object(s) not initialized"
|
102
102
|
)
|
103
103
|
return False
|
104
|
-
|
104
|
+
|
105
105
|
return validate_signature(
|
106
106
|
signed_cert._cert, signing_cert._cert, signing_cert.public_key
|
107
|
-
)
|
107
|
+
)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
Environment variable access utilities for Foundation.
|
3
|
+
|
4
|
+
Provides consistent environment variable handling with validation,
|
5
|
+
testing support, and integration with Foundation's configuration system.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from provide.foundation.env.core import (
|
9
|
+
get_env,
|
10
|
+
get_env_bool,
|
11
|
+
get_env_float,
|
12
|
+
get_env_int,
|
13
|
+
get_env_list,
|
14
|
+
has_env,
|
15
|
+
set_env,
|
16
|
+
unset_env,
|
17
|
+
)
|
18
|
+
|
19
|
+
__all__ = [
|
20
|
+
"get_env",
|
21
|
+
"get_env_bool",
|
22
|
+
"get_env_float",
|
23
|
+
"get_env_int",
|
24
|
+
"get_env_list",
|
25
|
+
"has_env",
|
26
|
+
"set_env",
|
27
|
+
"unset_env",
|
28
|
+
]
|