This command marks the specified STS token as revoked by adding it to the OM's revoked STS token table. - * Subsequent S3 requests using the same session token will be rejected once the revocation - * state has propagated.
+ *This command records a revocation cutoff for the given original access key ID in the OM's + * revoked STS token table. Subsequent S3 requests using STS tokens created before that cutoff + * will be rejected once the revocation state has propagated.
*/ @Command(name = "revokeststoken", - description = "Revoke S3 STS token for the given session token") + description = "Revoke S3 STS tokens for the given original access key ID") public class RevokeSTSTokenHandler extends S3Handler { - @Option(names = "-t", + @Option(names = {"-o", "--original-access-key-id"}, required = true, - description = "STS session token") - private String sessionToken; + description = "Original long-lived access key ID whose STS tokens should be revoked") + private String originalAccessKeyId; @Option(names = "-y", description = "Continue without interactive user confirmation") @@ -56,8 +56,8 @@ protected void execute(OzoneClient client, OzoneAddress address) throws IOException { if (!yes) { - out().print("Enter 'y' to confirm STS token revocation for sessionToken '" + - sessionToken + "': "); + out().print( + "Enter 'y' to confirm STS token revocation for originalAccessKeyId '" + originalAccessKeyId + "': "); out().flush(); final Scanner scanner = new Scanner(new InputStreamReader(System.in, StandardCharsets.UTF_8)); final String confirmation = scanner.next().trim().toLowerCase(); @@ -67,7 +67,7 @@ protected void execute(OzoneClient client, OzoneAddress address) } } - client.getObjectStore().revokeSTSToken(sessionToken); - out().println("STS token revoked for sessionToken '" + sessionToken + "'."); + client.getObjectStore().revokeSTSToken(originalAccessKeyId); + out().println("STS tokens revoked for originalAccessKeyId '" + originalAccessKeyId + "'."); } } 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 bd045ef04e03..32af4cacd4f3 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 @@ -36,6 +36,7 @@ import org.apache.hadoop.ozone.client.protocol.ClientProtocol; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo; +import org.apache.hadoop.ozone.om.helpers.CallerIdentityInfo; import org.apache.hadoop.ozone.om.helpers.BucketLayout; import org.apache.hadoop.ozone.om.helpers.DeleteTenantState; import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs; @@ -813,12 +814,21 @@ public AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName, } /** - * Revokes an STS token. - * @param sessionToken The STS sessionToken + * Returns the caller identity for the current S3-authenticated request. + * @return CallerIdentityInfo containing account, arn, and userId + * @throws IOException if an error occurs during the GetCallerIdentity operation + */ + public CallerIdentityInfo getCallerIdentity() throws IOException { + return proxy.getCallerIdentity(); + } + + /** + * Revokes STS tokens for the given original access key ID. + * @param originalAccessKeyId The original long-lived access key ID whose STS tokens to revoke * @throws IOException if an error occurs while revoking the STS token */ - public void revokeSTSToken(String sessionToken) throws IOException { - proxy.revokeSTSToken(sessionToken); + public void revokeSTSToken(String originalAccessKeyId) throws IOException { + proxy.revokeSTSToken(originalAccessKeyId); } /** 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 807cd2757cfc..73f099879bc6 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 @@ -48,6 +48,7 @@ import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo; +import org.apache.hadoop.ozone.om.helpers.CallerIdentityInfo; import org.apache.hadoop.ozone.om.helpers.DeleteTenantState; import org.apache.hadoop.ozone.om.helpers.ErrorInfo; import org.apache.hadoop.ozone.om.helpers.LeaseKeyInfo; @@ -1648,11 +1649,18 @@ AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName, int du String awsIamSessionPolicy, String requestId) throws IOException; /** - * Revokes an STS token. - * @param sessionToken The STS sessionToken + * Returns the caller identity for the current S3-authenticated request. + * @return CallerIdentityInfo containing account, arn, and userId + * @throws IOException if an error occurs during the GetCallerIdentity operation + */ + CallerIdentityInfo getCallerIdentity() throws IOException; + + /** + * Revokes STS tokens for the given original access key ID. + * @param originalAccessKeyId The original long-lived access key ID whose STS tokens to revoke * @throws IOException if an error occurs while revoking the STS token */ - void revokeSTSToken(String sessionToken) throws IOException; + void revokeSTSToken(String originalAccessKeyId) throws IOException; /** * Gets the lifecycle configuration information. 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 733c915dcd03..22fad1944429 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 @@ -130,6 +130,7 @@ import org.apache.hadoop.ozone.om.OmConfig; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo; +import org.apache.hadoop.ozone.om.helpers.CallerIdentityInfo; import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo; import org.apache.hadoop.ozone.om.helpers.BucketEncryptionKeyInfo; import org.apache.hadoop.ozone.om.helpers.BucketLayout; @@ -3022,8 +3023,13 @@ public AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName, } @Override - public void revokeSTSToken(String sessionToken) throws IOException { - ozoneManagerClient.revokeSTSToken(sessionToken); + public CallerIdentityInfo getCallerIdentity() throws IOException { + return ozoneManagerClient.getCallerIdentity(); + } + + @Override + public void revokeSTSToken(String originalAccessKeyId) throws IOException { + ozoneManagerClient.revokeSTSToken(originalAccessKeyId); } @Override diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java index 5da80215fad8..31530900cf2c 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java @@ -238,6 +238,7 @@ public static boolean isReadOnly(OMRequest omRequest) { case FinalizeUpgradeProgress: case PrepareStatus: case GetS3VolumeContext: + case GetCallerIdentity: case ListTenant: case TenantGetUserInfo: case TenantListUser: @@ -383,6 +384,7 @@ public static boolean shouldSendToFollower(OMRequest omRequest) { case FinalizeUpgradeProgress: case PrepareStatus: case GetS3VolumeContext: + case GetCallerIdentity: case ListTenant: case TenantGetUserInfo: case TenantListUser: diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/CallerIdentityInfo.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/CallerIdentityInfo.java new file mode 100644 index 000000000000..5a014f19fcfd --- /dev/null +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/CallerIdentityInfo.java @@ -0,0 +1,88 @@ +/* + * 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 java.util.Objects; +import net.jcip.annotations.Immutable; +import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetCallerIdentityResponse; + +/** + * Utility class to handle GetCallerIdentityResponse protobuf message. + */ +@Immutable +public class CallerIdentityInfo { + + private final String account; + private final String arn; + private final String userId; + + public CallerIdentityInfo(String account, String arn, String userId) { + this.account = account; + this.arn = arn; + this.userId = userId; + } + + public String getAccount() { + return account; + } + + public String getArn() { + return arn; + } + + public String getUserId() { + return userId; + } + + public static CallerIdentityInfo fromProtobuf(GetCallerIdentityResponse response) { + return new CallerIdentityInfo(response.getAccount(), response.getArn(), response.getUserId()); + } + + public GetCallerIdentityResponse getProtobuf() { + return GetCallerIdentityResponse.newBuilder() + .setAccount(account) + .setArn(arn) + .setUserId(userId) + .build(); + } + + @Override + public String toString() { + return "CallerIdentityInfo{" + "account='" + account + "', arn='" + arn + "', userId='" + userId + "'}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + final CallerIdentityInfo that = (CallerIdentityInfo) o; + return Objects.equals(account, that.account) && Objects.equals(arn, that.arn) && + Objects.equals(userId, that.userId); + } + + @Override + public int hashCode() { + return Objects.hash(account, arn, userId); + } +} 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 index 763c8fe9bfa4..17bf084f9b31 100644 --- 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 @@ -40,9 +40,47 @@ public final class S3STSUtils { // AWS limit for session policy is 2048 characters public static final int MAX_SESSION_POLICY_LENGTH = 2048; + public static final String STS_TOKEN_PREFIX = "ASIA"; + public static final String STS_ACCESS_KEY_ID_ALLOWED_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public static final int STS_ACCESS_KEY_ID_ALLOWED_CHARS_LENGTH = STS_ACCESS_KEY_ID_ALLOWED_CHARS.length(); + public static final int STS_ACCESS_KEY_ID_RANDOM_LENGTH = 20; + + public static final String OZONE_STATIC_ACCOUNT_ID = "123456789012"; + private S3STSUtils() { } + /** + * Builds an IAM user ARN for the given Kerberos short name. + */ + public static String toIamUserArn(String kerberosShortName) { + return "arn:aws:iam::" + OZONE_STATIC_ACCOUNT_ID + ":user/" + kerberosShortName; + } + + /** + * Resolves the caller identity for GetCallerIdentity with permanent S3 credentials. + * + * @param resolvedPrincipal full Kerberos principal of the caller + * @param kerberosShortName short username + * @return caller identity with account, arn, and userId + */ + public static CallerIdentityInfo resolveCallerIdentityForPermanentCredentials(String resolvedPrincipal, + String kerberosShortName) { + return new CallerIdentityInfo(OZONE_STATIC_ACCOUNT_ID, toIamUserArn(kerberosShortName), resolvedPrincipal); + } + + /** + * Resolves the caller identity for GetCallerIdentity with temporary STS credentials. + * + * @param assumedRoleId assumed role ID from the STS token + * @param assumedRoleUserArn assumed role user ARN from the STS token + * @return caller identity with account, arn, and userId + */ + public static CallerIdentityInfo resolveCallerIdentityForStsCredentials(String assumedRoleId, + String assumedRoleUserArn) { + return new CallerIdentityInfo(OZONE_STATIC_ACCOUNT_ID, assumedRoleUserArn, assumedRoleId); + } + /** * Adds standard AssumeRole audit params. */ 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 604669487c68..7cea19d7a909 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 @@ -30,6 +30,7 @@ import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo; +import org.apache.hadoop.ozone.om.helpers.CallerIdentityInfo; import org.apache.hadoop.ozone.om.helpers.DBUpdates; import org.apache.hadoop.ozone.om.helpers.DeleteTenantState; import org.apache.hadoop.ozone.om.helpers.ErrorInfo; @@ -1336,11 +1337,20 @@ default AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName } /** - * Revokes an STS token. - * @param sessionToken The STS sessionToken + * Returns the caller identity for the current S3-authenticated request. + * @return CallerIdentityInfo containing account, arn, and userId + * @throws IOException if an error occurs during the GetCallerIdentity operation + */ + default CallerIdentityInfo getCallerIdentity() throws IOException { + throw new UnsupportedOperationException("OzoneManager does not require this to be implemented"); + } + + /** + * Revokes STS tokens for the given original access key ID. + * @param originalAccessKeyId The original long-lived access key ID whose STS tokens to revoke * @throws IOException if an error occurs while revoking the STS token */ - default void revokeSTSToken(String sessionToken) throws IOException { + default void revokeSTSToken(String originalAccessKeyId) 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 c60bc60db700..b5a9e3a2bbcf 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 @@ -60,6 +60,7 @@ import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.helpers.AssumeRoleResponseInfo; import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo; +import org.apache.hadoop.ozone.om.helpers.CallerIdentityInfo; import org.apache.hadoop.ozone.om.helpers.DBUpdates; import org.apache.hadoop.ozone.om.helpers.DeleteTenantState; import org.apache.hadoop.ozone.om.helpers.ErrorInfo; @@ -2981,10 +2982,19 @@ public AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName, } @Override - public void revokeSTSToken(String sessionToken) throws IOException { + public CallerIdentityInfo getCallerIdentity() throws IOException { + final OMRequest omRequest = createOMRequest(Type.GetCallerIdentity) + .setGetCallerIdentityRequest(OzoneManagerProtocolProtos.GetCallerIdentityRequest.newBuilder().build()) + .build(); + + return CallerIdentityInfo.fromProtobuf(handleError(submitRequest(omRequest)).getGetCallerIdentityResponse()); + } + + @Override + public void revokeSTSToken(String originalAccessKeyId) throws IOException { final OzoneManagerProtocolProtos.RevokeSTSTokenRequest request = OzoneManagerProtocolProtos.RevokeSTSTokenRequest.newBuilder() - .setSessionToken(sessionToken) + .setOriginalAccessKeyId(originalAccessKeyId) .build(); final OMRequest omRequest = createOMRequest(Type.RevokeSTSToken) diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestCallerIdentityInfo.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestCallerIdentityInfo.java new file mode 100644 index 000000000000..f6bc0334acfe --- /dev/null +++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestCallerIdentityInfo.java @@ -0,0 +1,107 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetCallerIdentityResponse; +import org.junit.jupiter.api.Test; + +/** + * Test CallerIdentityInfo. + */ +public class TestCallerIdentityInfo { + + private static final String ACCOUNT = "123456789012"; + private static final String ARN = "arn:aws:iam::123456789012:user/om"; + private static final String USER_ID = "om/polarisclient@root.comops.site"; + + @Test + public void testConstructor() { + final CallerIdentityInfo identity = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + + assertEquals(ACCOUNT, identity.getAccount()); + assertEquals(ARN, identity.getArn()); + assertEquals(USER_ID, identity.getUserId()); + } + + @Test + public void testProtobufConversion() { + final CallerIdentityInfo identity = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + + final GetCallerIdentityResponse proto = identity.getProtobuf(); + + assertNotNull(proto); + assertEquals(ACCOUNT, proto.getAccount()); + assertEquals(ARN, proto.getArn()); + assertEquals(USER_ID, proto.getUserId()); + } + + @Test + public void testFromProtobuf() { + final GetCallerIdentityResponse proto = GetCallerIdentityResponse.newBuilder() + .setAccount(ACCOUNT) + .setArn(ARN) + .setUserId(USER_ID) + .build(); + + final CallerIdentityInfo identity = CallerIdentityInfo.fromProtobuf(proto); + + assertEquals(ACCOUNT, identity.getAccount()); + assertEquals(ARN, identity.getArn()); + assertEquals(USER_ID, identity.getUserId()); + } + + @Test + public void testProtobufRoundTrip() { + final CallerIdentityInfo original = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + + final CallerIdentityInfo recovered = CallerIdentityInfo.fromProtobuf(original.getProtobuf()); + + assertEquals(original, recovered); + } + + @Test + public void testEqualsAndHashCodeWithIdenticalObjects() { + final CallerIdentityInfo identity1 = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + final CallerIdentityInfo identity2 = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + + assertEquals(identity1, identity2); + assertEquals(identity1.hashCode(), identity2.hashCode()); + } + + @Test + public void testNotEqualsWithDifferentArn() { + final CallerIdentityInfo identity1 = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + final CallerIdentityInfo identity2 = new CallerIdentityInfo( + ACCOUNT, "arn:aws:iam::123456789012:user/other", USER_ID); + + assertNotEquals(identity1, identity2); + assertNotEquals(identity1.hashCode(), identity2.hashCode()); + } + + @Test + public void testToString() { + final CallerIdentityInfo identity = new CallerIdentityInfo(ACCOUNT, ARN, USER_ID); + + assertEquals( + "CallerIdentityInfo{account='123456789012', arn='" + ARN + "', userId='" + USER_ID + "'}", identity.toString()); + } +} diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestS3STSUtilsCallerIdentity.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestS3STSUtilsCallerIdentity.java new file mode 100644 index 000000000000..f991bd7bffdc --- /dev/null +++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestS3STSUtilsCallerIdentity.java @@ -0,0 +1,59 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test caller identity resolution helpers in S3STSUtils. + */ +public class TestS3STSUtilsCallerIdentity { + + private static final String PRINCIPAL = "om/polarisclient@example.com"; + private static final String KERBEROS_SHORT_NAME = "om"; + private static final String ASSUMED_ROLE_ID = "AROATEST123456789:testsess"; + private static final String ASSUMED_ROLE_USER_ARN = + "arn:aws:sts::123456789012:assumed-role/test-role/testsess"; + + @Test + public void testToIamUserArn() { + assertEquals("arn:aws:iam::123456789012:user/om", S3STSUtils.toIamUserArn(KERBEROS_SHORT_NAME)); + } + + @Test + public void testResolveCallerIdentityForPermanentCredentials() { + final CallerIdentityInfo identity = S3STSUtils.resolveCallerIdentityForPermanentCredentials( + PRINCIPAL, KERBEROS_SHORT_NAME); + + assertEquals(S3STSUtils.OZONE_STATIC_ACCOUNT_ID, identity.getAccount()); + assertEquals("arn:aws:iam::123456789012:user/om", identity.getArn()); + assertEquals(PRINCIPAL, identity.getUserId()); + } + + @Test + public void testResolveCallerIdentityForStsCredentials() { + final CallerIdentityInfo identity = S3STSUtils.resolveCallerIdentityForStsCredentials( + ASSUMED_ROLE_ID, ASSUMED_ROLE_USER_ARN); + + assertEquals(S3STSUtils.OZONE_STATIC_ACCOUNT_ID, identity.getAccount()); + assertEquals(ASSUMED_ROLE_USER_ARN, identity.getArn()); + assertEquals(ASSUMED_ROLE_ID, identity.getUserId()); + } +} diff --git a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.resource b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.resource index 19cb6f4e2022..02c16a9418c2 100644 --- a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.resource +++ b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.resource @@ -103,10 +103,14 @@ Assume Role And Get Temporary Credentials ${stsAccessKeyId} = Execute echo '${json}' | jq -r '.Credentials.AccessKeyId' ${stsSecretKey} = Execute echo '${json}' | jq -r '.Credentials.SecretAccessKey' ${stsSessionToken} = Execute echo '${json}' | jq -r '.Credentials.SessionToken' + ${assumedRoleId} = Execute echo '${json}' | jq -r '.AssumedRoleUser.AssumedRoleId' + ${assumedRoleUserArn} = Execute echo '${json}' | jq -r '.AssumedRoleUser.Arn' Should Start With ${stsAccessKeyId} ASIA Set Global Variable ${STS_ACCESS_KEY_ID} ${stsAccessKeyId} Set Global Variable ${STS_SECRET_KEY} ${stsSecretKey} Set Global Variable ${STS_SESSION_TOKEN} ${stsSessionToken} + Set Global Variable ${STS_ASSUMED_ROLE_ID} ${assumedRoleId} + Set Global Variable ${STS_ASSUMED_ROLE_USER_ARN} ${assumedRoleUserArn} ${expected_duration} = Set Variable ${duration_seconds} # Ensure the expected duration defaults to 3600 seconds (1 hour) if not specified @@ -328,3 +332,36 @@ Assert Listed Keys Should Equal Sort List ${expected_sorted} ${actual_list} = Evaluate json.loads($actual_keys_json) modules=json Lists Should Be Equal ${actual_list} ${expected_sorted} + +Get Caller Identity + [Arguments] ${profile} + ${json} = Execute aws sts get-caller-identity --endpoint-url ${STS_ENDPOINT_URL} --output json --profile ${profile} + ${account} = Execute echo '${json}' | jq -r '.Account' + ${arn} = Execute echo '${json}' | jq -r '.Arn' + ${userId} = Execute echo '${json}' | jq -r '.UserId' + [Return] ${json} ${account} ${arn} ${userId} + +Get Caller Identity Using Curl + # AWS CLI always sends Version=2011-06-15 and rejects unknown flags, so use curl to test version validation. + # curl 7.76.1's --aws-sigv4 omits the port from the signed canonical "host" header (it signs "host:s3g" + # while sending "Host: s3g:9880"), so Ozone's SigV4 validation for the non-default STS port rejects it. + # Send an explicit Host header without the port so the sent and signed host values match. + # This should also work for newer versions of curl as well + [Arguments] ${perm_access_key_id} ${perm_secret_key} ${api_version}=2011-06-15 ${extra_curl_params}=${EMPTY} + ${sts_host} = Evaluate urllib.parse.urlparse("${STS_ENDPOINT_URL}").hostname modules=urllib.parse + ${cmd} = Set Variable curl --silent --show-error --include --request POST --aws-sigv4 "aws:amz:us-east-1:sts" --user '${perm_access_key_id}:${perm_secret_key}' --header "Host: ${sts_host}" --header "Content-Type: application/x-www-form-urlencoded" --data-urlencode "Action=GetCallerIdentity" + ${cmd} = Set Variable If '${api_version}' != '${EMPTY}' ${cmd} --data-urlencode "Version=${api_version}" ${cmd} + ${cmd} = Set Variable If '${extra_curl_params}' != '${EMPTY}' ${cmd} ${extra_curl_params} ${cmd} + ${cmd} = Set Variable ${cmd} ${STS_ENDPOINT_URL} + ${output} = Execute And Ignore Error ${cmd} + [Return] ${output} + +Get Caller Identity Should Fail + [Arguments] ${expected_error_contains} ${api_version}=${EMPTY} + ${output} = Get Caller Identity Using Curl ${PERMANENT_ACCESS_KEY_ID} ${PERMANENT_SECRET_KEY} api_version=${api_version} + Should Contain ${output} ${expected_error_contains} + @{http_codes} = Get Regexp Matches ${output} (?m)^HTTP/[0-9.]+ ([0-9]{3}) 1 + ${code_count} = Get Length ${http_codes} + Should Be True ${code_count} > 0 Expected to find an HTTP status code in curl output, but none was found. + ${http_code} = Get From List ${http_codes} -1 + Should Be Equal As Strings ${http_code} 400 diff --git a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.robot b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.robot index c0362d747048..b62a1053f5a7 100644 --- a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.robot +++ b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-sts.robot @@ -66,6 +66,7 @@ ${ACTION_MATCHES_PUTOBJECT_CREATE_WRITE_ROLE_ARN} arn:aws:iam::123456789012:rol ${ACTION_MATCHES_GETOBJECT_PUTOBJECT_ROLE_ARN} arn:aws:iam::123456789012:role/${ACTION_MATCHES_GETOBJECT_PUTOBJECT_ROLE} ${ACTION_MATCHES_UPLOADPARTCOPY_EXPECTED_OWNER_ROLE_ARN} arn:aws:iam::123456789012:role/${ACTION_MATCHES_UPLOADPARTCOPY_EXPECTED_OWNER_ROLE} ${ACTION_MATCHES_GET_STAR_READ_ROLE_ARN} arn:aws:iam::123456789012:role/${ACTION_MATCHES_GET_STAR_READ_ROLE} +${TEST_USER_ADMIN} testuser ${TEST_USER_NON_ADMIN} testuser2 @{ICEBERG_OBJECT_KEYS} file1.txt file1again.txt folder/pepper.txt folder/salt.txt userA/userA.txt userB/userB.txt userAfile.txt @{ICEBERG_LISTABLE_OBJECT_KEYS_OBS} file1.txt file1again.txt folder/pepper.txt folder/salt.txt userA/userA.txt userB/userB.txt userAfile.txt zeroByteFile zeroByteFolder/ @@ -254,6 +255,17 @@ Configure STS Profile With Bogus Credential Part Configure STS Profile ${STS_ACCESS_KEY_ID} ${STS_SECRET_KEY} bogusSessionToken END +Verify STS Token Revocation And Post Revocation Assume Role + [Arguments] ${bucket} ${role_arn} ${revoker_user} ${revoker_keytab} + Assume Role And Configure STS Profile perm_access_key_id=${PERMANENT_ACCESS_KEY_ID} perm_secret_key=${PERMANENT_SECRET_KEY} role_arn=${role_arn} + Get Object Should Succeed ${bucket} ${ICEBERG_BUCKET_TESTFILE} + Kinit test user ${revoker_user} ${revoker_keytab} + ${output} = Execute ozone s3 revokeststoken -o ${PERMANENT_ACCESS_KEY_ID} -y ${OM_HA_PARAM} + Should Contain ${output} STS tokens revoked for originalAccessKeyId + Get Object Should Fail ${bucket} ${ICEBERG_BUCKET_TESTFILE} AccessDenied + Assume Role And Configure STS Profile perm_access_key_id=${PERMANENT_ACCESS_KEY_ID} perm_secret_key=${PERMANENT_SECRET_KEY} role_arn=${role_arn} + Get Object Should Succeed ${bucket} ${ICEBERG_BUCKET_TESTFILE} + *** Test Cases *** Create User in Ranger ${user_json} = Set Variable { "loginId": "${ICEBERG_SVC_CATALOG_USER}", "name": "${ICEBERG_SVC_CATALOG_USER}", "password": "Password123", "firstName": "Iceberg REST", "lastName": "Catalog", "emailAddress": "${ICEBERG_SVC_CATALOG_USER}@example.com", "userRoleList": ["ROLE_USER"], "userPermList": [ { "moduleId": 1, "isAllowed": 1 }, { "moduleId": 3, "isAllowed": 1 }, { "moduleId": 7, "isAllowed": 1 } ] } @@ -558,27 +570,32 @@ Verify Token Revocation via CLI FOR ${bucket} ${role_arn} IN ... ${ICEBERG_BUCKET_OBS} ${ICEBERG_ALL_ACCESS_ROLE_OBS_ARN} ... ${ICEBERG_BUCKET_FSO} ${ICEBERG_ALL_ACCESS_ROLE_FSO_ARN} - Assume Role And Configure STS Profile perm_access_key_id=${PERMANENT_ACCESS_KEY_ID} perm_secret_key=${PERMANENT_SECRET_KEY} role_arn=${role_arn} - ${output} = Execute ozone s3 revokeststoken -t ${STS_SESSION_TOKEN} -y ${OM_HA_PARAM} - Should Contain ${output} STS token revoked for sessionToken - # Trying to use the token for even get-object should now fail. - Get Object Should Fail ${bucket} ${ICEBERG_BUCKET_TESTFILE} AccessDenied + # Owner of the original access key can revoke the STS token. + Verify STS Token Revocation And Post Revocation Assume Role ${bucket} ${role_arn} ${ICEBERG_SVC_CATALOG_USER} ${ICEBERG_SVC_CATALOG_USER}.keytab + # S3 admin can also revoke an STS token owned by another user. + Verify STS Token Revocation And Post Revocation Assume Role ${bucket} ${role_arn} ${TEST_USER_ADMIN} ${TEST_USER_ADMIN}.keytab END Non-Admin Cannot Revoke STS Token FOR ${role_arn} IN ${ICEBERG_ALL_ACCESS_ROLE_OBS_ARN} ${ICEBERG_ALL_ACCESS_ROLE_FSO_ARN} # Create a token first. Assume Role And Get Temporary Credentials perm_access_key_id=${PERMANENT_ACCESS_KEY_ID} perm_secret_key=${PERMANENT_SECRET_KEY} role_arn=${role_arn} - ${token_to_revoke} = Set Variable ${STS_SESSION_TOKEN} # Kinit as non-admin user. Kinit test user ${TEST_USER_NON_ADMIN} ${TEST_USER_NON_ADMIN}.keytab # Try to revoke - should give USER_MISMATCH error. - ${output} = Execute And Ignore Error ozone s3 revokeststoken -t ${token_to_revoke} -y ${OM_HA_PARAM} + ${output} = Execute And Ignore Error ozone s3 revokeststoken -o ${PERMANENT_ACCESS_KEY_ID} -y ${OM_HA_PARAM} Should Contain ${output} USER_MISMATCH END +Revoke STS Token Should Fail For Unknown Original Access Key Id + # Revoking a bogus originalAccessKeyId must fail before writing to the revocation table. + Kinit test user ${TEST_USER_ADMIN} ${TEST_USER_ADMIN}.keytab + ${output} = Execute And Ignore Error ozone s3 revokeststoken -o bogus-original-access-key-id -y ${OM_HA_PARAM} + Should Contain ${output} ACCESS_ID_NOT_FOUND + Should Contain ${output} does not exist + List Objects V1 and V2 IAM Session Policy Matrix for OBS and FSO Kinit test user ${ICEBERG_SVC_CATALOG_USER} ${ICEBERG_SVC_CATALOG_USER}.keytab @@ -613,6 +630,13 @@ Tampered STS Token Service, Policy, or Signature Must Fail Get Object Should Fail ${ICEBERG_BUCKET_OBS} ${ICEBERG_BUCKET_TESTFILE} AccessDenied Put Object Should Fail ${ICEBERG_BUCKET_OBS} ${ICEBERG_BUCKET_TESTFILE} AccessDenied + # Exercise malformed token decoding with incorrect token structure. Unlike the earlier + # bogusSessionToken credential-part check, this literal decodes to a negative Writable + # length and covers unchecked decoder failures such as NegativeArraySizeException. + Configure STS Profile ${STS_ACCESS_KEY_ID} ${STS_SECRET_KEY} not-a-valid-token + Get Object Should Fail ${ICEBERG_BUCKET_OBS} ${ICEBERG_BUCKET_TESTFILE} AccessDenied + Put Object Should Fail ${ICEBERG_BUCKET_OBS} ${ICEBERG_BUCKET_TESTFILE} AccessDenied + Assume Role Session Policy With Multiple Buckets Should Access All Buckets ${session_policy} = Set Variable {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::${ICEBERG_BUCKET_OBS}/*"},{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::${ICEBERG_BUCKET_FSO}/*"}]} Assume Role And Get Temporary Credentials policy_json=${session_policy} perm_access_key_id=${PERMANENT_ACCESS_KEY_ID} perm_secret_key=${PERMANENT_SECRET_KEY} role_arn=${ICEBERG_MULTI_BUCKET_ROLE_ARN} @@ -1240,6 +1264,38 @@ STS session policy containing only GetObject must deny DeleteObjects ${output} = Execute aws s3api --endpoint-url ${S3G_ENDPOINT_URL} delete-bucket --bucket ${bucket} --profile sts Should Not Contain ${output} AccessDenied +Get Caller Identity With Permanent Credentials Should Succeed + Configure AWS Profile permanent ${PERMANENT_ACCESS_KEY_ID} ${PERMANENT_SECRET_KEY} + ${json} ${account} ${arn} ${userId} = Get Caller Identity permanent + Should Be Equal ${account} 123456789012 + ${principal} = Execute klist | awk '/Default principal/ {print $3}' + Should Be Equal ${userId} ${principal} + Should Be Equal ${arn} arn:aws:iam::123456789012:user/${ICEBERG_SVC_CATALOG_USER} + +Get Caller Identity With STS Credentials Should Succeed + Assume Role And Configure STS Profile perm_access_key_id=${PERMANENT_ACCESS_KEY_ID} perm_secret_key=${PERMANENT_SECRET_KEY} role_arn=${ICEBERG_ALL_ACCESS_ROLE_OBS_ARN} + ${json} ${account} ${arn} ${userId} = Get Caller Identity sts + Should Be Equal ${account} 123456789012 + Should Be Equal ${userId} ${STS_ASSUMED_ROLE_ID} + Should Be Equal ${arn} ${STS_ASSUMED_ROLE_USER_ARN} + +Get Caller Identity Ignores Extra Parameters + # curl 7.76.1 produces an invalid SigV4 signature for STS GET requests with --get --data-urlencode. + ${extra_curl_params} = Set Variable --data-urlencode "RoleArn=${ICEBERG_ALL_ACCESS_ROLE_OBS_ARN}" --data-urlencode "RoleSessionName=${ROLE_SESSION_NAME}" --data-urlencode "DurationSeconds=3600" + ${output} = Get Caller Identity Using Curl ${PERMANENT_ACCESS_KEY_ID} ${PERMANENT_SECRET_KEY} extra_curl_params=${extra_curl_params} + Should Contain ${output} 123456789012 + @{http_codes} = Get Regexp Matches ${output} (?m)^HTTP/[0-9.]+ ([0-9]{3}) 1 + ${code_count} = Get Length ${http_codes} + Should Be True ${code_count} > 0 Expected to find an HTTP status code in curl output, but none was found. + ${http_code} = Get From List ${http_codes} -1 + Should Be Equal As Strings ${http_code} 200 + +Get Caller Identity Rejects Missing Version + Get Caller Identity Should Fail InvalidAction + +Get Caller Identity Rejects Invalid Version + Get Caller Identity Should Fail InvalidAction api_version=2020-01-01 + Expired STS temporary credentials must return ExpiredToken on S3 APIs # Increase timeout to account for 15 minute STS token expiration plus the time to execute the api calls [Timeout] 25 minutes diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto index c06d54209a0b..062a92d20fa9 100644 --- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto +++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto @@ -169,6 +169,7 @@ enum Type { AssumeRole = 153; RevokeSTSToken = 154; DeleteRevokedSTSTokens = 155; + GetCallerIdentity = 156; } enum SafeMode { @@ -333,6 +334,7 @@ message OMRequest { optional RevokeSTSTokenRequest revokeSTSTokenRequest = 155; optional DeleteRevokedSTSTokensRequest deleteRevokedSTSTokensRequest = 156; optional UpdateAssumeRoleRequest updateAssumeRoleRequest = 157; + optional GetCallerIdentityRequest getCallerIdentityRequest = 158; } message OMResponse { @@ -481,6 +483,7 @@ message OMResponse { optional AssumeRoleResponse assumeRoleResponse = 153; optional RevokeSTSTokenResponse revokeSTSTokenResponse = 154; optional DeleteRevokedSTSTokensResponse deleteRevokedSTSTokensResponse = 155; + optional GetCallerIdentityResponse getCallerIdentityResponse = 156; } enum Status { @@ -1600,6 +1603,8 @@ message OMTokenProto { optional string originalAccessKeyId = 18; optional string secretAccessKey = 19; optional string sessionPolicy = 20; + optional string assumedRoleId = 21; + optional string assumedRoleUserArn = 22; } message SecretKeyProto { @@ -2534,23 +2539,34 @@ message UpdateAssumeRoleRequest { } message RevokeSTSTokenRequest { - required string sessionToken = 1; + required string originalAccessKeyId = 1; + // Leader-generated revocation cutoff, replicated across OMs in HA mode. + optional uint64 revocationTimeMillis = 2; } message RevokeSTSTokenResponse { } /** - This will contain a list of revoked STS session tokens whose entries should be removed from + This will contain a list of originalAccessKeyIds whose revocation entries should be removed from the s3RevokedStsTokenTable. */ message DeleteRevokedSTSTokensRequest { - repeated string sessionToken = 1; + repeated string originalAccessKeyId = 1; } message DeleteRevokedSTSTokensResponse { } +message GetCallerIdentityRequest { +} + +message GetCallerIdentityResponse { + optional string account = 1; + optional string arn = 2; + optional string userId = 3; +} + enum ReadConsistencyProto { // Unspecified consistency, the read consistency behavior is decided // by the OM diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java index 97f6cf920365..f1a7a51d3e26 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java @@ -540,7 +540,7 @@ protected void initializeOmTables(CacheType cacheType, compactionLogTable = initializer.get(OMDBDefinition.COMPACTION_LOG_TABLE_DEF); - // sessionToken -> insertionTimeMillis + // originalAccessKeyId -> revocationTimeMillis // FULL_CACHE keeps revocations in memory as there are not expected to be many s3RevokedStsTokenTable = initializer.get( OMDBDefinition.S3_REVOKED_STS_TOKEN_TABLE_DEF, cacheType); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java index 2e99871e17c4..08600bb99504 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java @@ -60,7 +60,7 @@ * | userTable | /user :- UserVolumeInfo | * | dTokenTable | OzoneTokenID :- renew_time | * | s3SecretTable | s3g_access_key_id :- s3Secret | - * | s3RevokedStsTokenTable | sts_session_token :- insertionTimeMillis | + * | s3RevokedStsTokenTable | originalAccessKeyId :- revocationTimeMillis | * |------------------------------------------------------------------------| * } * @@ -169,7 +169,10 @@ public final class OMDBDefinition extends DBDefinition.WithMap { S3SecretValue.getCodec()); public static final String S3_REVOKED_STS_TOKEN_TABLE = "s3RevokedStsTokenTable"; - /** s3RevokedStsTokenTable: sts_session_token :- insertionTimeMillis.*/ + /** + * s3RevokedStsTokenTable: originalAccessKeyId :- revocationTimeMillis. + * The value is the revocation cutoff in milliseconds. + */ public static final DBColumnFamilyDefinitionThis request marks an STS session token as revoked by inserting - * it into the {@code s3RevokedStsTokenTable}. Subsequent S3 requests - * authenticated with the same STS session token will be rejected when the - * revocation state has propagated.
+ *The client submits {@link RevokeSTSTokenRequest} with {@code originalAccessKeyId} only. On the + * leader, {@code preExecute} captures the revocation cutoff in {@code revocationTimeMillis} and + * replicates the updated request through Ratis so every OM applies the same cutoff.
+ * + *This request records a revocation cutoff for the given {@code originalAccessKeyId} in the + * {@code s3RevokedStsTokenTable}. Subsequent S3 requests authenticated with STS tokens whose + * {@code creationTime} is strictly before the cutoff will be rejected when the revocation state + * has propagated.
*/ public class S3RevokeSTSTokenRequest extends OMClientRequest { @@ -61,48 +69,89 @@ public S3RevokeSTSTokenRequest(OMRequest omRequest) { @Override public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { final OMRequest omRequest = super.preExecute(ozoneManager); - final OzoneManagerProtocolProtos.RevokeSTSTokenRequest revokeReq = - omRequest.getRevokeSTSTokenRequest(); + final RevokeSTSTokenRequest revokeReq = omRequest.getRevokeSTSTokenRequest(); + validateRevokeRequestFields(revokeReq); - // Get the original (long-lived) access key id from the session token - // and enforce the same permission model that is used for S3 secret + // Use the original (long-lived) access key ID from the request and enforce + // the same permission model that is used for S3 secret // operations (get/set/revoke). Only the owner of the original access // key (i.e. the creator of the STS token) or an S3 / tenant admin is allowed // to revoke its temporary STS credentials. - final String sessionToken = revokeReq.getSessionToken(); - final STSTokenIdentifier stsTokenIdentifier = STSSecurityUtil.constructValidateAndDecryptSTSToken( - sessionToken, ozoneManager.getSecretKeyClient(), CLOCK); - final String originalAccessKeyId = stsTokenIdentifier.getOriginalAccessKeyId(); + final String originalAccessKeyId = revokeReq.getOriginalAccessKeyId(); final UserGroupInformation ugi = S3SecretRequestHelper.getOrCreateUgi(originalAccessKeyId); S3SecretRequestHelper.checkAccessIdSecretOpPermission(ozoneManager, ugi, originalAccessKeyId); - return omRequest; + if (!ozoneManager.getS3SecretManager().hasS3Secret(originalAccessKeyId)) { + throw new OMException("originalAccessKeyId does not exist: " + originalAccessKeyId, ACCESS_ID_NOT_FOUND); + } + + final long revocationTimeMillis = CLOCK.millis(); + final RevokeSTSTokenRequest updatedRevokeReq = revokeReq.toBuilder() + .setRevocationTimeMillis(revocationTimeMillis) + .build(); + + return omRequest.toBuilder() + .setRevokeSTSTokenRequest(updatedRevokeReq) + .build(); } @Override public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, ExecutionContext context) { final OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(getOmRequest()); + IOException exception = null; + OMClientResponse omClientResponse; + final Map
- * Currently supports only AssumeRole operation. Other STS operations will
- * return appropriate error responses.
+ * Currently supports AssumeRole and GetCallerIdentity operations. Other STS
+ * operations will return appropriate error responses.
*
* @see AWS STS API Reference
*/
@@ -113,7 +114,8 @@ public class S3STSEndpoint extends S3STSEndpointBase {
static {
try {
- JAXB_CONTEXT = JAXBContext.newInstance(S3AssumeRoleResponseXml.class);
+ JAXB_CONTEXT = JAXBContext.newInstance(
+ S3AssumeRoleResponseXml.class, S3GetCallerIdentityResponseXml.class, S3STSResponseMetadata.class);
} catch (JAXBException e) {
throw new RuntimeException("Failed to initialize JAXBContext: " + e, e);
}
@@ -193,11 +195,12 @@ private Response handleSTSRequest(Set