adoroszlai commented on code in PR #9516:
URL: https://github.com/apache/ozone/pull/9516#discussion_r2660861488


##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java:
##########
@@ -96,6 +90,8 @@ public class BucketEndpoint extends EndpointBase {
   private boolean listKeysShallowEnabled;
   private int maxKeysLimit = 1000;
 
+  private List<BucketOperationHandler> putHandlers;

Review Comment:
   `BucketOperationHandler` will eventually handle other operations (`get`, 
etc.), so this should be simply `handlers`.



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/AclHandler.java:
##########
@@ -0,0 +1,279 @@
+/*
+ * 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.hadoop.ozone.s3.endpoint;
+
+import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS;
+import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NOT_IMPLEMENTED;
+import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.OzoneAcl;
+import org.apache.hadoop.ozone.audit.S3GAction;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.client.OzoneVolume;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
+import org.apache.hadoop.ozone.om.helpers.OzoneAclUtil;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
+import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.util.Time;
+import org.apache.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handler for bucket ACL operations (?acl query parameter).
+ * Implements PUT operations for bucket Access Control Lists.
+ *
+ * This handler extends EndpointBase to inherit all required functionality
+ * (configuration, headers, request context, audit logging, metrics, etc.).
+ */
+public class AclHandler extends EndpointBase implements BucketOperationHandler 
{
+
+  private static final Logger LOG = LoggerFactory.getLogger(AclHandler.class);
+
+  /**
+   * Determine if this handler should handle the current request.
+   * @return true if the request has the "acl" query parameter
+   */
+  private boolean shouldHandle() {
+    return queryParams().get(QueryParams.ACL) != null;
+  }
+
+  /**
+   * Implement acl put.
+   * <p>
+   * see: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAcl.html
+   */
+  @Override
+  public Response handlePutRequest(String bucketName, InputStream body)
+      throws IOException, OS3Exception {
+
+    if (!shouldHandle()) {
+      return null;  // Not responsible for this request
+    }
+
+    long startNanos = Time.monotonicNowNanos();
+    S3GAction s3GAction = S3GAction.PUT_ACL;
+
+    String grantReads = getHeaders().getHeaderString(S3Acl.GRANT_READ);
+    String grantWrites = getHeaders().getHeaderString(S3Acl.GRANT_WRITE);
+    String grantReadACP = getHeaders().getHeaderString(S3Acl.GRANT_READ_ACP);
+    String grantWriteACP = getHeaders().getHeaderString(S3Acl.GRANT_WRITE_ACP);
+    String grantFull = getHeaders().getHeaderString(S3Acl.GRANT_FULL_CONTROL);
+
+    try {
+      OzoneBucket bucket = getBucket(bucketName);
+      S3Owner.verifyBucketOwnerCondition(getHeaders(), bucketName, 
bucket.getOwner());
+      OzoneVolume volume = getVolume();
+
+      List<OzoneAcl> ozoneAclListOnBucket = new ArrayList<>();
+      List<OzoneAcl> ozoneAclListOnVolume = new ArrayList<>();
+
+      if (grantReads == null && grantWrites == null && grantReadACP == null
+          && grantWriteACP == null && grantFull == null) {
+        // Handle grants in body
+        S3BucketAcl putBucketAclRequest =
+            new PutBucketAclRequestUnmarshaller().readFrom(body);
+        ozoneAclListOnBucket.addAll(
+            S3Acl.s3AclToOzoneNativeAclOnBucket(putBucketAclRequest));
+        ozoneAclListOnVolume.addAll(
+            S3Acl.s3AclToOzoneNativeAclOnVolume(putBucketAclRequest));
+      } else {
+        // Handle grants in headers
+        if (grantReads != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantReads,
+              S3Acl.ACLType.READ.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantReads,
+              S3Acl.ACLType.READ.getValue()));
+        }
+        if (grantWrites != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantWrites,
+              S3Acl.ACLType.WRITE.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantWrites,
+              S3Acl.ACLType.WRITE.getValue()));
+        }
+        if (grantReadACP != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantReadACP,
+              S3Acl.ACLType.READ_ACP.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantReadACP,
+              S3Acl.ACLType.READ_ACP.getValue()));
+        }
+        if (grantWriteACP != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantWriteACP,
+              S3Acl.ACLType.WRITE_ACP.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantWriteACP,
+              S3Acl.ACLType.WRITE_ACP.getValue()));
+        }
+        if (grantFull != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantFull,
+              S3Acl.ACLType.FULL_CONTROL.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantFull,
+              S3Acl.ACLType.FULL_CONTROL.getValue()));
+        }
+      }
+
+      // A put request will reset all previous ACLs on bucket
+      bucket.setAcl(ozoneAclListOnBucket);
+
+      // A put request will reset input user/group's permission on volume
+      List<OzoneAcl> acls = bucket.getAcls();
+      List<OzoneAcl> aclsToRemoveOnVolume = new ArrayList<>();
+      List<OzoneAcl> currentAclsOnVolume = volume.getAcls();
+
+      // Remove input user/group's permission from Volume first
+      if (!currentAclsOnVolume.isEmpty()) {
+        for (OzoneAcl acl : acls) {
+          if (acl.getAclScope() == ACCESS) {
+            aclsToRemoveOnVolume.addAll(OzoneAclUtil.filterAclList(
+                acl.getName(), acl.getType(), currentAclsOnVolume));
+          }
+        }
+        for (OzoneAcl acl : aclsToRemoveOnVolume) {
+          volume.removeAcl(acl);
+        }
+      }
+
+      // Add new permission on Volume
+      for (OzoneAcl acl : ozoneAclListOnVolume) {
+        volume.addAcl(acl);
+      }
+
+      getMetrics().updatePutAclSuccessStats(startNanos);
+      auditWriteSuccess(s3GAction);
+      return Response.status(HttpStatus.SC_OK).build();
+
+    } catch (OMException exception) {
+      getMetrics().updatePutAclFailureStats(startNanos);
+      auditWriteFailure(s3GAction, exception);
+      if (exception.getResult() == ResultCodes.BUCKET_NOT_FOUND) {
+        throw newError(S3ErrorTable.NO_SUCH_BUCKET, bucketName, exception);
+      } else if (isAccessDenied(exception)) {
+        throw newError(S3ErrorTable.ACCESS_DENIED, bucketName, exception);
+      }
+      throw exception;
+    } catch (OS3Exception ex) {
+      getMetrics().updatePutAclFailureStats(startNanos);
+      auditWriteFailure(s3GAction, ex);
+      throw ex;
+    }
+  }
+
+  /**
+   * Convert ACL string to Ozone ACL on bucket.
+   *
+   * Example: x-amz-grant-write: id="111122223333", id="555566667777"
+   */
+  private List<OzoneAcl> getAndConvertAclOnBucket(String value,
+                                                  String permission)
+      throws OS3Exception {
+    return parseAndConvertAcl(value, permission, true);
+  }
+
+  /**
+   * Convert ACL string to Ozone ACL on volume.
+   */
+  private List<OzoneAcl> getAndConvertAclOnVolume(String value,
+                                                  String permission)
+      throws OS3Exception {
+    return parseAndConvertAcl(value, permission, false);
+  }
+
+  /**
+   * Parse ACL string and convert to Ozone ACLs.
+   *
+   * This is a common method extracted from getAndConvertAclOnBucket and
+   * getAndConvertAclOnVolume to reduce code duplication.
+   *
+   * @param value the ACL header value (e.g., "id=\"user1\",id=\"user2\"")
+   * @param permission the S3 permission type (READ, WRITE, etc.)
+   * @param isBucket true for bucket ACL, false for volume ACL
+   * @return list of OzoneAcl objects
+   * @throws OS3Exception if parsing fails or grantee type is not supported
+   */
+  private List<OzoneAcl> parseAndConvertAcl(String value, String permission,
+                                            boolean isBucket)
+      throws OS3Exception {
+    List<OzoneAcl> ozoneAclList = new ArrayList<>();
+    if (StringUtils.isEmpty(value)) {
+      return ozoneAclList;
+    }
+
+    String[] subValues = value.split(",");
+    for (String acl : subValues) {
+      String[] part = acl.split("=");
+      if (part.length != 2) {
+        throw newError(S3ErrorTable.INVALID_ARGUMENT, acl);
+      }
+
+      S3Acl.ACLIdentityType type =
+          S3Acl.ACLIdentityType.getTypeFromHeaderType(part[0]);
+      if (type == null || !type.isSupported()) {
+        LOG.warn("S3 grantee {} is null or not supported", part[0]);
+        throw newError(NOT_IMPLEMENTED, part[0]);
+      }
+
+      String userId = part[1];
+
+      if (isBucket) {
+        // Build ACL on Bucket
+        EnumSet<IAccessAuthorizer.ACLType> aclsOnBucket =
+            S3Acl.getOzoneAclOnBucketFromS3Permission(permission);
+        ozoneAclList.add(OzoneAcl.of(
+            IAccessAuthorizer.ACLIdentityType.USER,
+            userId,
+            OzoneAcl.AclScope.DEFAULT,
+            aclsOnBucket
+        ));

Review Comment:
   nit: please `import static` for `USER` and `DEFAULT`, then this can be 
written in a single line.
   
   (Also two other similar statements below.)



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketOperationHandler.java:
##########
@@ -0,0 +1,48 @@
+/*
+ * 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.hadoop.ozone.s3.endpoint;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.ws.rs.core.Response;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+
+/**
+ * Interface for handling bucket operations using chain of responsibility 
pattern.
+ * Each implementation handles a specific S3 bucket subresource operation
+ * (e.g., ?acl, ?lifecycle, ?notification).
+ *
+ * Implementations should extend EndpointBase to inherit all required 
functionality
+ * (configuration, headers, request context, audit logging, metrics, etc.).
+ */
+public interface BucketOperationHandler {
+
+  /**
+   * Handle the bucket PUT operation if this handler is responsible for it.
+   * The handler inspects the request (query parameters, headers, etc.) to 
determine
+   * if it should handle the request.
+   *
+   * @param bucketName the name of the bucket
+   * @param body the request body stream
+   * @return Response if this handler handles the request, null otherwise
+   * @throws IOException if an I/O error occurs
+   * @throws OS3Exception if an S3-specific error occurs
+   */
+  Response handlePutRequest(String bucketName, InputStream body)
+      throws IOException, OS3Exception;

Review Comment:
   Some handlers may not implement `PUT`, so let's make this a `default` method 
that `return null`.



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/AclHandler.java:
##########
@@ -0,0 +1,279 @@
+/*
+ * 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.hadoop.ozone.s3.endpoint;
+
+import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS;
+import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NOT_IMPLEMENTED;
+import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.OzoneAcl;
+import org.apache.hadoop.ozone.audit.S3GAction;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.client.OzoneVolume;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
+import org.apache.hadoop.ozone.om.helpers.OzoneAclUtil;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
+import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.util.Time;
+import org.apache.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handler for bucket ACL operations (?acl query parameter).
+ * Implements PUT operations for bucket Access Control Lists.
+ *
+ * This handler extends EndpointBase to inherit all required functionality
+ * (configuration, headers, request context, audit logging, metrics, etc.).
+ */
+public class AclHandler extends EndpointBase implements BucketOperationHandler 
{

Review Comment:
   Please rename to `BucketAclHandler`.  Object ACL is not implemented yet, but 
is planned (HDDS-9631).



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/AclHandler.java:
##########
@@ -0,0 +1,279 @@
+/*
+ * 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.hadoop.ozone.s3.endpoint;
+
+import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS;
+import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NOT_IMPLEMENTED;
+import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.OzoneAcl;
+import org.apache.hadoop.ozone.audit.S3GAction;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.client.OzoneVolume;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
+import org.apache.hadoop.ozone.om.helpers.OzoneAclUtil;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
+import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.util.Time;
+import org.apache.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handler for bucket ACL operations (?acl query parameter).
+ * Implements PUT operations for bucket Access Control Lists.
+ *
+ * This handler extends EndpointBase to inherit all required functionality
+ * (configuration, headers, request context, audit logging, metrics, etc.).
+ */
+public class AclHandler extends EndpointBase implements BucketOperationHandler 
{
+
+  private static final Logger LOG = LoggerFactory.getLogger(AclHandler.class);
+
+  /**
+   * Determine if this handler should handle the current request.
+   * @return true if the request has the "acl" query parameter
+   */
+  private boolean shouldHandle() {
+    return queryParams().get(QueryParams.ACL) != null;
+  }
+
+  /**
+   * Implement acl put.
+   * <p>
+   * see: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAcl.html
+   */
+  @Override
+  public Response handlePutRequest(String bucketName, InputStream body)
+      throws IOException, OS3Exception {
+
+    if (!shouldHandle()) {
+      return null;  // Not responsible for this request
+    }
+
+    long startNanos = Time.monotonicNowNanos();
+    S3GAction s3GAction = S3GAction.PUT_ACL;
+
+    String grantReads = getHeaders().getHeaderString(S3Acl.GRANT_READ);
+    String grantWrites = getHeaders().getHeaderString(S3Acl.GRANT_WRITE);
+    String grantReadACP = getHeaders().getHeaderString(S3Acl.GRANT_READ_ACP);
+    String grantWriteACP = getHeaders().getHeaderString(S3Acl.GRANT_WRITE_ACP);
+    String grantFull = getHeaders().getHeaderString(S3Acl.GRANT_FULL_CONTROL);
+
+    try {
+      OzoneBucket bucket = getBucket(bucketName);
+      S3Owner.verifyBucketOwnerCondition(getHeaders(), bucketName, 
bucket.getOwner());
+      OzoneVolume volume = getVolume();
+
+      List<OzoneAcl> ozoneAclListOnBucket = new ArrayList<>();
+      List<OzoneAcl> ozoneAclListOnVolume = new ArrayList<>();
+
+      if (grantReads == null && grantWrites == null && grantReadACP == null
+          && grantWriteACP == null && grantFull == null) {
+        // Handle grants in body
+        S3BucketAcl putBucketAclRequest =
+            new PutBucketAclRequestUnmarshaller().readFrom(body);
+        ozoneAclListOnBucket.addAll(
+            S3Acl.s3AclToOzoneNativeAclOnBucket(putBucketAclRequest));
+        ozoneAclListOnVolume.addAll(
+            S3Acl.s3AclToOzoneNativeAclOnVolume(putBucketAclRequest));
+      } else {
+        // Handle grants in headers
+        if (grantReads != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantReads,
+              S3Acl.ACLType.READ.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantReads,
+              S3Acl.ACLType.READ.getValue()));
+        }
+        if (grantWrites != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantWrites,
+              S3Acl.ACLType.WRITE.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantWrites,
+              S3Acl.ACLType.WRITE.getValue()));
+        }
+        if (grantReadACP != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantReadACP,
+              S3Acl.ACLType.READ_ACP.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantReadACP,
+              S3Acl.ACLType.READ_ACP.getValue()));
+        }
+        if (grantWriteACP != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantWriteACP,
+              S3Acl.ACLType.WRITE_ACP.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantWriteACP,
+              S3Acl.ACLType.WRITE_ACP.getValue()));
+        }
+        if (grantFull != null) {
+          ozoneAclListOnBucket.addAll(getAndConvertAclOnBucket(grantFull,
+              S3Acl.ACLType.FULL_CONTROL.getValue()));
+          ozoneAclListOnVolume.addAll(getAndConvertAclOnVolume(grantFull,
+              S3Acl.ACLType.FULL_CONTROL.getValue()));
+        }
+      }
+
+      // A put request will reset all previous ACLs on bucket
+      bucket.setAcl(ozoneAclListOnBucket);
+
+      // A put request will reset input user/group's permission on volume
+      List<OzoneAcl> acls = bucket.getAcls();
+      List<OzoneAcl> aclsToRemoveOnVolume = new ArrayList<>();
+      List<OzoneAcl> currentAclsOnVolume = volume.getAcls();
+
+      // Remove input user/group's permission from Volume first
+      if (!currentAclsOnVolume.isEmpty()) {
+        for (OzoneAcl acl : acls) {
+          if (acl.getAclScope() == ACCESS) {
+            aclsToRemoveOnVolume.addAll(OzoneAclUtil.filterAclList(
+                acl.getName(), acl.getType(), currentAclsOnVolume));
+          }
+        }
+        for (OzoneAcl acl : aclsToRemoveOnVolume) {
+          volume.removeAcl(acl);
+        }
+      }
+
+      // Add new permission on Volume
+      for (OzoneAcl acl : ozoneAclListOnVolume) {
+        volume.addAcl(acl);
+      }
+
+      getMetrics().updatePutAclSuccessStats(startNanos);
+      auditWriteSuccess(s3GAction);
+      return Response.status(HttpStatus.SC_OK).build();
+
+    } catch (OMException exception) {
+      getMetrics().updatePutAclFailureStats(startNanos);
+      auditWriteFailure(s3GAction, exception);
+      if (exception.getResult() == ResultCodes.BUCKET_NOT_FOUND) {
+        throw newError(S3ErrorTable.NO_SUCH_BUCKET, bucketName, exception);
+      } else if (isAccessDenied(exception)) {
+        throw newError(S3ErrorTable.ACCESS_DENIED, bucketName, exception);
+      }
+      throw exception;
+    } catch (OS3Exception ex) {
+      getMetrics().updatePutAclFailureStats(startNanos);
+      auditWriteFailure(s3GAction, ex);
+      throw ex;
+    }
+  }
+
+  /**
+   * Convert ACL string to Ozone ACL on bucket.
+   *
+   * Example: x-amz-grant-write: id="111122223333", id="555566667777"
+   */
+  private List<OzoneAcl> getAndConvertAclOnBucket(String value,
+                                                  String permission)

Review Comment:
   nit: Please do not format method signature like this. Whenever visibility / 
return type / method name / other modifiers are changed, we would have to 
reindent all parameters.
   
   I know it's the same as it was in `BucketEndpoint`, but we can improve 
formatting when moving code to a new file.
   
   (Applies to few other similar methods.)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to