This is an automated email from the ASF dual-hosted git repository.
sodonnell 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 0f3b26b1b29 HDDS-14420. [STS] Add audit logging to endpoint and
OzoneManager for STS (#9687)
0f3b26b1b29 is described below
commit 0f3b26b1b29b49cf1210b7896aa1895168884703
Author: fmorg-git <[email protected]>
AuthorDate: Fri Jan 30 07:52:39 2026 -0800
HDDS-14420. [STS] Add audit logging to endpoint and OzoneManager for STS
(#9687)
---
.../apache/hadoop/ozone/client/ObjectStore.java | 5 +-
.../ozone/client/protocol/ClientProtocol.java | 3 +-
.../apache/hadoop/ozone/client/rpc/RpcClient.java | 4 +-
.../apache/hadoop/ozone/om/helpers/S3STSUtils.java | 44 ++++++++
.../ozone/om/protocol/OzoneManagerProtocol.java | 3 +-
...OzoneManagerProtocolClientSideTranslatorPB.java | 5 +-
.../src/main/proto/OmClientProtocol.proto | 1 +
.../hadoop/ozone/om/helpers/OMAuditLogger.java | 2 +
.../request/s3/security/S3AssumeRoleRequest.java | 81 ++++++++------
.../s3/security/TestS3AssumeRoleRequest.java | 105 +++++++++++++-----
.../org/apache/hadoop/ozone/audit/S3GAction.java | 5 +-
.../ozone/s3/S3STSHeadersResponseFilter.java | 49 ++++++++
.../org/apache/hadoop/ozone/s3sts/Application.java | 3 +
.../apache/hadoop/ozone/s3sts/S3STSEndpoint.java | 123 ++++++++++++++-------
.../hadoop/ozone/s3sts/S3STSEndpointBase.java | 26 ++++-
.../hadoop/ozone/client/ClientProtocolStub.java | 8 +-
.../hadoop/ozone/s3sts/TestS3STSEndpoint.java | 73 ++++++++++--
17 files changed, 415 insertions(+), 125 deletions(-)
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
index 4e6e1b0ae9d..f423979bde2 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
@@ -758,12 +758,13 @@ public Iterator<OzoneSnapshotDiff> listSnapshotDiffJobs(
* @param roleSessionName The session name (should be unique) for
this operation
* @param durationSeconds The duration in seconds for the token
validity
* @param awsIamSessionPolicy The AWS IAM JSON session policy
+ * @param requestId The requestId from the STS endpoint
* @return AssumeRoleResponseInfo The AssumeRole response information
containing temporary credentials
* @throws IOException if an error occurs during the AssumeRole
operation
*/
public AssumeRoleResponseInfo assumeRole(String roleArn, String
roleSessionName, int durationSeconds,
- String awsIamSessionPolicy) throws IOException {
- return proxy.assumeRole(roleArn, roleSessionName, durationSeconds,
awsIamSessionPolicy);
+ String awsIamSessionPolicy, String requestId) throws IOException {
+ return proxy.assumeRole(roleArn, roleSessionName, durationSeconds,
awsIamSessionPolicy, requestId);
}
/**
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 88c28285616..96e8b654474 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -1367,11 +1367,12 @@ void deleteObjectTagging(String volumeName, String
bucketName, String keyName)
* @param roleSessionName The session name (should be unique) for
this operation
* @param durationSeconds The duration in seconds for the token
validity
* @param awsIamSessionPolicy The AWS IAM JSON session policy
+ * @param requestId The requestId from the STS endpoint
* @return AssumeRoleResponseInfo The AssumeRole response information
containing temporary credentials
* @throws IOException if an error occurs during the AssumeRole
operation
*/
AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName,
int durationSeconds,
- String awsIamSessionPolicy) throws IOException;
+ String awsIamSessionPolicy, String requestId) throws IOException;
/**
* Revokes an STS token.
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index 67e2ac203bf..ef01ebd87e5 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -2793,8 +2793,8 @@ public void deleteObjectTagging(String volumeName, String
bucketName,
@Override
public AssumeRoleResponseInfo assumeRole(String roleArn, String
roleSessionName, int durationSeconds,
- String awsIamSessionPolicy) throws IOException {
- return ozoneManagerClient.assumeRole(roleArn, roleSessionName,
durationSeconds, awsIamSessionPolicy);
+ String awsIamSessionPolicy, String requestId) throws IOException {
+ return ozoneManagerClient.assumeRole(roleArn, roleSessionName,
durationSeconds, awsIamSessionPolicy, requestId);
}
@Override
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/S3STSUtils.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/S3STSUtils.java
new file mode 100644
index 00000000000..8d261e6c68e
--- /dev/null
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/S3STSUtils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.om.helpers;
+
+import com.google.common.base.Strings;
+import java.util.Map;
+
+/**
+ * Utility class containing constants and validation methods shared by STS
endpoint and OzoneManager processing.
+ */
+public final class S3STSUtils {
+
+ private S3STSUtils() {
+ }
+
+ /**
+ * Adds standard AssumeRole audit params.
+ */
+ public static void addAssumeRoleAuditParams(Map<String, String> auditParams,
String roleArn, String roleSessionName,
+ String awsIamSessionPolicy, int duration, String requestId) {
+
+ auditParams.put("action", "AssumeRole");
+ auditParams.put("roleArn", roleArn);
+ auditParams.put("roleSessionName", roleSessionName);
+ auditParams.put("duration", String.valueOf(duration));
+ auditParams.put("isPolicyIncluded",
Strings.isNullOrEmpty(awsIamSessionPolicy) ? "N" : "Y");
+ auditParams.put("requestId", requestId);
+ }
+}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
index 2661bc82366..5e4dafb925b 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
@@ -1184,11 +1184,12 @@ default void deleteObjectTagging(OmKeyArgs args) throws
IOException {
* @param roleSessionName The session name (should be unique) for
this operation
* @param durationSeconds The duration in seconds for the token
validity
* @param awsIamSessionPolicy The AWS IAM JSON session policy
+ * @param requestId The requestId from the STS endpoint
* @return AssumeRoleResponseInfo The AssumeRole response information
containing temporary credentials
* @throws IOException if an error occurs during the AssumeRole
operation
*/
default AssumeRoleResponseInfo assumeRole(String roleArn, String
roleSessionName, int durationSeconds,
- String awsIamSessionPolicy) throws IOException {
+ String awsIamSessionPolicy, String requestId) throws IOException {
throw new UnsupportedOperationException("OzoneManager does not require
this to be implemented");
}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index 8bbc3320dc1..8b778ae95d0 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -2659,13 +2659,14 @@ public void deleteObjectTagging(OmKeyArgs args) throws
IOException {
@Override
public AssumeRoleResponseInfo assumeRole(String roleArn, String
roleSessionName, int durationSeconds,
- String awsIamSessionPolicy) throws IOException {
+ String awsIamSessionPolicy, String requestId) throws IOException {
final OzoneManagerProtocolProtos.AssumeRoleRequest.Builder request =
OzoneManagerProtocolProtos.AssumeRoleRequest.newBuilder()
.setRoleArn(roleArn)
.setRoleSessionName(roleSessionName)
.setDurationSeconds(durationSeconds)
- .setAwsIamSessionPolicy(awsIamSessionPolicy != null ?
awsIamSessionPolicy : "");
+ .setAwsIamSessionPolicy(awsIamSessionPolicy != null ?
awsIamSessionPolicy : "")
+ .setRequestId(requestId);
final OMRequest omRequest = createOMRequest(Type.AssumeRole)
.setAssumeRoleRequest(request)
diff --git
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 00892f79a71..707f8ac567d 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -2379,6 +2379,7 @@ message AssumeRoleRequest {
required string roleSessionName = 2;
optional int32 durationSeconds = 3 [default = 3600];
optional string awsIamSessionPolicy = 4;
+ required string requestId = 5;
}
message AssumeRoleResponse {
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
index e6185f3d65a..2c17d233547 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
@@ -95,6 +95,8 @@ private static void init() {
CMD_AUDIT_ACTION_MAP.put(Type.GetObjectTagging,
OMAction.GET_OBJECT_TAGGING);
CMD_AUDIT_ACTION_MAP.put(Type.PutObjectTagging,
OMAction.PUT_OBJECT_TAGGING);
CMD_AUDIT_ACTION_MAP.put(Type.DeleteObjectTagging,
OMAction.DELETE_OBJECT_TAGGING);
+ CMD_AUDIT_ACTION_MAP.put(Type.AssumeRole, OMAction.S3_ASSUME_ROLE);
+ CMD_AUDIT_ACTION_MAP.put(Type.RevokeSTSToken, OMAction.REVOKE_STS_TOKEN);
}
private static OMAction getAction(OzoneManagerProtocolProtos.OMRequest
request) {
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3AssumeRoleRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3AssumeRoleRequest.java
index aecba45f32c..1b00454b70c 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3AssumeRoleRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3AssumeRoleRequest.java
@@ -24,19 +24,25 @@
import java.net.InetAddress;
import java.security.SecureRandom;
import java.time.Clock;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
import org.apache.hadoop.ipc.ProtobufRpcEngine;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.OMAction;
import org.apache.hadoop.ozone.om.OzoneAclUtils;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext;
+import org.apache.hadoop.ozone.om.helpers.S3STSUtils;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.om.response.s3.security.S3AssumeRoleResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssumeRoleRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssumeRoleResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
@@ -89,33 +95,39 @@ public OMClientResponse validateAndUpdateCache(OzoneManager
ozoneManager, Execut
final OMRequest omRequest = getOmRequest();
final AssumeRoleRequest assumeRoleRequest =
omRequest.getAssumeRoleRequest();
final int durationSeconds = assumeRoleRequest.getDurationSeconds();
-
- // Validate duration
- if (durationSeconds < MIN_TOKEN_EXPIRATION_SECONDS || durationSeconds >
MAX_TOKEN_EXPIRATION_SECONDS) {
- final OMException omException = new OMException(
- "Duration must be between " + MIN_TOKEN_EXPIRATION_SECONDS + " and "
+ MAX_TOKEN_EXPIRATION_SECONDS,
- OMException.ResultCodes.INVALID_REQUEST);
- return new S3AssumeRoleResponse(
-
createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest),
omException));
- }
-
- // Validate role session name
final String roleSessionName = assumeRoleRequest.getRoleSessionName();
- final S3AssumeRoleResponse roleSessionNameErrorResponse =
validateRoleSessionName(roleSessionName, omRequest);
- if (roleSessionNameErrorResponse != null) {
- return roleSessionNameErrorResponse;
- }
-
final String roleArn = assumeRoleRequest.getRoleArn();
+ final String awsIamSessionPolicy =
assumeRoleRequest.getAwsIamSessionPolicy();
+ final String requestId = assumeRoleRequest.getRequestId();
+
+ final Map<String, String> auditMap = new HashMap<>();
+ // In HA environments, only the tempAccessKeyId on the leader is used by
S3G, so it could be helpful to
+ // have the leader information
+ auditMap.put("omRole", ozoneManager.isLeaderReady() ? "LEADER" :
"FOLLOWER");
+ final AuditLogger auditLogger = ozoneManager.getAuditLogger();
+ final OzoneManagerProtocolProtos.UserInfo userInfo =
omRequest.getUserInfo();
+ S3STSUtils.addAssumeRoleAuditParams(
+ auditMap, roleArn, roleSessionName, awsIamSessionPolicy,
durationSeconds, requestId);
+
+ Exception exception = null;
+ OMClientResponse omClientResponse;
try {
+ // Validate duration
+ if (durationSeconds < MIN_TOKEN_EXPIRATION_SECONDS || durationSeconds >
MAX_TOKEN_EXPIRATION_SECONDS) {
+ throw new OMException(
+ "Duration must be between " + MIN_TOKEN_EXPIRATION_SECONDS + " and
" + MAX_TOKEN_EXPIRATION_SECONDS,
+ OMException.ResultCodes.INVALID_REQUEST);
+ }
+
+ // Validate role session name
+ validateRoleSessionName(roleSessionName);
+
// Validate role ARN and extract role
final String targetRoleName =
AwsRoleArnValidator.validateAndExtractRoleNameFromArn(roleArn);
if (!omRequest.hasS3Authentication()) {
- final String msg = "S3AssumeRoleRequest does not have S3
authentication";
- final OMException omException = new OMException(msg,
OMException.ResultCodes.INVALID_REQUEST);
- return new S3AssumeRoleResponse(
-
createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest),
omException));
+ throw new OMException(
+ "S3AssumeRoleRequest does not have S3 authentication",
OMException.ResultCodes.INVALID_REQUEST);
}
// Generate temporary AWS credentials using cryptographically strong
SecureRandom
@@ -134,6 +146,9 @@ public OMClientResponse validateAndUpdateCache(OzoneManager
ozoneManager, Execut
// Calculate expiration of session token
final long expirationEpochSeconds =
clock.instant().plusSeconds(durationSeconds).getEpochSecond();
+ // Add tempAccessKeyId to the log so it can be determined which
permanent user created the tempAccessKeyId
+ auditMap.put("tempAccessKeyId", tempAccessKeyId);
+
final AssumeRoleResponse.Builder responseBuilder =
AssumeRoleResponse.newBuilder()
.setAccessKeyId(tempAccessKeyId)
.setSecretAccessKey(secretAccessKey)
@@ -141,39 +156,41 @@ public OMClientResponse
validateAndUpdateCache(OzoneManager ozoneManager, Execut
.setExpirationEpochSeconds(expirationEpochSeconds)
.setAssumedRoleId(assumedRoleId);
- return new S3AssumeRoleResponse(
+ omClientResponse = new S3AssumeRoleResponse(
OmResponseUtil.getOMResponseBuilder(omRequest)
.setAssumeRoleResponse(responseBuilder.build())
.build());
} catch (OMException e) {
- return new
S3AssumeRoleResponse(createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest),
e));
+ exception = e;
+ omClientResponse = new S3AssumeRoleResponse(
+
createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest), e));
} catch (IOException e) {
final OMException omException = new OMException(
"Failed to generate STS token for role: " + roleArn, e,
OMException.ResultCodes.INTERNAL_ERROR);
- return new S3AssumeRoleResponse(
+ exception = omException;
+ omClientResponse = new S3AssumeRoleResponse(
createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest),
omException));
}
+
+ // Audit log
+ markForAudit(auditLogger, buildAuditMessage(OMAction.S3_ASSUME_ROLE,
auditMap, exception, userInfo));
+
+ return omClientResponse;
}
/**
* Ensures RoleSessionName is valid.
*/
- private S3AssumeRoleResponse validateRoleSessionName(String roleSessionName,
OMRequest omRequest) {
+ private void validateRoleSessionName(String roleSessionName) throws
OMException {
if (StringUtils.isBlank(roleSessionName)) {
- final OMException omException = new OMException(
- "RoleSessionName is required",
OMException.ResultCodes.INVALID_REQUEST);
- return new S3AssumeRoleResponse(
-
createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest),
omException));
+ throw new OMException("RoleSessionName is required",
OMException.ResultCodes.INVALID_REQUEST);
}
if (roleSessionName.length() < ASSUME_ROLE_SESSION_NAME_MIN_LENGTH ||
roleSessionName.length() > ASSUME_ROLE_SESSION_NAME_MAX_LENGTH) {
- final OMException omException = new OMException(
+ throw new OMException(
"RoleSessionName length must be between " +
ASSUME_ROLE_SESSION_NAME_MIN_LENGTH + " and " +
ASSUME_ROLE_SESSION_NAME_MAX_LENGTH,
OMException.ResultCodes.INVALID_REQUEST);
- return new S3AssumeRoleResponse(
-
createErrorOMResponse(OmResponseUtil.getOMResponseBuilder(omRequest),
omException));
}
- return null;
}
/**
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3AssumeRoleRequest.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3AssumeRoleRequest.java
index 3ae3c3c7599..bda871386fe 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3AssumeRoleRequest.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3AssumeRoleRequest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,9 +43,12 @@
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.security.symmetric.ManagedSecretKey;
import org.apache.hadoop.hdds.security.symmetric.SecretKeySignerClient;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.om.OMMultiTenantManager;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext;
+import org.apache.hadoop.ozone.om.helpers.OMAuditLogger;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssumeRoleRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssumeRoleResponse;
@@ -87,14 +91,21 @@ public class TestS3AssumeRoleRequest {
private static final String OM_HOST = "om-host";
private static final InetAddress LOOPBACK_IP =
InetAddress.getLoopbackAddress();
private static final Set<OzoneGrant> EMPTY_GRANTS =
Collections.singleton(new OzoneGrant(emptySet(), emptySet()));
+ private static final String REQUEST_ID = UUID.randomUUID().toString();
+
+ private static final Pattern ABC_PATTERN_32 = Pattern.compile("^[ABC]{32}$");
+ private static final Pattern XYZ_PATTERN = Pattern.compile("^[XYZ]$");
private OzoneManager ozoneManager;
private ExecutionContext context;
private IAccessAuthorizer accessAuthorizer;
+ private AuditLogger auditLogger;
@BeforeEach
public void setup() throws IOException {
ozoneManager = mock(OzoneManager.class);
+ auditLogger = mock(AuditLogger.class);
+ when(ozoneManager.getAuditLogger()).thenReturn(auditLogger);
final OzoneConfiguration configuration = new OzoneConfiguration();
when(ozoneManager.getConfiguration()).thenReturn(configuration);
@@ -135,15 +146,17 @@ public void testInvalidDurationTooShort() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(899) // less than 900
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.INVALID_REQUEST);
assertThat(omResponse.getMessage()).isEqualTo("Duration must be between
900 and 43200");
assertThat(omResponse.hasAssumeRoleResponse()).isFalse();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -154,15 +167,17 @@ public void testInvalidDurationTooLong() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(43201) // more than 43200
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.INVALID_REQUEST);
assertThat(omResponse.getMessage()).isEqualTo("Duration must be between
900 and 43200");
assertThat(omResponse.hasAssumeRoleResponse()).isFalse();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -173,14 +188,16 @@ public void testValidDurationMaxBoundary() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(43200) // exactly max
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.OK);
assertThat(omResponse.hasAssumeRoleResponse()).isTrue();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -191,14 +208,16 @@ public void testValidDurationMinBoundary() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(900) // exactly min
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.OK);
assertThat(omResponse.hasAssumeRoleResponse()).isTrue();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -211,15 +230,17 @@ public void testMissingS3Authentication() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(3600)
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.INVALID_REQUEST);
assertThat(omResponse.getMessage()).isEqualTo("S3AssumeRoleRequest does
not have S3 authentication");
assertThat(omResponse.hasAssumeRoleResponse()).isFalse();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -231,10 +252,11 @@ public void
testSuccessfulAssumeRoleGeneratesCredentials() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(durationSeconds)
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse clientResponse = new S3AssumeRoleRequest(omRequest,
CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse clientResponse =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = clientResponse.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.OK);
@@ -260,6 +282,7 @@ public void testSuccessfulAssumeRoleGeneratesCredentials() {
// Verify expiration added durationSeconds
final long expirationEpochSeconds =
assumeRoleResponse.getExpirationEpochSeconds();
assertThat(expirationEpochSeconds).isEqualTo(CLOCK.instant().getEpochSecond() +
durationSeconds);
+ assertMarkForAuditCalled(request);
}
@Test
@@ -268,7 +291,7 @@ public void testGenerateSecureRandomStringUsingChars() {
final int length = 32;
final String s = S3AssumeRoleRequest.generateSecureRandomStringUsingChars(
chars, chars.length(), length);
- assertThat(s).hasSize(length).matches(Pattern.compile("^[ABC]{" + length +
"}$"));
+ assertThat(s).hasSize(length).matches(ABC_PATTERN_32);
// Test with length 0
final String empty =
S3AssumeRoleRequest.generateSecureRandomStringUsingChars(
@@ -278,7 +301,7 @@ public void testGenerateSecureRandomStringUsingChars() {
// Test with length 1
final String single =
S3AssumeRoleRequest.generateSecureRandomStringUsingChars(
"XYZ", 3, 1);
- assertThat(single).hasSize(1).matches(Pattern.compile("^[XYZ]$"));
+ assertThat(single).hasSize(1).matches(XYZ_PATTERN);
}
@Test
@@ -290,12 +313,13 @@ public void testAssumeRoleCredentialsAreUnique() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(3600)
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response1 = new S3AssumeRoleRequest(omRequest,
CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
- final OMClientResponse response2 = new S3AssumeRoleRequest(omRequest,
CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request1 = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response1 =
request1.validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request2 = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response2 =
request2.validateAndUpdateCache(ozoneManager, context);
final AssumeRoleResponse assumeRoleResponse1 =
response1.getOMResponse().getAssumeRoleResponse();
final AssumeRoleResponse assumeRoleResponse2 =
response2.getOMResponse().getAssumeRoleResponse();
@@ -311,6 +335,10 @@ public void testAssumeRoleCredentialsAreUnique() {
// Different assumed role IDs
assertThat(assumeRoleResponse1.getAssumedRoleId()).isNotEqualTo(assumeRoleResponse2.getAssumedRoleId());
+
+ OMAuditLogger.log(request1.getAuditBuilder());
+ OMAuditLogger.log(request2.getAuditBuilder());
+ verify(auditLogger, times(2)).logWrite(any(AuditMessage.class));
}
@Test
@@ -321,12 +349,14 @@ public void testAssumeRoleWithEmptySessionName() {
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName("")
.setDurationSeconds(3600)
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
assertThat(response.getOMResponse().getStatus()).isEqualTo(Status.INVALID_REQUEST);
assertThat(response.getOMResponse().getMessage()).isEqualTo("RoleSessionName is
required");
+ assertMarkForAuditCalled(request);
}
@Test
@@ -336,15 +366,17 @@ public void testInvalidAssumeRoleSessionNameTooShort() {
AssumeRoleRequest.newBuilder()
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName("T") // Less than 2 characters
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.INVALID_REQUEST);
assertThat(omResponse.getMessage()).isEqualTo("RoleSessionName length must
be between 2 and 64");
assertThat(omResponse.hasAssumeRoleResponse()).isFalse();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -355,15 +387,17 @@ public void testInvalidRoleSessionNameTooLong() {
AssumeRoleRequest.newBuilder()
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(tooLongRoleSessionName)
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.INVALID_REQUEST);
assertThat(omResponse.getMessage()).isEqualTo("RoleSessionName length must
be between 2 and 64");
assertThat(omResponse.hasAssumeRoleResponse()).isFalse();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -374,14 +408,16 @@ public void testValidRoleSessionNameMaxLengthBoundary() {
AssumeRoleRequest.newBuilder()
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName(roleSessionName) // exactly max length
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.OK);
assertThat(omResponse.hasAssumeRoleResponse()).isTrue();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -391,14 +427,16 @@ public void testValidRoleSessionNameMinLengthBoundary() {
AssumeRoleRequest.newBuilder()
.setRoleArn(ROLE_ARN_1)
.setRoleSessionName("TT") // exactly min length
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
final OMResponse omResponse = response.getOMResponse();
assertThat(omResponse.getStatus()).isEqualTo(Status.OK);
assertThat(omResponse.hasAssumeRoleResponse()).isTrue();
+ assertMarkForAuditCalled(request);
}
@Test
@@ -411,11 +449,13 @@ public void testAssumeRoleWithSessionPolicyPresent() {
.setRoleSessionName(SESSION_NAME)
.setDurationSeconds(3600)
.setAwsIamSessionPolicy(sessionPolicy)
+ .setRequestId(REQUEST_ID)
).build();
- final OMClientResponse response = new S3AssumeRoleRequest(omRequest, CLOCK)
- .validateAndUpdateCache(ozoneManager, context);
+ final S3AssumeRoleRequest request = new S3AssumeRoleRequest(omRequest,
CLOCK);
+ final OMClientResponse response =
request.validateAndUpdateCache(ozoneManager, context);
assertThat(response.getOMResponse().getStatus()).isEqualTo(Status.OK);
+ assertMarkForAuditCalled(request);
}
@Test
@@ -530,6 +570,11 @@ private static OMRequest.Builder baseOmRequestBuilder() {
.setAccessId(ORIGINAL_ACCESS_KEY_ID)
);
}
+
+ private void assertMarkForAuditCalled(S3AssumeRoleRequest request) {
+ OMAuditLogger.log(request.getAuditBuilder());
+ verify(auditLogger).logWrite(any(AuditMessage.class));
+ }
}
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/audit/S3GAction.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/audit/S3GAction.java
index 6c295b7aafc..991e0be15f9 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/audit/S3GAction.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/audit/S3GAction.java
@@ -53,7 +53,10 @@ public enum S3GAction implements AuditAction {
GET_OBJECT_TAGGING,
PUT_OBJECT_TAGGING,
DELETE_OBJECT_TAGGING,
- PUT_OBJECT_ACL;
+ PUT_OBJECT_ACL,
+
+ // STS endpoint
+ ASSUME_ROLE;
@Override
public String getAction() {
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3STSHeadersResponseFilter.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3STSHeadersResponseFilter.java
new file mode 100644
index 00000000000..675f3139a29
--- /dev/null
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3STSHeadersResponseFilter.java
@@ -0,0 +1,49 @@
+/*
+ * 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 java.io.IOException;
+import javax.inject.Inject;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.Provider;
+import org.apache.hadoop.ozone.s3sts.S3STSEnabled;
+
+/**
+ * This class adds common header responses for STS requests.
+ */
+@Provider
+@S3STSEnabled
+public class S3STSHeadersResponseFilter implements ContainerResponseFilter {
+
+ @Inject
+ private RequestIdentifier requestIdentifier;
+
+ @Override
+ public void filter(ContainerRequestContext containerRequestContext,
+ ContainerResponseContext containerResponseContext) throws IOException {
+
+ // Add STS-specific headers
+ containerResponseContext.getHeaders().add("Server", "Ozone");
+ containerResponseContext.getHeaders()
+ .add("X-Amz-Sts-Extended-Request-Id", requestIdentifier.getAmzId());
+ containerResponseContext.getHeaders()
+ .add("x-amzn-RequestId", requestIdentifier.getRequestId());
+ }
+}
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
index 1605532db1c..b4db14dfa61 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3sts/Application.java
@@ -17,6 +17,7 @@
package org.apache.hadoop.ozone.s3sts;
+import org.apache.hadoop.ozone.s3.S3STSHeadersResponseFilter;
import org.apache.hadoop.ozone.s3.exception.OSTSExceptionMapper;
import org.glassfish.jersey.server.ResourceConfig;
@@ -27,6 +28,8 @@ public class Application extends ResourceConfig {
public Application() {
packages("org.apache.hadoop.ozone.s3sts");
register(org.apache.hadoop.ozone.s3.AuthorizationFilter.class);
+ register(org.apache.hadoop.ozone.s3.ClientIpFilter.class);
register(OSTSExceptionMapper.class);
+ register(S3STSHeadersResponseFilter.class);
}
}
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
index 9d6b1b8d77f..62bef03586a 100644
---
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
@@ -22,13 +22,15 @@
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.NOT_IMPLEMENTED;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import java.io.IOException;
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
-import java.util.UUID;
+import java.util.Map;
+import javax.inject.Inject;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -40,8 +42,11 @@
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
+import org.apache.hadoop.ozone.audit.S3GAction;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo;
+import org.apache.hadoop.ozone.om.helpers.S3STSUtils;
+import org.apache.hadoop.ozone.s3.RequestIdentifier;
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
import org.apache.hadoop.ozone.s3.exception.OSTSException;
import org.slf4j.Logger;
@@ -75,6 +80,11 @@ public class S3STSEndpoint extends S3STSEndpointBase {
private static final String GET_ACCESS_KEY_INFO_ACTION = "GetAccessKeyInfo";
private static final String EXPECTED_VERSION = "2011-06-15";
+ private static final String VALIDATION_ERROR = "ValidationError";
+ private static final String INVALID_ACTION = "InvalidAction";
+ private static final String INTERNAL_FAILURE = "InternalFailure";
+ private static final String ACCESS_DENIED = "AccessDenied";
+ private static final String INVALID_CLIENT_TOKEN_ID = "InvalidClientTokenId";
// Default token duration (in seconds) - AWS default is 3600 (1 hour)
// TODO - add these constants and also validations in a common place that
both endpoint and backend can use
@@ -83,6 +93,14 @@ public class S3STSEndpoint extends S3STSEndpointBase {
private static final int MIN_DURATION_SECONDS = 900; // 15 minutes
private static final int MAX_SESSION_POLICY_SIZE = 2048;
+ @Inject
+ private RequestIdentifier requestIdentifier;
+
+ @VisibleForTesting
+ public void setRequestIdentifier(RequestIdentifier requestIdentifier) {
+ this.requestIdentifier = requestIdentifier;
+ }
+
/**
* STS endpoint that handles GET requests with query parameters.
* AWS STS supports both GET and POST requests.
@@ -133,7 +151,8 @@ public Response post(
private Response handleSTSRequest(String action, String roleArn, String
roleSessionName,
Integer durationSeconds, String version, String awsIamSessionPolicy)
throws OS3Exception {
- final String requestId = UUID.randomUUID().toString();
+ final String requestId = requestIdentifier.getRequestId();
+ // NOTE: invalid, missing or unsupported actions are not added to the
audit log
try {
if (action == null) {
// Amazon STS has a different structure for the XML error response
when the action is missing
@@ -154,10 +173,10 @@ private Response handleSTSRequest(String action, String
roleArn, String roleSess
case DECODE_AUTHORIZATION_MESSAGE_ACTION:
case GET_ACCESS_KEY_INFO_ACTION:
throw new OSTSException(
- "InvalidAction", "Operation " + action + " is not supported yet.",
NOT_IMPLEMENTED.getStatusCode());
+ INVALID_ACTION, "Operation " + action + " is not supported yet.",
NOT_IMPLEMENTED.getStatusCode());
default:
throw new OSTSException(
- "InvalidAction", "Could not find operation " + action + " for
version " +
+ INVALID_ACTION, "Could not find operation " + action + " for
version " +
(version == null ? "NO_VERSION_SPECIFIED. Expected version is: "
+ EXPECTED_VERSION : version),
BAD_REQUEST.getStatusCode());
}
@@ -166,107 +185,136 @@ private Response handleSTSRequest(String action, String
roleArn, String roleSess
} catch (Exception ex) {
LOG.error("Unexpected error during STS request", ex);
throw new OSTSException(
- "InternalFailure", "An internal error has occurred.",
INTERNAL_SERVER_ERROR.getStatusCode(), "Receiver");
- }
- }
-
- private int validateDuration(Integer durationSeconds) throws
IllegalArgumentException {
- 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");
+ INTERNAL_FAILURE, "An internal error has occurred.",
INTERNAL_SERVER_ERROR.getStatusCode(), "Receiver");
}
-
- return durationSeconds;
}
private Response handleAssumeRole(String roleArn, String roleSessionName,
Integer durationSeconds,
String awsIamSessionPolicy, String version, String requestId) throws
OSTSException {
- // Validate parameters
final String action = "AssumeRole";
+ final Map<String, String> auditParams = getAuditParameters();
+ S3STSUtils.addAssumeRoleAuditParams(
+ auditParams, roleArn, roleSessionName, awsIamSessionPolicy,
+ durationSeconds == null ? DEFAULT_DURATION_SECONDS : durationSeconds,
+ requestId);
+
int duration;
try {
+ // Validate parameters
duration = validateDuration(durationSeconds);
} catch (IllegalArgumentException e) {
- throw new OSTSException("ValidationError", e.getMessage(),
BAD_REQUEST.getStatusCode());
+ final OSTSException exception = new OSTSException(VALIDATION_ERROR,
e.getMessage(), BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
if (version == null || !version.equals(EXPECTED_VERSION)) {
- throw new OSTSException(
- "InvalidAction", "Could not find operation " + action + " for
version " +
+ final OSTSException exception = new OSTSException(
+ INVALID_ACTION, "Could not find operation " + action + " for version
" +
(version == null ? "NO_VERSION_SPECIFIED. Expected version is: " +
EXPECTED_VERSION : version),
BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
if (roleArn == null || roleArn.isEmpty()) {
- throw new OSTSException(
- "ValidationError", "Value null at 'roleArn' failed to satisfy
constraint: Member must not be null",
+ final OSTSException exception = new OSTSException(
+ VALIDATION_ERROR, "Value null at 'roleArn' failed to satisfy
constraint: Member must not be null",
BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
if (roleSessionName == null || roleSessionName.isEmpty()) {
- throw new OSTSException(
- "ValidationError", "Value null at 'roleSessionName' failed to
satisfy constraint: Member must not be null",
+ final OSTSException exception = new OSTSException(
+ VALIDATION_ERROR, "Value null at 'roleSessionName' failed to satisfy
constraint: Member must not be null",
BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
// Validate role session name format (AWS requirements)
if (!isValidRoleSessionName(roleSessionName)) {
- throw new OSTSException(
- "ValidationError", "Invalid RoleSessionName: must be 2-64 characters
long and " +
+ final OSTSException exception = new OSTSException(
+ VALIDATION_ERROR, "Invalid RoleSessionName: must be 2-64 characters
long and " +
"contain only alphanumeric characters, +, =, ,, ., @, -",
BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
// Check Policy size if available
if (awsIamSessionPolicy != null && awsIamSessionPolicy.length() >
MAX_SESSION_POLICY_SIZE) {
- throw new OSTSException(
- "ValidationError", "Value '" + awsIamSessionPolicy + "' at 'policy'
failed to satisfy constraint: Member " +
+ final OSTSException exception = new OSTSException(
+ VALIDATION_ERROR, "Value '" + awsIamSessionPolicy + "' at 'policy'
failed to satisfy constraint: Member " +
"must have length less than or equal to 2048",
BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
final String assumedRoleUserArn;
try {
assumedRoleUserArn = toAssumedRoleUserArn(roleArn, roleSessionName);
} catch (IllegalArgumentException e) {
- throw new OSTSException("ValidationError", e.getMessage(),
BAD_REQUEST.getStatusCode());
+ final OSTSException exception = new OSTSException(VALIDATION_ERROR,
e.getMessage(), BAD_REQUEST.getStatusCode());
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, exception));
+ throw exception;
}
try {
final AssumeRoleResponseInfo responseInfo = getClient()
.getObjectStore()
- .assumeRole(roleArn, roleSessionName, duration, awsIamSessionPolicy);
+ .assumeRole(roleArn, roleSessionName, duration, awsIamSessionPolicy,
requestId);
// Generate AssumeRole response
final String responseXml =
generateAssumeRoleResponse(assumedRoleUserArn, responseInfo, requestId);
+
+
getAuditLogger().logWriteSuccess(buildAuditMessageForSuccess(S3GAction.ASSUME_ROLE,
auditParams));
+
return Response.ok(responseXml)
.header("Content-Type", "text/xml")
.build();
} catch (IOException e) {
LOG.error("Error during AssumeRole processing", e);
+
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, e));
+
if (e instanceof OMException) {
final OMException omException = (OMException) e;
if (omException.getResult() == OMException.ResultCodes.ACCESS_DENIED ||
omException.getResult() ==
OMException.ResultCodes.PERMISSION_DENIED ||
omException.getResult() == OMException.ResultCodes.TOKEN_EXPIRED) {
throw new OSTSException(
- "AccessDenied", "User is not authorized to perform:
sts:AssumeRole on resource: " + roleArn,
+ ACCESS_DENIED, "User is not authorized to perform:
sts:AssumeRole on resource: " + roleArn,
FORBIDDEN.getStatusCode());
}
if (omException.getResult() == OMException.ResultCodes.INVALID_TOKEN) {
throw new OSTSException(
- "InvalidClientTokenId", "The security token included in the
request is invalid.",
+ INVALID_CLIENT_TOKEN_ID, "The security token included in the
request is invalid.",
FORBIDDEN.getStatusCode());
}
}
- throw new OSTSException("InternalFailure", "An internal error has
occurred.",
- INTERNAL_SERVER_ERROR.getStatusCode(), "Receiver");
+ throw new OSTSException(
+ INTERNAL_FAILURE, "An internal error has occurred.",
INTERNAL_SERVER_ERROR.getStatusCode(), "Receiver");
+ } catch (Exception e) {
+
getAuditLogger().logWriteFailure(buildAuditMessageForFailure(S3GAction.ASSUME_ROLE,
auditParams, e));
+ throw e;
}
}
+ private int validateDuration(Integer durationSeconds) throws
IllegalArgumentException {
+ 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 boolean isValidRoleSessionName(String roleSessionName) {
if (roleSessionName.length() < 2 || roleSessionName.length() > 64) {
return false;
@@ -335,6 +383,7 @@ private String toAssumedRoleUserArn(String roleArn, String
roleSessionName) {
}
final String roleName = resource.substring("role/".length());
+ //noinspection StringBufferReplaceableByString
final StringBuilder stringBuilder = new StringBuilder("arn:");
stringBuilder.append(partition);
stringBuilder.append(":sts::");
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
index 0de5e6c1374..027784b0edc 100644
---
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
@@ -48,8 +48,18 @@ public class S3STSEndpointBase implements Auditor {
@Inject
private SignatureInfo signatureInfo;
- protected static final AuditLogger AUDIT =
- new AuditLogger(AuditLoggerType.S3GLOGGER);
+ protected static final AuditLogger DEFAULT_AUDIT = new
AuditLogger(AuditLoggerType.S3GLOGGER);
+
+ private AuditLogger auditLogger = DEFAULT_AUDIT;
+
+ protected AuditLogger getAuditLogger() {
+ return auditLogger;
+ }
+
+ @VisibleForTesting
+ public void setAuditLogger(AuditLogger auditLogger) {
+ this.auditLogger = auditLogger;
+ }
@PostConstruct
public void initialization() {
@@ -70,6 +80,14 @@ private AuditMessage.Builder
auditMessageBaseBuilder(AuditAction op,
AuditMessage.Builder builder = new AuditMessage.Builder()
.forOperation(op)
.withParams(auditMap);
+
+ if (signatureInfo != null) {
+ String accessId = signatureInfo.getAwsAccessId();
+ if (accessId != null && !accessId.isEmpty()) {
+ builder.setUser(accessId);
+ }
+ }
+
if (context != null) {
builder.atIp(AuditUtils.getClientIpAddress(context));
}
@@ -111,4 +129,8 @@ public void setContext(ContainerRequestContext context) {
public void setSignatureInfo(SignatureInfo signatureInfo) {
this.signatureInfo = signatureInfo;
}
+
+ protected Map<String, String> getAuditParameters() {
+ return AuditUtils.getAuditParameters(context);
+ }
}
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
index 477b6876af8..304349f4371 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
@@ -805,12 +805,8 @@ public void deleteObjectTagging(String volumeName, String
bucketName, String key
}
@Override
- public AssumeRoleResponseInfo assumeRole(
- String roleArn,
- String roleSessionName,
- int durationSeconds,
- String awsIamSessionPolicy
- ) throws IOException {
+ public AssumeRoleResponseInfo assumeRole(String roleArn, String
roleSessionName, int durationSeconds,
+ String awsIamSessionPolicy, String requestId) throws IOException {
return null;
}
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestS3STSEndpoint.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestS3STSEndpoint.java
index aefb525448b..34891d08945 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestS3STSEndpoint.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3sts/TestS3STSEndpoint.java
@@ -26,31 +26,37 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.time.Instant;
import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.client.ObjectStore;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.client.OzoneClientStub;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo;
import org.apache.hadoop.ozone.s3.OzoneConfigurationHolder;
+import org.apache.hadoop.ozone.s3.RequestIdentifier;
import org.apache.hadoop.ozone.s3.exception.OSTSException;
import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
@@ -61,15 +67,13 @@
public class TestS3STSEndpoint {
private S3STSEndpoint endpoint;
private ObjectStore objectStore;
+ private AuditLogger auditLogger;
private static final String ROLE_ARN =
"arn:aws:iam::123456789012:role/test-role";
private static final String ROLE_SESSION_NAME = "test-session";
private static final String ROLE_USER_ARN =
"arn:aws:sts::123456789012:assumed-role/test-role/" + ROLE_SESSION_NAME;
private static final String STS_NS =
"https://sts.amazonaws.com/doc/2011-06-15/";
private static final String AWS_FAULT_NS =
"http://webservices.amazon.com/AWSFault/2005-15-09";
- @Mock
- private ContainerRequestContext context;
-
@BeforeEach
public void setup() throws Exception {
OzoneConfiguration config = new OzoneConfiguration();
@@ -77,9 +81,16 @@ public void setup() throws Exception {
OzoneConfigurationHolder.setConfiguration(config);
OzoneClient clientStub = spy(new OzoneClientStub());
+ final ContainerRequestContext context =
mock(ContainerRequestContext.class);
+ final RequestIdentifier requestIdentifier = mock(RequestIdentifier.class);
+ final UriInfo uriInfo = mock(UriInfo.class);
+ when(context.getUriInfo()).thenReturn(uriInfo);
+ when(uriInfo.getPathParameters()).thenReturn(new MultivaluedHashMap<>());
+ when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>());
+
// Stub assumeRole to return deterministic credentials.
objectStore = mock(ObjectStore.class);
- when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any()))
+ when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any(),
anyString()))
.thenReturn(new AssumeRoleResponseInfo(
"ASIA1234567890123456",
"mySecretAccessKey",
@@ -91,6 +102,12 @@ public void setup() throws Exception {
endpoint = new S3STSEndpoint();
endpoint.setClient(clientStub);
endpoint.setContext(context);
+ auditLogger = mock(AuditLogger.class);
+ endpoint.setAuditLogger(auditLogger);
+
+ when(requestIdentifier.getRequestId()).thenReturn("test-request-id");
+ endpoint.setRequestIdentifier(requestIdentifier);
+
SignatureInfo signatureInfo = new
SignatureInfo.Builder(SignatureInfo.Version.V4)
.setAwsAccessId("test-user")
.setSignature("some-signature")
@@ -105,6 +122,8 @@ public void testStsAssumeRoleValidForGetMethod() throws
Exception {
"AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600, "2011-06-15", null);
assertEquals(200, response.getStatus());
+ verify(auditLogger).logWriteSuccess(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteFailure(any(AuditMessage.class));
String responseXml = (String) response.getEntity();
assertNotNull(responseXml);
@@ -141,6 +160,8 @@ public void testStsAssumeRoleValidForPostMethod() throws
Exception {
final Response response = endpoint.post("AssumeRole", ROLE_ARN,
ROLE_SESSION_NAME, 3600, "2011-06-15", null);
assertEquals(200, response.getStatus());
+ verify(auditLogger).logWriteSuccess(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteFailure(any(AuditMessage.class));
final String responseXml = (String) response.getEntity();
assertNotNull(responseXml);
@@ -174,6 +195,7 @@ public void testStsNullAction() throws Exception {
final Response response = endpoint.get(null, ROLE_ARN, ROLE_SESSION_NAME,
3600, "2011-06-15", null);
assertEquals(400, response.getStatus());
+ verifyNoInteractions(auditLogger);
final String errorMessage = (String) response.getEntity();
assertEquals("<UnknownOperationException/>", errorMessage);
@@ -188,6 +210,7 @@ public void testStsUnsupportedActionWithVersionSupplied()
throws Exception {
endpoint.get("UnsupportedAction", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verifyNoInteractions(auditLogger);
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -201,6 +224,7 @@ public void
testStsUnsupportedActionWithVersionNotSupplied() throws Exception {
endpoint.get("UnsupportedAction", ROLE_ARN, ROLE_SESSION_NAME, 3600,
null, null));
assertEquals(400, ex.getHttpCode());
+ verifyNoInteractions(auditLogger);
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -214,6 +238,8 @@ public void testStsAssumeRoleWithInvalidVersion() throws
Exception {
endpoint.get("AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2000-01-01", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -227,6 +253,8 @@ public void testStsInvalidDuration() throws Exception {
endpoint.get("AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, -1,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -238,9 +266,11 @@ public void testStsNullDurationUsesDefault3600() throws
Exception {
final Response response = endpoint.get(
"AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, null, "2011-06-15", null);
assertEquals(200, response.getStatus());
+ verify(auditLogger).logWriteSuccess(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteFailure(any(AuditMessage.class));
final ArgumentCaptor<Integer> durationCaptor =
ArgumentCaptor.forClass(Integer.class);
- verify(objectStore).assumeRole(anyString(), anyString(),
durationCaptor.capture(), any());
+ verify(objectStore).assumeRole(anyString(), anyString(),
durationCaptor.capture(), any(), anyString());
assertEquals(3600, durationCaptor.getValue());
}
@@ -252,6 +282,8 @@ public void testStsPolicyTooLarge() throws Exception {
endpoint.get("AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2011-06-15", tooLargePolicy));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -267,6 +299,8 @@ public void testStsInvalidRoleArn() throws Exception {
endpoint.get("AssumeRole", invalidRoleArn, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -280,6 +314,8 @@ public void testStsMissingRoleArn() throws Exception {
endpoint.get("AssumeRole", null, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -294,6 +330,8 @@ public void testStsInvalidRoleArnMissingRoleName() throws
Exception {
assertEquals(400, ex.getHttpCode());
assertEquals("ValidationError", ex.getCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -308,6 +346,8 @@ public void testStsInvalidRoleArnMissingAccountId() throws
Exception {
assertEquals(400, ex.getHttpCode());
assertEquals("ValidationError", ex.getCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -321,6 +361,7 @@ public void testStsWhenActionNotImplemented() throws
Exception {
endpoint.get("GetSessionToken", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(501, ex.getHttpCode());
+ verifyNoInteractions(auditLogger);
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -334,6 +375,8 @@ public void testStsMissingRoleSessionName() throws
Exception {
endpoint.get("AssumeRole", ROLE_ARN, null, 3600, "2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -347,6 +390,8 @@ public void
testStsInvalidRoleSessionNameWithInvalidCharacter() throws Exception
endpoint.get("AssumeRole", ROLE_ARN, invalidSession, 3600,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -360,6 +405,8 @@ public void testStsInvalidRoleSessionNameTooShort() throws
Exception {
endpoint.get("AssumeRole", ROLE_ARN, invalidSession, 3600,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -374,6 +421,8 @@ public void testStsInvalidRoleArnResourceType() throws
Exception {
endpoint.get("AssumeRole", invalidRoleArn, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(400, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -382,13 +431,15 @@ public void testStsInvalidRoleArnResourceType() throws
Exception {
@Test
public void testStsInternalFailureWhenBackendThrows() throws Exception {
- when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any()))
+ when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any(),
anyString()))
.thenThrow(new RuntimeException("some unexpected error"));
final OSTSException ex = assertThrows(OSTSException.class, () ->
endpoint.get("AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(500, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -397,13 +448,15 @@ public void testStsInternalFailureWhenBackendThrows()
throws Exception {
@Test
public void testStsAccessDenied() throws Exception {
- when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any()))
+ when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any(),
anyString()))
.thenThrow(new OMException("Permission denied",
OMException.ResultCodes.ACCESS_DENIED));
final OSTSException ex = assertThrows(OSTSException.class, () ->
endpoint.get("AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(403, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
@@ -413,13 +466,15 @@ public void testStsAccessDenied() throws Exception {
@Test
public void testStsIOExceptionWrappedAsInternalFailure() throws Exception {
- when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any()))
+ when(objectStore.assumeRole(anyString(), anyString(), anyInt(), any(),
anyString()))
.thenThrow(new IOException("An IO error occurred"));
final OSTSException ex = assertThrows(OSTSException.class, () ->
endpoint.get("AssumeRole", ROLE_ARN, ROLE_SESSION_NAME, 3600,
"2011-06-15", null));
assertEquals(500, ex.getHttpCode());
+ verify(auditLogger).logWriteFailure(any(AuditMessage.class));
+ verify(auditLogger, never()).logWriteSuccess(any(AuditMessage.class));
final String requestId = "test-request-id";
ex.setRequestId(requestId);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]