zakariya-s commented on code in PR #2932:
URL: https://github.com/apache/iceberg-rust/pull/2932#discussion_r3735924461
##########
crates/iceberg/src/io/storage/mod.rs:
##########
@@ -139,4 +140,107 @@ pub trait StorageFactory: Debug + Send + Sync {
/// A `Result` containing an `Arc<dyn Storage>` on success, or an error
/// if the storage could not be created.
fn build(&self, config: &StorageConfig) -> Result<Arc<dyn Storage>>;
+
+ /// Build a new Storage instance, optionally supplying a credential
provider
+ /// that the backend can call to obtain and refresh short-lived
credentials.
+ fn build_with_credentials(
+ &self,
+ config: &StorageConfig,
+ credential_provider: Option<Arc<dyn StorageCredentialProvider>>,
+ ) -> Result<Arc<dyn Storage>> {
+ if credential_provider.is_some() {
+ return Err(Error::new(
+ ErrorKind::FeatureUnsupported,
+ "Storage factory does not support refreshable credential
providers",
+ ));
+ }
+
+ self.build(config)
+ }
+}
+
+/// Supplies fresh, backend-specific storage credentials on demand.
+///
+/// A catalog that vends temporary credentials implements this trait so that
+/// storage backends can re-fetch credentials as they approach expiry instead
+/// of failing once the initial token's TTL runs out.
+///
+/// # Caching
+///
+/// [`load_credential`](Self::load_credential) may be called very frequently —
+/// the S3 backend, for example, rebuilds its operator (and therefore its
+/// signer) on every file operation. Implementations must cache internally and
+/// only re-fetch when the current credential is at or near expiry; otherwise
+/// every object-store request would trigger a call back to the catalog.
+#[async_trait]
+pub trait StorageCredentialProvider: Debug + Send + Sync {
+ /// Return whether this provider has refresh configuration for `path`.
+ ///
+ /// Backends use this before replacing their normal credential chain. The
+ /// default is `true` for single-backend providers; multi-backend providers
+ /// should return `false` for schemes they do not configure.
+ fn supports_path(&self, _path: &str) -> bool {
+ true
+ }
+
+ /// Load a fresh credential for the storage location identified by `path`.
+ ///
+ /// `path` is the absolute location being accessed (e.g.
+ /// `s3://bucket/warehouse/db/table/...`). Providers that vend distinct
+ /// credentials per location prefix use it to select the most specific
+ /// match. When the selected credential has a declared
+ /// [`StorageCredential::prefix`], it must cover `path`.
+ async fn load_credential(&self, path: &str) -> Result<StorageCredential>;
+}
+
+/// A vended storage credential together with its scope and expiry.
+#[derive(Clone, Debug)]
+pub struct StorageCredential {
+ /// Storage-location prefix this credential is scoped to. `None`
represents a
+ /// credential without a declared scope, sourced from flat storage
properties.
+ pub prefix: Option<String>,
+ /// The backend-specific credential material.
+ pub kind: StorageCredentialKind,
+ /// When the credential expires, if known. `None` means non-expiring and
+ /// backends treat such a credential as always valid and never refresh it.
+ pub expires_at: Option<SystemTime>,
+}
+
+/// Backend-specific credential material.
+#[derive(Clone, Debug)]
+pub enum StorageCredentialKind {
+ /// Amazon S3 credentials.
+ S3(S3Credential),
+ /// Google Cloud Storage credentials.
+ Gcs(GcsCredential),
+}
+
+/// Temporary Amazon S3 credentials.
+#[derive(Clone)]
+pub struct S3Credential {
+ /// AWS access key ID.
+ pub access_key_id: String,
+ /// AWS secret access key.
+ pub secret_access_key: String,
+ /// AWS session token, set for temporary (STS/vended) credentials.
+ pub session_token: Option<String>,
+}
+
+impl Debug for S3Credential {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("S3Credential").finish_non_exhaustive()
+ }
+}
+
+/// Temporary Google Cloud Storage credentials (an OAuth2 access token).
+#[derive(Clone)]
+pub struct GcsCredential {
+ /// OAuth2 bearer token used to access GCS.
+ pub token: String,
+}
Review Comment:
Sorry this wasn't intentional, they're no longer public
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]