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

jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 10e294399 [#4993] feat(iceberg): integrate credential framework to 
iceberg REST server (#5134)
10e294399 is described below

commit 10e2943996675eaf7c729b63648f9647d76a4c74
Author: FANNG <[email protected]>
AuthorDate: Fri Oct 18 18:38:22 2024 +0800

    [#4993] feat(iceberg): integrate credential framework to iceberg REST 
server (#5134)
    
    ### What changes were proposed in this pull request?
    integrate credential framework to iceberg REST server
    
    ### Why are the changes needed?
    
    Fix: #4993
    
    ### Does this PR introduce _any_ user-facing change?
    No
    
    ### How was this patch tested?
    set up a local environment to request credential with the configuration
    
`spark.sql.catalog.rest.header.X-Iceberg-Access-Delegation=vended-credentials`
---
 .../lakehouse/iceberg/IcebergConstants.java        |   5 +-
 .../gravitino/credential/CredentialConstants.java  |  26 ++
 .../iceberg/ops/TestIcebergTableUpdate.java        |   4 +-
 .../credential/CredentialPropertyUtils.java        |  38 +++
 .../credential/CredentialProviderManager.java      |  73 +++++
 .../gravitino/credential/CredentialUtils.java      |  32 +++
 iceberg/iceberg-common/build.gradle.kts            |   1 +
 .../gravitino/iceberg/common/IcebergConfig.java    |  16 +-
 ...ider.java => IcebergCatalogConfigProvider.java} |  14 +-
 .../iceberg/common/ops/IcebergCatalogWrapper.java  |   9 +-
 ...va => DynamicIcebergCatalogConfigProvider.java} |  25 +-
 ...ava => StaticIcebergCatalogConfigProvider.java} |  19 +-
 .../service/IcebergCatalogWrapperManager.java      |  88 ++++--
 .../service/rest/IcebergTableOperations.java       |  99 ++++++-
 ... TestDynamicIcebergCatalogWrapperProvider.java} |  20 +-
 ...> TestStaticIcebergCatalogWrapperProvider.java} |  23 +-
 .../service/extension/DummyCredentialProvider.java |  66 +++++
 ...gBasedIcebergCatalogWrapperProviderForTest.java |  30 --
 .../service/rest/IcebergCatalogWrapperForTest.java |   6 +
 .../rest/IcebergCatalogWrapperManagerForTest.java  |  37 +++
 .../iceberg/service/rest/IcebergRestTestUtil.java  |  16 +-
 .../service/rest/TestIcebergTableOperations.java   | 302 ++++++++++++---------
 ....apache.gravitino.credential.CredentialProvider |  19 ++
 23 files changed, 711 insertions(+), 257 deletions(-)

diff --git 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
index 21462b9ca..004bde0bd 100644
--- 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
+++ 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
@@ -68,7 +68,10 @@ public class IcebergConstants {
   public static final String ICEBERG_REST_CATALOG_CACHE_EVICTION_INTERVAL =
       "catalog-cache-eviction-interval-ms";
 
-  public static final String ICEBERG_REST_CATALOG_PROVIDER = 
"catalog-provider";
+  public static final String ICEBERG_REST_CATALOG_CONFIG_PROVIDER = 
"catalog-config-provider";
+  public static final String STATIC_ICEBERG_CATALOG_CONFIG_PROVIDER_NAME = 
"static-config-provider";
+  public static final String DYNAMIC_ICEBERG_CATALOG_CONFIG_PROVIDER_NAME =
+      "dynamic-config-provider";
 
   public static final String GRAVITINO_URI = "gravitino-uri";
 
diff --git 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/CredentialConstants.java
 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/CredentialConstants.java
new file mode 100644
index 000000000..596268395
--- /dev/null
+++ 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/CredentialConstants.java
@@ -0,0 +1,26 @@
+/*
+ *  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.credential;
+
+public class CredentialConstants {
+  public static final String CREDENTIAL_PROVIDER_TYPE = 
"credential-provider-type";
+
+  private CredentialConstants() {}
+}
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/ops/TestIcebergTableUpdate.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/ops/TestIcebergTableUpdate.java
index c4bac4df1..37124dc5f 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/ops/TestIcebergTableUpdate.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/ops/TestIcebergTableUpdate.java
@@ -18,9 +18,11 @@
  */
 package org.apache.gravitino.catalog.lakehouse.iceberg.ops;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
 import 
org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper.IcebergTableChange;
 import org.apache.gravitino.rel.TableChange;
@@ -79,7 +81,7 @@ public class TestIcebergTableUpdate {
 
   @BeforeEach
   public void init() {
-    icebergCatalogWrapper = new IcebergCatalogWrapper();
+    icebergCatalogWrapper = new IcebergCatalogWrapper(new 
IcebergConfig(Collections.emptyMap()));
     icebergCatalogWrapperHelper =
         new IcebergCatalogWrapperHelper(icebergCatalogWrapper.getCatalog());
     createNamespace(TEST_NAMESPACE_NAME);
diff --git 
a/common/src/main/java/org/apache/gravitino/credential/CredentialPropertyUtils.java
 
b/common/src/main/java/org/apache/gravitino/credential/CredentialPropertyUtils.java
new file mode 100644
index 000000000..255e54fbf
--- /dev/null
+++ 
b/common/src/main/java/org/apache/gravitino/credential/CredentialPropertyUtils.java
@@ -0,0 +1,38 @@
+/*
+ *  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.credential;
+
+import java.util.Map;
+
+/**
+ * Helper class to generate specific credential properties for different table 
format and engine.
+ */
+public class CredentialPropertyUtils {
+  /**
+   * Transforms a specific credential into a map of Iceberg properties.
+   *
+   * @param credential the credential to be transformed into Iceberg properties
+   * @return a map of Iceberg properties derived from the credential
+   */
+  public static Map<String, String> toIcebergProperties(Credential credential) 
{
+    // todo: transform specific credential to iceberg properties
+    return credential.toProperties();
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/credential/CredentialProviderManager.java
 
b/core/src/main/java/org/apache/gravitino/credential/CredentialProviderManager.java
new file mode 100644
index 000000000..b583bedcf
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/credential/CredentialProviderManager.java
@@ -0,0 +1,73 @@
+/*
+ *  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.credential;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CredentialProviderManager {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(CredentialProviderManager.class);
+  private Map<String, CredentialProvider> credentialProviders;
+
+  public CredentialProviderManager() {
+    this.credentialProviders = new ConcurrentHashMap<>();
+  }
+
+  public void registerCredentialProvider(
+      String catalogName, CredentialProvider credentialProvider) {
+    CredentialProvider current = credentialProviders.putIfAbsent(catalogName, 
credentialProvider);
+    Preconditions.checkState(
+        !credentialProvider.equals(current),
+        String.format(
+            "Should not register multiple times to CredentialProviderManager, 
catalog: %s, "
+                + "credential provider: %s",
+            catalogName, credentialProvider.credentialType()));
+    LOG.info(
+        "Register catalog:%s credential provider:%s to 
CredentialProviderManager",
+        catalogName, credentialProvider.credentialType());
+  }
+
+  public void unregisterCredentialProvider(String catalogName) {
+    CredentialProvider credentialProvider = 
credentialProviders.remove(catalogName);
+    // Not all catalog has credential provider
+    if (credentialProvider != null) {
+      LOG.info(
+          "Unregister catalog:{} credential provider:{} to 
CredentialProviderManager",
+          catalogName,
+          credentialProvider.credentialType());
+      try {
+        credentialProvider.close();
+      } catch (IOException e) {
+        LOG.warn("Close credential provider failed", e);
+      }
+    }
+  }
+
+  @Nullable
+  public CredentialProvider getCredentialProvider(String catalogName) {
+    return credentialProviders.get(catalogName);
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java 
b/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java
new file mode 100644
index 000000000..ad81953ac
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java
@@ -0,0 +1,32 @@
+/*
+ *  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.credential;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.gravitino.utils.PrincipalUtils;
+
+public class CredentialUtils {
+  public static Credential vendCredential(CredentialProvider 
credentialProvider, String path) {
+    PathBasedCredentialContext pathBasedCredentialContext =
+        new PathBasedCredentialContext(
+            PrincipalUtils.getCurrentUserName(), ImmutableSet.of(path), 
ImmutableSet.of());
+    return credentialProvider.getCredential(pathBasedCredentialContext);
+  }
+}
diff --git a/iceberg/iceberg-common/build.gradle.kts 
b/iceberg/iceberg-common/build.gradle.kts
index 23b3d30db..abc9a05a5 100644
--- a/iceberg/iceberg-common/build.gradle.kts
+++ b/iceberg/iceberg-common/build.gradle.kts
@@ -25,6 +25,7 @@ plugins {
 }
 
 dependencies {
+  implementation(project(":api"))
   implementation(project(":catalogs:catalog-common"))
   implementation(project(":core")) {
     exclude("*")
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
index fd7b52050..638b4172c 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
@@ -32,6 +32,7 @@ import 
org.apache.gravitino.catalog.lakehouse.iceberg.IcebergPropertiesUtils;
 import org.apache.gravitino.config.ConfigBuilder;
 import org.apache.gravitino.config.ConfigConstants;
 import org.apache.gravitino.config.ConfigEntry;
+import org.apache.gravitino.credential.CredentialConstants;
 import org.apache.gravitino.storage.OSSProperties;
 import org.apache.gravitino.storage.S3Properties;
 
@@ -201,13 +202,13 @@ public class IcebergConfig extends Config implements 
OverwriteDefaultConfig {
           .longConf()
           .createWithDefault(3600000L);
 
-  public static final ConfigEntry<String> ICEBERG_REST_CATALOG_PROVIDER =
-      new ConfigBuilder(IcebergConstants.ICEBERG_REST_CATALOG_PROVIDER)
+  public static final ConfigEntry<String> ICEBERG_REST_CATALOG_CONFIG_PROVIDER 
=
+      new ConfigBuilder(IcebergConstants.ICEBERG_REST_CATALOG_CONFIG_PROVIDER)
           .doc(
-              "Catalog provider class name, you can develop a class that 
implements `IcebergCatalogWrapperProvider` and add the corresponding jar file 
to the Iceberg REST service classpath directory.")
+              "Catalog provider class name, you can develop a class that 
implements `IcebergCatalogConfigProvider` and add the corresponding jar file to 
the Iceberg REST service classpath directory.")
           .version(ConfigConstants.VERSION_0_7_0)
           .stringConf()
-          .createWithDefault("config-based-provider");
+          
.createWithDefault(IcebergConstants.STATIC_ICEBERG_CATALOG_CONFIG_PROVIDER_NAME);
 
   public static final ConfigEntry<String> GRAVITINO_URI =
       new ConfigBuilder(IcebergConstants.GRAVITINO_URI)
@@ -233,6 +234,13 @@ public class IcebergConfig extends Config implements 
OverwriteDefaultConfig {
           .toSequence()
           .createWithDefault(Collections.emptyList());
 
+  public static final ConfigEntry<String> CREDENTIAL_PROVIDER_TYPE =
+      new ConfigBuilder(CredentialConstants.CREDENTIAL_PROVIDER_TYPE)
+          .doc("The credential provider type for Iceberg")
+          .version(ConfigConstants.VERSION_0_7_0)
+          .stringConf()
+          .create();
+
   public String getJdbcDriver() {
     return get(JDBC_DRIVER);
   }
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapperProvider.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogConfigProvider.java
similarity index 71%
rename from 
iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapperProvider.java
rename to 
iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogConfigProvider.java
index 758aa46aa..fc0d488a1 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapperProvider.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogConfigProvider.java
@@ -19,12 +19,14 @@
 package org.apache.gravitino.iceberg.common.ops;
 
 import java.util.Map;
+import java.util.Optional;
+import org.apache.gravitino.iceberg.common.IcebergConfig;
 
 /**
- * IcebergCatalogWrapperProvider is an interface defining how Iceberg REST 
catalog server gets
- * Iceberg catalogs.
+ * {@code IcebergCatalogConfigProvider} is an interface defining how Iceberg 
REST catalog server
+ * gets Iceberg catalog configurations.
  */
-public interface IcebergCatalogWrapperProvider {
+public interface IcebergCatalogConfigProvider {
 
   /**
    * @param properties The parameters for creating Provider which from 
configurations whose prefix
@@ -33,8 +35,8 @@ public interface IcebergCatalogWrapperProvider {
   void initialize(Map<String, String> properties);
 
   /**
-   * @param catalogName a param send by clients.
-   * @return the instance of IcebergCatalogWrapper.
+   * @param catalogName Iceberg catalog name.
+   * @return the configuration of Iceberg catalog.
    */
-  IcebergCatalogWrapper getIcebergTableOps(String catalogName);
+  Optional<IcebergConfig> getIcebergCatalogConfig(String catalogName);
 }
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
index 6ff4bf2ce..95e82aa22 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
@@ -22,7 +22,6 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import java.sql.Driver;
 import java.sql.DriverManager;
-import java.util.Collections;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
@@ -104,10 +103,6 @@ public class IcebergCatalogWrapper implements 
AutoCloseable {
     this.catalogPropertiesMap = icebergConfig.getIcebergCatalogProperties();
   }
 
-  public IcebergCatalogWrapper() {
-    this(new IcebergConfig(Collections.emptyMap()));
-  }
-
   private void validateNamespace(Optional<Namespace> namespace) {
     namespace.ifPresent(
         n ->
@@ -160,7 +155,7 @@ public class IcebergCatalogWrapper implements AutoCloseable 
{
   /**
    * Reload hadoop configuration, this is useful when the hadoop configuration 
UserGroupInformation
    * is shared by multiple threads. UserGroupInformation#authenticationMethod 
was first initialized
-   * in KerberosClient, however, when switching to iceberg-rest thead,
+   * in KerberosClient, however, when switching to iceberg-rest thread,
    * UserGroupInformation#authenticationMethod will be reset to the default 
value; we need to
    * reinitialize it again.
    */
@@ -271,7 +266,7 @@ public class IcebergCatalogWrapper implements AutoCloseable 
{
   private void closeMySQLCatalogResource() {
     try {
       // Close thread AbandonedConnectionCleanupThread if we are using 
`com.mysql.cj.jdbc.Driver`,
-      // for driver `com.mysql.jdbc.Driver` (deprecated), the daemon thead 
maybe not this one.
+      // for driver `com.mysql.jdbc.Driver` (deprecated), the daemon thread 
maybe not this one.
       Class.forName("com.mysql.cj.jdbc.AbandonedConnectionCleanupThread")
           .getMethod("uncheckedShutdown")
           .invoke(null);
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/GravitinoBasedIcebergCatalogWrapperProvider.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/DynamicIcebergCatalogConfigProvider.java
similarity index 81%
rename from 
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/GravitinoBasedIcebergCatalogWrapperProvider.java
rename to 
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/DynamicIcebergCatalogConfigProvider.java
index a38fd9cf3..4965f4bc1 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/GravitinoBasedIcebergCatalogWrapperProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/DynamicIcebergCatalogConfigProvider.java
@@ -21,14 +21,15 @@ package org.apache.gravitino.iceberg.provider;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import java.util.Map;
+import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergPropertiesUtils;
 import org.apache.gravitino.client.GravitinoAdminClient;
+import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
-import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
-import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapperProvider;
+import org.apache.gravitino.iceberg.common.ops.IcebergCatalogConfigProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,13 +40,10 @@ import org.slf4j.LoggerFactory;
  *
  * <p>The catalogName is iceberg_catalog
  */
-public class GravitinoBasedIcebergCatalogWrapperProvider
-    implements IcebergCatalogWrapperProvider, AutoCloseable {
+public class DynamicIcebergCatalogConfigProvider
+    implements IcebergCatalogConfigProvider, AutoCloseable {
   public static final Logger LOG =
-      
LoggerFactory.getLogger(GravitinoBasedIcebergCatalogWrapperProvider.class);
-
-  public static final String GRAVITINO_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME =
-      "gravitino-based-provider";
+      LoggerFactory.getLogger(DynamicIcebergCatalogConfigProvider.class);
 
   private String gravitinoMetalake;
 
@@ -66,14 +64,19 @@ public class GravitinoBasedIcebergCatalogWrapperProvider
   }
 
   @Override
-  public IcebergCatalogWrapper getIcebergTableOps(String catalogName) {
+  public Optional<IcebergConfig> getIcebergCatalogConfig(String catalogName) {
     Preconditions.checkArgument(
         StringUtils.isNotBlank(catalogName), "blank catalogName is illegal");
     Preconditions.checkArgument(
         !IcebergConstants.GRAVITINO_DEFAULT_CATALOG.equals(catalogName),
         IcebergConstants.GRAVITINO_DEFAULT_CATALOG + " is illegal in 
gravitino-based-provider");
 
-    Catalog catalog = 
client.loadMetalake(gravitinoMetalake).loadCatalog(catalogName);
+    Catalog catalog;
+    try {
+      catalog = 
client.loadMetalake(gravitinoMetalake).loadCatalog(catalogName);
+    } catch (NoSuchCatalogException e) {
+      return Optional.empty();
+    }
 
     Preconditions.checkArgument(
         "lakehouse-iceberg".equals(catalog.provider()),
@@ -81,7 +84,7 @@ public class GravitinoBasedIcebergCatalogWrapperProvider
 
     Map<String, String> properties =
         
IcebergPropertiesUtils.toIcebergCatalogProperties(catalog.properties());
-    return new IcebergCatalogWrapper(new IcebergConfig(properties));
+    return Optional.of(new IcebergConfig(properties));
   }
 
   @VisibleForTesting
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/ConfigBasedIcebergCatalogWrapperProvider.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/StaticIcebergCatalogConfigProvider.java
similarity index 78%
rename from 
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/ConfigBasedIcebergCatalogWrapperProvider.java
rename to 
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/StaticIcebergCatalogConfigProvider.java
index 522bca39f..aa7f10321 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/ConfigBasedIcebergCatalogWrapperProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/StaticIcebergCatalogConfigProvider.java
@@ -24,8 +24,7 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
-import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
-import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapperProvider;
+import org.apache.gravitino.iceberg.common.ops.IcebergCatalogConfigProvider;
 import org.apache.gravitino.utils.MapUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,11 +39,9 @@ import org.slf4j.LoggerFactory;
  * gravitino.iceberg-rest.catalog.hive_proxy.catalog-backend = hive
  * gravitino.iceberg-rest.catalog.hive_proxy.uri = thrift://{host}:{port} ...
  */
-public class ConfigBasedIcebergCatalogWrapperProvider implements 
IcebergCatalogWrapperProvider {
+public class StaticIcebergCatalogConfigProvider implements 
IcebergCatalogConfigProvider {
   public static final Logger LOG =
-      LoggerFactory.getLogger(ConfigBasedIcebergCatalogWrapperProvider.class);
-
-  public static final String CONFIG_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME = 
"config-based-provider";
+      LoggerFactory.getLogger(StaticIcebergCatalogConfigProvider.class);
 
   @VisibleForTesting Map<String, IcebergConfig> catalogConfigs;
 
@@ -68,14 +65,8 @@ public class ConfigBasedIcebergCatalogWrapperProvider 
implements IcebergCatalogW
   }
 
   @Override
-  public IcebergCatalogWrapper getIcebergTableOps(String catalogName) {
-    IcebergConfig icebergConfig = this.catalogConfigs.get(catalogName);
-    if (icebergConfig == null) {
-      String errorMsg = String.format("%s can not match any catalog", 
catalogName);
-      LOG.warn(errorMsg);
-      throw new RuntimeException(errorMsg);
-    }
-    return new IcebergCatalogWrapper(icebergConfig);
+  public Optional<IcebergConfig> getIcebergCatalogConfig(String catalogName) {
+    return Optional.ofNullable(catalogConfigs.get(catalogName));
   }
 
   private Optional<String> getCatalogName(String catalogConfigKey) {
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
index 17342acf7..823f42ddb 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
@@ -21,41 +21,48 @@ package org.apache.gravitino.iceberg.service;
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
 import com.github.benmanes.caffeine.cache.Scheduler;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
+import org.apache.gravitino.credential.CredentialProvider;
+import org.apache.gravitino.credential.CredentialProviderFactory;
+import org.apache.gravitino.credential.CredentialProviderManager;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
+import org.apache.gravitino.iceberg.common.ops.IcebergCatalogConfigProvider;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
-import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapperProvider;
-import 
org.apache.gravitino.iceberg.provider.ConfigBasedIcebergCatalogWrapperProvider;
-import 
org.apache.gravitino.iceberg.provider.GravitinoBasedIcebergCatalogWrapperProvider;
+import 
org.apache.gravitino.iceberg.provider.DynamicIcebergCatalogConfigProvider;
+import 
org.apache.gravitino.iceberg.provider.StaticIcebergCatalogConfigProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class IcebergCatalogWrapperManager implements AutoCloseable {
   public static final Logger LOG = 
LoggerFactory.getLogger(IcebergCatalogWrapperManager.class);
 
-  private static final ImmutableMap<String, String> 
ICEBERG_TABLE_OPS_PROVIDER_NAMES =
+  private static final ImmutableMap<String, String> 
ICEBERG_CATALOG_CONFIG_PROVIDER_NAMES =
       ImmutableMap.of(
-          
ConfigBasedIcebergCatalogWrapperProvider.CONFIG_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME,
-          ConfigBasedIcebergCatalogWrapperProvider.class.getCanonicalName(),
-          GravitinoBasedIcebergCatalogWrapperProvider
-              .GRAVITINO_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME,
-          
GravitinoBasedIcebergCatalogWrapperProvider.class.getCanonicalName());
+          IcebergConstants.STATIC_ICEBERG_CATALOG_CONFIG_PROVIDER_NAME,
+          StaticIcebergCatalogConfigProvider.class.getCanonicalName(),
+          IcebergConstants.DYNAMIC_ICEBERG_CATALOG_CONFIG_PROVIDER_NAME,
+          DynamicIcebergCatalogConfigProvider.class.getCanonicalName());
 
-  private final Cache<String, IcebergCatalogWrapper> icebergTableOpsCache;
+  private final Cache<String, IcebergCatalogWrapper> 
icebergCatalogWrapperCache;
 
-  private final IcebergCatalogWrapperProvider provider;
+  private final IcebergCatalogConfigProvider provider;
+
+  private CredentialProviderManager credentialProviderManager;
 
   public IcebergCatalogWrapperManager(Map<String, String> properties) {
-    this.provider = createProvider(properties);
+    this.credentialProviderManager = new CredentialProviderManager();
+    this.provider = createIcebergCatalogConfigProvider(properties);
     this.provider.initialize(properties);
-    this.icebergTableOpsCache =
+    this.icebergCatalogWrapperCache =
         Caffeine.newBuilder()
             .expireAfterWrite(
                 (new IcebergConfig(properties))
@@ -63,8 +70,10 @@ public class IcebergCatalogWrapperManager implements 
AutoCloseable {
                 TimeUnit.MILLISECONDS)
             .removalListener(
                 (k, v, c) -> {
-                  LOG.info("Remove IcebergCatalogWrapper cache {}.", k);
-                  closeIcebergTableOps((IcebergCatalogWrapper) v);
+                  String catalogName = (String) k;
+                  LOG.info("Remove IcebergCatalogWrapper cache {}.", 
catalogName);
+                  closeIcebergCatalogWrapper((IcebergCatalogWrapper) v);
+                  
credentialProviderManager.unregisterCredentialProvider(catalogName);
                 })
             .scheduler(
                 Scheduler.forScheduledExecutorService(
@@ -72,7 +81,7 @@ public class IcebergCatalogWrapperManager implements 
AutoCloseable {
                         1,
                         new ThreadFactoryBuilder()
                             .setDaemon(true)
-                            .setNameFormat("table-ops-cleaner-%d")
+                            
.setNameFormat("iceberg-catalog-wrapper-cleaner-%d")
                             .build())))
             .build();
   }
@@ -85,13 +94,40 @@ public class IcebergCatalogWrapperManager implements 
AutoCloseable {
   public IcebergCatalogWrapper getOps(String rawPrefix) {
     String catalogName = getCatalogName(rawPrefix);
     IcebergCatalogWrapper tableOps =
-        icebergTableOpsCache.get(catalogName, k -> 
provider.getIcebergTableOps(catalogName));
+        icebergCatalogWrapperCache.get(catalogName, k -> 
createCatalogWrapper(catalogName));
     // Reload conf to reset UserGroupInformation or icebergTableOps will 
always use
     // Simple auth.
     tableOps.reloadHadoopConf();
     return tableOps;
   }
 
+  public CredentialProvider getCredentialProvider(String prefix) {
+    String catalogName = getCatalogName(prefix);
+    return credentialProviderManager.getCredentialProvider(catalogName);
+  }
+
+  @VisibleForTesting
+  protected IcebergCatalogWrapper createIcebergCatalogWrapper(IcebergConfig 
icebergConfig) {
+    return new IcebergCatalogWrapper(icebergConfig);
+  }
+
+  private IcebergCatalogWrapper createCatalogWrapper(String catalogName) {
+    Optional<IcebergConfig> icebergConfig = 
provider.getIcebergCatalogConfig(catalogName);
+    if (!icebergConfig.isPresent()) {
+      throw new RuntimeException("Couldn't find Iceberg configuration for " + 
catalogName);
+    }
+
+    IcebergConfig config = icebergConfig.get();
+    String credentialProviderType = 
config.get(IcebergConfig.CREDENTIAL_PROVIDER_TYPE);
+    if (StringUtils.isNotBlank(credentialProviderType)) {
+      CredentialProvider credentialProvider =
+          CredentialProviderFactory.create(credentialProviderType, 
config.getAllConfig());
+      credentialProviderManager.registerCredentialProvider(catalogName, 
credentialProvider);
+    }
+
+    return createIcebergCatalogWrapper(icebergConfig.get());
+  }
+
   private String getCatalogName(String rawPrefix) {
     String prefix = shelling(rawPrefix);
     Preconditions.checkArgument(
@@ -103,14 +139,16 @@ public class IcebergCatalogWrapperManager implements 
AutoCloseable {
     return prefix;
   }
 
-  private IcebergCatalogWrapperProvider createProvider(Map<String, String> 
properties) {
+  private IcebergCatalogConfigProvider createIcebergCatalogConfigProvider(
+      Map<String, String> properties) {
     String providerName =
-        (new 
IcebergConfig(properties)).get(IcebergConfig.ICEBERG_REST_CATALOG_PROVIDER);
-    String className = 
ICEBERG_TABLE_OPS_PROVIDER_NAMES.getOrDefault(providerName, providerName);
+        (new 
IcebergConfig(properties)).get(IcebergConfig.ICEBERG_REST_CATALOG_CONFIG_PROVIDER);
+    String className =
+        ICEBERG_CATALOG_CONFIG_PROVIDER_NAMES.getOrDefault(providerName, 
providerName);
     LOG.info("Load Iceberg catalog provider: {}.", className);
     try {
       Class<?> providerClz = Class.forName(className);
-      return (IcebergCatalogWrapperProvider) 
providerClz.getDeclaredConstructor().newInstance();
+      return (IcebergCatalogConfigProvider) 
providerClz.getDeclaredConstructor().newInstance();
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
@@ -127,17 +165,17 @@ public class IcebergCatalogWrapperManager implements 
AutoCloseable {
     }
   }
 
-  private void closeIcebergTableOps(IcebergCatalogWrapper ops) {
+  private void closeIcebergCatalogWrapper(IcebergCatalogWrapper 
catalogWrapper) {
     try {
-      ops.close();
+      catalogWrapper.close();
     } catch (Exception ex) {
-      LOG.warn("Close Iceberg table ops fail: {}, {}", ops, ex);
+      LOG.warn("Close Iceberg table catalog wrapper fail: {}, {}", 
catalogWrapper, ex);
     }
   }
 
   @Override
   public void close() throws Exception {
-    icebergTableOpsCache.invalidateAll();
+    icebergCatalogWrapperCache.invalidateAll();
     if (provider instanceof AutoCloseable) {
       ((AutoCloseable) provider).close();
     }
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
index 0c383e520..33023343e 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
@@ -22,6 +22,8 @@ import com.codahale.metrics.annotation.ResponseMetered;
 import com.codahale.metrics.annotation.Timed;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Map;
 import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
@@ -29,6 +31,8 @@ import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HEAD;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.NotSupportedException;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -37,16 +41,24 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.CredentialPropertyUtils;
+import org.apache.gravitino.credential.CredentialProvider;
+import org.apache.gravitino.credential.CredentialUtils;
 import org.apache.gravitino.iceberg.service.IcebergCatalogWrapperManager;
 import org.apache.gravitino.iceberg.service.IcebergObjectMapper;
 import org.apache.gravitino.iceberg.service.IcebergRestUtils;
 import org.apache.gravitino.iceberg.service.metrics.IcebergMetricsManager;
 import org.apache.gravitino.metrics.MetricNames;
 import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.ServiceUnavailableException;
 import org.apache.iceberg.rest.RESTUtil;
 import org.apache.iceberg.rest.requests.CreateTableRequest;
 import org.apache.iceberg.rest.requests.ReportMetricsRequest;
 import org.apache.iceberg.rest.requests.UpdateTableRequest;
+import org.apache.iceberg.rest.responses.LoadTableResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,6 +69,9 @@ public class IcebergTableOperations {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(IcebergTableOperations.class);
 
+  @VisibleForTesting
+  public static final String X_ICEBERG_ACCESS_DELEGATION = 
"X-Iceberg-Access-Delegation";
+
   private IcebergCatalogWrapperManager icebergCatalogWrapperManager;
   private IcebergMetricsManager icebergMetricsManager;
 
@@ -92,15 +107,24 @@ public class IcebergTableOperations {
   public Response createTable(
       @PathParam("prefix") String prefix,
       @PathParam("namespace") String namespace,
-      CreateTableRequest createTableRequest) {
+      CreateTableRequest createTableRequest,
+      @HeaderParam(X_ICEBERG_ACCESS_DELEGATION) String accessDelegation) {
+    boolean isCredentialVending = isCredentialVending(accessDelegation);
     LOG.info(
-        "Create Iceberg table, namespace: {}, create table request: {}",
+        "Create Iceberg table, namespace: {}, create table request: {}, 
accessDelegation: {}, isCredentialVending: {}",
         namespace,
-        createTableRequest);
-    return IcebergRestUtils.ok(
+        createTableRequest,
+        accessDelegation,
+        isCredentialVending);
+    LoadTableResponse loadTableResponse =
         icebergCatalogWrapperManager
             .getOps(prefix)
-            .createTable(RESTUtil.decodeNamespace(namespace), 
createTableRequest));
+            .createTable(RESTUtil.decodeNamespace(namespace), 
createTableRequest);
+    if (isCredentialVending) {
+      return IcebergRestUtils.ok(injectCredentialConfig(prefix, 
loadTableResponse));
+    } else {
+      return IcebergRestUtils.ok(loadTableResponse);
+    }
   }
 
   @POST
@@ -162,12 +186,26 @@ public class IcebergTableOperations {
       @PathParam("prefix") String prefix,
       @PathParam("namespace") String namespace,
       @PathParam("table") String table,
-      @DefaultValue("all") @QueryParam("snapshots") String snapshots) {
+      @DefaultValue("all") @QueryParam("snapshots") String snapshots,
+      @HeaderParam(X_ICEBERG_ACCESS_DELEGATION) String accessDelegation) {
+    boolean isCredentialVending = isCredentialVending(accessDelegation);
+    LOG.info(
+        "Load iceberg table, namespace: {}, table: {}, access delegation: {}, "
+            + "credential vending: {}",
+        namespace,
+        table,
+        accessDelegation,
+        isCredentialVending);
     // todo support snapshots
     TableIdentifier tableIdentifier =
         TableIdentifier.of(RESTUtil.decodeNamespace(namespace), table);
-    return IcebergRestUtils.ok(
-        
icebergCatalogWrapperManager.getOps(prefix).loadTable(tableIdentifier));
+    LoadTableResponse loadTableResponse =
+        icebergCatalogWrapperManager.getOps(prefix).loadTable(tableIdentifier);
+    if (isCredentialVending) {
+      return IcebergRestUtils.ok(injectCredentialConfig(prefix, 
loadTableResponse));
+    } else {
+      return IcebergRestUtils.ok(loadTableResponse);
+    }
   }
 
   @HEAD
@@ -210,4 +248,49 @@ public class IcebergTableOperations {
       return updateTableRequest.toString();
     }
   }
+
+  private LoadTableResponse injectCredentialConfig(
+      String prefix, LoadTableResponse loadTableResponse) {
+    CredentialProvider credentialProvider =
+        icebergCatalogWrapperManager.getCredentialProvider(prefix);
+    if (credentialProvider == null) {
+      throw new NotSupportedException(
+          "Doesn't support credential vending, please add "
+              + CredentialConstants.CREDENTIAL_PROVIDER_TYPE
+              + " to the catalog configurations");
+    }
+    Credential credential =
+        CredentialUtils.vendCredential(
+            credentialProvider, loadTableResponse.tableMetadata().location());
+    if (credential == null) {
+      throw new ServiceUnavailableException(
+          "Couldn't generate credential for %s", 
credentialProvider.credentialType());
+    }
+    Map<String, String> credentialConfig = 
CredentialPropertyUtils.toIcebergProperties(credential);
+    return LoadTableResponse.builder()
+        .withTableMetadata(loadTableResponse.tableMetadata())
+        .addAllConfig(loadTableResponse.config())
+        .addAllConfig(credentialConfig)
+        .build();
+  }
+
+  private boolean isCredentialVending(String accessDelegation) {
+    if (StringUtils.isBlank(accessDelegation)) {
+      return false;
+    }
+    if ("vended-credentials".equalsIgnoreCase(accessDelegation)) {
+      return true;
+    }
+    if ("remote-signing".equalsIgnoreCase(accessDelegation)) {
+      throw new UnsupportedOperationException(
+          "Gravitino IcebergRESTServer doesn't support remote signing");
+    } else {
+      throw new IllegalArgumentException(
+          X_ICEBERG_ACCESS_DELEGATION
+              + ": "
+              + accessDelegation
+              + " is illegal, Iceberg REST spec 
supports:[vended-credentials,remote-signing], "
+              + "Gravitino Iceberg REST server supports: vended-credentials");
+    }
+  }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestGravitinoBasedIcebergCatalogWrapperProvider.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestDynamicIcebergCatalogWrapperProvider.java
similarity index 87%
rename from 
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestGravitinoBasedIcebergCatalogWrapperProvider.java
rename to 
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestDynamicIcebergCatalogWrapperProvider.java
index 8acac4ffd..f9ffbb427 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestGravitinoBasedIcebergCatalogWrapperProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestDynamicIcebergCatalogWrapperProvider.java
@@ -30,7 +30,7 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
-public class TestGravitinoBasedIcebergCatalogWrapperProvider {
+public class TestDynamicIcebergCatalogWrapperProvider {
   @Test
   public void testValidIcebergTableOps() {
     String hiveCatalogName = "hive_backend";
@@ -71,14 +71,15 @@ public class 
TestGravitinoBasedIcebergCatalogWrapperProvider {
               }
             });
 
-    GravitinoBasedIcebergCatalogWrapperProvider provider =
-        new GravitinoBasedIcebergCatalogWrapperProvider();
+    DynamicIcebergCatalogConfigProvider provider = new 
DynamicIcebergCatalogConfigProvider();
     GravitinoAdminClient client = Mockito.mock(GravitinoAdminClient.class);
     
Mockito.when(client.loadMetalake(Mockito.any())).thenReturn(gravitinoMetalake);
     provider.setClient(client);
 
-    IcebergCatalogWrapper hiveOps = 
provider.getIcebergTableOps(hiveCatalogName);
-    IcebergCatalogWrapper jdbcOps = 
provider.getIcebergTableOps(jdbcCatalogName);
+    IcebergCatalogWrapper hiveOps =
+        new 
IcebergCatalogWrapper(provider.getIcebergCatalogConfig(hiveCatalogName).get());
+    IcebergCatalogWrapper jdbcOps =
+        new 
IcebergCatalogWrapper(provider.getIcebergCatalogConfig(jdbcCatalogName).get());
 
     Assertions.assertEquals(hiveCatalogName, hiveOps.getCatalog().name());
     Assertions.assertEquals(jdbcCatalogName, jdbcOps.getCatalog().name());
@@ -101,16 +102,15 @@ public class 
TestGravitinoBasedIcebergCatalogWrapperProvider {
     GravitinoAdminClient client = Mockito.mock(GravitinoAdminClient.class);
     
Mockito.when(client.loadMetalake(Mockito.any())).thenReturn(gravitinoMetalake);
 
-    GravitinoBasedIcebergCatalogWrapperProvider provider =
-        new GravitinoBasedIcebergCatalogWrapperProvider();
+    DynamicIcebergCatalogConfigProvider provider = new 
DynamicIcebergCatalogConfigProvider();
     provider.setClient(client);
 
     Assertions.assertThrowsExactly(
-        IllegalArgumentException.class, () -> 
provider.getIcebergTableOps(invalidCatalogName));
+        IllegalArgumentException.class, () -> 
provider.getIcebergCatalogConfig(invalidCatalogName));
     Assertions.assertThrowsExactly(
-        IllegalArgumentException.class, () -> provider.getIcebergTableOps(""));
+        IllegalArgumentException.class, () -> 
provider.getIcebergCatalogConfig(""));
     Assertions.assertThrowsExactly(
         IllegalArgumentException.class,
-        () -> 
provider.getIcebergTableOps(IcebergConstants.GRAVITINO_DEFAULT_CATALOG));
+        () -> 
provider.getIcebergCatalogConfig(IcebergConstants.GRAVITINO_DEFAULT_CATALOG));
   }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestConfigBasedIcebergCatalogWrapperProvider.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestStaticIcebergCatalogWrapperProvider.java
similarity index 86%
rename from 
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestConfigBasedIcebergCatalogWrapperProvider.java
rename to 
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestStaticIcebergCatalogWrapperProvider.java
index 99e83f2e4..69f5b5ad2 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestConfigBasedIcebergCatalogWrapperProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestStaticIcebergCatalogWrapperProvider.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.iceberg.provider;
 
 import com.google.common.collect.Maps;
 import java.util.Map;
+import java.util.Optional;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
@@ -31,7 +32,8 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
-public class TestConfigBasedIcebergCatalogWrapperProvider {
+public class TestStaticIcebergCatalogWrapperProvider {
+
   @Test
   public void testValidIcebergTableOps() {
     String hiveCatalogName = "hive_backend";
@@ -58,16 +60,18 @@ public class TestConfigBasedIcebergCatalogWrapperProvider {
     config.put("catalog-backend", "memory");
     config.put("warehouse", "/tmp/");
 
-    ConfigBasedIcebergCatalogWrapperProvider provider =
-        new ConfigBasedIcebergCatalogWrapperProvider();
+    StaticIcebergCatalogConfigProvider provider = new 
StaticIcebergCatalogConfigProvider();
     provider.initialize(config);
 
     IcebergConfig hiveIcebergConfig = 
provider.catalogConfigs.get(hiveCatalogName);
     IcebergConfig jdbcIcebergConfig = 
provider.catalogConfigs.get(jdbcCatalogName);
     IcebergConfig defaultIcebergConfig = 
provider.catalogConfigs.get(defaultCatalogName);
-    IcebergCatalogWrapper hiveOps = 
provider.getIcebergTableOps(hiveCatalogName);
-    IcebergCatalogWrapper jdbcOps = 
provider.getIcebergTableOps(jdbcCatalogName);
-    IcebergCatalogWrapper defaultOps = 
provider.getIcebergTableOps(defaultCatalogName);
+    IcebergCatalogWrapper hiveOps =
+        new 
IcebergCatalogWrapper(provider.getIcebergCatalogConfig(hiveCatalogName).get());
+    IcebergCatalogWrapper jdbcOps =
+        new 
IcebergCatalogWrapper(provider.getIcebergCatalogConfig(jdbcCatalogName).get());
+    IcebergCatalogWrapper defaultOps =
+        new 
IcebergCatalogWrapper(provider.getIcebergCatalogConfig(defaultCatalogName).get());
 
     Assertions.assertEquals(
         hiveCatalogName, 
hiveIcebergConfig.get(IcebergConfig.CATALOG_BACKEND_NAME));
@@ -102,11 +106,10 @@ public class TestConfigBasedIcebergCatalogWrapperProvider 
{
   @ParameterizedTest
   @ValueSource(strings = {"", "not_match"})
   public void testInvalidIcebergTableOps(String catalogName) {
-    ConfigBasedIcebergCatalogWrapperProvider provider =
-        new ConfigBasedIcebergCatalogWrapperProvider();
+    StaticIcebergCatalogConfigProvider provider = new 
StaticIcebergCatalogConfigProvider();
     provider.initialize(Maps.newHashMap());
 
-    Assertions.assertThrowsExactly(
-        RuntimeException.class, () -> 
provider.getIcebergTableOps(catalogName));
+    Optional<IcebergConfig> config = 
provider.getIcebergCatalogConfig(catalogName);
+    Assertions.assertEquals(Optional.empty(), config);
   }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java
new file mode 100644
index 000000000..6b1e4c087
--- /dev/null
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java
@@ -0,0 +1,66 @@
+/*
+ *  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.iceberg.service.extension;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.credential.CredentialContext;
+import org.apache.gravitino.credential.CredentialProvider;
+
+public class DummyCredentialProvider implements CredentialProvider {
+  public static final String DUMMY_CREDENTIAL_TYPE = "iceberg-rest-dummy-test";
+
+  public static class SimpleCredential implements Credential {
+    @Override
+    public String credentialType() {
+      return DUMMY_CREDENTIAL_TYPE;
+    }
+
+    @Override
+    public long expireTimeInMs() {
+      return 0;
+    }
+
+    @Override
+    public Map<String, String> credentialInfo() {
+      return new HashMap<>();
+    }
+  }
+
+  @Override
+  public void initialize(Map<String, String> properties) {}
+
+  @Override
+  public String credentialType() {
+    return DUMMY_CREDENTIAL_TYPE;
+  }
+
+  @Nullable
+  @Override
+  public Credential getCredential(CredentialContext context) {
+    return new SimpleCredential();
+  }
+
+  @Override
+  public void close() throws IOException {}
+}
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergCatalogWrapperProviderForTest.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergCatalogWrapperProviderForTest.java
deleted file mode 100644
index 222391bcc..000000000
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergCatalogWrapperProviderForTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.iceberg.service.rest;
-
-import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
-import 
org.apache.gravitino.iceberg.provider.ConfigBasedIcebergCatalogWrapperProvider;
-
-public class ConfigBasedIcebergCatalogWrapperProviderForTest
-    extends ConfigBasedIcebergCatalogWrapperProvider {
-  @Override
-  public IcebergCatalogWrapper getIcebergTableOps(String prefix) {
-    return new IcebergCatalogWrapperForTest();
-  }
-}
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperForTest.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperForTest.java
index 69c0a50e4..f6326dd22 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperForTest.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperForTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.gravitino.iceberg.service.rest;
 
+import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
 import org.apache.iceberg.PartitionSpec;
 import org.apache.iceberg.Schema;
@@ -30,7 +31,12 @@ import org.apache.iceberg.types.Types.NestedField;
 import org.apache.iceberg.types.Types.StringType;
 import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
 
+// Used to override registerTable
 public class IcebergCatalogWrapperForTest extends IcebergCatalogWrapper {
+  public IcebergCatalogWrapperForTest(IcebergConfig icebergConfig) {
+    super(icebergConfig);
+  }
+
   @Override
   public LoadTableResponse registerTable(Namespace namespace, 
RegisterTableRequest request) {
     if (request.name().contains("fail")) {
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperManagerForTest.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperManagerForTest.java
new file mode 100644
index 000000000..7d359926a
--- /dev/null
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergCatalogWrapperManagerForTest.java
@@ -0,0 +1,37 @@
+/*
+ *  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.iceberg.service.rest;
+
+import java.util.Map;
+import org.apache.gravitino.iceberg.common.IcebergConfig;
+import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
+import org.apache.gravitino.iceberg.service.IcebergCatalogWrapperManager;
+
+// Provide a custom catalogWrapper to do test like `registerTable`
+public class IcebergCatalogWrapperManagerForTest extends 
IcebergCatalogWrapperManager {
+  public IcebergCatalogWrapperManagerForTest(Map<String, String> properties) {
+    super(properties);
+  }
+
+  @Override
+  public IcebergCatalogWrapper createIcebergCatalogWrapper(IcebergConfig 
icebergConfig) {
+    return new IcebergCatalogWrapperForTest(icebergConfig);
+  }
+}
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
index 4fc645132..1a085a251 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
@@ -24,10 +24,13 @@ import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
+import org.apache.gravitino.credential.CredentialConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
+import 
org.apache.gravitino.iceberg.provider.StaticIcebergCatalogConfigProvider;
 import org.apache.gravitino.iceberg.service.IcebergCatalogWrapperManager;
 import org.apache.gravitino.iceberg.service.IcebergExceptionMapper;
 import org.apache.gravitino.iceberg.service.IcebergObjectMapperProvider;
+import org.apache.gravitino.iceberg.service.extension.DummyCredentialProvider;
 import org.apache.gravitino.iceberg.service.metrics.IcebergMetricsManager;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.jackson.JacksonFeature;
@@ -74,12 +77,17 @@ public class IcebergRestTestUtil {
 
     if (bindIcebergTableOps) {
       Map<String, String> catalogConf = Maps.newHashMap();
-      catalogConf.put(String.format("catalog.%s.catalog-backend-name", 
PREFIX), PREFIX);
+      String catalogConfigPrefix = "catalog." + PREFIX;
       catalogConf.put(
-          IcebergConstants.ICEBERG_REST_CATALOG_PROVIDER,
-          ConfigBasedIcebergCatalogWrapperProviderForTest.class.getName());
+          IcebergConstants.ICEBERG_REST_CATALOG_CONFIG_PROVIDER,
+          StaticIcebergCatalogConfigProvider.class.getName());
+      catalogConf.put(String.format("%s.catalog-backend-name", 
catalogConfigPrefix), PREFIX);
+      catalogConf.put(
+          CredentialConstants.CREDENTIAL_PROVIDER_TYPE,
+          DummyCredentialProvider.DUMMY_CREDENTIAL_TYPE);
+      // used to override register table interface
       IcebergCatalogWrapperManager icebergCatalogWrapperManager =
-          new IcebergCatalogWrapperManager(catalogConf);
+          new IcebergCatalogWrapperManagerForTest(catalogConf);
 
       IcebergMetricsManager icebergMetricsManager = new 
IcebergMetricsManager(new IcebergConfig());
       resourceConfig.register(
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergTableOperations.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergTableOperations.java
index 6037302b8..809a4ff2c 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergTableOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergTableOperations.java
@@ -29,6 +29,8 @@ import javax.ws.rs.core.Application;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.iceberg.service.extension.DummyCredentialProvider;
 import org.apache.iceberg.MetadataUpdate;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.TableMetadata;
@@ -55,6 +57,12 @@ import org.junit.jupiter.params.provider.ValueSource;
 
 public class TestIcebergTableOperations extends TestIcebergNamespaceOperations 
{
 
+  private static final Schema tableSchema =
+      new Schema(NestedField.of(1, false, "foo_string", StringType.get()));
+
+  private static final Schema newTableSchema =
+      new Schema(NestedField.of(2, false, "foo_string1", StringType.get()));
+
   @Override
   protected Application configure() {
     ResourceConfig resourceConfig =
@@ -66,11 +74,163 @@ public class TestIcebergTableOperations extends 
TestIcebergNamespaceOperations {
     return resourceConfig;
   }
 
-  private static final Schema tableSchema =
-      new Schema(NestedField.of(1, false, "foo_string", StringType.get()));
+  @Test
+  void testCreateTable() {
+    verifyCreateTableFail("create_foo1", 404);
 
-  private static final Schema newTableSchema =
-      new Schema(NestedField.of(2, false, "foo_string1", StringType.get()));
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+
+    verifyCreateTableSucc("create_foo1");
+
+    verifyCreateTableFail("create_foo1", 409);
+    verifyCreateTableFail("", 400);
+  }
+
+  @Test
+  void testLoadTable() {
+    verifyLoadTableFail("load_foo1", 404);
+
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyCreateTableSucc("load_foo1");
+    verifyLoadTableSucc("load_foo1");
+
+    verifyLoadTableFail("load_foo2", 404);
+  }
+
+  @Test
+  void testDropTable() {
+    verifyDropTableFail("drop_foo1", 404);
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyDropTableFail("drop_foo1", 404);
+
+    verifyCreateTableSucc("drop_foo1");
+    verifyDropTableSucc("drop_foo1");
+    verifyLoadTableFail("drop_foo1", 404);
+  }
+
+  @Test
+  void testUpdateTable() {
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyCreateTableSucc("update_foo1");
+    TableMetadata metadata = getTableMeta("update_foo1");
+    verifyUpdateSucc("update_foo1", metadata);
+
+    verifyDropTableSucc("update_foo1");
+    verifyUpdateTableFail("update_foo1", 404, metadata);
+
+    verifyDropNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyUpdateTableFail("update_foo1", 404, metadata);
+  }
+
+  @ParameterizedTest
+  @ValueSource(strings = {"", IcebergRestTestUtil.PREFIX})
+  void testListTables(String prefix) {
+    setUrlPathWithPrefix(prefix);
+    verifyListTableFail(404);
+
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyCreateTableSucc("list_foo1");
+    verifyCreateTableSucc("list_foo2");
+    verifyListTableSucc(ImmutableSet.of("list_foo1", "list_foo2"));
+  }
+
+  @Test
+  void testTableExits() {
+    verifyTableExistsStatusCode("exists_foo2", 404);
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyTableExistsStatusCode("exists_foo2", 404);
+
+    verifyCreateTableSucc("exists_foo1");
+    verifyTableExistsStatusCode("exists_foo1", 200);
+    verifyLoadTableSucc("exists_foo1");
+  }
+
+  @ParameterizedTest
+  @ValueSource(strings = {"", IcebergRestTestUtil.PREFIX})
+  void testRenameTable(String prefix) {
+    setUrlPathWithPrefix(prefix);
+    // namespace not exits
+    verifyRenameTableFail("rename_foo1", "rename_foo3", 404);
+
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyCreateTableSucc("rename_foo1");
+    // rename
+    verifyRenameTableSucc("rename_foo1", "rename_foo2");
+    verifyLoadTableFail("rename_foo1", 404);
+    verifyLoadTableSucc("rename_foo2");
+
+    // source table not exists
+    verifyRenameTableFail("rename_foo1", "rename_foo3", 404);
+
+    // dest table exists
+    verifyCreateTableSucc("rename_foo3");
+    verifyRenameTableFail("rename_foo2", "rename_foo3", 409);
+  }
+
+  @Test
+  void testReportTableMetrics() {
+
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+    verifyCreateTableSucc("metrics_foo1");
+
+    ImmutableCommitMetricsResult commitMetrics = 
ImmutableCommitMetricsResult.builder().build();
+    CommitReport commitReport =
+        ImmutableCommitReport.builder()
+            .tableName("metrics_foo1")
+            .snapshotId(-1)
+            .sequenceNumber(-1)
+            .operation("append")
+            .commitMetrics(commitMetrics)
+            .build();
+    ReportMetricsRequest request = ReportMetricsRequest.of(commitReport);
+    Response response =
+        getReportMetricsClientBuilder("metrics_foo1")
+            .post(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE));
+
+    Assertions.assertEquals(Status.NO_CONTENT.getStatusCode(), 
response.getStatus());
+  }
+
+  @Test
+  void testCreateTableWithCredentialVending() {
+    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
+
+    // create the table without credential vending
+    Response response = doCreateTable("create_without_credential_vending");
+    Assertions.assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    LoadTableResponse loadTableResponse = 
response.readEntity(LoadTableResponse.class);
+    
Assertions.assertTrue(!loadTableResponse.config().containsKey(Credential.CREDENTIAL_TYPE));
+
+    // create the table with credential vending
+    String tableName = "create_with_credential_vending";
+    response = doCreateTableWithCredentialVending(tableName);
+    Assertions.assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    loadTableResponse = response.readEntity(LoadTableResponse.class);
+    Assertions.assertEquals(
+        DummyCredentialProvider.DUMMY_CREDENTIAL_TYPE,
+        loadTableResponse.config().get(Credential.CREDENTIAL_TYPE));
+
+    // load the table without credential vending
+    response = doLoadTable(tableName);
+    Assertions.assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    loadTableResponse = response.readEntity(LoadTableResponse.class);
+    
Assertions.assertTrue(!loadTableResponse.config().containsKey(Credential.CREDENTIAL_TYPE));
+
+    // load the table with credential vending
+    response = doLoadTableWithCredentialVending(tableName);
+    Assertions.assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    loadTableResponse = response.readEntity(LoadTableResponse.class);
+    Assertions.assertEquals(
+        DummyCredentialProvider.DUMMY_CREDENTIAL_TYPE,
+        loadTableResponse.config().get(Credential.CREDENTIAL_TYPE));
+  }
+
+  private Response doCreateTableWithCredentialVending(String name) {
+    CreateTableRequest createTableRequest =
+        
CreateTableRequest.builder().withName(name).withSchema(tableSchema).build();
+    return getTableClientBuilder()
+        .header(IcebergTableOperations.X_ICEBERG_ACCESS_DELEGATION, 
"vended-credentials")
+        .post(Entity.entity(createTableRequest, 
MediaType.APPLICATION_JSON_TYPE));
+  }
 
   private Response doCreateTable(String name) {
     CreateTableRequest createTableRequest =
@@ -103,6 +263,12 @@ public class TestIcebergTableOperations extends 
TestIcebergNamespaceOperations {
     return getTableClientBuilder(Optional.of(name)).head();
   }
 
+  private Response doLoadTableWithCredentialVending(String name) {
+    return getTableClientBuilder(Optional.of(name))
+        .header(IcebergTableOperations.X_ICEBERG_ACCESS_DELEGATION, 
"vended-credentials")
+        .get();
+  }
+
   private Response doLoadTable(String name) {
     return getTableClientBuilder(Optional.of(name)).get();
   }
@@ -116,6 +282,12 @@ public class TestIcebergTableOperations extends 
TestIcebergNamespaceOperations {
         .post(Entity.entity(updateTableRequest, 
MediaType.APPLICATION_JSON_TYPE));
   }
 
+  private TableMetadata getTableMeta(String tableName) {
+    Response response = doLoadTable(tableName);
+    LoadTableResponse loadTableResponse = 
response.readEntity(LoadTableResponse.class);
+    return loadTableResponse.tableMetadata();
+  }
+
   private void verifyUpdateTableFail(String name, int status, TableMetadata 
base) {
     Response response = doUpdateTable(name, base);
     Assertions.assertEquals(status, response.getStatus());
@@ -204,126 +376,4 @@ public class TestIcebergTableOperations extends 
TestIcebergNamespaceOperations {
     Response response = doCreateTable(name);
     Assertions.assertEquals(status, response.getStatus());
   }
-
-  @Test
-  void testCreateTable() {
-    verifyCreateTableFail("create_foo1", 404);
-
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-
-    verifyCreateTableSucc("create_foo1");
-
-    verifyCreateTableFail("create_foo1", 409);
-    verifyCreateTableFail("", 400);
-  }
-
-  @Test
-  void testLoadTable() {
-    verifyLoadTableFail("load_foo1", 404);
-
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyCreateTableSucc("load_foo1");
-    verifyLoadTableSucc("load_foo1");
-
-    verifyLoadTableFail("load_foo2", 404);
-  }
-
-  @Test
-  void testDropTable() {
-    verifyDropTableFail("drop_foo1", 404);
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyDropTableFail("drop_foo1", 404);
-
-    verifyCreateTableSucc("drop_foo1");
-    verifyDropTableSucc("drop_foo1");
-    verifyLoadTableFail("drop_foo1", 404);
-  }
-
-  private TableMetadata getTableMeta(String tableName) {
-    Response response = doLoadTable(tableName);
-    LoadTableResponse loadTableResponse = 
response.readEntity(LoadTableResponse.class);
-    return loadTableResponse.tableMetadata();
-  }
-
-  @Test
-  void testUpdateTable() {
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyCreateTableSucc("update_foo1");
-    TableMetadata metadata = getTableMeta("update_foo1");
-    verifyUpdateSucc("update_foo1", metadata);
-
-    verifyDropTableSucc("update_foo1");
-    verifyUpdateTableFail("update_foo1", 404, metadata);
-
-    verifyDropNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyUpdateTableFail("update_foo1", 404, metadata);
-  }
-
-  @ParameterizedTest
-  @ValueSource(strings = {"", IcebergRestTestUtil.PREFIX})
-  void testListTables(String prefix) {
-    setUrlPathWithPrefix(prefix);
-    verifyListTableFail(404);
-
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyCreateTableSucc("list_foo1");
-    verifyCreateTableSucc("list_foo2");
-    verifyListTableSucc(ImmutableSet.of("list_foo1", "list_foo2"));
-  }
-
-  @Test
-  void testTableExits() {
-    verifyTableExistsStatusCode("exists_foo2", 404);
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyTableExistsStatusCode("exists_foo2", 404);
-
-    verifyCreateTableSucc("exists_foo1");
-    verifyTableExistsStatusCode("exists_foo1", 200);
-    verifyLoadTableSucc("exists_foo1");
-  }
-
-  @ParameterizedTest
-  @ValueSource(strings = {"", IcebergRestTestUtil.PREFIX})
-  void testRenameTable(String prefix) {
-    setUrlPathWithPrefix(prefix);
-    // namespace not exits
-    verifyRenameTableFail("rename_foo1", "rename_foo3", 404);
-
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyCreateTableSucc("rename_foo1");
-    // rename
-    verifyRenameTableSucc("rename_foo1", "rename_foo2");
-    verifyLoadTableFail("rename_foo1", 404);
-    verifyLoadTableSucc("rename_foo2");
-
-    // source table not exists
-    verifyRenameTableFail("rename_foo1", "rename_foo3", 404);
-
-    // dest table exists
-    verifyCreateTableSucc("rename_foo3");
-    verifyRenameTableFail("rename_foo2", "rename_foo3", 409);
-  }
-
-  @Test
-  void testReportTableMetrics() {
-
-    verifyCreateNamespaceSucc(IcebergRestTestUtil.TEST_NAMESPACE_NAME);
-    verifyCreateTableSucc("metrics_foo1");
-
-    ImmutableCommitMetricsResult commitMetrics = 
ImmutableCommitMetricsResult.builder().build();
-    CommitReport commitReport =
-        ImmutableCommitReport.builder()
-            .tableName("metrics_foo1")
-            .snapshotId(-1)
-            .sequenceNumber(-1)
-            .operation("append")
-            .commitMetrics(commitMetrics)
-            .build();
-    ReportMetricsRequest request = ReportMetricsRequest.of(commitReport);
-    Response response =
-        getReportMetricsClientBuilder("metrics_foo1")
-            .post(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE));
-
-    Assertions.assertEquals(Status.NO_CONTENT.getStatusCode(), 
response.getStatus());
-  }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
 
b/iceberg/iceberg-rest-server/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
new file mode 100644
index 000000000..25a4f2d46
--- /dev/null
+++ 
b/iceberg/iceberg-rest-server/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+org.apache.gravitino.iceberg.service.extension.DummyCredentialProvider

Reply via email to