provide-foundation 0.0.0.dev0__py3-none-any.whl → 0.0.0.dev2__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 (161) hide show
  1. provide/foundation/__init__.py +41 -23
  2. provide/foundation/archive/__init__.py +23 -0
  3. provide/foundation/archive/base.py +70 -0
  4. provide/foundation/archive/bzip2.py +157 -0
  5. provide/foundation/archive/gzip.py +159 -0
  6. provide/foundation/archive/operations.py +334 -0
  7. provide/foundation/archive/tar.py +164 -0
  8. provide/foundation/archive/zip.py +203 -0
  9. provide/foundation/cli/__init__.py +2 -2
  10. provide/foundation/cli/commands/deps.py +13 -7
  11. provide/foundation/cli/commands/logs/__init__.py +1 -1
  12. provide/foundation/cli/commands/logs/query.py +1 -1
  13. provide/foundation/cli/commands/logs/send.py +1 -1
  14. provide/foundation/cli/commands/logs/tail.py +1 -1
  15. provide/foundation/cli/decorators.py +11 -10
  16. provide/foundation/cli/main.py +1 -1
  17. provide/foundation/cli/testing.py +2 -35
  18. provide/foundation/cli/utils.py +21 -17
  19. provide/foundation/config/__init__.py +35 -2
  20. provide/foundation/config/base.py +2 -2
  21. provide/foundation/config/converters.py +479 -0
  22. provide/foundation/config/defaults.py +67 -0
  23. provide/foundation/config/env.py +4 -19
  24. provide/foundation/config/loader.py +9 -3
  25. provide/foundation/config/sync.py +19 -4
  26. provide/foundation/console/input.py +5 -5
  27. provide/foundation/console/output.py +35 -13
  28. provide/foundation/context/__init__.py +8 -4
  29. provide/foundation/context/core.py +85 -109
  30. provide/foundation/core.py +1 -2
  31. provide/foundation/crypto/__init__.py +2 -0
  32. provide/foundation/crypto/certificates/__init__.py +34 -0
  33. provide/foundation/crypto/certificates/base.py +173 -0
  34. provide/foundation/crypto/certificates/certificate.py +290 -0
  35. provide/foundation/crypto/certificates/factory.py +213 -0
  36. provide/foundation/crypto/certificates/generator.py +138 -0
  37. provide/foundation/crypto/certificates/loader.py +130 -0
  38. provide/foundation/crypto/certificates/operations.py +198 -0
  39. provide/foundation/crypto/certificates/trust.py +107 -0
  40. provide/foundation/errors/__init__.py +2 -3
  41. provide/foundation/errors/decorators.py +0 -231
  42. provide/foundation/errors/types.py +0 -97
  43. provide/foundation/eventsets/__init__.py +0 -0
  44. provide/foundation/eventsets/display.py +84 -0
  45. provide/foundation/eventsets/registry.py +160 -0
  46. provide/foundation/eventsets/resolver.py +192 -0
  47. provide/foundation/eventsets/sets/das.py +128 -0
  48. provide/foundation/eventsets/sets/database.py +125 -0
  49. provide/foundation/eventsets/sets/http.py +153 -0
  50. provide/foundation/eventsets/sets/llm.py +139 -0
  51. provide/foundation/eventsets/sets/task_queue.py +107 -0
  52. provide/foundation/eventsets/types.py +70 -0
  53. provide/foundation/file/directory.py +13 -22
  54. provide/foundation/file/lock.py +3 -1
  55. provide/foundation/hub/components.py +77 -515
  56. provide/foundation/hub/config.py +151 -0
  57. provide/foundation/hub/discovery.py +62 -0
  58. provide/foundation/hub/handlers.py +81 -0
  59. provide/foundation/hub/lifecycle.py +194 -0
  60. provide/foundation/hub/manager.py +4 -4
  61. provide/foundation/hub/processors.py +44 -0
  62. provide/foundation/integrations/__init__.py +11 -0
  63. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  64. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  65. provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
  66. provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
  67. provide/foundation/integrations/openobserve/config.py +37 -0
  68. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
  70. provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
  71. provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
  72. provide/foundation/logger/__init__.py +3 -10
  73. provide/foundation/logger/config/logging.py +68 -298
  74. provide/foundation/logger/config/telemetry.py +41 -121
  75. provide/foundation/logger/core.py +0 -2
  76. provide/foundation/logger/custom_processors.py +1 -0
  77. provide/foundation/logger/factories.py +11 -2
  78. provide/foundation/logger/processors/main.py +20 -84
  79. provide/foundation/logger/setup/__init__.py +5 -1
  80. provide/foundation/logger/setup/coordinator.py +76 -24
  81. provide/foundation/logger/setup/processors.py +2 -9
  82. provide/foundation/logger/trace.py +27 -0
  83. provide/foundation/metrics/otel.py +10 -10
  84. provide/foundation/observability/__init__.py +2 -2
  85. provide/foundation/process/__init__.py +9 -0
  86. provide/foundation/process/exit.py +47 -0
  87. provide/foundation/process/lifecycle.py +115 -59
  88. provide/foundation/resilience/__init__.py +35 -0
  89. provide/foundation/resilience/circuit.py +164 -0
  90. provide/foundation/resilience/decorators.py +220 -0
  91. provide/foundation/resilience/fallback.py +193 -0
  92. provide/foundation/resilience/retry.py +325 -0
  93. provide/foundation/streams/config.py +79 -0
  94. provide/foundation/streams/console.py +7 -8
  95. provide/foundation/streams/core.py +6 -3
  96. provide/foundation/streams/file.py +12 -2
  97. provide/foundation/testing/__init__.py +84 -2
  98. provide/foundation/testing/archive/__init__.py +24 -0
  99. provide/foundation/testing/archive/fixtures.py +217 -0
  100. provide/foundation/testing/cli.py +30 -17
  101. provide/foundation/testing/common/__init__.py +32 -0
  102. provide/foundation/testing/common/fixtures.py +236 -0
  103. provide/foundation/testing/file/__init__.py +40 -0
  104. provide/foundation/testing/file/content_fixtures.py +316 -0
  105. provide/foundation/testing/file/directory_fixtures.py +107 -0
  106. provide/foundation/testing/file/fixtures.py +52 -0
  107. provide/foundation/testing/file/special_fixtures.py +153 -0
  108. provide/foundation/testing/logger.py +117 -11
  109. provide/foundation/testing/mocking/__init__.py +46 -0
  110. provide/foundation/testing/mocking/fixtures.py +331 -0
  111. provide/foundation/testing/process/__init__.py +48 -0
  112. provide/foundation/testing/process/async_fixtures.py +405 -0
  113. provide/foundation/testing/process/fixtures.py +56 -0
  114. provide/foundation/testing/process/subprocess_fixtures.py +209 -0
  115. provide/foundation/testing/threading/__init__.py +38 -0
  116. provide/foundation/testing/threading/basic_fixtures.py +101 -0
  117. provide/foundation/testing/threading/data_fixtures.py +99 -0
  118. provide/foundation/testing/threading/execution_fixtures.py +263 -0
  119. provide/foundation/testing/threading/fixtures.py +54 -0
  120. provide/foundation/testing/threading/sync_fixtures.py +97 -0
  121. provide/foundation/testing/time/__init__.py +32 -0
  122. provide/foundation/testing/time/fixtures.py +409 -0
  123. provide/foundation/testing/transport/__init__.py +30 -0
  124. provide/foundation/testing/transport/fixtures.py +280 -0
  125. provide/foundation/tools/__init__.py +58 -0
  126. provide/foundation/tools/base.py +348 -0
  127. provide/foundation/tools/cache.py +268 -0
  128. provide/foundation/tools/downloader.py +224 -0
  129. provide/foundation/tools/installer.py +254 -0
  130. provide/foundation/tools/registry.py +223 -0
  131. provide/foundation/tools/resolver.py +321 -0
  132. provide/foundation/tools/verifier.py +186 -0
  133. provide/foundation/tracer/otel.py +7 -11
  134. provide/foundation/tracer/spans.py +2 -2
  135. provide/foundation/transport/__init__.py +155 -0
  136. provide/foundation/transport/base.py +171 -0
  137. provide/foundation/transport/client.py +266 -0
  138. provide/foundation/transport/config.py +140 -0
  139. provide/foundation/transport/errors.py +79 -0
  140. provide/foundation/transport/http.py +232 -0
  141. provide/foundation/transport/middleware.py +360 -0
  142. provide/foundation/transport/registry.py +167 -0
  143. provide/foundation/transport/types.py +45 -0
  144. provide/foundation/utils/deps.py +14 -12
  145. provide/foundation/utils/parsing.py +49 -4
  146. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
  147. provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
  148. provide/foundation/cli/commands/logs/generate_old.py +0 -569
  149. provide/foundation/crypto/certificates.py +0 -896
  150. provide/foundation/logger/emoji/__init__.py +0 -44
  151. provide/foundation/logger/emoji/matrix.py +0 -209
  152. provide/foundation/logger/emoji/sets.py +0 -458
  153. provide/foundation/logger/emoji/types.py +0 -56
  154. provide/foundation/logger/setup/emoji_resolver.py +0 -64
  155. provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
  156. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  157. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  158. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
  159. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
  160. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
  161. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,130 @@
1
+ """Certificate loading utilities."""
2
+
3
+ from datetime import UTC
4
+ import os
5
+ from pathlib import Path
6
+ import traceback
7
+
8
+ try:
9
+ from cryptography import x509
10
+ from cryptography.hazmat.primitives import serialization
11
+ from cryptography.hazmat.primitives.asymmetric import ec, rsa
12
+ from cryptography.hazmat.primitives.serialization import load_pem_private_key
13
+
14
+ _HAS_CRYPTO = True
15
+ except ImportError:
16
+ x509 = None
17
+ serialization = None
18
+ ec = None
19
+ rsa = None
20
+ load_pem_private_key = None
21
+ _HAS_CRYPTO = False
22
+
23
+ from provide.foundation import logger
24
+ from provide.foundation.crypto.certificates.base import CertificateBase, CertificateError
25
+
26
+
27
+ def load_from_uri_or_pem(data: str) -> str:
28
+ """Load PEM data either directly from a string or from a file URI."""
29
+ try:
30
+ if data.startswith("file://"):
31
+ path_str = data.removeprefix("file://")
32
+ if os.name == "nt" and path_str.startswith("//"):
33
+ path = Path(path_str)
34
+ else:
35
+ path_str = path_str.lstrip("/")
36
+ if os.name != "nt" and data.startswith("file:///"):
37
+ path_str = "/" + path_str
38
+ path = Path(path_str)
39
+
40
+ logger.debug(f"📜📂🚀 Loading data from file: {path}")
41
+ with path.open("r", encoding="utf-8") as f:
42
+ loaded_data = f.read().strip()
43
+ logger.debug("📜📂✅ Loaded data from file")
44
+ return loaded_data
45
+
46
+ loaded_data = data.strip()
47
+ if not loaded_data.startswith("-----BEGIN"):
48
+ logger.warning("📜📂⚠️ Data doesn't look like PEM format")
49
+ return loaded_data
50
+ except Exception as e:
51
+ logger.error(f"📜📂❌ Failed to load data: {e}", extra={"error": str(e)})
52
+ raise CertificateError(f"Failed to load data: {e}") from e
53
+
54
+
55
+ 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
+ """
60
+ Load a certificate and optionally its private key from PEM data or file URIs.
61
+
62
+ Returns:
63
+ Tuple of (CertificateBase, X509Certificate, private_key, cert_pem, key_pem)
64
+ """
65
+ try:
66
+ logger.debug("📜🔑🚀 Loading certificate from provided data")
67
+ cert_data = load_from_uri_or_pem(cert_pem_or_uri)
68
+
69
+ logger.debug("📜🔑🔍 Loading X.509 certificate from PEM data")
70
+ x509_cert = x509.load_pem_x509_certificate(cert_data.encode("utf-8"))
71
+ logger.debug("📜🔑✅ X.509 certificate object loaded from PEM")
72
+
73
+ private_key = None
74
+ key_data = None
75
+
76
+ if key_pem_or_uri:
77
+ logger.debug("📜🔑🚀 Loading private key")
78
+ key_data = load_from_uri_or_pem(key_pem_or_uri)
79
+
80
+ loaded_priv_key = load_pem_private_key(
81
+ key_data.encode("utf-8"), password=None
82
+ )
83
+ if not isinstance(
84
+ loaded_priv_key,
85
+ rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey,
86
+ ):
87
+ raise CertificateError(
88
+ f"Loaded private key is of unsupported type: {type(loaded_priv_key)}. "
89
+ "Expected RSA or ECDSA private key."
90
+ )
91
+ private_key = loaded_priv_key
92
+ logger.debug("📜🔑✅ Private key object loaded and type validated")
93
+
94
+ # Extract certificate details for CertificateBase
95
+ loaded_not_valid_before = x509_cert.not_valid_before_utc
96
+ loaded_not_valid_after = x509_cert.not_valid_after_utc
97
+ if loaded_not_valid_before.tzinfo is None:
98
+ loaded_not_valid_before = loaded_not_valid_before.replace(tzinfo=UTC)
99
+ if loaded_not_valid_after.tzinfo is None:
100
+ loaded_not_valid_after = loaded_not_valid_after.replace(tzinfo=UTC)
101
+
102
+ cert_public_key = x509_cert.public_key()
103
+ if not isinstance(
104
+ cert_public_key, rsa.RSAPublicKey | ec.EllipticCurvePublicKey
105
+ ):
106
+ raise CertificateError(
107
+ f"Certificate's public key is of unsupported type: {type(cert_public_key)}. "
108
+ "Expected RSA or ECDSA public key."
109
+ )
110
+
111
+ base = CertificateBase(
112
+ subject=x509_cert.subject,
113
+ issuer=x509_cert.issuer,
114
+ public_key=cert_public_key,
115
+ not_valid_before=loaded_not_valid_before,
116
+ not_valid_after=loaded_not_valid_after,
117
+ serial_number=x509_cert.serial_number,
118
+ )
119
+ logger.debug("📜🔑✅ Reconstructed CertificateBase from loaded cert")
120
+
121
+ return base, x509_cert, private_key, cert_data, key_data
122
+
123
+ except Exception as e:
124
+ logger.error(
125
+ f"📜❌ Failed to load certificate. Error: {type(e).__name__}: {e}",
126
+ extra={"error": str(e), "trace": traceback.format_exc()},
127
+ )
128
+ raise CertificateError(
129
+ f"Failed to initialize certificate. Original error: {type(e).__name__}"
130
+ ) from e
@@ -0,0 +1,198 @@
1
+ """Certificate operations: CA creation, signing, and trust verification."""
2
+
3
+ import traceback
4
+ from typing import cast
5
+
6
+ try:
7
+ from cryptography import x509
8
+ from cryptography.hazmat.primitives import hashes, serialization
9
+ from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa
10
+ from cryptography.x509 import Certificate as X509Certificate
11
+ from cryptography.x509.oid import ExtendedKeyUsageOID
12
+
13
+ _HAS_CRYPTO = True
14
+ except ImportError:
15
+ # Stub out cryptography types for type hints
16
+ x509 = None
17
+ hashes = None
18
+ serialization = None
19
+ ec = None
20
+ padding = None
21
+ rsa = None
22
+ X509Certificate = None
23
+ ExtendedKeyUsageOID = None
24
+ _HAS_CRYPTO = False
25
+
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,
32
+ )
33
+ from provide.foundation.crypto.certificates.base import CertificateBase, CertificateError, KeyPair, PublicKey
34
+
35
+
36
+ def create_x509_certificate(
37
+ base: CertificateBase,
38
+ private_key: "KeyPair",
39
+ alt_names: list[str] | None = None,
40
+ issuer_name_override: "x509.Name | None" = None,
41
+ signing_key_override: "KeyPair | None" = None,
42
+ is_ca: bool = False,
43
+ is_client_cert: bool = False,
44
+ ) -> "X509Certificate":
45
+ """Internal helper to build and sign the X.509 certificate object."""
46
+ try:
47
+ logger.debug("📜📝🚀 create_x509_certificate: Building certificate")
48
+
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
51
+
52
+ if not actual_signing_key:
53
+ raise CertificateError(
54
+ "Cannot sign certificate without a signing key (either own or override)"
55
+ )
56
+
57
+ builder = (
58
+ x509.CertificateBuilder()
59
+ .subject_name(base.subject)
60
+ .issuer_name(actual_issuer_name)
61
+ .public_key(base.public_key)
62
+ .serial_number(base.serial_number)
63
+ .not_valid_before(base.not_valid_before)
64
+ .not_valid_after(base.not_valid_after)
65
+ )
66
+
67
+ san_list = [x509.DNSName(name) for name in (alt_names or []) if name]
68
+ if san_list:
69
+ builder = builder.add_extension(
70
+ x509.SubjectAlternativeName(san_list), critical=False
71
+ )
72
+ logger.debug(f"📜📝✅ Added SANs: {alt_names or []}")
73
+
74
+ builder = builder.add_extension(
75
+ x509.BasicConstraints(ca=is_ca, path_length=None),
76
+ critical=True,
77
+ )
78
+
79
+ if is_ca:
80
+ builder = builder.add_extension(
81
+ x509.KeyUsage(
82
+ digital_signature=False,
83
+ key_encipherment=False,
84
+ key_agreement=False,
85
+ content_commitment=False,
86
+ data_encipherment=False,
87
+ key_cert_sign=True,
88
+ crl_sign=True,
89
+ encipher_only=False,
90
+ decipher_only=False,
91
+ ),
92
+ critical=True,
93
+ )
94
+ else:
95
+ builder = builder.add_extension(
96
+ x509.KeyUsage(
97
+ digital_signature=True,
98
+ key_encipherment=(
99
+ True
100
+ if not is_client_cert
101
+ and isinstance(base.public_key, rsa.RSAPublicKey)
102
+ else False
103
+ ),
104
+ key_agreement=(
105
+ True
106
+ if isinstance(base.public_key, ec.EllipticCurvePublicKey)
107
+ else False
108
+ ),
109
+ content_commitment=False,
110
+ data_encipherment=False,
111
+ key_cert_sign=False,
112
+ crl_sign=False,
113
+ encipher_only=False,
114
+ decipher_only=False,
115
+ ),
116
+ critical=True,
117
+ )
118
+ extended_usages = []
119
+ if is_client_cert:
120
+ extended_usages.append(ExtendedKeyUsageOID.CLIENT_AUTH)
121
+ else:
122
+ extended_usages.append(ExtendedKeyUsageOID.SERVER_AUTH)
123
+
124
+ if extended_usages:
125
+ builder = builder.add_extension(
126
+ x509.ExtendedKeyUsage(extended_usages),
127
+ critical=False,
128
+ )
129
+
130
+ logger.debug(
131
+ f"📜📝✅ Added BasicConstraints (is_ca={is_ca}), "
132
+ f"KeyUsage, ExtendedKeyUsage (is_client_cert={is_client_cert})"
133
+ )
134
+
135
+ signed_cert = builder.sign(
136
+ private_key=actual_signing_key,
137
+ algorithm=hashes.SHA256(),
138
+ )
139
+ logger.debug("📜📝✅ Certificate signed successfully")
140
+ return signed_cert
141
+
142
+ except Exception as e:
143
+ logger.error(
144
+ f"📜❌ create_x509_certificate: Failed: {e}",
145
+ extra={"error": str(e), "trace": traceback.format_exc()},
146
+ )
147
+ raise CertificateError("Failed to create X.509 certificate object") from e
148
+
149
+
150
+ def validate_signature(signed_cert_obj: "X509Certificate", signing_cert_obj: "X509Certificate", signing_public_key: "PublicKey") -> bool:
151
+ """Internal helper: Validates signature and issuer/subject match."""
152
+ if signed_cert_obj.issuer != signing_cert_obj.subject:
153
+ logger.debug(
154
+ f"📜🔍❌ Signature validation failed: Issuer/Subject mismatch. "
155
+ f"Signed Issuer='{signed_cert_obj.issuer}', "
156
+ f"Signing Subject='{signing_cert_obj.subject}'"
157
+ )
158
+ return False
159
+
160
+ try:
161
+ if not signing_public_key:
162
+ logger.error(
163
+ "📜🔍❌ Cannot validate signature: Signing certificate has no public key"
164
+ )
165
+ return False
166
+
167
+ signature = signed_cert_obj.signature
168
+ tbs_certificate_bytes = signed_cert_obj.tbs_certificate_bytes
169
+ signature_hash_algorithm = signed_cert_obj.signature_hash_algorithm
170
+
171
+ if not signature_hash_algorithm:
172
+ logger.error("📜🔍❌ Cannot validate signature: Unknown hash algorithm")
173
+ return False
174
+
175
+ if isinstance(signing_public_key, rsa.RSAPublicKey):
176
+ cast(rsa.RSAPublicKey, signing_public_key).verify(
177
+ signature,
178
+ tbs_certificate_bytes,
179
+ padding.PKCS1v15(),
180
+ signature_hash_algorithm,
181
+ )
182
+ elif isinstance(signing_public_key, ec.EllipticCurvePublicKey):
183
+ cast(ec.EllipticCurvePublicKey, signing_public_key).verify(
184
+ signature,
185
+ tbs_certificate_bytes,
186
+ ec.ECDSA(signature_hash_algorithm),
187
+ )
188
+ else:
189
+ logger.error(
190
+ f"📜🔍❌ Unsupported signing public key type: {type(signing_public_key)}"
191
+ )
192
+ return False
193
+
194
+ return True
195
+
196
+ except Exception as e:
197
+ logger.debug(f"📜🔍❌ Signature validation failed: {type(e).__name__}: {e}")
198
+ return False
@@ -0,0 +1,107 @@
1
+ """Certificate trust chain and verification utilities."""
2
+
3
+ try:
4
+ from cryptography import x509
5
+ from cryptography.hazmat.primitives.asymmetric import ec, rsa
6
+
7
+ _HAS_CRYPTO = True
8
+ except ImportError:
9
+ x509 = None
10
+ ec = None
11
+ rsa = None
12
+ _HAS_CRYPTO = False
13
+
14
+ from provide.foundation import logger
15
+ from provide.foundation.crypto.certificates.base import CertificateError
16
+ from provide.foundation.crypto.certificates.operations import validate_signature
17
+
18
+
19
+ def verify_trust(
20
+ cert: "Certificate",
21
+ other_cert: "Certificate",
22
+ trust_chain: list["Certificate"],
23
+ ) -> bool:
24
+ """
25
+ Verifies if the other_cert is trusted based on this certificate's trust chain.
26
+
27
+ Args:
28
+ cert: The certificate doing the verification
29
+ other_cert: The certificate to verify
30
+ trust_chain: List of trusted certificates
31
+
32
+ Returns:
33
+ True if the certificate is trusted, False otherwise
34
+ """
35
+ if other_cert is None:
36
+ raise CertificateError("Cannot verify trust: other_cert is None")
37
+
38
+ logger.debug(
39
+ f"📜🔍🚀 Verifying trust for cert S/N {other_cert.serial_number} "
40
+ f"against chain of S/N {cert.serial_number}"
41
+ )
42
+
43
+ if not other_cert.is_valid:
44
+ logger.debug(
45
+ "📜🔍⚠️ Trust verification failed: Other certificate is not valid"
46
+ )
47
+ return False
48
+ if not other_cert.public_key:
49
+ raise CertificateError(
50
+ "Cannot verify trust: Other certificate has no public key"
51
+ )
52
+
53
+ if cert == other_cert:
54
+ logger.debug(
55
+ "📜🔍✅ Trust verified: Certificates are identical (based on subject/serial)"
56
+ )
57
+ return True
58
+
59
+ if other_cert in trust_chain:
60
+ logger.debug(
61
+ "📜🔍✅ Trust verified: Other certificate found in trust chain"
62
+ )
63
+ return True
64
+
65
+ for trusted_cert in trust_chain:
66
+ logger.debug(
67
+ f"📜🔍🔁 Checking signature against trusted cert S/N {trusted_cert.serial_number}"
68
+ )
69
+ if validate_signature_wrapper(
70
+ signed_cert=other_cert, signing_cert=trusted_cert
71
+ ):
72
+ logger.debug(
73
+ f"📜🔍✅ Trust verified: Other cert signed by trusted cert S/N "
74
+ f"{trusted_cert.serial_number}"
75
+ )
76
+ return True
77
+
78
+ logger.debug(
79
+ "📜🔍❌ Trust verification failed: Other certificate not identical, "
80
+ "not in chain, and not signed by any cert in chain"
81
+ )
82
+ return False
83
+
84
+
85
+ def validate_signature_wrapper(
86
+ signed_cert: "Certificate",
87
+ signing_cert: "Certificate"
88
+ ) -> bool:
89
+ """
90
+ Internal helper: Validates signature and issuer/subject match.
91
+
92
+ Args:
93
+ signed_cert: The certificate that was signed
94
+ signing_cert: The certificate that did the signing
95
+
96
+ Returns:
97
+ True if signature is valid, False otherwise
98
+ """
99
+ if not hasattr(signed_cert, "_cert") or not hasattr(signing_cert, "_cert"):
100
+ logger.error(
101
+ "📜🔍❌ Cannot validate signature: Certificate object(s) not initialized"
102
+ )
103
+ return False
104
+
105
+ return validate_signature(
106
+ signed_cert._cert, signing_cert._cert, signing_cert.public_key
107
+ )
@@ -6,6 +6,8 @@ and utilities for robust error handling throughout the application.
6
6
  """
7
7
 
8
8
  from provide.foundation.errors.auth import AuthenticationError, AuthorizationError
9
+ # Re-export from resilience module for compatibility
10
+ from provide.foundation.resilience.decorators import retry as retry_on_error
9
11
  from provide.foundation.errors.base import FoundationError
10
12
  from provide.foundation.errors.config import (
11
13
  ConfigurationError,
@@ -20,7 +22,6 @@ from provide.foundation.errors.context import (
20
22
  )
21
23
  from provide.foundation.errors.decorators import (
22
24
  fallback_on_error,
23
- retry_on_error,
24
25
  suppress_and_log,
25
26
  with_error_handling,
26
27
  )
@@ -50,7 +51,6 @@ from provide.foundation.errors.safe_decorators import log_only_error_context
50
51
  from provide.foundation.errors.types import (
51
52
  ErrorCode,
52
53
  ErrorMetadata,
53
- RetryPolicy,
54
54
  )
55
55
 
56
56
  __all__ = [
@@ -77,7 +77,6 @@ __all__ = [
77
77
  "ProcessError",
78
78
  "ProcessTimeoutError",
79
79
  "ResourceError",
80
- "RetryPolicy",
81
80
  "RuntimeError",
82
81
  "StateError",
83
82
  "TimeoutError",