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));
+  }
 }

Reply via email to