From d005fa376ee8d90794eeb39f9b82b1c98fd64329 Mon Sep 17 00:00:00 2001 From: sravani-revuri Date: Wed, 29 Jul 2026 18:45:38 +0530 Subject: [PATCH] HDDS-15645. ListObjectsV2 returns Owner when FetchOwner is not requested --- .../s3/commontypes/RequestParameters.java | 14 +++++++ .../ozone/s3/endpoint/BucketEndpoint.java | 19 +++++++--- .../apache/hadoop/ozone/s3/util/S3Consts.java | 2 + .../ozone/s3/endpoint/TestBucketList.java | 37 +++++++++++++++++++ 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/commontypes/RequestParameters.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/commontypes/RequestParameters.java index 127abe871211..ebcddf592640 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/commontypes/RequestParameters.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/commontypes/RequestParameters.java @@ -65,6 +65,20 @@ default void setInt(String key, int value) { } } + default boolean getBoolean(String key, boolean defaultValue) { + final String value = get(key); + if (value == null) { + return defaultValue; + } + if ("true".equalsIgnoreCase(value)) { + return true; + } + if ("false".equalsIgnoreCase(value)) { + return false; + } + throw S3ErrorTable.newError(S3ErrorTable.INVALID_ARGUMENT, key); + } + /** Mutable implementation based on {@link MultivaluedMap}. */ final class MultivaluedMapImpl implements Mutable { private final MultivaluedMap params; diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java index ad545667fea5..7840f195d0f5 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java @@ -113,7 +113,7 @@ Response handleGetRequest(S3RequestContext context, String bucketName) throws IO int maxKeys = queryParams().getInt(QueryParams.MAX_KEYS, 1000); String prefix = queryParams().get(QueryParams.PREFIX, ""); String startAfter = queryParams().get(QueryParams.START_AFTER); - + boolean includeOwner = shouldIncludeOwnerInListResponse(); Iterator ozoneKeyIterator = null; // AWS S3 treats an empty continuation-token as no token: list from the // start and echo the empty token back (see setContinueToken below). @@ -217,11 +217,11 @@ Response handleGetRequest(S3RequestContext context, String bucketName) throws IO } else { // means our key is matched with prefix if prefix is given and it // does not have any common prefix. - addKey(response, next); + addKey(response, next, includeOwner); count++; } } else { - addKey(response, next); + addKey(response, next, includeOwner); count++; } @@ -402,7 +402,7 @@ public MultiDeleteResponse multiDelete( return result; } - private void addKey(ListObjectResponse response, OzoneKey next) { + private void addKey(ListObjectResponse response, OzoneKey next, boolean includeOwner) { KeyMetadata keyMetadata = new KeyMetadata(); keyMetadata.setKey(EncodingTypeObject.createNullable(next.getName(), response.getEncodingType())); @@ -414,8 +414,9 @@ private void addKey(ListObjectResponse response, OzoneKey next) { keyMetadata.setStorageClass(S3StorageType.fromReplicationConfig( next.getReplicationConfig()).toString()); keyMetadata.setLastModified(next.getModificationTime()); - String displayName = next.getOwner(); - keyMetadata.setOwner(S3Owner.of(displayName)); + if (includeOwner) { + keyMetadata.setOwner(S3Owner.of(next.getOwner())); + } response.addKey(keyMetadata); } @@ -439,4 +440,10 @@ protected void init() { .build(); handler = new AuditingBucketOperationHandler(chain); } + + private boolean shouldIncludeOwnerInListResponse() { + int listType = queryParams().getInt(QueryParams.LIST_TYPE, 1); + boolean fetchOwner = queryParams().getBoolean(QueryParams.FETCH_OWNER, false); + return listType != 2 || fetchOwner; + } } diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java index a2ed3a84c250..ac00bee9ae8d 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java @@ -144,6 +144,8 @@ public static final class QueryParams { public static final String DELIMITER = "delimiter"; public static final String ENCODING_TYPE = "encoding-type"; public static final String KEY_MARKER = "key-marker"; + public static final String FETCH_OWNER = "fetch-owner"; + public static final String LIST_TYPE = "list-type"; // GetBucketLocation is not implemented public static final String LOCATION = "location"; public static final String MARKER = "marker"; diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketList.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketList.java index f78c2cc620c5..3ce9e70153a2 100644 --- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketList.java +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketList.java @@ -700,6 +700,43 @@ public void continuationTokenXmlElementName() throws Exception { "response must not use the non-AWS element"); } + @Test + public void listObjectOwnerOmittedForListV2ByDefault() throws OS3Exception, IOException { + OzoneClient client = createClientWithKeys("key1", "key2"); + BucketEndpoint endpoint = newBucketEndpointBuilder().setClient(client).build(); + + endpoint.queryParamsForTest().setInt(QueryParams.LIST_TYPE, 2); + ListObjectResponse response = (ListObjectResponse) endpoint.get("b1").getEntity(); + + assertEquals(2, response.getContents().size()); + assertNull(response.getContents().get(0).getOwner()); + assertNull(response.getContents().get(1).getOwner()); + } + + @Test + public void listObjectOwnerOmittedForListV2WhenFetchOwnerFalse() throws OS3Exception, IOException { + OzoneClient client = createClientWithKeys("key1"); + BucketEndpoint endpoint = newBucketEndpointBuilder().setClient(client).build(); + + endpoint.queryParamsForTest().setInt(QueryParams.LIST_TYPE, 2); + endpoint.queryParamsForTest().set(QueryParams.FETCH_OWNER, "false"); + ListObjectResponse response = (ListObjectResponse) endpoint.get("b1").getEntity(); + + assertNull(response.getContents().get(0).getOwner()); + } + + @Test + public void listObjectOwnerIncludedForListV2WhenFetchOwnerTrue() throws OS3Exception, IOException { + OzoneClient client = createClientWithKeys("key1"); + BucketEndpoint endpoint = newBucketEndpointBuilder().setClient(client).build(); + + endpoint.queryParamsForTest().setInt(QueryParams.LIST_TYPE, 2); + endpoint.queryParamsForTest().set(QueryParams.FETCH_OWNER, "true"); + ListObjectResponse response = (ListObjectResponse) endpoint.get("b1").getEntity(); + + assertNotNull(response.getContents().get(0).getOwner()); + } + private OzoneClient createClientWithKeys(String... keys) throws IOException { OzoneClient client = new OzoneClientStub();