This is an automated email from the ASF dual-hosted git repository.
jerryshao 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 8c78d1b836 [Cherry-pick to branch-1.3] [#14029] fix(core): Expose
inferred credential-providers in catalog properties (#14030) (#14070)
8c78d1b836 is described below
commit 8c78d1b8369be5790162ad6b2732a3594d7d40ea
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Oct 22 10:06:18 2026 +0800
[Cherry-pick to branch-1.3] [#14029] fix(core): Expose inferred
credential-providers in catalog properties (#14030) (#14070)
**Cherry-pick Information:**
- Original commit: 7cad7718d0239bd05cf022b7052fb87937e792da
- Target branch: `branch-1.3`
- Status: ⚠️ **Has conflicts - manual resolution required**
---------
Co-authored-by: Yuhui <[email protected]>
Co-authored-by: diqiu50 <[email protected]>
---
.../catalog/jdbc/TestJdbcCatalogCredential.java | 4 +
.../apache/gravitino/connector/BaseCatalog.java | 10 ++
.../TestBaseCatalogCredentialProviders.java | 111 +++++++++++++++++++++
docs/security/credential-vending.md | 2 +
.../testsets/jdbc-mysql/00008_alter_catalog.txt | 8 +-
5 files changed, 131 insertions(+), 4 deletions(-)
diff --git
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
index cedf2e6ee5..cabab9724e 100644
---
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
+++
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
@@ -231,6 +231,10 @@ public class TestJdbcCatalogCredential {
Map<String, String> publicProps = jdbcCatalog.properties();
Assertions.assertFalse(publicProps.containsKey(JdbcConfig.USERNAME.getKey()));
Assertions.assertFalse(publicProps.containsKey(JdbcConfig.PASSWORD.getKey()));
+ // The inferred provider name is exposed without backfill
+ Assertions.assertEquals(
+ JdbcCredential.JDBC_CREDENTIAL_TYPE,
+ publicProps.get(CredentialConstants.CREDENTIAL_PROVIDERS));
// propertiesWithCredentialProviders must still see the raw credentials
Map<String, String> credProps =
jdbcCatalog.propertiesWithCredentialProviders();
diff --git a/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
b/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
index 2bc1a07a9f..777a2cce54 100644
--- a/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
+++ b/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
@@ -494,6 +494,16 @@ public abstract class BaseCatalog<T extends BaseCatalog>
tempProperties.putIfAbsent(
PROPERTY_IN_USE,
catalogPropertiesMetadata().getDefaultValue(PROPERTY_IN_USE).toString());
+ // Expose the inferred provider names (never the credential values)
so that clients
+ // make routing decisions from the same provider list the server
vends credentials
+ // with. An explicitly configured list is returned as-is.
+ if
(StringUtils.isBlank(tempProperties.get(CredentialConstants.CREDENTIAL_PROVIDERS)))
{
+ String inferredProviders =
+
propertiesWithCredentialProviders().get(CredentialConstants.CREDENTIAL_PROVIDERS);
+ if (StringUtils.isNotBlank(inferredProviders)) {
+ tempProperties.put(CredentialConstants.CREDENTIAL_PROVIDERS,
inferredProviders);
+ }
+ }
properties = tempProperties;
}
diff --git
a/core/src/test/java/org/apache/gravitino/connector/TestBaseCatalogCredentialProviders.java
b/core/src/test/java/org/apache/gravitino/connector/TestBaseCatalogCredentialProviders.java
new file mode 100644
index 0000000000..8b9ff46acd
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/connector/TestBaseCatalogCredentialProviders.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.connector;
+
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.S3SecretKeyCredential;
+import org.apache.gravitino.meta.AuditInfo;
+import org.apache.gravitino.meta.CatalogEntity;
+import org.apache.gravitino.storage.S3Properties;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class TestBaseCatalogCredentialProviders {
+
+ @Test
+ void testInferredCredentialProvidersExposed() throws IllegalAccessException {
+ Map<String, String> props = new HashMap<>();
+ props.put(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID, "access-key");
+ props.put(S3Properties.GRAVITINO_S3_SECRET_ACCESS_KEY, "secret-key");
+
+ Map<String, String> result = newCatalog(props).properties();
+ Assertions.assertEquals(
+ S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE,
+ result.get(CredentialConstants.CREDENTIAL_PROVIDERS));
+ }
+
+ @Test
+ void testExplicitCredentialProvidersKept() throws IllegalAccessException {
+ Map<String, String> props = new HashMap<>();
+ props.put(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID, "access-key");
+ props.put(S3Properties.GRAVITINO_S3_SECRET_ACCESS_KEY, "secret-key");
+ props.put(CredentialConstants.CREDENTIAL_PROVIDERS, "s3-token");
+
+ Map<String, String> result = newCatalog(props).properties();
+ Assertions.assertEquals("s3-token",
result.get(CredentialConstants.CREDENTIAL_PROVIDERS));
+ }
+
+ @Test
+ void testNoCredentialProvidersWithoutCredentials() throws
IllegalAccessException {
+ Map<String, String> props = new HashMap<>();
+ props.put(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID, "access-key");
+
+ Map<String, String> result = newCatalog(props).properties();
+
Assertions.assertFalse(result.containsKey(CredentialConstants.CREDENTIAL_PROVIDERS));
+ }
+
+ private TestCatalogImpl newCatalog(Map<String, String> props) throws
IllegalAccessException {
+ CatalogEntity entity =
+ CatalogEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(Namespace.of("metalake"))
+ .withType(Catalog.Type.RELATIONAL)
+ .withProvider("test")
+ .withComment("comment")
+ .withProperties(props)
+ .withAuditInfo(
+
AuditInfo.builder().withCreator("tester").withCreateTime(Instant.now()).build())
+ .build();
+ TestCatalogImpl catalog = new TestCatalogImpl();
+ FieldUtils.writeField(catalog, "entity", entity, true);
+ return catalog;
+ }
+
+ private static class TestCatalogImpl extends BaseCatalog<TestCatalogImpl> {
+
+ @Override
+ public String shortName() {
+ return "test";
+ }
+
+ @Override
+ protected CatalogOperations newOps(Map<String, String> config) {
+ return Mockito.mock(CatalogOperations.class);
+ }
+
+ @Override
+ protected Capability newCapability() {
+ return Capability.DEFAULT;
+ }
+
+ @Override
+ public PropertiesMetadata catalogPropertiesMetadata() throws
UnsupportedOperationException {
+ return BaseCatalogPropertiesMetadata.BASIC_CATALOG_PROPERTIES_METADATA;
+ }
+ }
+}
diff --git a/docs/security/credential-vending.md
b/docs/security/credential-vending.md
index 09d2c0aab1..7604f51548 100644
--- a/docs/security/credential-vending.md
+++ b/docs/security/credential-vending.md
@@ -38,6 +38,8 @@ Gravitino credential vending is used to generate temporary or
static credentials
| `credential-cache-expire-ratio` |
`gravitino.iceberg-rest.credential-cache-expire-ratio` | Ratio of the
credential's expiration time when Gravitino remove credential from the cache. |
0.15 | No |
| `credential-cache-max-size` |
`gravitino.iceberg-rest.cache-max-size` | Max size for the
credential cache. |
10000 | No |
+If a catalog does not set `credential-providers`, Gravitino infers providers
from the credential properties present. The inferred provider list is returned
as the catalog's `credential-providers` property when the catalog is loaded, so
clients such as the Spark connector see the same list the server vends
credentials with. Only the provider names are returned, never the credential
values.
+
## Build-in Credentials Configurations
### S3 Credentials
diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00008_alter_catalog.txt
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00008_alter_catalog.txt
index 608e8a55cb..594601a6ac 100644
---
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00008_alter_catalog.txt
+++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00008_alter_catalog.txt
@@ -1,17 +1,17 @@
CALL
-"gt_mysql_xxx1","jdbc-mysql","{""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false""}","test"
+"gt_mysql_xxx1","jdbc-mysql","{""credential-providers"":""jdbc-user-password"",""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false""}","test"
CALL
-"gt_mysql_xxx1","jdbc-mysql","{""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false"",""test_key"":""test_value"",""trino.bypass.join-pushdown.strategy"":""EAGER""}","test"
+"gt_mysql_xxx1","jdbc-mysql","{""credential-providers"":""jdbc-user-password"",""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false"",""test_key"":""test_value"",""trino.bypass.join-pushdown.strategy"":""EAGER""}","test"
CALL
-"gt_mysql_xxx1","jdbc-mysql","{""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false"",""test_key"":""test_value""}","test"
+"gt_mysql_xxx1","jdbc-mysql","{""credential-providers"":""jdbc-user-password"",""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false"",""test_key"":""test_value""}","test"
CALL
-"gt_mysql_xxx1","jdbc-mysql","{""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false"",""trino.bypass.join-pushdown.strategy"":""EAGER""}","test"
+"gt_mysql_xxx1","jdbc-mysql","{""credential-providers"":""jdbc-user-password"",""in-use"":""true"",""jdbc-driver"":""com.mysql.cj.jdbc.Driver"",""jdbc-url"":""jdbc:mysql://%/?useSSL=false"",""trino.bypass.join-pushdown.strategy"":""EAGER""}","test"
CALL