Copilot commented on code in PR #11853:
URL: https://github.com/apache/gravitino/pull/11853#discussion_r3505843157


##########
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java:
##########
@@ -104,38 +108,50 @@ public Optional<IcebergConfig> 
getIcebergCatalogConfig(String catalogName) {
         "lakehouse-iceberg".equals(catalog.provider()),
         String.format("%s.%s is not iceberg catalog", gravitinoMetalake, 
catalogName));
 
-    // Sensitive credentials (e.g. jdbc-password) are marked hidden in 
PropertiesMetadata and
-    // filtered out of catalog.properties(). We need two different strategies 
to recover them:
+    // Sensitive credentials are marked hidden in PropertiesMetadata and 
filtered out of
+    // catalog.properties(). We need two different strategies to recover them:
     //
     // Auxiliary mode: the catalog is a BaseCatalog running in the same JVM as 
the Gravitino
     // server. Call propertiesWithCredentialProviders() which returns the raw 
entity properties
-    // including all hidden fields.
+    // including all hidden fields, then generate credentials locally.
     //
     // Standalone mode: the catalog is a client-side object obtained via the 
Gravitino REST API.
-    // Call getCredentials() to retrieve vended credentials, then inject any 
JdbcCredential
-    // fields into the properties map so the JDBC backend can connect.
+    // Call supportsCredentials().getCredentials() to retrieve vended 
credentials.
     Map<String, String> catalogProperties;
     if (catalog instanceof BaseCatalog) {
       catalogProperties = ((BaseCatalog<?>) 
catalog).propertiesWithCredentialProviders();
     } else {
       catalogProperties = new HashMap<>(catalog.properties());
-      if (catalog instanceof SupportsCredentials) {
-        Arrays.stream(((SupportsCredentials) catalog).getCredentials())
-            .filter(c -> c instanceof JdbcCredential)
-            .map(c -> (JdbcCredential) c)
-            .findFirst()
-            .ifPresent(
-                jdbc -> {
-                  catalogProperties.putIfAbsent(
-                      IcebergConstants.GRAVITINO_JDBC_USER, jdbc.jdbcUser());
-                  catalogProperties.putIfAbsent(
-                      IcebergConstants.GRAVITINO_JDBC_PASSWORD, 
jdbc.jdbcPassword());
-                });
-      }
     }
+    CredentialPropertyUtils.applyIcebergCredentials(
+        getCatalogCredentials(catalog, catalogProperties), catalogProperties);
     return 
Optional.of(getIcebergConfigFromCatalogProperties(catalogProperties));
   }
 
+  private static Credential[] getCatalogCredentials(
+      Catalog catalog, Map<String, String> catalogProperties) {
+    if (catalog instanceof BaseCatalog) {
+      return getInternalCatalogCredentials((BaseCatalog<?>) catalog, 
catalogProperties);
+    }
+
+    return CredentialPropertyUtils.getCredentials(catalog);
+  }
+
+  private static Credential[] getInternalCatalogCredentials(
+      BaseCatalog<?> catalog, Map<String, String> catalogProperties) {
+    List<Credential> credentials = new ArrayList<>();
+    CatalogCredentialContext context =
+        new CatalogCredentialContext(PrincipalUtils.getCurrentUserName());
+    for (String credentialType :
+        CredentialUtils.getCredentialProvidersByOrder(() -> 
catalogProperties)) {

Review Comment:
   `CredentialUtils.getCredentialProvidersByOrder(...)` returns a `Set`, which 
does not preserve the configured provider order (see 
core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java:54-76). 
Because later credentials can overwrite earlier properties during injection, 
iterating an unordered set can make precedence nondeterministic when multiple 
providers are configured. Consider iterating the ordered `List<String>` from 
`CredentialConfig.CREDENTIAL_PROVIDERS` instead.



##########
catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java:
##########
@@ -138,6 +144,22 @@ public void initialize(
     this.icebergViewCatalogOperations = new 
IcebergViewCatalogOperations(icebergCatalogWrapper);
   }
 
+  private static void injectCredentialConfig(
+      Map<String, String> conf, String catalogName, Map<String, String> 
resultConf) {
+    List<Credential> credentials = new ArrayList<>();
+    CatalogCredentialContext context =
+        new CatalogCredentialContext(PrincipalUtils.getCurrentUserName());
+    try (CatalogCredentialManager credentialManager =
+        new CatalogCredentialManager(catalogName, conf)) {
+      for (String credentialProvider : 
CredentialUtils.getCredentialProvidersByOrder(() -> conf)) {
+        credentialManager.getCredential(credentialProvider, 
context).ifPresent(credentials::add);
+      }
+    }
+
+    CredentialPropertyUtils.applyIcebergCredentials(
+        credentials.toArray(new Credential[0]), resultConf);

Review Comment:
   `AwsIrsaCredential`/`S3TokenCredential` are time-bound session credentials, 
but they’re injected into the catalog config once during `initialize(...)`. If 
the Iceberg catalog wrapper (and its FileIO) is long-lived, these static 
session keys will eventually expire and server-side S3 access can start 
failing. Consider using a refreshable credentials mechanism (e.g., pass 
web-identity inputs through so the AWS SDK refreshes automatically, or 
invalidate/rebuild the catalog wrapper when `s3.session-token-expires-at-ms` is 
near/over expiry).



##########
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java:
##########
@@ -104,38 +108,50 @@ public Optional<IcebergConfig> 
getIcebergCatalogConfig(String catalogName) {
         "lakehouse-iceberg".equals(catalog.provider()),
         String.format("%s.%s is not iceberg catalog", gravitinoMetalake, 
catalogName));
 
-    // Sensitive credentials (e.g. jdbc-password) are marked hidden in 
PropertiesMetadata and
-    // filtered out of catalog.properties(). We need two different strategies 
to recover them:
+    // Sensitive credentials are marked hidden in PropertiesMetadata and 
filtered out of
+    // catalog.properties(). We need two different strategies to recover them:
     //
     // Auxiliary mode: the catalog is a BaseCatalog running in the same JVM as 
the Gravitino
     // server. Call propertiesWithCredentialProviders() which returns the raw 
entity properties
-    // including all hidden fields.
+    // including all hidden fields, then generate credentials locally.
     //
     // Standalone mode: the catalog is a client-side object obtained via the 
Gravitino REST API.
-    // Call getCredentials() to retrieve vended credentials, then inject any 
JdbcCredential
-    // fields into the properties map so the JDBC backend can connect.
+    // Call supportsCredentials().getCredentials() to retrieve vended 
credentials.
     Map<String, String> catalogProperties;
     if (catalog instanceof BaseCatalog) {
       catalogProperties = ((BaseCatalog<?>) 
catalog).propertiesWithCredentialProviders();
     } else {
       catalogProperties = new HashMap<>(catalog.properties());
-      if (catalog instanceof SupportsCredentials) {
-        Arrays.stream(((SupportsCredentials) catalog).getCredentials())
-            .filter(c -> c instanceof JdbcCredential)
-            .map(c -> (JdbcCredential) c)
-            .findFirst()
-            .ifPresent(
-                jdbc -> {
-                  catalogProperties.putIfAbsent(
-                      IcebergConstants.GRAVITINO_JDBC_USER, jdbc.jdbcUser());
-                  catalogProperties.putIfAbsent(
-                      IcebergConstants.GRAVITINO_JDBC_PASSWORD, 
jdbc.jdbcPassword());
-                });
-      }
     }
+    CredentialPropertyUtils.applyIcebergCredentials(
+        getCatalogCredentials(catalog, catalogProperties), catalogProperties);
     return 
Optional.of(getIcebergConfigFromCatalogProperties(catalogProperties));

Review Comment:
   This injects vended session credentials directly into `catalogProperties` 
and returns an `IcebergConfig` that may be cached and reused. For expiring 
credentials like `AwsIrsaCredential`, this can lead to stale credentials being 
used later unless there is an explicit refresh/invalidation mechanism tied to 
`s3.session-token-expires-at-ms`. Consider adding a refresh strategy 
(invalidate/reload when near expiry) or prefer passing web-identity inputs 
through so the AWS SDK can refresh automatically.



##########
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/provider/TestDynamicIcebergConfigProvider.java:
##########
@@ -361,6 +363,52 @@ public void 
testHttpCatalogFetcherUsedWhenAuthorizationDisabled() throws Illegal
     Assertions.assertTrue(icebergConfig.isPresent());
   }
 
+  @Test
+  public void testInjectsVendedStorageCredentialsIntoIcebergConfig() {
+    String metalakeName = "test_metalake";
+    String catalogName = "credential_catalog";
+
+    Catalog mockCatalog = Mockito.mock(Catalog.class);
+    SupportsCredentials supportsCredentials = 
Mockito.mock(SupportsCredentials.class);
+    Mockito.when(mockCatalog.provider()).thenReturn("lakehouse-iceberg");
+    Mockito.when(mockCatalog.properties())
+        .thenReturn(
+            new HashMap<String, String>() {
+              {
+                put(IcebergConstants.CATALOG_BACKEND, "custom");
+                put(IcebergConstants.CATALOG_BACKEND_NAME, catalogName);
+              }
+            });

Review Comment:
   Avoid double-brace initialization here; it creates an anonymous subclass and 
can retain an implicit reference, making the test harder to read and 
potentially leaking memory. Prefer constructing a normal map and returning it 
from the stub.



##########
catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java:
##########
@@ -138,6 +144,22 @@ public void initialize(
     this.icebergViewCatalogOperations = new 
IcebergViewCatalogOperations(icebergCatalogWrapper);
   }
 
+  private static void injectCredentialConfig(
+      Map<String, String> conf, String catalogName, Map<String, String> 
resultConf) {
+    List<Credential> credentials = new ArrayList<>();
+    CatalogCredentialContext context =
+        new CatalogCredentialContext(PrincipalUtils.getCurrentUserName());
+    try (CatalogCredentialManager credentialManager =
+        new CatalogCredentialManager(catalogName, conf)) {
+      for (String credentialProvider : 
CredentialUtils.getCredentialProvidersByOrder(() -> conf)) {
+        credentialManager.getCredential(credentialProvider, 
context).ifPresent(credentials::add);

Review Comment:
   `CredentialUtils.getCredentialProvidersByOrder(...)` returns a `Set` 
(core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java:54-76),
 so provider order from configuration is not preserved. Since credential 
injection can overwrite keys (e.g., S3/JDBC properties), iterating an unordered 
set can make the effective credentials nondeterministic when multiple providers 
are present. Prefer iterating the ordered `List<String>` from 
`CredentialConfig.CREDENTIAL_PROVIDERS`.



-- 
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]

Reply via email to