Copilot commented on code in PR #10197:
URL: https://github.com/apache/ozone/pull/10197#discussion_r3386827594


##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java:
##########
@@ -692,6 +687,22 @@ private RequestContext 
maybeAttachSessionPolicyFromThreadLocal(RequestContext co
         .build();
   }
 
+  /**
+   * Attaches s3 action to RequestContext if an S3Authentication is found in 
the Ozone Manager thread local,
+   * and it has an s3 action.  Otherwise, returns the RequestContext as it was 
before.
+   * @param context the original RequestContext
+   * @return RequestContext as before or with s3 action embedded
+   */
+  private RequestContext maybeAttachS3ActionFromThreadLocal(RequestContext 
context) {
+    final S3Authentication s3Authentication = OzoneManager.getS3Auth();
+    if (s3Authentication == null || !s3Authentication.hasS3Action()) {
+      return context;
+    }
+    return context.toBuilder()
+        .setS3Action(s3Authentication.getS3Action())
+        .build();

Review Comment:
   `maybeAttachS3ActionFromThreadLocal` only checks `hasS3Action()`. With 
protobuf `optional string`, `hasS3Action()` can be true even when the value is 
the empty string, which would then propagate an empty action into 
`RequestContext` and potentially cause unexpected authorization behavior. It 
should treat empty action the same as unset (similar to how sessionToken is 
handled elsewhere).



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java:
##########
@@ -1103,22 +1107,25 @@ private CopyObjectResponse copyObject(OzoneVolume 
volume,
         throw ex;
       }
 
-      try (OzoneInputStream src = getClientProtocol().getKey(volume.getName(),
-          sourceBucket, sourceKey)) {
+      try (OzoneInputStream src = runWithS3ActionString(
+              "GetObject", () -> getClientProtocol().getKey(volume.getName(), 
sourceBucket, sourceKey));
+           DigestInputStream sourceDigestInputStream = new 
DigestInputStream(src, md5Digest)) {
         getMetrics().updateCopyKeyMetadataStats(startNanos);
-        sourceDigestInputStream = new DigestInputStream(src, 
getMD5DigestInstance());
-        copy(volume, sourceDigestInputStream, sourceKeyLen, destkey, 
destBucket, replicationConfig,
-                customMetadata, perf, startNanos, tags, writeConditions);
-      }
+        runWithS3ActionString("PutObject", () -> {
+          copy(volume, sourceDigestInputStream, sourceKeyLen, destkey, 
destBucket,
+              replicationConfig, customMetadata, perf, startNanos, tags, 
writeConditions);
+          return null;
+        });
 
-      final OzoneKeyDetails destKeyDetails = getClientProtocol().getKeyDetails(
-          volume.getName(), destBucket, destkey);
+        final OzoneKeyDetails destKeyDetails = 
getClientProtocol().getKeyDetails(
+            volume.getName(), destBucket, destkey);

Review Comment:
   In CopyObject/UploadPartCopy flow, `destKeyDetails` is fetched after the 
`PutObject` phase but outside the `runWithS3ActionString("PutObject", ...)` 
override. At this point the thread-local action will have reverted (likely to 
`GetObject`), which can incorrectly require `GetObject` permission on the 
destination object just to build the response (ETag/LastModified), breaking 
CopyObject for STS policies that grant PutObject on the destination but not 
GetObject.



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java:
##########
@@ -225,6 +228,39 @@ protected void init() {
     // hook method
   }
 
+  /**
+   * Sets the IAM S3 action on thread-local {@link S3Auth} for fine-grained 
STS authorization.
+   * Called when the handler resolves the {@link S3GAction}.
+   */
+  protected void applyS3Action(S3GAction action) {
+    if (s3Auth != null) {
+      s3Auth.setS3Action(S3GActionIamMapper.toS3ActionString(action));
+    }
+  }
+
+  /**
+   * Temporarily override the S3 action string set on {@link S3Auth} for 
authorization.
+   * <p>
+   * This does not change S3G auditing (which is based on {@link S3GAction}).
+   * The action string is the IAM-style S3 action name without the {@code s3:} 
prefix (for example
+   * {@code GetObject}, {@code PutObject}, {@code GetObjectTagging}).
+   * This is used for special case APIs like CopyObject that don't have a 1-1 
s3 action mapping, but
+   * requires GetObject on the source file and PutObject on the destination 
file.
+   */
+  protected <T, E extends Exception> T runWithS3ActionString(String s3Action, 
CheckedSupplier<T, E> checkedSupplier)
+      throws E {
+    if (s3Auth == null) {
+      return checkedSupplier.get();
+    }
+    final String originalS3Action = s3Auth.getS3Action();
+    s3Auth.setS3Action(s3Action);
+    try {
+      return checkedSupplier.get();
+    } finally {
+      s3Auth.setS3Action(originalS3Action);
+    }
+  }
+  
   protected OzoneVolume getVolume() throws IOException {

Review Comment:
   There is trailing whitespace on an otherwise blank line, which can trip some 
whitespace/lint checks and makes diffs noisier.



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