This is an automated email from the ASF dual-hosted git repository.
roryqi 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 8f0903de28 [Minor] Just a minor (#11229)
8f0903de28 is described below
commit 8f0903de2839ba2680ec1b7e66ef521d6beb37de
Author: roryqi <[email protected]>
AuthorDate: Wed May 27 19:14:42 2026 +0800
[Minor] Just a minor (#11229)
Just a minor.
Co-authored-by: Claude Opus 4.7 <[email protected]>
---
.../s3/credential/AwsIrsaCredentialGenerator.java | 28 ++++++--
.../gravitino/s3/credential/S3TokenGenerator.java | 28 ++++++--
.../s3/credential/TestS3PolicyPrefix.java | 37 ++++++++---
.../gravitino/credential/CredentialConstants.java | 11 ++++
.../credential/config/S3CredentialConfig.java | 18 +++++
.../catalog/fileset/FilesetCatalogImpl.java | 16 +++++
.../fileset/TestFilesetCatalogCredential.java | 77 ++++++++++++++++++++++
.../catalog/lakehouse/iceberg/IcebergCatalog.java | 4 ++
.../lakehouse/iceberg/TestIcebergCatalog.java | 28 ++++++++
9 files changed, 223 insertions(+), 24 deletions(-)
diff --git
a/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/AwsIrsaCredentialGenerator.java
b/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/AwsIrsaCredentialGenerator.java
index 3e83c57e98..cd5ccad8c5 100644
---
a/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/AwsIrsaCredentialGenerator.java
+++
b/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/AwsIrsaCredentialGenerator.java
@@ -59,6 +59,7 @@ public class AwsIrsaCredentialGenerator implements
CredentialGenerator<AwsIrsaCr
private int tokenExpireSecs;
private String region;
private String stsEndpoint;
+ private boolean listLocationPrefix;
@Override
public void initialize(Map<String, String> properties) {
@@ -70,6 +71,7 @@ public class AwsIrsaCredentialGenerator implements
CredentialGenerator<AwsIrsaCr
this.tokenExpireSecs = s3CredentialConfig.tokenExpireInSecs();
this.region = s3CredentialConfig.region();
this.stsEndpoint = s3CredentialConfig.stsEndpoint();
+ this.listLocationPrefix = s3CredentialConfig.listLocationPrefix();
}
@Override
@@ -213,7 +215,9 @@ public class AwsIrsaCredentialGenerator implements
CredentialGenerator<AwsIrsaCr
.addAction("s3:ListBucket")
.addResource(key))
.addConditions(
- IamConditionOperator.STRING_LIKE, "s3:prefix",
listPrefixes(rawPath));
+ IamConditionOperator.STRING_LIKE,
+ "s3:prefix",
+ listPrefixes(rawPath, listLocationPrefix));
bucketGetLocationStatementBuilder.computeIfAbsent(
bucketArn,
@@ -253,17 +257,27 @@ public class AwsIrsaCredentialGenerator implements
CredentialGenerator<AwsIrsaCr
}
/**
- * Builds the {@code s3:prefix} condition values for a {@code ListBucket}
statement. The prefixes
- * are restricted to the location and its descendants by requiring a
trailing slash, so a vended
- * credential cannot enumerate keys in adjacent locations (e.g. {@code
path/to/table_new}) that
- * merely share the location's string prefix. The empty path (bucket root)
is preserved so listing
- * the whole bucket still works.
+ * Builds the {@code s3:prefix} condition values for a {@code ListBucket}
statement. By default
+ * the prefixes are restricted to the location and its descendants by
requiring a trailing slash,
+ * so a vended credential cannot enumerate keys in adjacent locations (e.g.
{@code
+ * path/to/table_new}) that merely share the location's string prefix. The
empty path (bucket
+ * root) is preserved so listing the whole bucket still works.
+ *
+ * <p>When {@code includeLocationPrefix} is {@code true}, the bare location
prefix is also
+ * allowed. This is required for the Hadoop FileSystem API, whose {@code
getFileStatus} issues a
+ * HEAD on the bare location key: that HEAD returns 404 (instead of 403)
only if {@code
+ * ListBucket} permits the bare prefix. The trade-off is that the credential
can then enumerate
+ * sibling key names sharing the prefix.
*
* @param rawPath the object key prefix of the location, without a leading
slash
+ * @param includeLocationPrefix whether to also allow the bare location
prefix
* @return the allowed {@code s3:prefix} values
*/
- static List<String> listPrefixes(String rawPath) {
+ static List<String> listPrefixes(String rawPath, boolean
includeLocationPrefix) {
String dirPrefix = rawPath.isEmpty() || rawPath.endsWith("/") ? rawPath :
rawPath + "/";
+ if (includeLocationPrefix && !rawPath.equals(dirPrefix)) {
+ return Arrays.asList(rawPath, dirPrefix, addWildcardToPath(rawPath));
+ }
return Arrays.asList(dirPrefix, addWildcardToPath(rawPath));
}
diff --git
a/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/S3TokenGenerator.java
b/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/S3TokenGenerator.java
index e0ed31f45e..7cb3bc14e8 100644
---
a/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/S3TokenGenerator.java
+++
b/bundles/aws/src/main/java/org/apache/gravitino/s3/credential/S3TokenGenerator.java
@@ -56,6 +56,7 @@ public class S3TokenGenerator implements
CredentialGenerator<S3TokenCredential>
private String roleArn;
private String externalID;
private int tokenExpireSecs;
+ private boolean listLocationPrefix;
@Override
public void initialize(Map<String, String> properties) {
@@ -63,6 +64,7 @@ public class S3TokenGenerator implements
CredentialGenerator<S3TokenCredential>
this.roleArn = s3CredentialConfig.s3RoleArn();
this.externalID = s3CredentialConfig.externalID();
this.tokenExpireSecs = s3CredentialConfig.tokenExpireInSecs();
+ this.listLocationPrefix = s3CredentialConfig.listLocationPrefix();
this.stsClient = createStsClient(s3CredentialConfig);
}
@@ -148,7 +150,9 @@ public class S3TokenGenerator implements
CredentialGenerator<S3TokenCredential>
.addAction("s3:ListBucket")
.addResource(key))
.addConditions(
- IamConditionOperator.STRING_LIKE, "s3:prefix",
listPrefixes(rawPath));
+ IamConditionOperator.STRING_LIKE,
+ "s3:prefix",
+ listPrefixes(rawPath, listLocationPrefix));
bucketGetLocationStatementBuilder.computeIfAbsent(
bucketArn,
@@ -203,17 +207,27 @@ public class S3TokenGenerator implements
CredentialGenerator<S3TokenCredential>
}
/**
- * Builds the {@code s3:prefix} condition values for a {@code ListBucket}
statement. The prefixes
- * are restricted to the location and its descendants by requiring a
trailing slash, so a vended
- * credential cannot enumerate keys in adjacent locations (e.g. {@code
path/to/table_new}) that
- * merely share the location's string prefix. The empty path (bucket root)
is preserved so listing
- * the whole bucket still works.
+ * Builds the {@code s3:prefix} condition values for a {@code ListBucket}
statement. By default
+ * the prefixes are restricted to the location and its descendants by
requiring a trailing slash,
+ * so a vended credential cannot enumerate keys in adjacent locations (e.g.
{@code
+ * path/to/table_new}) that merely share the location's string prefix. The
empty path (bucket
+ * root) is preserved so listing the whole bucket still works.
+ *
+ * <p>When {@code includeLocationPrefix} is {@code true}, the bare location
prefix is also
+ * allowed. This is required for the Hadoop FileSystem API, whose {@code
getFileStatus} issues a
+ * HEAD on the bare location key: that HEAD returns 404 (instead of 403)
only if {@code
+ * ListBucket} permits the bare prefix. The trade-off is that the credential
can then enumerate
+ * sibling key names sharing the prefix.
*
* @param rawPath the object key prefix of the location, without a leading
slash
+ * @param includeLocationPrefix whether to also allow the bare location
prefix
* @return the allowed {@code s3:prefix} values
*/
- static List<String> listPrefixes(String rawPath) {
+ static List<String> listPrefixes(String rawPath, boolean
includeLocationPrefix) {
String dirPrefix = rawPath.isEmpty() || rawPath.endsWith("/") ? rawPath :
rawPath + "/";
+ if (includeLocationPrefix && !rawPath.equals(dirPrefix)) {
+ return Arrays.asList(rawPath, dirPrefix, addWildcardToPath(rawPath));
+ }
return Arrays.asList(dirPrefix, addWildcardToPath(rawPath));
}
diff --git
a/bundles/aws/src/test/java/org/apache/gravitino/s3/credential/TestS3PolicyPrefix.java
b/bundles/aws/src/test/java/org/apache/gravitino/s3/credential/TestS3PolicyPrefix.java
index d9fcc068e1..6e948d8560 100644
---
a/bundles/aws/src/test/java/org/apache/gravitino/s3/credential/TestS3PolicyPrefix.java
+++
b/bundles/aws/src/test/java/org/apache/gravitino/s3/credential/TestS3PolicyPrefix.java
@@ -24,30 +24,47 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
- * Verifies that the {@code s3:prefix} condition for {@code ListBucket}
statements is scoped to the
- * location and its descendants, so a vended credential cannot enumerate keys
in adjacent locations
- * sharing the same string prefix (e.g. {@code path/to/table_new} for location
{@code
- * path/to/table}).
+ * Verifies the {@code s3:prefix} condition for {@code ListBucket} statements.
By default it is
+ * scoped to the location and its descendants, so a vended credential cannot
enumerate keys in
+ * adjacent locations sharing the same string prefix (e.g. {@code
path/to/table_new} for location
+ * {@code path/to/table}). When the location prefix is enabled (fileset
catalogs), the bare location
+ * prefix is additionally allowed so a directory-root {@code getFileStatus}
HEAD returns 404.
*/
public class TestS3PolicyPrefix {
@Test
void testListPrefixesExcludeAdjacentLocations() {
- // The bare path "path/to/table" must NOT be allowed, otherwise ListBucket
with that prefix
- // would enumerate keys under the sibling "path/to/table_new".
- List<String> prefixes = S3TokenGenerator.listPrefixes("path/to/table");
+ // Default (secure): the bare path "path/to/table" must NOT be allowed,
otherwise ListBucket
+ // with that prefix would enumerate keys under the sibling
"path/to/table_new".
+ List<String> prefixes = S3TokenGenerator.listPrefixes("path/to/table",
false);
Assertions.assertEquals(2, prefixes.size());
Assertions.assertTrue(prefixes.contains("path/to/table/"));
Assertions.assertTrue(prefixes.contains("path/to/table/*"));
Assertions.assertFalse(prefixes.contains("path/to/table"));
// The IRSA generator shares the same scoping rule.
- Assertions.assertEquals(prefixes,
AwsIrsaCredentialGenerator.listPrefixes("path/to/table"));
+ Assertions.assertEquals(
+ prefixes, AwsIrsaCredentialGenerator.listPrefixes("path/to/table",
false));
+ }
+
+ @Test
+ void testListPrefixesIncludeLocationPrefix() {
+ // With the location prefix enabled, the bare path is added so the
directory-root HEAD returns
+ // 404 instead of 403, while the descendant prefixes remain.
+ List<String> prefixes = S3TokenGenerator.listPrefixes("path/to/table",
true);
+ Assertions.assertEquals(3, prefixes.size());
+ Assertions.assertTrue(prefixes.contains("path/to/table"));
+ Assertions.assertTrue(prefixes.contains("path/to/table/"));
+ Assertions.assertTrue(prefixes.contains("path/to/table/*"));
+
+ Assertions.assertEquals(
+ prefixes, AwsIrsaCredentialGenerator.listPrefixes("path/to/table",
true));
}
@Test
void testListPrefixesWithTrailingSlash() {
- List<String> prefixes = S3TokenGenerator.listPrefixes("path/to/table/");
+ // A trailing slash already denotes a directory; enabling the location
prefix adds nothing new.
+ List<String> prefixes = S3TokenGenerator.listPrefixes("path/to/table/",
true);
Assertions.assertEquals(2, prefixes.size());
Assertions.assertTrue(prefixes.contains("path/to/table/"));
Assertions.assertTrue(prefixes.contains("path/to/table/*"));
@@ -57,7 +74,7 @@ public class TestS3PolicyPrefix {
void testListPrefixesForBucketRoot() {
// For the bucket root the empty prefix must be preserved so listing the
whole bucket still
// works; "/*" alone would not match the empty list prefix.
- List<String> prefixes = S3TokenGenerator.listPrefixes("");
+ List<String> prefixes = S3TokenGenerator.listPrefixes("", true);
Assertions.assertEquals(2, prefixes.size());
Assertions.assertTrue(prefixes.contains(""));
Assertions.assertTrue(prefixes.contains("/*"));
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
index 220a128e53..ea10725b7b 100644
---
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
@@ -29,6 +29,17 @@ public class CredentialConstants {
public static final String CREDENTIAL_CACHE_EXPIRE_RATIO =
"credential-cache-expire-ratio";
public static final String CREDENTIAL_CACHE_MAX_SIZE =
"credential-cache-max-size";
public static final String S3_TOKEN_EXPIRE_IN_SECS =
"s3-token-expire-in-secs";
+
+ /**
+ * Whether the vended {@code s3:ListBucket} statement also allows the bare
location prefix. When
+ * {@code true}, a directory-root {@code getFileStatus} HEAD returns 404
instead of 403, at the
+ * cost of allowing enumeration of sibling keys that share the location's
string prefix. Defaults
+ * to {@code false} (secure); fileset catalogs enable it because access goes
through the Hadoop
+ * FileSystem API.
+ */
+ public static final String S3_CREDENTIAL_LIST_LOCATION_PREFIX =
+ "s3-credential-list-location-prefix";
+
public static final String OSS_TOKEN_EXPIRE_IN_SECS =
"oss-token-expire-in-secs";
public static final String ADLS_TOKEN_EXPIRE_IN_SECS =
"adls-token-expire-in-secs";
diff --git
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/config/S3CredentialConfig.java
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/config/S3CredentialConfig.java
index 1bdf7b2fad..61547e6746 100644
---
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/config/S3CredentialConfig.java
+++
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/credential/config/S3CredentialConfig.java
@@ -83,6 +83,20 @@ public class S3CredentialConfig extends Config {
.stringConf()
.create();
+ /**
+ * Internal flag, determined by the catalog type rather than configured by
users. When set, the
+ * vended {@code s3:ListBucket} statement also allows the bare location
prefix, so a
+ * directory-root {@code getFileStatus} returns 404 instead of 403, at the
cost of allowing
+ * enumeration of sibling keys sharing the location prefix.
+ */
+ public static final ConfigEntry<Boolean> S3_LIST_LOCATION_PREFIX =
+ new ConfigBuilder(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX)
+ .doc("Whether the vended s3:ListBucket statement also allows the
bare location prefix")
+ .version(ConfigConstants.VERSION_1_3_0)
+ .internal()
+ .booleanConf()
+ .createWithDefault(false);
+
public S3CredentialConfig(Map<String, String> properties) {
super(false);
loadFromMap(properties, k -> true);
@@ -118,4 +132,8 @@ public class S3CredentialConfig extends Config {
public String stsEndpoint() {
return this.get(S3_STS_ENDPOINT);
}
+
+ public boolean listLocationPrefix() {
+ return this.get(S3_LIST_LOCATION_PREFIX);
+ }
}
diff --git
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogImpl.java
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogImpl.java
index 76984f92ca..4402e45127 100644
---
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogImpl.java
+++
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogImpl.java
@@ -18,11 +18,13 @@
*/
package org.apache.gravitino.catalog.fileset;
+import java.util.HashMap;
import java.util.Map;
import org.apache.gravitino.connector.BaseCatalog;
import org.apache.gravitino.connector.CatalogOperations;
import org.apache.gravitino.connector.PropertiesMetadata;
import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.credential.CredentialConstants;
/**
* Hadoop catalog is a fileset catalog that can manage filesets on the Hadoop
Compatible File
@@ -68,4 +70,18 @@ public class FilesetCatalogImpl extends
BaseCatalog<FilesetCatalogImpl> {
public PropertiesMetadata filesetPropertiesMetadata() throws
UnsupportedOperationException {
return FILESET_PROPERTIES_META;
}
+
+ /**
+ * Fileset access goes through the Hadoop FileSystem API (e.g. GVFS over
S3A), whose {@code
+ * getFileStatus} issues a HEAD on the directory-root key. That HEAD returns
404 (instead of 403)
+ * only when the vended {@code s3:ListBucket} statement allows the bare
location prefix, so this
+ * catalog enables that internal flag. It is determined by the catalog type
and is not meant to be
+ * configured by users.
+ */
+ @Override
+ public Map<String, String> propertiesWithCredentialProviders() {
+ Map<String, String> properties = new
HashMap<>(super.propertiesWithCredentialProviders());
+ properties.put(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX,
"true");
+ return properties;
+ }
}
diff --git
a/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogCredential.java
b/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogCredential.java
new file mode 100644
index 0000000000..11d8978947
--- /dev/null
+++
b/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogCredential.java
@@ -0,0 +1,77 @@
+/*
+ * 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.catalog.fileset;
+
+import com.google.common.collect.Maps;
+import java.time.Instant;
+import java.util.Map;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.meta.AuditInfo;
+import org.apache.gravitino.meta.CatalogEntity;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Verifies that the fileset catalog enables the internal S3 location-prefix
flag so a
+ * directory-root {@code getFileStatus} HEAD returns 404 instead of 403,
regardless of any
+ * user-provided value.
+ */
+public class TestFilesetCatalogCredential {
+
+ private static FilesetCatalogImpl newCatalog(Map<String, String> properties)
{
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+ CatalogEntity entity =
+ CatalogEntity.builder()
+ .withId(1L)
+ .withName("catalog")
+ .withNamespace(Namespace.of("metalake"))
+ .withType(Catalog.Type.FILESET)
+ .withProvider("fileset")
+ .withProperties(properties)
+ .withAuditInfo(auditInfo)
+ .build();
+ return new
FilesetCatalogImpl().withCatalogConf(properties).withCatalogEntity(entity);
+ }
+
+ @Test
+ void testLocationPrefixEnabledByDefault() {
+ FilesetCatalogImpl catalog = newCatalog(Maps.newHashMap());
+ Assertions.assertEquals(
+ "true",
+ catalog
+ .propertiesWithCredentialProviders()
+ .get(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX));
+ }
+
+ @Test
+ void testLocationPrefixNotUserConfigurable() {
+ // A user attempt to disable it is overridden by the catalog type.
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX,
"false");
+ FilesetCatalogImpl catalog = newCatalog(properties);
+ Assertions.assertEquals(
+ "true",
+ catalog
+ .propertiesWithCredentialProviders()
+ .get(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX));
+ }
+}
diff --git
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
index 653831464d..6b6b375596 100644
---
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
+++
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
@@ -94,6 +94,10 @@ public class IcebergCatalog extends
BaseCatalog<IcebergCatalog> {
@Evolving
public Map<String, String> propertiesWithCredentialProviders() {
Map<String, String> properties =
Maps.newHashMap(super.propertiesWithCredentialProviders());
+ // Iceberg is security-first: the vended s3:ListBucket statement keeps the
bare location prefix
+ // disabled so a credential cannot enumerate sibling keys sharing the
location prefix. This is
+ // determined by the catalog type and is not meant to be configured by
users.
+ properties.put(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX,
"false");
return applyDefaultCredentialProviders(properties);
}
diff --git
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
index ca8fb57c34..9249088593 100644
---
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
+++
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
@@ -366,6 +366,34 @@ public class TestIcebergCatalog {
Assertions.assertEquals("custom-provider", credentialProviders);
}
+ @Test
+ void testS3ListLocationPrefixForcedDisabled() {
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+ // Iceberg is security-first: a user attempt to enable the location prefix
is overridden.
+ Map<String, String> props = Maps.newHashMap();
+ props.put(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX, "true");
+
+ CatalogEntity entity =
+ CatalogEntity.builder()
+ .withId(5L)
+ .withName("secure-catalog")
+ .withNamespace(Namespace.of("metalake"))
+ .withType(IcebergCatalog.Type.RELATIONAL)
+ .withProvider("iceberg")
+ .withAuditInfo(auditInfo)
+ .withProperties(props)
+ .build();
+
+ IcebergCatalog catalog = new
IcebergCatalog().withCatalogConf(props).withCatalogEntity(entity);
+ Assertions.assertEquals(
+ "false",
+ catalog
+ .propertiesWithCredentialProviders()
+ .get(CredentialConstants.S3_CREDENTIAL_LIST_LOCATION_PREFIX));
+ }
+
@Test
void testJdbcBackendWithOSSCredentialProviders() {
AuditInfo auditInfo =