This is an automated email from the ASF dual-hosted git repository.

mchades pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new 612932ce68 [Cherry-pick to branch-1.3] [#11601] fix(flink-connector): 
Propagate REST auth on catalog-store load path (#11694) (#11724)
612932ce68 is described below

commit 612932ce684b0391ee80e6ed7dc8cab76c7a2b26
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jun 18 09:36:47 2026 +0800

    [Cherry-pick to branch-1.3] [#11601] fix(flink-connector): Propagate REST 
auth on catalog-store load path (#11694) (#11724)
    
    **Cherry-pick Information:**
    - Original commit: 1014f2677c8f10e78f7e8c140103df65c63b197b
    - Target branch: `branch-1.3`
    - Status: ✅ Clean cherry-pick (no conflicts)
    
    Co-authored-by: Yuhui <[email protected]>
---
 .../iceberg/GravitinoIcebergCatalogFactory.java    | 55 ++++++++++++++++------
 .../TestGravitinoIcebergCatalogFactory.java        | 30 +++++++++++-
 .../test/iceberg/FlinkIcebergJdbcCatalogIT.java    | 36 +++-----------
 3 files changed, 77 insertions(+), 44 deletions(-)

diff --git 
a/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/iceberg/GravitinoIcebergCatalogFactory.java
 
b/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/iceberg/GravitinoIcebergCatalogFactory.java
index b39ef2e53c..022ff34613 100644
--- 
a/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/iceberg/GravitinoIcebergCatalogFactory.java
+++ 
b/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/iceberg/GravitinoIcebergCatalogFactory.java
@@ -122,35 +122,62 @@ public class GravitinoIcebergCatalogFactory implements 
BaseCatalogFactory {
     Map<String, String> icebergCatalogOptions = 
Maps.newHashMap(catalogOptions);
     String catalogBackend =
         
catalogOptions.get(IcebergPropertiesConstants.GRAVITINO_ICEBERG_CATALOG_BACKEND);
-    // Only infer `catalog-type` from the backend when neither `catalog-type` 
nor `catalog-impl` is
-    // already set, otherwise an explicitly provided `catalog-impl` would 
conflict with it.
+    // catalogBackend is only present here on the CREATE CATALOG path (raw 
user SQL options). On
+    // the USE CATALOG path (loading a catalog already persisted in Gravitino),
+    // IcebergPropertiesConverter has already renamed catalog-backend -> 
catalog-type during
+    // property conversion, so catalogBackend is null even for a REST/JDBC 
catalog. This
+    // normalizes the CREATE CATALOG case so both paths converge on the same 
catalog-type key.
+    String catalogType = normalizeCatalogType(icebergCatalogOptions, 
catalogBackend);
+    // catalogType (not catalogBackend, which is unreliable as explained 
above) must be used for
+    // every backend check from here on, or it silently no-ops on the USE 
CATALOG path -- this was
+    // the root cause of #11601 (REST auth never propagated when loading a 
persisted catalog).
+    propagateRestAuthIfNeeded(icebergCatalogOptions, catalogType);
+    translateJdbcBackendToCatalogImpl(icebergCatalogOptions, catalogType);
+    // The outer Flink factory is `gravitino-iceberg`, but the nested Iceberg 
factory still expects
+    // `catalog-type=iceberg` when building the native Iceberg catalog 
instance.
+    icebergCatalogOptions.put(CommonCatalogOptions.CATALOG_TYPE.key(), 
"iceberg");
+    return icebergCatalogOptions;
+  }
+
+  // Copies catalog-backend into catalog-type so the CREATE CATALOG path lines 
up with the
+  // USE CATALOG path, where the rename already happened upstream. Skipped 
when catalog-type or
+  // catalog-impl is already set, otherwise an explicitly provided 
catalog-impl would conflict
+  // with it. Returns the effective backend type for downstream gating: the 
normalized catalog-type
+  // when present, or catalog-backend as a fallback (e.g. when catalog-impl 
was already set).
+  private static String normalizeCatalogType(
+      Map<String, String> icebergCatalogOptions, String catalogBackend) {
     if (catalogBackend != null
         && 
!icebergCatalogOptions.containsKey(IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE)
         && 
!icebergCatalogOptions.containsKey(IcebergPropertiesConstants.ICEBERG_CATALOG_IMPL))
 {
       
icebergCatalogOptions.put(IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE, 
catalogBackend);
     }
-    // A REST backend connects directly to the Iceberg REST service, bypassing 
the Gravitino
-    // server's auth proxy, so propagate the Gravitino client's authentication 
to the REST client,
-    // unless the user has already configured REST auth explicitly.
-    if 
(IcebergPropertiesConstants.ICEBERG_CATALOG_BACKEND_REST.equalsIgnoreCase(catalogBackend)
+    return icebergCatalogOptions.getOrDefault(
+        IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE, catalogBackend);
+  }
+
+  // A REST backend connects directly to the Iceberg REST service, bypassing 
the Gravitino
+  // server's auth proxy, so the Gravitino client's authentication must be 
propagated to the REST
+  // client, unless the user has already configured REST auth explicitly.
+  private static void propagateRestAuthIfNeeded(
+      Map<String, String> icebergCatalogOptions, String catalogType) {
+    if 
(IcebergPropertiesConstants.ICEBERG_CATALOG_BACKEND_REST.equalsIgnoreCase(catalogType)
         && !icebergCatalogOptions.containsKey(AuthProperties.AUTH_TYPE)) {
       icebergCatalogOptions.putAll(
           IcebergPropertiesConverter.INSTANCE.toRestAuthProperties(
               GravitinoCatalogManager.get().getGravitinoClientConfig()));
     }
-    // Iceberg's FlinkCatalogFactory only accepts hive/hadoop/rest as 
`catalog-type`; a JDBC backend
-    // must be loaded through `catalog-impl` instead. The two keys are 
mutually exclusive, so drop
-    // `catalog-type` and use `putIfAbsent` to respect an explicitly provided 
`catalog-impl`.
-    String catalogType = 
icebergCatalogOptions.get(IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE);
+  }
+
+  // Iceberg's FlinkCatalogFactory only accepts hive/hadoop/rest as 
catalog-type; a JDBC backend
+  // must be loaded through catalog-impl instead. The two keys are mutually 
exclusive, so
+  // catalog-type is dropped, and putIfAbsent respects an explicitly provided 
catalog-impl.
+  private static void translateJdbcBackendToCatalogImpl(
+      Map<String, String> icebergCatalogOptions, String catalogType) {
     if 
(IcebergPropertiesConstants.ICEBERG_CATALOG_BACKEND_JDBC.equalsIgnoreCase(catalogType))
 {
       
icebergCatalogOptions.remove(IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE);
       icebergCatalogOptions.putIfAbsent(
           IcebergPropertiesConstants.ICEBERG_CATALOG_IMPL,
           IcebergPropertiesConstants.ICEBERG_JDBC_CATALOG_IMPL);
     }
-    // The outer Flink factory is `gravitino-iceberg`, but the nested Iceberg 
factory still expects
-    // `catalog-type=iceberg` when building the native Iceberg catalog 
instance.
-    icebergCatalogOptions.put(CommonCatalogOptions.CATALOG_TYPE.key(), 
"iceberg");
-    return icebergCatalogOptions;
   }
 }
diff --git 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/iceberg/TestGravitinoIcebergCatalogFactory.java
 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/iceberg/TestGravitinoIcebergCatalogFactory.java
index 6b89862ab3..b65dfb2191 100644
--- 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/iceberg/TestGravitinoIcebergCatalogFactory.java
+++ 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/iceberg/TestGravitinoIcebergCatalogFactory.java
@@ -22,6 +22,7 @@ package org.apache.gravitino.flink.connector.iceberg;
 import com.google.common.collect.ImmutableMap;
 import java.util.Map;
 import org.apache.flink.table.catalog.CommonCatalogOptions;
+import org.apache.iceberg.rest.auth.AuthProperties;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -105,7 +106,11 @@ class TestGravitinoIcebergCatalogFactory {
             IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE,
             IcebergPropertiesConstants.ICEBERG_CATALOG_BACKEND_REST,
             IcebergPropertiesConstants.ICEBERG_CATALOG_URI,
-            "http://localhost:9001/iceberg/";);
+            "http://localhost:9001/iceberg/";,
+            // Pre-set so the REST auth-propagation branch (which needs a live
+            // GravitinoCatalogManager) is skipped; that branch is covered 
separately below.
+            AuthProperties.AUTH_TYPE,
+            "none");
 
     Map<String, String> result = factory.toIcebergCatalogOptions(options);
 
@@ -115,4 +120,27 @@ class TestGravitinoIcebergCatalogFactory {
     
Assertions.assertFalse(result.containsKey(IcebergPropertiesConstants.ICEBERG_CATALOG_IMPL));
     Assertions.assertEquals("iceberg", 
result.get(CommonCatalogOptions.CATALOG_TYPE.key()));
   }
+
+  @Test
+  void testRestAuthPropagatedWhenLoadedFromCatalogStore() {
+    // Simulates the catalog-store load path (`USE CATALOG ...`): by the time 
options reach this
+    // factory, IcebergPropertiesConverter has already renamed 
`catalog-backend` -> `catalog-type`
+    // during property conversion, so only `catalog-type=rest` is present, no 
`catalog-backend`.
+    Map<String, String> options =
+        ImmutableMap.of(
+            IcebergPropertiesConstants.ICEBERG_CATALOG_TYPE,
+            IcebergPropertiesConstants.ICEBERG_CATALOG_BACKEND_REST,
+            IcebergPropertiesConstants.ICEBERG_CATALOG_URI,
+            "http://localhost:9001/iceberg/";);
+
+    // Regression check for #11601: the REST auth-propagation branch must be 
gated on the
+    // normalized `catalog-type`, not the renamed-away `catalog-backend` key, 
otherwise it would
+    // silently skip auth propagation on this path. GravitinoCatalogManager 
isn't initialized in
+    // this unit test, so reaching it (and failing there) proves the gate now 
fires correctly.
+    IllegalStateException exception =
+        Assertions.assertThrows(
+            IllegalStateException.class, () -> 
factory.toIcebergCatalogOptions(options));
+    String msg = exception.getMessage();
+    Assertions.assertTrue(msg != null && 
msg.contains("GravitinoCatalogManager"));
+  }
 }
diff --git 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/integration/test/iceberg/FlinkIcebergJdbcCatalogIT.java
 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/integration/test/iceberg/FlinkIcebergJdbcCatalogIT.java
index cdd01e6240..2be4e715ca 100644
--- 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/integration/test/iceberg/FlinkIcebergJdbcCatalogIT.java
+++ 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/integration/test/iceberg/FlinkIcebergJdbcCatalogIT.java
@@ -23,8 +23,6 @@ import com.google.common.collect.Maps;
 import java.sql.SQLException;
 import java.util.Map;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
-import org.apache.gravitino.credential.CredentialConstants;
-import org.apache.gravitino.credential.JdbcCredential;
 import 
org.apache.gravitino.flink.connector.iceberg.GravitinoIcebergCatalogFactoryOptions;
 import org.apache.gravitino.flink.connector.iceberg.IcebergPropertiesConstants;
 import org.apache.gravitino.integration.test.container.ContainerSuite;
@@ -61,36 +59,19 @@ public abstract class FlinkIcebergJdbcCatalogIT extends 
FlinkIcebergCatalogIT {
     catalogProperties.put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, 
mySQLContainer.getPassword());
     catalogProperties.put(IcebergConstants.GRAVITINO_JDBC_DRIVER, 
getDriverClassName());
     catalogProperties.put(IcebergConstants.IO_IMPL, 
"org.apache.iceberg.hadoop.HadoopFileIO");
-    // Align the catalog name that the server-side and Flink-side native 
Iceberg JdbcCatalog use for
-    // the `catalog_name` column in the JDBC backend tables. The Flink-side 
native catalog is named
-    // after the Flink catalog (the Gravitino catalog name), while the 
server-side defaults to the
-    // `catalog-backend` value (`jdbc`). Without this, tables created through 
Gravitino are
-    // invisible
-    // to the Flink-side reads/writes. The table read/write tests run on the 
default catalog.
+    // Align catalog_name between server-side and Flink-side JdbcCatalog so 
tables created through
+    // Gravitino are visible to the Flink-side reads/writes.
     catalogProperties.put(IcebergConstants.CATALOG_BACKEND_NAME, 
DEFAULT_ICEBERG_CATALOG);
-    // Gravitino hides `jdbc-user`/`jdbc-password`, so they never reach the 
Flink-side native
-    // Iceberg
-    // catalog through the loaded properties. Enable credential vending so the 
server hands the JDBC
-    // user/password to the client, which GravitinoIcebergCatalog.open() 
injects into the native
-    // catalog via CredentialPropertyUtils.applyIcebergCredentials. This 
covers all access to an
-    // already-created catalog.
-    catalogProperties.put(
-        CredentialConstants.CREDENTIAL_PROVIDERS, 
JdbcCredential.JDBC_CREDENTIAL_TYPE);
-    // Vending cannot help at CREATE time: Flink opens the catalog (and 
Iceberg's JdbcCatalog
-    // eagerly
-    // connects to the database) before the catalog is persisted in Gravitino, 
so getCredentials()
-    // finds nothing to vend. The descriptor-based create path therefore needs 
the native
-    // `jdbc.user`/`jdbc.password` directly. See 
testCreateGravitinoIcebergCatalog.
+    // `credential-providers` is intentionally omitted: the lakehouse-iceberg 
catalog auto-enables
+    // `jdbc-user-password` for JDBC backends, so 
GravitinoIcebergCatalog.open() injects the
+    // credentials via credential vending. This exercises the real 
auto-vending path.
+    // `jdbc.user`/`jdbc.password` are only needed for CREATE CATALOG, where 
Flink opens the
+    // catalog before it is persisted in Gravitino and vending is not yet 
available.
     catalogProperties.put("jdbc.user", mySQLContainer.getUsername());
     catalogProperties.put("jdbc.password", mySQLContainer.getPassword());
     return catalogProperties;
   }
 
-  /**
-   * The base WITH clause only carries backend/uri/warehouse, but a JDBC 
backend also needs the jdbc
-   * driver, credential vending, and native jdbc credentials (the latter for 
the create-time eager
-   * connect, since vending is not available until the catalog is persisted in 
Gravitino).
-   */
   @Override
   protected String buildCreateCatalogSql(String catalogName) {
     return String.format(
@@ -102,7 +83,6 @@ public abstract class FlinkIcebergJdbcCatalogIT extends 
FlinkIcebergCatalogIT {
             + "'jdbc-user'='%s',"
             + "'jdbc-password'='%s',"
             + "'jdbc-driver'='%s',"
-            + "'%s'='%s',"
             + "'jdbc.user'='%s',"
             + "'jdbc.password'='%s'"
             + ")",
@@ -114,8 +94,6 @@ public abstract class FlinkIcebergJdbcCatalogIT extends 
FlinkIcebergCatalogIT {
         mySQLContainer.getUsername(),
         mySQLContainer.getPassword(),
         getDriverClassName(),
-        CredentialConstants.CREDENTIAL_PROVIDERS,
-        JdbcCredential.JDBC_CREDENTIAL_TYPE,
         mySQLContainer.getUsername(),
         mySQLContainer.getPassword());
   }

Reply via email to