From 95a88be763abdb624302559f384a75bd58b01ae7 Mon Sep 17 00:00:00 2001 From: Anton Mokhovikov Date: Wed, 26 Apr 2023 18:57:47 -0700 Subject: [PATCH] Tagging Specific Error Code (#419) --- ...UnauthorizedTaggingOperationException.java | 31 ++++++++++++ .../proxy/ExceptionMessages.java | 1 + .../proxy/HandlerErrorCode.java | 5 ++ .../proxy/delay/CappedExponential.java | 22 ++++++-- ...thorizedTaggingOperationExceptionTest.java | 50 +++++++++++++++++++ .../cloudformation/proxy/DelayTest.java | 23 +++------ 6 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 src/main/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationException.java create mode 100644 src/test/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationExceptionTest.java diff --git a/src/main/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationException.java b/src/main/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationException.java new file mode 100644 index 00000000..679c848e --- /dev/null +++ b/src/main/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationException.java @@ -0,0 +1,31 @@ +/* +* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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 software.amazon.cloudformation.exceptions; + +import software.amazon.cloudformation.proxy.HandlerErrorCode; + +public class CfnUnauthorizedTaggingOperationException extends BaseHandlerException { + + private static final long serialVersionUID = -1646136434112354328L; + private static final HandlerErrorCode ERROR_CODE = HandlerErrorCode.UnauthorizedTaggingOperation; + + public CfnUnauthorizedTaggingOperationException() { + this(null); + } + + public CfnUnauthorizedTaggingOperationException(final Throwable cause) { + super(ERROR_CODE.getMessage(), cause, ERROR_CODE); + } +} diff --git a/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java b/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java index 8de9d061..717cbcca 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java +++ b/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java @@ -16,6 +16,7 @@ final class ExceptionMessages { static final String ACCESS_DENIED = "Access denied for operation '%s'."; + static final String UNAUTHORIZED_TAGGING_OPERATION = "Unauthorized tagging operation"; static final String ALREADY_EXISTS = "Resource of type '%s' with identifier '%s' already exists."; static final String GENERAL_SERVICE_EXCEPTION = "Error occurred during operation '%s'."; static final String INTERNAL_FAILURE = "Internal error occurred."; diff --git a/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java b/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java index 18337ca7..8264aad8 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java +++ b/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java @@ -35,6 +35,11 @@ public enum HandlerErrorCode { * the customer has insufficient permissions to perform this action (Terminal) */ AccessDenied(ExceptionMessages.ACCESS_DENIED), + /** + * the customer has insufficient permissions to perform tagging + * create/update/delete/read action (Terminal) + */ + UnauthorizedTaggingOperation(ExceptionMessages.UNAUTHORIZED_TAGGING_OPERATION), /** * the customer's provided credentials were invalid (Terminal) diff --git a/src/main/java/software/amazon/cloudformation/proxy/delay/CappedExponential.java b/src/main/java/software/amazon/cloudformation/proxy/delay/CappedExponential.java index 97a4b98c..ac083565 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/delay/CappedExponential.java +++ b/src/main/java/software/amazon/cloudformation/proxy/delay/CappedExponential.java @@ -1,7 +1,20 @@ +/* +* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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 software.amazon.cloudformation.proxy.delay; import com.google.common.base.Preconditions; - import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -13,7 +26,10 @@ public class CappedExponential extends MinDelayAbstractBase { final Duration maxDelay; - CappedExponential(Duration timeout, Duration minDelay, Double powerBy, Duration maxDelay) { + CappedExponential(Duration timeout, + Duration minDelay, + Double powerBy, + Duration maxDelay) { super(timeout, minDelay); Preconditions.checkArgument(powerBy >= 1.0, "powerBy >= 1.0"); Preconditions.checkArgument(maxDelay != null && maxDelay.toMillis() >= 0, "maxDelay must be > 0"); @@ -29,7 +45,7 @@ public static Builder of() { public static final class Builder extends MinDelayBasedBuilder { private double powerBy = 2; - private Duration maxDelay =Duration.ofSeconds(20); + private Duration maxDelay = Duration.ofSeconds(20); public CappedExponential.Builder powerBy(Double powerBy) { this.powerBy = powerBy; diff --git a/src/test/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationExceptionTest.java b/src/test/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationExceptionTest.java new file mode 100644 index 00000000..88a707e6 --- /dev/null +++ b/src/test/java/software/amazon/cloudformation/exceptions/CfnUnauthorizedTaggingOperationExceptionTest.java @@ -0,0 +1,50 @@ +/* +* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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 software.amazon.cloudformation.exceptions; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Test; +import software.amazon.cloudformation.proxy.HandlerErrorCode; + +public class CfnUnauthorizedTaggingOperationExceptionTest { + @Test + public void cfnUnauthorizedTaggingOperation_isBaseHandlerException() { + assertThatExceptionOfType(BaseHandlerException.class).isThrownBy(() -> { + throw new CfnUnauthorizedTaggingOperationException(new RuntimeException()); + }); + } + + @Test + public void cfnUnauthorizedTaggingOperationException_singleArgConstructorHasMessage() { + assertThatExceptionOfType(BaseHandlerException.class).isThrownBy(() -> { + throw new CfnUnauthorizedTaggingOperationException(new RuntimeException()); + }).withCauseInstanceOf(RuntimeException.class).withMessageContaining("Unauthorized tagging operation"); + } + + @Test + public void cfnUnauthorizedTaggingOperationException_noCauseGiven() { + assertThatExceptionOfType(CfnUnauthorizedTaggingOperationException.class).isThrownBy(() -> { + throw new CfnUnauthorizedTaggingOperationException(); + }).withNoCause().withMessageContaining("Unauthorized tagging operation"); + } + + @Test + public void cfnUnauthorizedTaggingOperationException_errorCodeIsAppropriate() { + assertThatExceptionOfType(CfnUnauthorizedTaggingOperationException.class).isThrownBy(() -> { + throw new CfnUnauthorizedTaggingOperationException(new RuntimeException()); + }).satisfies(exception -> assertEquals(HandlerErrorCode.UnauthorizedTaggingOperation, exception.getErrorCode())); + } +} diff --git a/src/test/java/software/amazon/cloudformation/proxy/DelayTest.java b/src/test/java/software/amazon/cloudformation/proxy/DelayTest.java index 529f756d..51e717e7 100644 --- a/src/test/java/software/amazon/cloudformation/proxy/DelayTest.java +++ b/src/test/java/software/amazon/cloudformation/proxy/DelayTest.java @@ -203,13 +203,9 @@ public void exponentialDelays() { @Test public void cappedExponentialDelays() { Duration MAX_DELAY = Duration.ofSeconds(15); - final Delay cappedExponential = CappedExponential.of() - .timeout(Duration.ofMinutes(20)) - .maxDelay(MAX_DELAY) - .powerBy(1.3) - .minDelay(Duration.ofSeconds(1)) - .build(); - int[] results = {1, 1, 1, 1, 2, 2, 3, 4, 6, 8, 10, 13, 15, 15, 15, 15}; + final Delay cappedExponential = CappedExponential.of().timeout(Duration.ofMinutes(20)).maxDelay(MAX_DELAY).powerBy(1.3) + .minDelay(Duration.ofSeconds(1)).build(); + int[] results = { 1, 1, 1, 1, 2, 2, 3, 4, 6, 8, 10, 13, 15, 15, 15, 15 }; for (int tries = 0; tries <= 15; tries++) { Duration delay = cappedExponential.nextDelay(tries); assertThat(results[tries]).isEqualTo((int) delay.getSeconds()); @@ -218,7 +214,7 @@ public void cappedExponentialDelays() { } } - //If minDelay is not set, the retry is without delay. + // If minDelay is not set, the retry is without delay. final Delay cappedExponentialNoDelay = CappedExponential.of().timeout(Duration.ofSeconds(12)).build(); for (int tries = 0; tries <= 15; tries++) { Duration delay = cappedExponentialNoDelay.nextDelay(tries); @@ -228,14 +224,11 @@ public void cappedExponentialDelays() { } } - //If powerBy is not passed, it's set to default 2. - final Delay cappedExponentialNoPower = CappedExponential.of() - .timeout(Duration.ofMinutes(20)) - .maxDelay(MAX_DELAY) - .minDelay(Duration.ofSeconds(1)) - .build(); + // If powerBy is not passed, it's set to default 2. + final Delay cappedExponentialNoPower = CappedExponential.of().timeout(Duration.ofMinutes(20)).maxDelay(MAX_DELAY) + .minDelay(Duration.ofSeconds(1)).build(); - int[] resultsNoPower = {1, 1, 2, 4, 8, 15, 15, 15, 15, 15}; + int[] resultsNoPower = { 1, 1, 2, 4, 8, 15, 15, 15, 15, 15 }; for (int tries = 0; tries <= 6; tries++) { Duration delay = cappedExponentialNoPower.nextDelay(tries); assertThat(resultsNoPower[tries]).isEqualTo((int) delay.getSeconds());