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.
Files changed (163) hide show
  1. provide/foundation/__init__.py +36 -10
  2. provide/foundation/archive/__init__.py +1 -1
  3. provide/foundation/archive/base.py +15 -14
  4. provide/foundation/archive/bzip2.py +40 -40
  5. provide/foundation/archive/gzip.py +42 -42
  6. provide/foundation/archive/operations.py +93 -96
  7. provide/foundation/archive/tar.py +33 -31
  8. provide/foundation/archive/zip.py +52 -50
  9. provide/foundation/asynctools/__init__.py +20 -0
  10. provide/foundation/asynctools/core.py +126 -0
  11. provide/foundation/cli/__init__.py +2 -2
  12. provide/foundation/cli/commands/deps.py +15 -9
  13. provide/foundation/cli/commands/logs/__init__.py +3 -3
  14. provide/foundation/cli/commands/logs/generate.py +2 -2
  15. provide/foundation/cli/commands/logs/query.py +4 -4
  16. provide/foundation/cli/commands/logs/send.py +3 -3
  17. provide/foundation/cli/commands/logs/tail.py +3 -3
  18. provide/foundation/cli/decorators.py +11 -11
  19. provide/foundation/cli/main.py +1 -1
  20. provide/foundation/cli/testing.py +2 -40
  21. provide/foundation/cli/utils.py +21 -18
  22. provide/foundation/config/__init__.py +35 -2
  23. provide/foundation/config/base.py +2 -2
  24. provide/foundation/config/converters.py +477 -0
  25. provide/foundation/config/defaults.py +67 -0
  26. provide/foundation/config/env.py +6 -20
  27. provide/foundation/config/loader.py +10 -4
  28. provide/foundation/config/sync.py +8 -6
  29. provide/foundation/config/types.py +5 -5
  30. provide/foundation/config/validators.py +4 -4
  31. provide/foundation/console/input.py +5 -5
  32. provide/foundation/console/output.py +36 -14
  33. provide/foundation/context/__init__.py +8 -4
  34. provide/foundation/context/core.py +88 -110
  35. provide/foundation/crypto/certificates/__init__.py +9 -5
  36. provide/foundation/crypto/certificates/base.py +2 -2
  37. provide/foundation/crypto/certificates/certificate.py +48 -19
  38. provide/foundation/crypto/certificates/factory.py +26 -18
  39. provide/foundation/crypto/certificates/generator.py +24 -23
  40. provide/foundation/crypto/certificates/loader.py +24 -16
  41. provide/foundation/crypto/certificates/operations.py +17 -10
  42. provide/foundation/crypto/certificates/trust.py +21 -21
  43. provide/foundation/env/__init__.py +28 -0
  44. provide/foundation/env/core.py +218 -0
  45. provide/foundation/errors/__init__.py +3 -3
  46. provide/foundation/errors/decorators.py +0 -234
  47. provide/foundation/errors/types.py +0 -98
  48. provide/foundation/eventsets/display.py +13 -14
  49. provide/foundation/eventsets/registry.py +61 -31
  50. provide/foundation/eventsets/resolver.py +50 -46
  51. provide/foundation/eventsets/sets/das.py +8 -8
  52. provide/foundation/eventsets/sets/database.py +14 -14
  53. provide/foundation/eventsets/sets/http.py +21 -21
  54. provide/foundation/eventsets/sets/llm.py +16 -16
  55. provide/foundation/eventsets/sets/task_queue.py +13 -13
  56. provide/foundation/eventsets/types.py +7 -7
  57. provide/foundation/file/directory.py +14 -23
  58. provide/foundation/file/lock.py +4 -3
  59. provide/foundation/hub/components.py +75 -389
  60. provide/foundation/hub/config.py +157 -0
  61. provide/foundation/hub/discovery.py +63 -0
  62. provide/foundation/hub/handlers.py +89 -0
  63. provide/foundation/hub/lifecycle.py +195 -0
  64. provide/foundation/hub/manager.py +7 -4
  65. provide/foundation/hub/processors.py +49 -0
  66. provide/foundation/integrations/__init__.py +11 -0
  67. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  68. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/client.py +14 -14
  70. provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
  71. provide/foundation/integrations/openobserve/config.py +37 -0
  72. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  73. provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
  74. provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
  75. provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
  76. provide/foundation/logger/__init__.py +0 -1
  77. provide/foundation/logger/config/base.py +1 -1
  78. provide/foundation/logger/config/logging.py +69 -299
  79. provide/foundation/logger/config/telemetry.py +39 -121
  80. provide/foundation/logger/factories.py +2 -2
  81. provide/foundation/logger/processors/main.py +12 -10
  82. provide/foundation/logger/ratelimit/limiters.py +4 -4
  83. provide/foundation/logger/ratelimit/processor.py +1 -1
  84. provide/foundation/logger/setup/coordinator.py +39 -25
  85. provide/foundation/logger/setup/processors.py +3 -3
  86. provide/foundation/logger/setup/testing.py +14 -0
  87. provide/foundation/logger/trace.py +5 -5
  88. provide/foundation/metrics/__init__.py +1 -1
  89. provide/foundation/metrics/otel.py +3 -1
  90. provide/foundation/observability/__init__.py +3 -3
  91. provide/foundation/process/__init__.py +9 -0
  92. provide/foundation/process/exit.py +48 -0
  93. provide/foundation/process/lifecycle.py +69 -46
  94. provide/foundation/resilience/__init__.py +36 -0
  95. provide/foundation/resilience/circuit.py +166 -0
  96. provide/foundation/resilience/decorators.py +236 -0
  97. provide/foundation/resilience/fallback.py +208 -0
  98. provide/foundation/resilience/retry.py +327 -0
  99. provide/foundation/serialization/__init__.py +16 -0
  100. provide/foundation/serialization/core.py +70 -0
  101. provide/foundation/streams/config.py +78 -0
  102. provide/foundation/streams/console.py +4 -5
  103. provide/foundation/streams/core.py +5 -2
  104. provide/foundation/streams/file.py +12 -2
  105. provide/foundation/testing/__init__.py +29 -9
  106. provide/foundation/testing/archive/__init__.py +7 -7
  107. provide/foundation/testing/archive/fixtures.py +58 -54
  108. provide/foundation/testing/cli.py +30 -20
  109. provide/foundation/testing/common/__init__.py +13 -15
  110. provide/foundation/testing/common/fixtures.py +27 -57
  111. provide/foundation/testing/file/__init__.py +15 -15
  112. provide/foundation/testing/file/content_fixtures.py +289 -0
  113. provide/foundation/testing/file/directory_fixtures.py +107 -0
  114. provide/foundation/testing/file/fixtures.py +42 -516
  115. provide/foundation/testing/file/special_fixtures.py +145 -0
  116. provide/foundation/testing/logger.py +89 -8
  117. provide/foundation/testing/mocking/__init__.py +21 -21
  118. provide/foundation/testing/mocking/fixtures.py +80 -67
  119. provide/foundation/testing/process/__init__.py +23 -23
  120. provide/foundation/testing/process/async_fixtures.py +414 -0
  121. provide/foundation/testing/process/fixtures.py +48 -571
  122. provide/foundation/testing/process/subprocess_fixtures.py +210 -0
  123. provide/foundation/testing/threading/__init__.py +17 -17
  124. provide/foundation/testing/threading/basic_fixtures.py +105 -0
  125. provide/foundation/testing/threading/data_fixtures.py +101 -0
  126. provide/foundation/testing/threading/execution_fixtures.py +278 -0
  127. provide/foundation/testing/threading/fixtures.py +32 -502
  128. provide/foundation/testing/threading/sync_fixtures.py +100 -0
  129. provide/foundation/testing/time/__init__.py +11 -11
  130. provide/foundation/testing/time/fixtures.py +95 -83
  131. provide/foundation/testing/transport/__init__.py +9 -9
  132. provide/foundation/testing/transport/fixtures.py +54 -54
  133. provide/foundation/time/__init__.py +18 -0
  134. provide/foundation/time/core.py +63 -0
  135. provide/foundation/tools/__init__.py +2 -2
  136. provide/foundation/tools/base.py +68 -67
  137. provide/foundation/tools/cache.py +69 -74
  138. provide/foundation/tools/downloader.py +68 -62
  139. provide/foundation/tools/installer.py +51 -57
  140. provide/foundation/tools/registry.py +38 -45
  141. provide/foundation/tools/resolver.py +70 -68
  142. provide/foundation/tools/verifier.py +39 -50
  143. provide/foundation/tracer/spans.py +2 -14
  144. provide/foundation/transport/__init__.py +26 -33
  145. provide/foundation/transport/base.py +32 -30
  146. provide/foundation/transport/client.py +44 -49
  147. provide/foundation/transport/config.py +36 -107
  148. provide/foundation/transport/errors.py +13 -27
  149. provide/foundation/transport/http.py +69 -55
  150. provide/foundation/transport/middleware.py +113 -114
  151. provide/foundation/transport/registry.py +29 -27
  152. provide/foundation/transport/types.py +6 -6
  153. provide/foundation/utils/deps.py +17 -14
  154. provide/foundation/utils/parsing.py +49 -4
  155. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
  156. provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
  157. provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
  158. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  159. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  160. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
  161. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
  162. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
  163. {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 CertificateBase, CertificateError, PublicKey
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 verify_trust as verify_trust_impl
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
- "cert_pem_or_uri required when not generating"
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, organization_name, validity_days,
179
- key_type, key_size, ecdsa_curve
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 create_signed_certificate
204
+ from provide.foundation.crypto.certificates.factory import (
205
+ create_signed_certificate,
206
+ )
207
+
197
208
  return create_signed_certificate(
198
- ca_certificate, common_name, organization_name, validity_days,
199
- alt_names, key_type, key_size, ecdsa_curve, is_client_cert
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 create_self_signed_server_cert
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, organization_name, validity_days,
217
- alt_names, key_type, key_size, ecdsa_curve
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 validate_signature_wrapper
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 CertificateError, _require_crypto
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
- serialization.Encoding.PEM
173
- ).decode("utf-8")
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[CertificateBase, "x509.Certificate", "rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey", str, str]:
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
- "Certificate object (_cert) is None after creation"
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 CertificateBase, CertificateError
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
- key_pem_or_uri: str | None = None
58
- ) -> tuple[CertificateBase, "x509.Certificate", "rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None", str, str | None]:
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.constants import (
28
- DEFAULT_CERTIFICATE_CURVE,
29
- DEFAULT_CERTIFICATE_KEY_TYPE,
30
- DEFAULT_CERTIFICATE_VALIDITY_DAYS,
31
- DEFAULT_RSA_KEY_SIZE,
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 = issuer_name_override if issuer_name_override else base.issuer
50
- actual_signing_key = signing_key_override if signing_key_override else private_key
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(signed_cert_obj: "X509Certificate", signing_cert_obj: "X509Certificate", signing_public_key: "PublicKey") -> bool:
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
+ ]