This is an automated email from the ASF dual-hosted git repository.

pifta pushed a commit to branch HDDS-13323-sts
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/HDDS-13323-sts by this push:
     new bf1453d2ec4 HDDS-13345. STS port and endpoint skeleton (#9343)
bf1453d2ec4 is described below

commit bf1453d2ec48525556258e2781713a6691eda357
Author: len548 <[email protected]>
AuthorDate: Mon Jan 19 16:02:41 2026 +0100

    HDDS-13345. STS port and endpoint skeleton (#9343)
---
 .../common/src/main/resources/ozone-default.xml    |  48 ++++
 .../src/main/compose/ozonesecure/docker-config     |   1 +
 .../hadoop/ozone/TestOzoneConfigurationFields.java |   2 +
 .../java/org/apache/hadoop/ozone/s3/Gateway.java   |   4 +
 .../apache/hadoop/ozone/s3/S3STSHttpServer.java    | 105 ++++++++
 .../hadoop/ozone/s3/exception/S3ErrorTable.java    |   4 +
 .../ozone/s3/signature/AWSSignatureProcessor.java  |  75 +++++-
 .../s3/signature/AuthorizationV4HeaderParser.java  |   1 +
 .../s3/signature/AuthorizationV4QueryParser.java   |   2 +
 .../hadoop/ozone/s3/signature/SignatureInfo.java   |  38 ++-
 .../ozone/s3/signature/SignatureProcessor.java     |   4 +-
 .../ozone/s3/signature/StringToSignProducer.java   |  50 +---
 .../Application.java}                              |  29 +-
 .../apache/hadoop/ozone/s3sts/S3STSConfigKeys.java |  51 ++++
 .../S3STSEnabled.java}                             |  32 +--
 .../s3sts/S3STSEnabledEndpointRequestFilter.java   |  53 ++++
 .../apache/hadoop/ozone/s3sts/S3STSEndpoint.java   | 299 +++++++++++++++++++++
 .../hadoop/ozone/s3sts/S3STSEndpointBase.java      | 109 ++++++++
 .../package-info.java}                             |  26 +-
 .../main/resources/webapps/s3g-sts/WEB-INF/web.xml |  33 +++
 .../hadoop/ozone/s3/TestAuthorizationFilter.java   |  26 ++
 .../s3/signature/TestStringToSignProducer.java     |   1 +
 .../org/apache/hadoop/ozone/s3sts/TestSTS.java     | 110 ++++++++
 23 files changed, 992 insertions(+), 111 deletions(-)

diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml 
b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index f3ea84abb4a..630af4d397b 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -2022,6 +2022,54 @@
       will be used for http authentication.
     </description>
   </property>
+
+  <property>
+    <name>ozone.s3g.sts.http.enabled</name>
+    <value>false</value>
+    <tag>OZONE, S3GATEWAY</tag>
+    <description>
+      The boolean which enables the Ozone S3Gateway STS endpoint.
+    </description>
+  </property>
+  <property>
+    <name>ozone.s3g.sts.http-bind-host</name>
+    <value>0.0.0.0</value>
+    <tag>OZONE, S3GATEWAY</tag>
+    <description>
+      The bind host for the S3 Gateway STS HTTP server.
+      If this optional address is set, it overrides only the hostname portion 
of
+      ozone.s3g.sts.http-address.
+      If not set, the value of ozone.s3g.http-bind-host is used.
+    </description>
+  </property>
+  <property>
+    <name>ozone.s3g.sts.http-address</name>
+    <value>0.0.0.0:9880</value>
+    <tag>OZONE, S3GATEWAY</tag>
+    <description>
+      The HTTP address for the S3 Gateway STS endpoint.
+    </description>
+  </property>
+  <property>
+    <name>ozone.s3g.sts.https-bind-host</name>
+    <value>0.0.0.0</value>
+    <tag>OZONE, S3GATEWAY</tag>
+    <description>
+      The bind host for the S3 Gateway STS HTTPS server.
+      If this optional address is set, it overrides only the hostname portion 
of
+      ozone.s3g.sts.http-address.
+      If not set, the value of ozone.s3g.https-bind-host is used.
+    </description>
+  </property>
+  <property>
+    <name>ozone.s3g.sts.https-address</name>
+    <value>0.0.0.0:9881</value>
+    <tag>OZONE, S3GATEWAY</tag>
+    <description>
+      The HTTPS address for the S3 Gateway STS endpoint.
+    </description>
+  </property>
+
   <property>
     <name>ozone.s3g.metrics.percentiles.intervals.seconds</name>
     <value>60</value>
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config 
b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
index 5daf6c11fc9..3d5ebba9223 100644
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
@@ -93,6 +93,7 @@ 
OZONE-SITE.XML_hdds.datanode.kerberos.keytab.file=/etc/security/keytabs/dn.keyta
 
 OZONE-SITE.XML_ozone.security.http.kerberos.enabled=true
 OZONE-SITE.XML_ozone.s3g.secret.http.enabled=true
+OZONE-SITE.XML_ozone.s3g.sts.http.enabled=true
 
OZONE-SITE.XML_ozone.http.filter.initializers=org.apache.hadoop.security.AuthenticationFilterInitializer
 
 OZONE-SITE.XML_ozone.om.http.auth.type=kerberos
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestOzoneConfigurationFields.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestOzoneConfigurationFields.java
index 98ccd8fac8b..edb1484fd0f 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestOzoneConfigurationFields.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestOzoneConfigurationFields.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.ozone.recon.ReconServerConfigKeys;
 import org.apache.hadoop.ozone.s3.S3GatewayConfigKeys;
 import org.apache.hadoop.ozone.s3secret.S3SecretConfigKeys;
+import org.apache.hadoop.ozone.s3sts.S3STSConfigKeys;
 
 /**
  * Tests if configuration constants documented in ozone-defaults.xml.
@@ -45,6 +46,7 @@ public void initializeMemberVariables() {
             ReconConfigKeys.class, ReconServerConfigKeys.class,
             S3GatewayConfigKeys.class,
             S3SecretConfigKeys.class,
+            S3STSConfigKeys.class,
             SCMHTTPServerConfig.class,
             SCMHTTPServerConfig.ConfigStrings.class,
             ScmConfig.ConfigStrings.class
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
index 9126d8ed4a2..1f28dbf39bd 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
@@ -60,6 +60,7 @@ public class Gateway extends GenericCli implements 
Callable<Void> {
   private S3GatewayHttpServer httpServer;
   /** Servlets and static content on separate port. */
   private BaseHttpServer contentServer;
+  private BaseHttpServer stsServer;
   private S3GatewayMetrics metrics;
 
   private final JvmPauseMonitor jvmPauseMonitor = newJvmPauseMonitor("S3G");
@@ -80,6 +81,7 @@ public Void call() throws Exception {
     setHttpBaseDir(OzoneConfigurationHolder.configuration());
     httpServer = new 
S3GatewayHttpServer(OzoneConfigurationHolder.configuration(), "s3gateway");
     contentServer = new 
S3GatewayWebAdminServer(OzoneConfigurationHolder.configuration(), "s3g-web");
+    stsServer = new S3STSHttpServer(OzoneConfigurationHolder.configuration(), 
"s3g-sts");
     metrics = 
S3GatewayMetrics.create(OzoneConfigurationHolder.configuration());
     start();
 
@@ -104,12 +106,14 @@ public void start() throws IOException {
     jvmPauseMonitor.start();
     httpServer.start();
     contentServer.start();
+    stsServer.start();
   }
 
   public void stop() throws Exception {
     LOG.info("Stopping Ozone S3 gateway");
     httpServer.stop();
     contentServer.stop();
+    stsServer.stop();
     jvmPauseMonitor.stop();
     S3GatewayMetrics.unRegister();
   }
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3STSHttpServer.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3STSHttpServer.java
new file mode 100644
index 00000000000..f100e408942
--- /dev/null
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3STSHttpServer.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import static 
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_HTTP_AUTH_CONFIG_PREFIX;
+import static 
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_HTTP_AUTH_TYPE;
+import static 
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_HTTP_BIND_HOST_DEFAULT;
+import static 
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_KEYTAB_FILE;
+import static 
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTPS_ADDRESS_KEY;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTPS_BIND_HOST_KEY;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTPS_BIND_PORT_DEFAULT;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTP_ADDRESS_KEY;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTP_BIND_HOST_KEY;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTP_BIND_PORT_DEFAULT;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTP_ENABLED_KEY;
+
+import java.io.IOException;
+import org.apache.hadoop.hdds.conf.MutableConfigurationSource;
+import org.apache.hadoop.hdds.server.http.BaseHttpServer;
+
+/**
+ * HTTP server for the S3 Gateway STS endpoint.
+ */
+public class S3STSHttpServer extends BaseHttpServer {
+
+  S3STSHttpServer(MutableConfigurationSource conf, String name) throws 
IOException {
+    super(conf, name);
+  }
+
+  @Override
+  protected String getHttpAddressKey() {
+    return OZONE_S3G_STS_HTTP_ADDRESS_KEY;
+  }
+
+  @Override
+  protected String getHttpBindHostKey() {
+    return OZONE_S3G_STS_HTTP_BIND_HOST_KEY;
+  }
+
+  @Override
+  protected String getHttpsAddressKey() {
+    return OZONE_S3G_STS_HTTPS_ADDRESS_KEY;
+  }
+
+  @Override
+  protected String getHttpsBindHostKey() {
+    return OZONE_S3G_STS_HTTPS_BIND_HOST_KEY;
+  }
+
+  @Override
+  protected String getBindHostDefault() {
+    return OZONE_S3G_HTTP_BIND_HOST_DEFAULT;
+  }
+
+  @Override
+  protected int getHttpBindPortDefault() {
+    return OZONE_S3G_STS_HTTP_BIND_PORT_DEFAULT;
+  }
+
+  @Override
+  protected int getHttpsBindPortDefault() {
+    return OZONE_S3G_STS_HTTPS_BIND_PORT_DEFAULT;
+  }
+
+  @Override
+  protected String getKeytabFile() {
+    return OZONE_S3G_KEYTAB_FILE;
+  }
+
+  @Override
+  protected String getSpnegoPrincipal() {
+    return OZONE_S3G_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL;
+  }
+
+  @Override
+  protected String getEnabledKey() {
+    return OZONE_S3G_STS_HTTP_ENABLED_KEY;
+  }
+
+  @Override
+  protected String getHttpAuthType() {
+    return OZONE_S3G_HTTP_AUTH_TYPE;
+  }
+
+  @Override
+  protected String getHttpAuthConfigPrefix() {
+    return OZONE_S3G_HTTP_AUTH_CONFIG_PREFIX;
+  }
+}
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java
index 060ed83d1bc..3c9ecdb768a 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java
@@ -160,6 +160,10 @@ public final class S3ErrorTable {
       "Access Denied", "User doesn't have permission to access this resource 
due to a " +
       "bucket ownership mismatch.", HTTP_FORBIDDEN);
 
+  public static final OS3Exception PAYLOAD_TOO_LARGE = new OS3Exception(
+      "PayloadTooLarge", "Your request body size was too large.", 
HTTP_BAD_REQUEST
+  );
+
   private static Function<Exception, OS3Exception> generateInternalError =
       e -> new OS3Exception("InternalError", e.getMessage(), 
HTTP_INTERNAL_ERROR);
 
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
index 7d8f2fe04e9..9abf2fc227d 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
@@ -18,8 +18,19 @@
 package org.apache.hadoop.ozone.s3.signature;
 
 import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.MALFORMED_HEADER;
+import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.PAYLOAD_TOO_LARGE;
+import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
+import static org.apache.hadoop.ozone.s3.util.S3Consts.UNSIGNED_PAYLOAD;
+import static org.apache.hadoop.ozone.s3.util.S3Consts.X_AMZ_CONTENT_SHA256;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_PAYLOAD_HASH_MAX_VALUE;
 
 import com.google.common.annotations.VisibleForTesting;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -39,6 +50,7 @@
 import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
 import org.apache.hadoop.ozone.s3.signature.SignatureInfo.Version;
 import org.apache.hadoop.ozone.s3.util.AuditUtils;
+import org.apache.kerby.util.Hex;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,7 +72,7 @@ public class AWSSignatureProcessor implements 
SignatureProcessor {
   private ContainerRequestContext context;
 
   @Override
-  public SignatureInfo parseSignature() throws OS3Exception {
+  public SignatureInfo parseSignature() throws OS3Exception, IOException, 
NoSuchAlgorithmException {
 
     LowerCaseKeyStringMap headers =
         LowerCaseKeyStringMap.fromHeaderMap(context.getHeaders());
@@ -89,13 +101,57 @@ public SignatureInfo parseSignature() throws OS3Exception {
       }
     }
     if (signatureInfo == null) {
-      signatureInfo = new SignatureInfo.Builder(Version.NONE).build();
+      signatureInfo = new 
SignatureInfo.Builder(Version.NONE).setService("s3").build();
     }
+    String payloadHash = getPayloadHash(headers, signatureInfo);
+    signatureInfo.setPayloadHash(payloadHash);
     signatureInfo.setUnfilteredURI(
         context.getUriInfo().getRequestUri().getPath());
     return signatureInfo;
   }
 
+  private String getPayloadHash(Map<String, String> headers, SignatureInfo 
signatureInfo)
+      throws OS3Exception, NoSuchAlgorithmException, IOException {
+    if (signatureInfo.getVersion() == Version.V2) {
+      throw S3_AUTHINFO_CREATION_ERROR;
+    }
+    if (signatureInfo.getService().equals("s3")) {
+      if (!signatureInfo.isSignPayload()) {
+        // According to AWS Signature V4 documentation using Query Parameters
+        // 
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
+        return UNSIGNED_PAYLOAD;
+      }
+      String contentSignatureHeaderValue = headers.get(X_AMZ_CONTENT_SHA256);
+      // According to AWS Signature V4 documentation using Authorization Header
+      // 
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
+      // The x-amz-content-sha256 header is required
+      // for all AWS Signature Version 4 requests using Authorization header.
+      if (contentSignatureHeaderValue == null) {
+        LOG.error("The request must include " + X_AMZ_CONTENT_SHA256
+            + " header for signed payload");
+        throw S3_AUTHINFO_CREATION_ERROR;
+      }
+      // Simply return the header value of x-amz-content-sha256 as the payload 
hash
+      // These are the possible cases:
+      // 1. Actual payload checksum for single chunk upload
+      // 2. Unsigned payloads for multiple chunks upload
+      //    - UNSIGNED-PAYLOAD
+      //    - STREAMING-UNSIGNED-PAYLOAD-TRAILER
+      // 3. Signed payloads for multiple chunks upload
+      //    - STREAMING-AWS4-HMAC-SHA256-PAYLOAD
+      //    - STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER
+      //    - STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD
+      //    - STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER
+      return contentSignatureHeaderValue;
+    }
+    // For STS payload hash is calculated over the body
+    InputStream in = context.getEntityStream();
+    byte[] body = readAllBytes(in);
+    String payloadHash = 
Hex.encode(MessageDigest.getInstance("SHA-256").digest(body));
+    context.setEntityStream(new ByteArrayInputStream(body));
+    return payloadHash;
+  }
+
   private AuditMessage buildAuthFailureMessage(MalformedResourceException e) {
     AuditMessage message = new AuditMessage.Builder()
         .forOperation(AuthOperation.fromContext(context))
@@ -107,6 +163,21 @@ private AuditMessage 
buildAuthFailureMessage(MalformedResourceException e) {
     return message;
   }
 
+  private byte[] readAllBytes(InputStream in) throws OS3Exception, IOException 
{
+    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    byte[] chunk = new byte[8192];
+    int totalRead = 0;
+    int n;
+    while ((n = in.read(chunk)) != -1) {
+      if (totalRead + n > OZONE_S3G_STS_PAYLOAD_HASH_MAX_VALUE) {
+        throw PAYLOAD_TOO_LARGE;
+      }
+      buffer.write(chunk, 0, n);
+      totalRead += n;
+    }
+    return buffer.toByteArray();
+  }
+
   @VisibleForTesting
   public void setContext(ContainerRequestContext context) {
     this.context = context;
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java
index b327280d070..ed4ef4b13dd 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4HeaderParser.java
@@ -90,6 +90,7 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
         .setCredentialScope(credentialObj.createScope())
         .setAlgorithm(algorithm)
         .setSignPayload(true)
+        .setService(credentialObj.getAwsService())
         .build();
   }
 
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java
index a7031127181..04d0d8cc525 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AuthorizationV4QueryParser.java
@@ -91,7 +91,9 @@ public SignatureInfo parseSignature() throws 
MalformedResourceException {
         .setSignedHeaders(queryParameters.get("X-Amz-SignedHeaders"))
         .setCredentialScope(credential.createScope())
         .setAlgorithm(queryParameters.get("X-Amz-Algorithm"))
+        .setService(credential.getAwsService())
         .setSignPayload(false)
+        .setPayloadHash("UNSIGNED-PAYLOAD")
         .build();
   }
 
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
index 730481bce21..ffe8a8dddd6 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
@@ -55,6 +55,10 @@ public class SignatureInfo {
 
   private String stringToSign = null;
 
+  private String payloadHash = null;
+
+  private String service = null;
+
   public SignatureInfo() { }
 
   private SignatureInfo(Builder b) {
@@ -72,7 +76,9 @@ public void initialize(SignatureInfo signatureInfo) {
         .setAlgorithm(signatureInfo.getAlgorithm())
         .setSignPayload(signatureInfo.isSignPayload())
         .setUnfilteredURI(signatureInfo.getUnfilteredURI())
-        .setStringToSign(signatureInfo.getStringToSign()));
+        .setStringToSign(signatureInfo.getStringToSign())
+        .setPayloadHash(signatureInfo.getPayloadHash())
+        .setService(signatureInfo.getService()));
   }
 
   private void initialize(Builder b) {
@@ -87,6 +93,8 @@ private void initialize(Builder b) {
     this.signPayload = b.signPayload;
     this.unfilteredURI = b.unfilteredURI;
     this.stringToSign = b.stringToSign;
+    this.payloadHash = b.payloadHash;
+    this.service = b.service;
   }
 
   public String getAwsAccessId() {
@@ -141,6 +149,22 @@ public void setStrToSign(String strToSign) {
     this.stringToSign = strToSign;
   }
 
+  public String getPayloadHash() {
+    return this.payloadHash;
+  }
+
+  public void setPayloadHash(String payloadHash) {
+    this.payloadHash = payloadHash;
+  }
+
+  public String getService() {
+    return service;
+  }
+
+  public void setService(String service) {
+    this.service = service;
+  }
+
   /**
    * Signature version.
    */
@@ -163,6 +187,8 @@ public static class Builder {
     private boolean signPayload = true;
     private String unfilteredURI = null;
     private String stringToSign = null;
+    private String payloadHash = null;
+    private String service = null;
 
     public Builder(Version version) {
       this.version = version;
@@ -218,6 +244,16 @@ public Builder setStringToSign(String stringToSign) {
       return this;
     }
 
+    public Builder setPayloadHash(String payloadHash) {
+      this.payloadHash = payloadHash;
+      return this;
+    }
+
+    public Builder setService(String service) {
+      this.service = service;
+      return this;
+    }
+
     public SignatureInfo build() {
       return new SignatureInfo(this);
     }
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
index 32213e41daf..36788b4e4ea 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
@@ -17,6 +17,8 @@
 
 package org.apache.hadoop.ozone.s3.signature;
 
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
 import java.time.format.DateTimeFormatter;
 import org.apache.hadoop.ozone.s3.exception.OS3Exception;
 
@@ -39,5 +41,5 @@ public interface SignatureProcessor {
   /**
    * API to return string to sign.
    */
-  SignatureInfo parseSignature() throws OS3Exception;
+  SignatureInfo parseSignature() throws OS3Exception, IOException, 
NoSuchAlgorithmException;
 }
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
index e2f8d64a4d1..d9500c19632 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
@@ -19,7 +19,6 @@
 
 import static java.time.temporal.ChronoUnit.SECONDS;
 import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
-import static org.apache.hadoop.ozone.s3.util.S3Consts.UNSIGNED_PAYLOAD;
 import static org.apache.hadoop.ozone.s3.util.S3Consts.X_AMZ_CONTENT_SHA256;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -118,7 +117,6 @@ public static String createSignatureBase(
     }
     strToSign.append(signatureInfo.getDateTime()).append(NEWLINE);
     strToSign.append(credentialScope).append(NEWLINE);
-
     String canonicalRequest = buildCanonicalRequest(
         scheme,
         method,
@@ -126,7 +124,7 @@ public static String createSignatureBase(
         signatureInfo.getSignedHeaders(),
         headers,
         queryParams,
-        !signatureInfo.isSignPayload());
+        signatureInfo.getPayloadHash());
     strToSign.append(hash(canonicalRequest));
     if (LOG.isDebugEnabled()) {
       LOG.debug("canonicalRequest:[{}]", canonicalRequest);
@@ -160,7 +158,7 @@ public static String buildCanonicalRequest(
       String signedHeaders,
       Map<String, String> headers,
       Map<String, String> queryParams,
-      boolean unsignedPayload
+      String payloadHash
   ) throws OS3Exception {
 
     Iterable<String> parts = split("/", uri);
@@ -179,6 +177,9 @@ public static String buildCanonicalRequest(
       canonicalHeaders.append(':');
       if (headers.containsKey(header)) {
         String headerValue = headers.get(header);
+        if (header.equals("content-type")) {
+          headerValue = headerValue.toLowerCase();
+        }
         canonicalHeaders.append(headerValue);
         canonicalHeaders.append(NEWLINE);
 
@@ -197,10 +198,7 @@ public static String buildCanonicalRequest(
       }
     }
 
-    validateCanonicalHeaders(canonicalHeaders.toString(), headers,
-        unsignedPayload);
-
-    String payloadHash = getPayloadHash(headers, unsignedPayload);
+    validateCanonicalHeaders(canonicalHeaders.toString(), headers);
 
     return method + NEWLINE
         + canonicalUri + NEWLINE
@@ -210,37 +208,6 @@ public static String buildCanonicalRequest(
         + payloadHash;
   }
 
-  private static String getPayloadHash(Map<String, String> headers, boolean 
isUsingQueryParameter)
-      throws OS3Exception {
-    if (isUsingQueryParameter) {
-      // According to AWS Signature V4 documentation using Query Parameters
-      // 
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
-      return UNSIGNED_PAYLOAD;
-    }
-    String contentSignatureHeaderValue = headers.get(X_AMZ_CONTENT_SHA256);
-    // According to AWS Signature V4 documentation using Authorization Header
-    // 
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
-    // The x-amz-content-sha256 header is required
-    // for all AWS Signature Version 4 requests using Authorization header.
-    if (contentSignatureHeaderValue == null) {
-      LOG.error("The request must include " + X_AMZ_CONTENT_SHA256
-          + " header for signed payload");
-      throw S3_AUTHINFO_CREATION_ERROR;
-    }
-    // Simply return the header value of x-amz-content-sha256 as the payload 
hash
-    // These are the possible cases:
-    // 1. Actual payload checksum for single chunk upload
-    // 2. Unsigned payloads for multiple chunks upload
-    //    - UNSIGNED-PAYLOAD
-    //    - STREAMING-UNSIGNED-PAYLOAD-TRAILER
-    // 3. Signed payloads for multiple chunks upload
-    //    - STREAMING-AWS4-HMAC-SHA256-PAYLOAD
-    //    - STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER
-    //    - STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD
-    //    - STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER
-    return contentSignatureHeaderValue;
-  }
-
   /**
    * String join that also works with empty strings.
    *
@@ -357,9 +324,8 @@ static void validateSignedHeader(
    */
   private static void validateCanonicalHeaders(
       String canonicalHeaders,
-      Map<String, String> headers,
-      Boolean unsignedPaylod
-  ) throws OS3Exception {
+      Map<String, String> headers)
+      throws OS3Exception {
     if (!canonicalHeaders.contains(HOST + ":")) {
       LOG.error("The SignedHeaders list must include HTTP Host header");
       throw S3_AUTHINFO_CREATION_ERROR;
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
similarity index 57%
copy from 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
copy to 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
index 32213e41daf..65081d5d47f 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
@@ -15,29 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.hadoop.ozone.s3.signature;
+package org.apache.hadoop.ozone.s3sts;
 
-import java.time.format.DateTimeFormatter;
-import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import org.glassfish.jersey.server.ResourceConfig;
 
 /**
- * Parser to request auth parser for http request.
+ * JAX-RS application for the STS endpoint.
  */
-public interface SignatureProcessor {
-
-  String CONTENT_TYPE = "content-type";
-
-  String CONTENT_MD5 = "content-md5";
-
-  String AWS4_SIGNING_ALGORITHM = "AWS4-HMAC-SHA256";
-
-  String HOST_HEADER = "Host";
-
-  DateTimeFormatter DATE_FORMATTER =
-      DateTimeFormatter.ofPattern("yyyyMMdd");
-
-  /**
-   * API to return string to sign.
-   */
-  SignatureInfo parseSignature() throws OS3Exception;
+public class Application extends ResourceConfig {
+  public Application() {
+    packages("org.apache.hadoop.ozone.s3sts");
+    register(org.apache.hadoop.ozone.s3.AuthorizationFilter.class);
+  }
 }
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSConfigKeys.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSConfigKeys.java
new file mode 100644
index 00000000000..1512d3fc3c4
--- /dev/null
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSConfigKeys.java
@@ -0,0 +1,51 @@
+/*
+ * 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.s3sts;
+
+/**
+ * This class contains constants for configuration keys used
+ * in S3 STS endpoint.
+ */
+public final class S3STSConfigKeys {
+  public static final String OZONE_S3G_STS_HTTP_ENABLED_KEY =
+      "ozone.s3g.sts.http.enabled";
+  public static final String OZONE_S3G_STS_HTTP_BIND_HOST_KEY =
+      "ozone.s3g.sts.http-bind-host";
+  public static final String OZONE_S3G_STS_HTTPS_BIND_HOST_KEY =
+      "ozone.s3g.sts.https-bind-host";
+  public static final String OZONE_S3G_STS_HTTP_ADDRESS_KEY =
+      "ozone.s3g.sts.http-address";
+  public static final String OZONE_S3G_STS_HTTPS_ADDRESS_KEY =
+      "ozone.s3g.sts.https-address";
+  public static final int OZONE_S3G_STS_HTTP_BIND_PORT_DEFAULT = 9880;
+  public static final int OZONE_S3G_STS_HTTPS_BIND_PORT_DEFAULT = 9881;
+  // Max payload default size for STS AssumeRole API calls (32 KB)
+  // as STS AssumeRole has these parameters required in payload:
+  // Action=AssumeRole&RoleArn=...&RoleSessionName=...&DurationSeconds=...
+  // where RoleArn max length is 2048 and max bytes per character in UTF-8 
encoding is 12
+  // (2048 * 12 = 24576) + other parameters and overheads, so setting to 32 KB
+  // this limit can be adjusted via configuration if needed.
+  public static final int OZONE_S3G_STS_PAYLOAD_HASH_MAX_VALUE = 32768;
+
+  /**
+   * Never constructed.
+   */
+  private S3STSConfigKeys() {
+
+  }
+}
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEnabled.java
similarity index 58%
copy from 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
copy to 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEnabled.java
index 32213e41daf..84712a5fd1f 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEnabled.java
@@ -15,29 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.hadoop.ozone.s3.signature;
+package org.apache.hadoop.ozone.s3sts;
 
-import java.time.format.DateTimeFormatter;
-import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.NameBinding;
 
 /**
- * Parser to request auth parser for http request.
+ * Annotation to disable S3 STS Endpoint.
  */
-public interface SignatureProcessor {
-
-  String CONTENT_TYPE = "content-type";
-
-  String CONTENT_MD5 = "content-md5";
-
-  String AWS4_SIGNING_ALGORITHM = "AWS4-HMAC-SHA256";
-
-  String HOST_HEADER = "Host";
+@NameBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface S3STSEnabled {
+}
 
-  DateTimeFormatter DATE_FORMATTER =
-      DateTimeFormatter.ofPattern("yyyyMMdd");
 
-  /**
-   * API to return string to sign.
-   */
-  SignatureInfo parseSignature() throws OS3Exception;
-}
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEnabledEndpointRequestFilter.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEnabledEndpointRequestFilter.java
new file mode 100644
index 00000000000..50157ea75b0
--- /dev/null
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEnabledEndpointRequestFilter.java
@@ -0,0 +1,53 @@
+/*
+ * 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.s3sts;
+
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_HTTP_ENABLED_KEY;
+
+import java.io.IOException;
+import javax.inject.Inject;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+
+/**
+ * Filter that disables all endpoints annotated with {@link S3STSEnabled}.
+ * Condition is based on the value of the configuration key
+ * ozone.s3g.s3sts.http.enabled.
+ */
+@S3STSEnabled
+@Provider
+public class S3STSEnabledEndpointRequestFilter implements 
ContainerRequestFilter {
+  @Inject
+  private OzoneConfiguration ozoneConfiguration;
+
+  @Override
+  public void filter(ContainerRequestContext requestContext) throws 
IOException {
+    boolean isSTSEnabled = ozoneConfiguration.getBoolean(
+        OZONE_S3G_STS_HTTP_ENABLED_KEY, false);
+    if (!isSTSEnabled) {
+      requestContext.abortWith(Response.status(Response.Status.NOT_IMPLEMENTED)
+          .entity("STS endpoint is disabled.")
+          .type(MediaType.APPLICATION_XML_TYPE)
+          .build());
+    }
+  }
+}
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEndpoint.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEndpoint.java
new file mode 100644
index 00000000000..124581c6f26
--- /dev/null
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEndpoint.java
@@ -0,0 +1,299 @@
+/*
+ * 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.s3sts;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Base64;
+import java.util.Random;
+import java.util.UUID;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * AWS STS (Security Token Service) compatible endpoint for Ozone S3 Gateway.
+ * <p>
+ * This endpoint provides temporary security credentials compatible with
+ * AWS STS API, exposed on the port 9880 or 9881.
+ * <p>
+ * Currently supports only AssumeRole operation. Other STS operations will
+ * return appropriate error responses.
+ *
+ * @see <a href="https://docs.aws.amazon.com/STS/latest/APIReference/";>AWS STS 
API Reference</a>
+ */
+@Path("/")
+@S3STSEnabled
+public class S3STSEndpoint extends S3STSEndpointBase {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(S3STSEndpoint.class);
+
+  // STS API constants
+  private static final String STS_ACTION_PARAM = "Action";
+  private static final String ASSUME_ROLE_ACTION = "AssumeRole";
+  private static final String ROLE_ARN_PARAM = "RoleArn";
+  private static final String ROLE_DURATION_SECONDS_PARAM = "DurationSeconds";
+  private static final String GET_SESSION_TOKEN_ACTION = "GetSessionToken";
+  private static final String ASSUME_ROLE_WITH_SAML_ACTION = 
"AssumeRoleWithSAML";
+  private static final String ASSUME_ROLE_WITH_WEB_IDENTITY_ACTION = 
"AssumeRoleWithWebIdentity";
+  private static final String GET_CALLER_IDENTITY_ACTION = "GetCallerIdentity";
+  private static final String DECODE_AUTHORIZATION_MESSAGE_ACTION = 
"DecodeAuthorizationMessage";
+  private static final String GET_ACCESS_KEY_INFO_ACTION = "GetAccessKeyInfo";
+
+  // Default token duration (in seconds) - AWS default is 3600 (1 hour)
+  private static final int DEFAULT_DURATION_SECONDS = 3600;
+  private static final int MAX_DURATION_SECONDS = 43200; // 12 hours
+  private static final int MIN_DURATION_SECONDS = 900;   // 15 minutes
+
+  /**
+   * STS endpoint that handles GET requests with query parameters.
+   * AWS STS supports both GET and POST requests.
+   *
+   * @param action The STS action to perform (AssumeRole, GetSessionToken, 
etc.)
+   * @param roleArn The ARN of the role to assume (for AssumeRole)
+   * @param roleSessionName Session name for the role (for AssumeRole)
+   * @param durationSeconds Duration of the token validity in seconds
+   * @param version AWS STS API version (should be "2011-06-15")
+   * @return Response containing STS response XML or error
+   */
+  @GET
+  @Produces(MediaType.APPLICATION_XML)
+  public Response get(
+      @QueryParam("Action") String action,
+      @QueryParam("RoleArn") String roleArn,
+      @QueryParam("RoleSessionName") String roleSessionName,
+      @QueryParam("DurationSeconds") Integer durationSeconds,
+      @QueryParam("Version") String version) throws OS3Exception {
+
+    return handleSTSRequest(action, roleArn, roleSessionName, durationSeconds, 
version);
+  }
+
+  /**
+   * STS endpoint that handles POST requests with form data.
+   * AWS STS typically uses POST requests with form-encoded parameters.
+   *
+   * @param action The STS action to perform
+   * @param roleArn The ARN of the role to assume
+   * @param roleSessionName Session name for the role
+   * @param durationSeconds Duration of the token validity
+   * @param version AWS STS API version
+   * @return Response containing STS response XML or error
+   */
+  @POST
+  @Produces(MediaType.APPLICATION_XML)
+  public Response post(
+      @FormParam("Action") String action,
+      @FormParam("RoleArn") String roleArn,
+      @FormParam("RoleSessionName") String roleSessionName,
+      @FormParam("DurationSeconds") Integer durationSeconds,
+      @FormParam("Version") String version) throws OS3Exception {
+
+    return handleSTSRequest(action, roleArn, roleSessionName, durationSeconds, 
version);
+  }
+
+  private Response handleSTSRequest(String action, String roleArn, String 
roleSessionName,
+      Integer durationSeconds, String version) throws OS3Exception {
+    try {
+      if (action == null) {
+        return Response.status(Response.Status.BAD_REQUEST)
+            .entity("Missing required parameter: " + STS_ACTION_PARAM)
+            .build();
+      }
+      int duration;
+      try {
+        duration = validateDuration(durationSeconds);
+      } catch (IllegalArgumentException e) {
+        return Response.status(Response.Status.BAD_REQUEST)
+            .entity(e.getMessage())
+            .build();
+      }
+
+      if (version == null || !version.equals("2011-06-15")) {
+        return Response.status(Response.Status.BAD_REQUEST)
+            .entity("Invalid or missing Version parameter. Supported version 
is 2011-06-15.")
+            .build();
+      }
+
+      switch (action) {
+      case ASSUME_ROLE_ACTION:
+        return handleAssumeRole(roleArn, roleSessionName, duration);
+      // These operations are not supported yet
+      case GET_SESSION_TOKEN_ACTION:
+      case ASSUME_ROLE_WITH_SAML_ACTION:
+      case ASSUME_ROLE_WITH_WEB_IDENTITY_ACTION:
+      case GET_CALLER_IDENTITY_ACTION:
+      case DECODE_AUTHORIZATION_MESSAGE_ACTION:
+      case GET_ACCESS_KEY_INFO_ACTION:
+        return Response.status(Response.Status.NOT_IMPLEMENTED)
+            .entity("Operation " + action + " is not supported yet.")
+            .build();
+      default:
+        return Response.status(Response.Status.BAD_REQUEST)
+            .entity("Unsupported Action: " + action)
+            .build();
+      }
+    } catch (OS3Exception s3e) {
+      // Handle known S3 exceptions
+      LOG.error("S3 Error during STS request: {}", s3e.toXml());
+      throw s3e;
+    } catch (Exception ex) {
+      LOG.error("Unexpected error during STS request", ex);
+      return Response.serverError().build();
+    }
+  }
+
+  private int validateDuration(Integer durationSeconds) throws 
IllegalArgumentException, OS3Exception {
+    if (durationSeconds == null) {
+      return DEFAULT_DURATION_SECONDS;
+    }
+
+    if (durationSeconds < MIN_DURATION_SECONDS || durationSeconds > 
MAX_DURATION_SECONDS) {
+      throw new IllegalArgumentException(
+          "Invalid Value: " + ROLE_DURATION_SECONDS_PARAM + " must be between 
" + MIN_DURATION_SECONDS +
+              " and " + MAX_DURATION_SECONDS + " seconds");
+    }
+
+    return durationSeconds;
+  }
+
+  private Response handleAssumeRole(String roleArn, String roleSessionName, 
int duration)
+      throws IOException, OS3Exception {
+    // Validate required parameters for AssumeRole. RoleArn is required to 
pass the
+    if (roleArn == null || roleArn.isEmpty()) {
+      return Response.status(Response.Status.BAD_REQUEST)
+          .entity("Missing required parameter: " + ROLE_ARN_PARAM)
+          .build();
+    }
+
+    if (roleSessionName == null || roleSessionName.isEmpty()) {
+      return Response.status(Response.Status.BAD_REQUEST)
+          .entity("Missing required parameter: RoleSessionName")
+          .build();
+    }
+
+    // Validate role session name format (AWS requirements)
+    if (!isValidRoleSessionName(roleSessionName)) {
+      return Response.status(Response.Status.BAD_REQUEST)
+          .entity("Invalid RoleSessionName: must be 2-64 characters long and " 
+
+              "contain only alphanumeric characters, +, =, ,, ., @, -")
+          .build();
+    }
+
+    // TODO: Integrate with Ozone Manager to get actual temporary credentials
+    // String dummyCredentials = 
getClient().getObjectStore().getS3StsToken(userNameFromRequest());
+    // Generate AssumeRole response
+    String responseXml = generateAssumeRoleResponse(roleArn, roleSessionName, 
duration);
+
+    return Response.ok(responseXml)
+        .header("Content-Type", "text/xml")
+        .build();
+  }
+
+  private boolean isValidRoleSessionName(String roleSessionName) {
+    if (roleSessionName.length() < 2 || roleSessionName.length() > 64) {
+      return false;
+    }
+
+    // AWS allows: alphanumeric, +, =, ,, ., @, -
+    return roleSessionName.matches("[a-zA-Z0-9+=,.@\\-]+");
+  }
+
+  // TODO: replace mock implementation with actual logic to generate new 
credentials
+  private String generateAssumeRoleResponse(String roleArn, String 
roleSessionName, int duration) {
+    // Generate realistic-looking temporary credentials
+    String accessKeyId = "ASIA" + generateRandomAlphanumeric(16); // AWS temp 
keys start with ASIA
+    String secretAccessKey = generateRandomBase64(40);
+    String sessionToken = generateSessionToken();
+    String expiration = getExpirationTime(duration);
+
+    // Generate AssumedRoleId (format: AROLEID:RoleSessionName)
+    String roleId = "AROA" + generateRandomAlphanumeric(16);
+    String assumedRoleId = roleId + ":" + roleSessionName;
+
+    String requestId = UUID.randomUUID().toString();
+
+    return String.format(
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n" +
+            "<AssumeRoleResponse 
xmlns=\"https://sts.amazonaws.com/doc/2011-06-15/\";>%n" +
+            "  <AssumeRoleResult>%n" +
+            "    <Credentials>%n" +
+            "      <AccessKeyId>%s</AccessKeyId>%n" +
+            "      <SecretAccessKey>%s</SecretAccessKey>%n" +
+            "      <SessionToken>%s</SessionToken>%n" +
+            "      <Expiration>%s</Expiration>%n" +
+            "    </Credentials>%n" +
+            "    <AssumedRoleUser>%n" +
+            "      <AssumedRoleId>%s</AssumedRoleId>%n" +
+            "      <Arn>%s</Arn>%n" +
+            "    </AssumedRoleUser>%n" +
+            "  </AssumeRoleResult>%n" +
+            "  <ResponseMetadata>%n" +
+            "    <RequestId>%s</RequestId>%n" +
+            "  </ResponseMetadata>%n" +
+            "</AssumeRoleResponse>",
+        accessKeyId, secretAccessKey, sessionToken, expiration,
+        assumedRoleId, roleArn, requestId);
+  }
+
+  // TODO: this method should be removed once actual credential response from 
OM is implemented and used in the endpoint
+  private String generateRandomAlphanumeric(int length) {
+    String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+    StringBuilder sb = new StringBuilder();
+    Random random = new Random();
+    for (int i = 0; i < length; i++) {
+      sb.append(chars.charAt(random.nextInt(chars.length())));
+    }
+    return sb.toString();
+  }
+
+  // TODO: this method should be removed once actual credential response from 
OM is implemented and used in the endpoint
+  private String generateRandomBase64(int length) {
+    String chars = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    StringBuilder sb = new StringBuilder();
+    Random random = new Random();
+    for (int i = 0; i < length; i++) {
+      sb.append(chars.charAt((random.nextInt(chars.length()))));
+    }
+    return sb.toString();
+  }
+
+  // TODO: this method should be removed once actual credential response from 
OM is implemented and used in the endpoint
+  private String generateSessionToken() {
+    byte[] tokenBytes = new byte[128];
+    Random random = new Random();
+    for (int i = 0; i < tokenBytes.length; i++) {
+      tokenBytes[i] = (byte) random.nextInt(256);
+    }
+    return Base64.getEncoder().encodeToString(tokenBytes);
+  }
+
+  // TODO: this method should be removed once actual credential response from 
OM is implemented and used in the endpoint
+  private String getExpirationTime(int durationSeconds) {
+    Instant expiration = Instant.now().plusSeconds(durationSeconds);
+    return DateTimeFormatter.ISO_INSTANT.format(expiration);
+  }
+}
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEndpointBase.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEndpointBase.java
new file mode 100644
index 00000000000..ef753410f94
--- /dev/null
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/S3STSEndpointBase.java
@@ -0,0 +1,109 @@
+/*
+ * 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.s3sts;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Map;
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+import org.apache.hadoop.ozone.audit.AuditAction;
+import org.apache.hadoop.ozone.audit.AuditEventStatus;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditLoggerType;
+import org.apache.hadoop.ozone.audit.AuditMessage;
+import org.apache.hadoop.ozone.audit.Auditor;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.protocol.ClientProtocol;
+import org.apache.hadoop.ozone.om.protocol.S3Auth;
+import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
+import org.apache.hadoop.ozone.s3.util.AuditUtils;
+
+/**
+ * Base class for STS endpoints.
+ */
+public class S3STSEndpointBase implements Auditor {
+
+  @Context
+  private ContainerRequestContext context;
+
+  @Inject
+  private OzoneClient client;
+  @Inject
+  private SignatureInfo signatureInfo;
+
+  protected static final AuditLogger AUDIT =
+      new AuditLogger(AuditLoggerType.S3GLOGGER);
+
+  @PostConstruct
+  public void initialization() {
+    S3Auth s3Auth = new S3Auth(signatureInfo.getStringToSign(),
+        signatureInfo.getSignature(),
+        signatureInfo.getAwsAccessId(), signatureInfo.getAwsAccessId());
+    ClientProtocol clientProtocol = 
getClient().getObjectStore().getClientProxy();
+    clientProtocol.setThreadLocalS3Auth(s3Auth);
+  }
+
+  private AuditMessage.Builder auditMessageBaseBuilder(AuditAction op,
+      Map<String, String> auditMap) {
+    AuditMessage.Builder builder = new AuditMessage.Builder()
+        .forOperation(op)
+        .withParams(auditMap);
+    if (context != null) {
+      builder.atIp(AuditUtils.getClientIpAddress(context));
+    }
+    return builder;
+  }
+
+  @Override
+  public AuditMessage buildAuditMessageForSuccess(AuditAction op,
+      Map<String, String> auditMap) {
+    AuditMessage.Builder builder = auditMessageBaseBuilder(op, auditMap)
+        .withResult(AuditEventStatus.SUCCESS);
+    return builder.build();
+  }
+
+  @Override
+  public AuditMessage buildAuditMessageForFailure(AuditAction op,
+      Map<String, String> auditMap, Throwable throwable) {
+    AuditMessage.Builder builder = auditMessageBaseBuilder(op, auditMap)
+        .withResult(AuditEventStatus.FAILURE)
+        .withException(throwable);
+    return builder.build();
+  }
+
+  public OzoneClient getClient() {
+    return client;
+  }
+
+  @VisibleForTesting
+  public void setClient(OzoneClient ozoneClient) {
+    this.client = ozoneClient;
+  }
+
+  @VisibleForTesting
+  public void setContext(ContainerRequestContext context) {
+    this.context = context;
+  }
+
+  @VisibleForTesting
+  public void setSignatureInfo(SignatureInfo signatureInfo) {
+    this.signatureInfo = signatureInfo;
+  }
+}
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/package-info.java
similarity index 57%
copy from 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
copy to 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/package-info.java
index 32213e41daf..76f77800182 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureProcessor.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/package-info.java
@@ -15,29 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.hadoop.ozone.s3.signature;
-
-import java.time.format.DateTimeFormatter;
-import org.apache.hadoop.ozone.s3.exception.OS3Exception;
-
 /**
- * Parser to request auth parser for http request.
+ * This package contains the AWS STS (Security Token Service) compatible API 
for S3 Gateway.
  */
-public interface SignatureProcessor {
-
-  String CONTENT_TYPE = "content-type";
-
-  String CONTENT_MD5 = "content-md5";
-
-  String AWS4_SIGNING_ALGORITHM = "AWS4-HMAC-SHA256";
-
-  String HOST_HEADER = "Host";
-
-  DateTimeFormatter DATE_FORMATTER =
-      DateTimeFormatter.ofPattern("yyyyMMdd");
-
-  /**
-   * API to return string to sign.
-   */
-  SignatureInfo parseSignature() throws OS3Exception;
-}
+package org.apache.hadoop.ozone.s3sts;
diff --git 
a/hadoop-ozone/s3gateway/src/main/resources/webapps/s3g-sts/WEB-INF/web.xml 
b/hadoop-ozone/s3gateway/src/main/resources/webapps/s3g-sts/WEB-INF/web.xml
new file mode 100644
index 00000000000..d6dcf626dcc
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/main/resources/webapps/s3g-sts/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<!--
+  Licensed 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. See accompanying LICENSE file.
+-->
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";>
+  <servlet>
+    <servlet-name>sts-jaxrs</servlet-name>
+    
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    <init-param>
+      <param-name>javax.ws.rs.Application</param-name>
+      <param-value>org.apache.hadoop.ozone.s3sts.Application</param-value>
+    </init-param>
+    <load-on-startup>1</load-on-startup>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>sts-jaxrs</servlet-name>
+    <url-pattern>/sts/*</url-pattern>
+  </servlet-mapping>
+  <listener>
+    
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
+  </listener>
+</web-app>
diff --git 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
index 6df57448cad..5171138710e 100644
--- 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
+++ 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
@@ -20,6 +20,7 @@
 import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
 import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
 import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.MALFORMED_HEADER;
+import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.PAYLOAD_TOO_LARGE;
 import static 
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
 import static 
org.apache.hadoop.ozone.s3.signature.AWSSignatureProcessor.DATE_FORMATTER;
 import static 
org.apache.hadoop.ozone.s3.signature.SignatureParser.AUTHORIZATION_HEADER;
@@ -28,6 +29,7 @@
 import static 
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.HOST_HEADER;
 import static 
org.apache.hadoop.ozone.s3.signature.StringToSignProducer.X_AMAZ_DATE;
 import static org.apache.hadoop.ozone.s3.util.S3Consts.X_AMZ_CONTENT_SHA256;
+import static 
org.apache.hadoop.ozone.s3sts.S3STSConfigKeys.OZONE_S3G_STS_PAYLOAD_HASH_MAX_VALUE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -35,6 +37,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
@@ -117,6 +121,24 @@ public class TestAuthorizationFilter {
             "application/octet-stream",
             "/",
             S3_AUTHINFO_CREATION_ERROR.getErrorMessage()
+        ),
+        // Too huge payload for signature V4 of STS request
+        arguments(
+            "POST",
+            "AWS4-HMAC-SHA256 Credential=testuser1/" + CURDATE +
+            "/us-east-1/sts/aws4_request, " +
+                "SignedHeaders=content-type;host;" +
+                "x-amz-date, " +
+                "Signature" +
+                "=56ec73ba1974f8feda8365c3caef89c5d4a688d5f9baccf47" +
+                "65f46a14cd745ad",
+            "Content-SHA",
+            "s3g:9880",
+            "Content-SHA",
+            DATETIME,
+            "application/x-www-form-urlencoded; charset=utf-8",
+            "/sts",
+            PAYLOAD_TOO_LARGE.getErrorMessage()
         )
     );
   }
@@ -133,6 +155,10 @@ void testAuthFilterFailures(
       ContainerRequestContext context = setupContext(method, authHeader,
           contentMd5, host, amzContentSha256, date, contentType, path);
 
+      byte[] payloadBytes = new byte[OZONE_S3G_STS_PAYLOAD_HASH_MAX_VALUE + 1];
+      InputStream payLoadStream = new ByteArrayInputStream(payloadBytes);
+      when(context.getEntityStream()).thenReturn(payLoadStream);
+
       AWSSignatureProcessor awsSignatureProcessor = new 
AWSSignatureProcessor();
       awsSignatureProcessor.setContext(context);
 
diff --git 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
index cbce030ef69..1d9b89eb23c 100644
--- 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
+++ 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
@@ -94,6 +94,7 @@ public void validateDateRange(Credential credentialObj) {
             //NOOP
           }
         }.parseSignature();
+    signatureInfo.setPayloadHash("Content-SHA");
     signatureInfo.setUnfilteredURI("/buckets");
 
     headers.fixContentType();
diff --git 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestSTS.java
 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestSTS.java
new file mode 100644
index 00000000000..7696bd4d3ed
--- /dev/null
+++ 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestSTS.java
@@ -0,0 +1,110 @@
+/*
+ * 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.s3sts;
+
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_S3_ADMINISTRATORS;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Response;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientStub;
+import org.apache.hadoop.ozone.s3.OzoneConfigurationHolder;
+import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+/**
+ * Test for S3 STS endpoint.
+ */
+public class TestSTS {
+  private S3STSEndpoint endpoint;
+  private static final String ROLE_ARN = 
"arn:aws:iam::123456789012:role/test-role";
+  private static final String ROLE_SESSION_NAME = "test-session";
+
+  @Mock
+  private ContainerRequestContext context;
+
+  @BeforeEach
+  public void setup() throws Exception {
+    OzoneConfiguration config = new OzoneConfiguration();
+    config.set(OZONE_S3_ADMINISTRATORS, "test-user");
+    OzoneConfigurationHolder.setConfiguration(config);
+    OzoneClient clientStub = new OzoneClientStub();
+    endpoint = new S3STSEndpoint();
+    endpoint.setClient(clientStub);
+    endpoint.setContext(context);
+    SignatureInfo signatureInfo = new 
SignatureInfo.Builder(SignatureInfo.Version.V4)
+        .setAwsAccessId("test-user")
+        .setSignature("some-signature")
+        .setStringToSign("dummy-string")
+        .build();
+    endpoint.setSignatureInfo(signatureInfo);
+  }
+
+  @Test
+  public void testStsAssumeRole() throws Exception {
+    Response response = endpoint.get(
+        "AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600, "2011-06-15");
+
+    assertEquals(200, response.getStatus());
+
+    String responseXml = (String) response.getEntity();
+    assertNotNull(responseXml);
+    assertTrue(responseXml.contains("AssumeRoleResponse"));
+    assertTrue(responseXml.contains("AccessKeyId"));
+    assertTrue(responseXml.contains("SecretAccessKey"));
+    assertTrue(responseXml.contains("SessionToken"));
+    assertTrue(responseXml.contains("AssumedRoleUser"));
+    assertTrue(responseXml.contains(ROLE_ARN));
+  }
+
+  @Test
+  public void testStsInvalidDuration() throws Exception {
+    Response response = endpoint.get(
+        "AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, -1, "2011-06-15");
+
+    assertEquals(400, response.getStatus());
+    String errorMessage = (String) response.getEntity();
+    assertTrue(errorMessage.contains("Invalid Value: DurationSeconds"));
+  }
+
+  @Test
+  public void testStsUnsupportedAction() throws Exception {
+    Response response = endpoint.get(
+        "UnsupportedAction", ROLE_ARN, ROLE_SESSION_NAME, 3600, "2011-06-15");
+
+    assertEquals(400, response.getStatus());
+    String errorMessage = (String) response.getEntity();
+    assertTrue(errorMessage.contains("Unsupported Action"));
+  }
+
+  @Test
+  public void testStsInvalidVersion() throws Exception {
+    Response response = endpoint.get(
+        "AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600, "2000-01-01");
+
+    assertEquals(400, response.getStatus());
+    String errorMessage = (String) response.getEntity();
+    assertTrue(errorMessage.contains("Invalid or missing Version parameter. 
Supported version is 2011-06-15."));
+  }
+}


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

Reply via email to