This is an automated email from the ASF dual-hosted git repository. pvary pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/main by this push: new ede5b5a2fa Azure: Support access token authentication via the new `adls.token` property (#13825) ede5b5a2fa is described below commit ede5b5a2faa074bc8054f0a29efbe53dde3f3f3e Author: Kevin Liu <kevinjq...@users.noreply.github.com> AuthorDate: Mon Aug 18 23:35:41 2025 -0700 Azure: Support access token authentication via the new `adls.token` property (#13825) --- .../org/apache/iceberg/azure/AzureProperties.java | 21 ++++++++++++++++ .../apache/iceberg/azure/TestAzureProperties.java | 29 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/azure/src/main/java/org/apache/iceberg/azure/AzureProperties.java b/azure/src/main/java/org/apache/iceberg/azure/AzureProperties.java index f03c28ab6a..9e2143f943 100644 --- a/azure/src/main/java/org/apache/iceberg/azure/AzureProperties.java +++ b/azure/src/main/java/org/apache/iceberg/azure/AzureProperties.java @@ -18,10 +18,15 @@ */ package org.apache.iceberg.azure; +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.storage.common.StorageSharedKeyCredential; import com.azure.storage.file.datalake.DataLakeFileSystemClientBuilder; import java.io.Serializable; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Collections; import java.util.Map; import java.util.Optional; @@ -33,6 +38,7 @@ import org.apache.iceberg.relocated.com.google.common.collect.Maps; import org.apache.iceberg.rest.RESTUtil; import org.apache.iceberg.util.PropertyUtil; import org.apache.iceberg.util.SerializableMap; +import reactor.core.publisher.Mono; public class AzureProperties implements Serializable { public static final String ADLS_SAS_TOKEN_PREFIX = "adls.sas-token."; @@ -42,6 +48,7 @@ public class AzureProperties implements Serializable { public static final String ADLS_WRITE_BLOCK_SIZE = "adls.write.block-size-bytes"; public static final String ADLS_SHARED_KEY_ACCOUNT_NAME = "adls.auth.shared-key.account.name"; public static final String ADLS_SHARED_KEY_ACCOUNT_KEY = "adls.auth.shared-key.account.key"; + public static final String ADLS_TOKEN = "adls.token"; /** * When set, the {@link VendedAdlsCredentialProvider} will be used to fetch and refresh vended @@ -60,6 +67,7 @@ public class AzureProperties implements Serializable { private Long adlsWriteBlockSize; private String adlsRefreshCredentialsEndpoint; private boolean adlsRefreshCredentialsEnabled; + private String token; private Map<String, String> allProperties; public AzureProperties() {} @@ -92,6 +100,7 @@ public class AzureProperties implements Serializable { properties.get(ADLS_REFRESH_CREDENTIALS_ENDPOINT)); this.adlsRefreshCredentialsEnabled = PropertyUtil.propertyAsBoolean(properties, ADLS_REFRESH_CREDENTIALS_ENABLED, true); + this.token = properties.get(ADLS_TOKEN); this.allProperties = SerializableMap.copyOf(properties); } @@ -131,6 +140,18 @@ public class AzureProperties implements Serializable { } else if (namedKeyCreds != null) { builder.credential( new StorageSharedKeyCredential(namedKeyCreds.getKey(), namedKeyCreds.getValue())); + } else if (token != null && !token.isEmpty()) { + // Use TokenCredential with the provided token + TokenCredential tokenCredential = + new TokenCredential() { + @Override + public Mono<AccessToken> getToken(TokenRequestContext request) { + // Assume the token is valid for 1 hour from the current time + return Mono.just( + new AccessToken(token, OffsetDateTime.now(ZoneOffset.UTC).plusHours(1))); + } + }; + builder.credential(tokenCredential); } else { builder.credential(new DefaultAzureCredentialBuilder().build()); } diff --git a/azure/src/test/java/org/apache/iceberg/azure/TestAzureProperties.java b/azure/src/test/java/org/apache/iceberg/azure/TestAzureProperties.java index 6ac7d0693b..12cde198ea 100644 --- a/azure/src/test/java/org/apache/iceberg/azure/TestAzureProperties.java +++ b/azure/src/test/java/org/apache/iceberg/azure/TestAzureProperties.java @@ -34,8 +34,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import com.azure.core.credential.AccessToken; import com.azure.core.credential.AzureSasCredential; import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; import com.azure.identity.DefaultAzureCredential; import com.azure.storage.common.StorageSharedKeyCredential; import com.azure.storage.file.datalake.DataLakeFileSystemClientBuilder; @@ -48,6 +50,8 @@ import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; public class TestAzureProperties { @@ -206,4 +210,29 @@ public class TestAzureProperties { verify(clientBuilder).credential(any(StorageSharedKeyCredential.class)); verify(clientBuilder, never()).credential(any(TokenCredential.class)); } + + @Test + public void testAdlsToken() { + String testToken = "test-token-value"; + AzureProperties props = new AzureProperties(ImmutableMap.of("adls.token", testToken)); + + DataLakeFileSystemClientBuilder clientBuilder = mock(DataLakeFileSystemClientBuilder.class); + ArgumentCaptor<TokenCredential> credentialCaptor = + ArgumentCaptor.forClass(TokenCredential.class); + + props.applyClientConfiguration("account", clientBuilder); + + verify(clientBuilder).credential(credentialCaptor.capture()); + TokenCredential capturedCredential = credentialCaptor.getValue(); + + // Check that the credential returns the correct token + Mono<AccessToken> tokenMono = capturedCredential.getToken(new TokenRequestContext()); + AccessToken accessToken = tokenMono.block(); + assertThat(accessToken.getToken()).isEqualTo(testToken); + + // Verify other credential types were not used + verify(clientBuilder, never()).sasToken(any()); + verify(clientBuilder, never()).credential(any(StorageSharedKeyCredential.class)); + verify(clientBuilder, never()).credential(any(com.azure.identity.DefaultAzureCredential.class)); + } }