smengcl commented on code in PR #4473:
URL: https://github.com/apache/ozone/pull/4473#discussion_r1167042728


##########
hadoop-ozone/dist/src/main/smoketest/s3/objectputget.robot:
##########
@@ -49,7 +49,7 @@ Get object from s3
 Get object with wrong signature
     Pass Execution If          '${SECURITY_ENABLED}' == 'false'    Skip in 
unsecure cluster
     ${result} =                 Execute and Ignore Error   curl -i -H 
'Authorization: AWS scm/[email protected]:asdfqwerty' 
${ENDPOINT_URL}/${BUCKET}/${PREFIX}/putobject/key=value/f1
-                                Should contain             ${result}        
403 Forbidden
+                                Should contain             ${result}        
400 Bad Request

Review Comment:
   Looks like `403` is the expected status code for `SignatureDoesNotMatch`?
   
   ref: 
https://docs.aws.amazon.com/IAM/latest/UserGuide/signature-v4-troubleshooting.html



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }

Review Comment:
   ```suggestion
       if (algorithm == null) {
         throw new MalformedResourceException("Unspecified signature 
algorithm.");
       }
       if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
         throw new MalformedResourceException("Unsupported signature algorithm: 
" +
             algorithm);
       }
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java:
##########
@@ -83,6 +84,10 @@ public S3Auth getSignature() {
       if (signatureInfo.getVersion() == Version.V4) {
         stringToSign =
             StringToSignProducer.createSignatureBase(signatureInfo, context);
+      } else {
+        LOG.debug("AWS signature version not supported. version: {}",
+                signatureInfo.getVersion());
+        throw S3_AUTHINFO_CREATION_ERROR;

Review Comment:
   nit
   ```suggestion
           LOG.debug("Unsupported AWS signature version: {}",
                   signatureInfo.getVersion());
           throw S3_AUTHINFO_CREATION_ERROR;
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);
+      }
+    } else {
+      throw new MalformedResourceException("Invalid expiry duration. "
+              + "X-Amz-Expires should be between " + X_AMZ_EXPIRES_MIN
+              + "and" + X_AMZ_EXPIRES_MAX + " expiresString:" + expiresString);
+    }
+  }
+
+  private void validateCredential(Credential credential)
+          throws MalformedResourceException {
+    if (credential.getAccessKeyID().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS access id shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRegion().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS region shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRequest().isEmpty() ||
+            !(credential.getAwsRequest().equals("aws4_request"))) {
+      throw new MalformedResourceException(
+              "AWS request shouldn't be empty or invalid. credential:"
+                      + credential);
+    }
+    if (credential.getAwsService().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS service shouldn't be empty. credential:" + credential);
+    }
+    // Date should not be empty and should be properly formatted.
+    if (!credential.getDate().isEmpty()) {
+      try {
+        LocalDate.parse(credential.getDate(), DATE_FORMATTER);
+      } catch (DateTimeParseException ex) {
+        throw new MalformedResourceException(
+                "AWS date format is invalid. credential:" + credential);
       }
+    } else {
+      throw new MalformedResourceException(
+              "AWS date shouldn't be empty. credential:{}" + credential);
+    }
+  }
+
+  /**
+   * Validate Signed headers.
+   */
+  private void validateSignedHeaders()
+          throws MalformedResourceException {
+    String signedHeadersStr = queryParameters.get("X-Amz-SignedHeaders");
+    if (signedHeadersStr == null || isEmpty(signedHeadersStr)
+            || signedHeadersStr.split(";").length == 0) {

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);
+      }
+    } else {
+      throw new MalformedResourceException("Invalid expiry duration. "
+              + "X-Amz-Expires should be between " + X_AMZ_EXPIRES_MIN
+              + "and" + X_AMZ_EXPIRES_MAX + " expiresString:" + expiresString);
+    }
+  }
+
+  private void validateCredential(Credential credential)
+          throws MalformedResourceException {
+    if (credential.getAccessKeyID().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS access id shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRegion().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS region shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRequest().isEmpty() ||
+            !(credential.getAwsRequest().equals("aws4_request"))) {
+      throw new MalformedResourceException(
+              "AWS request shouldn't be empty or invalid. credential:"
+                      + credential);
+    }
+    if (credential.getAwsService().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS service shouldn't be empty. credential:" + credential);
+    }
+    // Date should not be empty and should be properly formatted.
+    if (!credential.getDate().isEmpty()) {
+      try {
+        LocalDate.parse(credential.getDate(), DATE_FORMATTER);
+      } catch (DateTimeParseException ex) {
+        throw new MalformedResourceException(
+                "AWS date format is invalid. credential:" + credential);
       }
+    } else {
+      throw new MalformedResourceException(
+              "AWS date shouldn't be empty. credential:{}" + credential);
+    }
+  }

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);
+      }
+    } else {
+      throw new MalformedResourceException("Invalid expiry duration. "
+              + "X-Amz-Expires should be between " + X_AMZ_EXPIRES_MIN
+              + "and" + X_AMZ_EXPIRES_MAX + " expiresString:" + expiresString);
+    }
+  }
+
+  private void validateCredential(Credential credential)
+          throws MalformedResourceException {
+    if (credential.getAccessKeyID().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS access id shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRegion().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS region shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRequest().isEmpty() ||
+            !(credential.getAwsRequest().equals("aws4_request"))) {
+      throw new MalformedResourceException(
+              "AWS request shouldn't be empty or invalid. credential:"
+                      + credential);
+    }
+    if (credential.getAwsService().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS service shouldn't be empty. credential:" + credential);
+    }
+    // Date should not be empty and should be properly formatted.
+    if (!credential.getDate().isEmpty()) {
+      try {
+        LocalDate.parse(credential.getDate(), DATE_FORMATTER);
+      } catch (DateTimeParseException ex) {
+        throw new MalformedResourceException(
+                "AWS date format is invalid. credential:" + credential);
       }
+    } else {
+      throw new MalformedResourceException(
+              "AWS date shouldn't be empty. credential:{}" + credential);
+    }
+  }
+
+  /**
+   * Validate Signed headers.
+   */
+  private void validateSignedHeaders()
+          throws MalformedResourceException {
+    String signedHeadersStr = queryParameters.get("X-Amz-SignedHeaders");
+    if (signedHeadersStr == null || isEmpty(signedHeadersStr)
+            || signedHeadersStr.split(";").length == 0) {
+      throw new MalformedResourceException("No signed headers found.");
+    }
+  }
+
+  /**
+   * Validate signature.
+   */
+  private void validateSignature()
+          throws MalformedResourceException {

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -183,19 +185,38 @@ public static String buildCanonicalRequest(
         canonicalHeaders.append(NEWLINE);
 
         // Set for testing purpose only to skip date and host validation.
-        validateSignedHeader(schema, header, headerValue);
+        try {
+          validateSignedHeader(schema, header, headerValue);
+        } catch (DateTimeParseException ex) {
+          LOG.error("DateTime format invalid.", ex);
+          throw S3_AUTHINFO_CREATION_ERROR;
+        }
 
       } else {
-        throw new RuntimeException("Header " + header + " not present in " +
-            "request but requested to be signed.");
+        LOG.error("Header " + header + " not present in " +
+                "request but requested to be signed.");

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -56,7 +67,14 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
       return null;
     }
 
-    validateDateAndExpires();
+    validateAlgorithm();
+    try {
+      validateDateAndExpires();
+    } catch (DateTimeParseException ex) {
+      throw new MalformedResourceException(
+              "Invalid X-Amz-Date format: "
+                      + queryParameters.get("X-Amz-Date"));
+    }

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.

Review Comment:
   ```suggestion
     /**
      * According to AWS documentation:
   ```



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java:
##########
@@ -154,18 +164,21 @@ public void testGetSignature() {
         fail("Empty AuthHeader must fail");
       }
     } catch (WebApplicationException ex) {
-      if (authHeader == null || authHeader.equals("")) {
-        // Empty auth header should be 403
-        Assert.assertEquals(HTTP_FORBIDDEN, ex.getResponse().getStatus());
-        // TODO: Should return XML in body like this (bot not for now):
-        // <Error>
-        //   <Code>AccessDenied</Code><Message>Access Denied</Message>
-        //   <RequestId>...</RequestId><HostId>...</HostId>
-        // </Error>
+      Assert.assertEquals(HTTP_BAD_REQUEST, ex.getResponse().getStatus());
+      if (authHeader == null || authHeader.equals("") ||
+              authHeader.startsWith("AWS ")) {

Review Comment:
   ```suggestion
         if (authHeader == null || authHeader.isEmpty() ||
             authHeader.startsWith("AWS ")) {
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -183,19 +185,38 @@ public static String buildCanonicalRequest(
         canonicalHeaders.append(NEWLINE);
 
         // Set for testing purpose only to skip date and host validation.
-        validateSignedHeader(schema, header, headerValue);
+        try {
+          validateSignedHeader(schema, header, headerValue);
+        } catch (DateTimeParseException ex) {
+          LOG.error("DateTime format invalid.", ex);
+          throw S3_AUTHINFO_CREATION_ERROR;
+        }
 
       } else {
-        throw new RuntimeException("Header " + header + " not present in " +
-            "request but requested to be signed.");
+        LOG.error("Header " + header + " not present in " +
+                "request but requested to be signed.");
+        throw S3_AUTHINFO_CREATION_ERROR;
       }
     }
 
+    validateCanonicalHeaders(canonicalHeaders.toString(), headers,
+            unsignedPayload);
+
     String payloadHash;
     if (UNSIGNED_PAYLOAD.equals(
         headers.get(X_AMZ_CONTENT_SHA256)) || unsignedPayload) {
       payloadHash = UNSIGNED_PAYLOAD;
     } else {
+      // According to AWS Sig V4 documentation
+      // https://docs.aws.amazon.com/AmazonS3/latest/API/
+      // sig-v4-header-based-auth.html
+      // Note: The x-amz-content-sha256 header is required
+      // for all AWS Signature Version 4 requests.(using Authorization header)
+      if (!headers.containsKey(X_AMZ_CONTENT_SHA256)) {
+        LOG.error("The request must include x-amz-content-sha256 header "
+                + "for signed paylods");

Review Comment:
   ```suggestion
           LOG.error("The request must include " + X_AMZ_CONTENT_SHA256
               + " header for signed payload");
   ```



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java:
##########
@@ -154,18 +164,21 @@ public void testGetSignature() {
         fail("Empty AuthHeader must fail");
       }
     } catch (WebApplicationException ex) {
-      if (authHeader == null || authHeader.equals("")) {
-        // Empty auth header should be 403
-        Assert.assertEquals(HTTP_FORBIDDEN, ex.getResponse().getStatus());
-        // TODO: Should return XML in body like this (bot not for now):
-        // <Error>
-        //   <Code>AccessDenied</Code><Message>Access Denied</Message>
-        //   <RequestId>...</RequestId><HostId>...</HostId>
-        // </Error>
+      Assert.assertEquals(HTTP_BAD_REQUEST, ex.getResponse().getStatus());
+      if (authHeader == null || authHeader.equals("") ||
+              authHeader.startsWith("AWS ")) {
+        // Empty auth header and unsupported AWS signature
+        // should fail with Invalid Request.
+        Assert.assertEquals(S3_AUTHINFO_CREATION_ERROR.getErrorMessage(),
+                ex.getMessage());
       } else {
-        // Other requests have stale timestamp and thus should fail
-        Assert.assertEquals(HTTP_BAD_REQUEST, ex.getResponse().getStatus());
+        // Other requests have stale timestamp and
+        // should fail with Malformed Authorization Header.
+        Assert.assertEquals(MALFORMED_HEADER.getErrorMessage(),
+                ex.getMessage());

Review Comment:
   ```suggestion
               ex.getMessage());
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {

Review Comment:
   ```suggestion
       if (dateString == null ||
           expiresString == null ||
           dateString.length() == 0 ||
           expiresString.length() == 0) {
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java:
##########
@@ -189,7 +191,14 @@ private Credential parseCredentials(String credential)
 
     // Date should not be empty and within valid range.
     if (!credentialObj.getDate().isEmpty()) {
-      validateDateRange(credentialObj);
+      try {
+        validateDateRange(credentialObj);
+      } catch (DateTimeParseException ex) {
+        throw new MalformedResourceException(
+                "AWS date format is invalid. credential:" + credential,
+                authHeader);

Review Comment:
   indentation
   
   ```suggestion
           throw new MalformedResourceException(
               "AWS date format is invalid. credential:" + credential, 
authHeader);
   ```
   
   You can avoid this in the future by importing ozone code style:
   
   
https://github.com/apache/ozone/blob/dd003040a41def491e8de003ef8539ce40854972/CONTRIBUTING.md#L115-L121



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);

Review Comment:
   nit
   ```suggestion
       final long expires = Long.parseLong(expiresString);
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");

Review Comment:
   ```suggestion
         throw new MalformedResourceException(
             "dateString or expiresString are missing or empty.");
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java:
##########
@@ -176,9 +177,10 @@ private Credential parseCredentials(String credential)
           "AWS region shouldn't be empty. credential: " + credential,
           authHeader);
     }
-    if (credentialObj.getAwsRequest().isEmpty()) {
+    if (credentialObj.getAwsRequest().isEmpty() ||
+            !(credentialObj.getAwsRequest().equals("aws4_request"))) {
       throw new MalformedResourceException(
-          "AWS request shouldn't be empty. credential:" + credential,
+          "AWS request shouldn't be empty or invalid. credential:" + 
credential,

Review Comment:
   ```suggestion
             "AWS request is empty or invalid. credential: " + credential,
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -65,9 +83,13 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     try {
       credential = new Credential(urlDecode(rawCredential));
     } catch (UnsupportedEncodingException e) {
-      throw new IllegalArgumentException(
-          "X-Amz-Credential is not proper URL encoded");
+      throw new MalformedResourceException(
+          "X-Amz-Credential is not proper URL encoded rawCredential:"
+                  + rawCredential);
     }

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4HeaderParser.java:
##########
@@ -347,6 +362,15 @@ public void testV4HeaderHashAlgoValidationFailure() throws 
Exception {
             + "=fe5f80f77d5fa3beca038a248ff027";
     Assert.assertNull(new AuthorizationV4HeaderParser(auth3, SAMPLE_DATE)
         .parseSignature());
+
+    // Invalid algorithm
+    String auth4 = "AWS4-ZAVC-HJUA123 " +
+            "Credential=" + curDate + "/us-east-1/s3/aws4_request, " +
+            "SignedHeaders=host;range;x-amz-date, " +
+            "Signature=fe5f80f77d5fa3beca038a248ff027";
+    LambdaTestUtils.intercept(MalformedResourceException.class, "",
+            () -> new AuthorizationV4HeaderParser(auth4, SAMPLE_DATE)
+                    .parseSignature());

Review Comment:
   ```suggestion
       // Invalid algorithm
       String auth4 = "AWS4-ZAVC-HJUA123 " +
           "Credential=" + curDate + "/us-east-1/s3/aws4_request, " + 
           "SignedHeaders=host;range;x-amz-date, " +
           "Signature=fe5f80f77d5fa3beca038a248ff027";
       LambdaTestUtils.intercept(MalformedResourceException.class, "",
           () -> new AuthorizationV4HeaderParser(auth4, SAMPLE_DATE)
               .parseSignature());
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java:
##########
@@ -176,9 +177,10 @@ private Credential parseCredentials(String credential)
           "AWS region shouldn't be empty. credential: " + credential,
           authHeader);
     }
-    if (credentialObj.getAwsRequest().isEmpty()) {
+    if (credentialObj.getAwsRequest().isEmpty() ||
+            !(credentialObj.getAwsRequest().equals("aws4_request"))) {

Review Comment:
   Wrap `"aws4_request"` in a constant?



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java:
##########


Review Comment:
   fix indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);
+      }
+    } else {
+      throw new MalformedResourceException("Invalid expiry duration. "
+              + "X-Amz-Expires should be between " + X_AMZ_EXPIRES_MIN
+              + "and" + X_AMZ_EXPIRES_MAX + " expiresString:" + expiresString);

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);
+      }
+    } else {
+      throw new MalformedResourceException("Invalid expiry duration. "
+              + "X-Amz-Expires should be between " + X_AMZ_EXPIRES_MIN
+              + "and" + X_AMZ_EXPIRES_MAX + " expiresString:" + expiresString);
+    }
+  }
+
+  private void validateCredential(Credential credential)
+          throws MalformedResourceException {
+    if (credential.getAccessKeyID().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS access id shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRegion().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS region shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRequest().isEmpty() ||
+            !(credential.getAwsRequest().equals("aws4_request"))) {
+      throw new MalformedResourceException(
+              "AWS request shouldn't be empty or invalid. credential:"
+                      + credential);
+    }
+    if (credential.getAwsService().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS service shouldn't be empty. credential:" + credential);
+    }
+    // Date should not be empty and should be properly formatted.
+    if (!credential.getDate().isEmpty()) {
+      try {
+        LocalDate.parse(credential.getDate(), DATE_FORMATTER);
+      } catch (DateTimeParseException ex) {
+        throw new MalformedResourceException(
+                "AWS date format is invalid. credential:" + credential);
       }
+    } else {
+      throw new MalformedResourceException(
+              "AWS date shouldn't be empty. credential:{}" + credential);
+    }
+  }
+
+  /**
+   * Validate Signed headers.
+   */
+  private void validateSignedHeaders()
+          throws MalformedResourceException {

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -321,4 +334,34 @@ static void validateSignedHeader(
     }
   }
 
+  /** According to AWS Sig V4 documentation

Review Comment:
   ```suggestion
     /**
      * According to AWS Sig V4 documentation:
   ```



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4QueryParser.java:
##########
@@ -32,27 +36,85 @@
  */
 public class TestAuthorizationV4QueryParser {
 
-  @Test(expected = IllegalArgumentException.class)
-  public void testExpiredHeaders() throws Exception {
+  private static final String DATETIME = ZonedDateTime.now().format(
+          StringToSignProducer.TIME_FORMATTER);

Review Comment:
   ```suggestion
         StringToSignProducer.TIME_FORMATTER);
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -183,19 +185,38 @@ public static String buildCanonicalRequest(
         canonicalHeaders.append(NEWLINE);
 
         // Set for testing purpose only to skip date and host validation.
-        validateSignedHeader(schema, header, headerValue);
+        try {
+          validateSignedHeader(schema, header, headerValue);
+        } catch (DateTimeParseException ex) {
+          LOG.error("DateTime format invalid.", ex);
+          throw S3_AUTHINFO_CREATION_ERROR;
+        }
 
       } else {
-        throw new RuntimeException("Header " + header + " not present in " +
-            "request but requested to be signed.");
+        LOG.error("Header " + header + " not present in " +
+                "request but requested to be signed.");
+        throw S3_AUTHINFO_CREATION_ERROR;
       }
     }
 
+    validateCanonicalHeaders(canonicalHeaders.toString(), headers,
+            unsignedPayload);

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -288,22 +309,14 @@ static void validateSignedHeader(
       String header,
       String headerValue
   )
-      throws OS3Exception {
+      throws OS3Exception, DateTimeParseException {
     switch (header) {
     case HOST:
-      try {
-        URI hostUri = new URI(schema + "://" + headerValue);
-        InetAddress.getByName(hostUri.getHost());
-        // TODO: Validate if current request is coming from same host.
-      } catch (UnknownHostException | URISyntaxException e) {
-        LOG.error("Host value mentioned in signed header is not valid. " +
-            "Host:{}", headerValue);
-        throw S3_AUTHINFO_CREATION_ERROR;
-      }
+      // TODO: Placeholder for any host validations.

Review Comment:
   What type of host validatation are we going to add here now that the old 
logic is removed?



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java:
##########
@@ -82,17 +104,116 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
     );
   }
 
+  /**
+   * Validate if algorithm is in expected format.
+   */
+  private void validateAlgorithm()
+          throws MalformedResourceException {
+    String algorithm = queryParameters.get("X-Amz-Algorithm");
+    if (algorithm == null) {
+      throw new MalformedResourceException("Missing hash algorithm.");
+    }
+    if (isEmpty(algorithm) || !algorithm.equals(AWS4_SIGNING_ALGORITHM)) {
+      throw new MalformedResourceException("Unexpected hash algorithm. Algo:"
+              + algorithm);
+    }
+  }
+
+  /** According to AWS documentation.
+   * https://docs.aws.amazon.com/AmazonS3/latest/
+   * API/sigv4-query-string-auth.html
+   * X-Amz-Date: The date and time format must follow the
+   * ISO 8601 standard, and must be formatted with the
+   * "yyyyMMddTHHmmssZ" format
+   * X-Amz-Expires: The minimum value you can set is 1,
+   * and the maximum is 604800
+   */
   @VisibleForTesting
-  protected void validateDateAndExpires() {
+  protected void validateDateAndExpires()
+          throws MalformedResourceException, DateTimeParseException {
     final String dateString = queryParameters.get("X-Amz-Date");
     final String expiresString = queryParameters.get("X-Amz-Expires");
-    if (expiresString != null && expiresString.length() > 0) {
-      final Long expires = Long.valueOf(expiresString);
-
+    if (dateString == null || expiresString == null ||
+            dateString.length() == 0 || expiresString.length() == 0) {
+      throw new MalformedResourceException(
+              "dateString or expiresString are missing or empty.");
+    }
+    final Long expires = Long.valueOf(expiresString);
+    if (expires >= X_AMZ_EXPIRES_MIN && expires <= X_AMZ_EXPIRES_MAX) {
       if (ZonedDateTime.parse(dateString, StringToSignProducer.TIME_FORMATTER)
           .plus(expires, SECONDS).isBefore(ZonedDateTime.now())) {
-        throw new IllegalArgumentException("Pre-signed S3 url is expired");
+        throw new MalformedResourceException("Pre-signed S3 url is expired. "
+                + "dateString:" + dateString
+                + " expiresString:" + expiresString);
+      }
+    } else {
+      throw new MalformedResourceException("Invalid expiry duration. "
+              + "X-Amz-Expires should be between " + X_AMZ_EXPIRES_MIN
+              + "and" + X_AMZ_EXPIRES_MAX + " expiresString:" + expiresString);
+    }
+  }
+
+  private void validateCredential(Credential credential)
+          throws MalformedResourceException {
+    if (credential.getAccessKeyID().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS access id shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRegion().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS region shouldn't be empty. credential: " + credential);
+    }
+    if (credential.getAwsRequest().isEmpty() ||
+            !(credential.getAwsRequest().equals("aws4_request"))) {
+      throw new MalformedResourceException(
+              "AWS request shouldn't be empty or invalid. credential:"
+                      + credential);
+    }
+    if (credential.getAwsService().isEmpty()) {
+      throw new MalformedResourceException(
+              "AWS service shouldn't be empty. credential:" + credential);
+    }
+    // Date should not be empty and should be properly formatted.
+    if (!credential.getDate().isEmpty()) {
+      try {
+        LocalDate.parse(credential.getDate(), DATE_FORMATTER);
+      } catch (DateTimeParseException ex) {
+        throw new MalformedResourceException(
+                "AWS date format is invalid. credential:" + credential);
       }
+    } else {
+      throw new MalformedResourceException(
+              "AWS date shouldn't be empty. credential:{}" + credential);
+    }
+  }
+
+  /**
+   * Validate Signed headers.
+   */
+  private void validateSignedHeaders()
+          throws MalformedResourceException {
+    String signedHeadersStr = queryParameters.get("X-Amz-SignedHeaders");
+    if (signedHeadersStr == null || isEmpty(signedHeadersStr)
+            || signedHeadersStr.split(";").length == 0) {
+      throw new MalformedResourceException("No signed headers found.");
+    }
+  }
+
+  /**
+   * Validate signature.
+   */
+  private void validateSignature()
+          throws MalformedResourceException {
+    String signature = queryParameters.get("X-Amz-Signature");
+    if (isEmpty(signature)) {
+      throw new MalformedResourceException("Signature can't be empty.");
+    }
+    try {
+      Hex.decodeHex(signature);
+    } catch (DecoderException e) {
+      throw new MalformedResourceException(
+              "Signature:" + signature +
+                      " should be in hexa-decimal encoding.");

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java:
##########
@@ -154,18 +164,21 @@ public void testGetSignature() {
         fail("Empty AuthHeader must fail");
       }
     } catch (WebApplicationException ex) {
-      if (authHeader == null || authHeader.equals("")) {
-        // Empty auth header should be 403
-        Assert.assertEquals(HTTP_FORBIDDEN, ex.getResponse().getStatus());
-        // TODO: Should return XML in body like this (bot not for now):
-        // <Error>
-        //   <Code>AccessDenied</Code><Message>Access Denied</Message>
-        //   <RequestId>...</RequestId><HostId>...</HostId>
-        // </Error>
+      Assert.assertEquals(HTTP_BAD_REQUEST, ex.getResponse().getStatus());
+      if (authHeader == null || authHeader.equals("") ||
+              authHeader.startsWith("AWS ")) {
+        // Empty auth header and unsupported AWS signature
+        // should fail with Invalid Request.
+        Assert.assertEquals(S3_AUTHINFO_CREATION_ERROR.getErrorMessage(),
+                ex.getMessage());

Review Comment:
   ```suggestion
               ex.getMessage());
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -321,4 +334,34 @@ static void validateSignedHeader(
     }
   }
 
+  /** According to AWS Sig V4 documentation
+   * 
https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
+   * The CanonicalHeaders list must include the following:
+   * HTTP host header..
+   * Any x-amz-* headers that you plan to include
+   * in your request must also be added.
+   *
+   * @param canonicalHeaders
+   * @param headers
+   */
+  private static void validateCanonicalHeaders(
+          String canonicalHeaders,
+          Map<String, String> headers,
+          Boolean unsignedPaylod

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4HeaderParser.java:
##########
@@ -143,6 +143,11 @@ public void testV4HeaderDateValidationFailure() throws 
Exception {
     String dateStr3 = DATE_FORMATTER.format(now.minus(2, DAYS));
     LambdaTestUtils.intercept(MalformedResourceException.class, "",
         () -> testRequestWithSpecificDate(dateStr3));
+
+    // Case 4: Invalid date format
+    String dateStr4 = now.toString();
+    LambdaTestUtils.intercept(MalformedResourceException.class, "",
+            () -> testRequestWithSpecificDate(dateStr4));

Review Comment:
   ```suggestion
           () -> testRequestWithSpecificDate(dateStr4));
   ```



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4HeaderParser.java:
##########
@@ -240,6 +245,16 @@ public void testV4HeaderRequestValidationFailure() throws 
Exception {
     LambdaTestUtils.intercept(MalformedResourceException.class, "",
         () -> new AuthorizationV4HeaderParser(auth3, SAMPLE_DATE)
             .parseSignature());
+
+    String auth4 =
+            "AWS4-HMAC-SHA256 Credential=ozone/" + curDate + "/us-east-1/s3" +
+                    "/invalid_request,"
+                    + "SignedHeaders=host;x-amz-content-sha256;x-amz-date,"
+                    + "Signature"
+                    + "=fe5f80f77d5fa3beca038a248ff027";
+    LambdaTestUtils.intercept(MalformedResourceException.class, "",
+            () -> new AuthorizationV4HeaderParser(auth4, SAMPLE_DATE)
+                    .parseSignature());

Review Comment:
   ```suggestion
       String auth4 =
           "AWS4-HMAC-SHA256 Credential=ozone/" + curDate + "/us-east-1/s3" +
               "/invalid_request,"
               + "SignedHeaders=host;x-amz-content-sha256;x-amz-date,"
               + "Signature"
               + "=fe5f80f77d5fa3beca038a248ff027";
       LambdaTestUtils.intercept(MalformedResourceException.class, "",
           () -> new AuthorizationV4HeaderParser(auth4, SAMPLE_DATE)
               .parseSignature());
   ```



##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java:
##########
@@ -321,4 +334,34 @@ static void validateSignedHeader(
     }
   }
 
+  /** According to AWS Sig V4 documentation
+   * 
https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
+   * The CanonicalHeaders list must include the following:
+   * HTTP host header..
+   * Any x-amz-* headers that you plan to include
+   * in your request must also be added.
+   *
+   * @param canonicalHeaders
+   * @param headers
+   */
+  private static void validateCanonicalHeaders(
+          String canonicalHeaders,
+          Map<String, String> headers,
+          Boolean unsignedPaylod
+  ) throws OS3Exception {
+    if (!canonicalHeaders.contains(HOST + ":")) {
+      LOG.error("The SignedHeaders list must include HTTP Host header");
+      throw S3_AUTHINFO_CREATION_ERROR;
+    }
+    for (String header : headers.keySet().stream()
+            .filter(s -> s.startsWith("x-amz-"))
+            .collect(Collectors.toSet())) {
+      if (!(canonicalHeaders.contains(header + ":"))) {
+        LOG.error("The SignedHeaders list must include all " +
+                "x-amz-* headers in the request");

Review Comment:
   indentation



##########
hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4QueryParser.java:
##########


Review Comment:
   need to fix indentation



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