diff --git a/smithy-aws-endpoints/build.gradle b/smithy-aws-endpoints/build.gradle
index 7eda2cdcdbb..2fdbcadaec0 100644
--- a/smithy-aws-endpoints/build.gradle
+++ b/smithy-aws-endpoints/build.gradle
@@ -11,6 +11,8 @@ ext {
}
dependencies {
+ api project(":smithy-aws-traits")
+ api project(":smithy-diff")
api project(":smithy-rules-engine")
api project(":smithy-model")
api project(":smithy-utils")
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/diff/EndpointSigV4Migration.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/diff/EndpointSigV4Migration.java
new file mode 100644
index 00000000000..1c12b24efd0
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/diff/EndpointSigV4Migration.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.diff;
+
+import static software.amazon.smithy.rulesengine.aws.language.functions.EndpointAuthUtils.isSigV4AEquivalentAuthScheme;
+import static software.amazon.smithy.rulesengine.aws.language.functions.EndpointAuthUtils.isSigV4EquivalentAuthScheme;
+import static software.amazon.smithy.rulesengine.language.EndpointRuleSet.EndpointPathCollector;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import software.amazon.smithy.diff.ChangedShape;
+import software.amazon.smithy.diff.Differences;
+import software.amazon.smithy.diff.evaluators.AbstractDiffEvaluator;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.knowledge.ServiceIndex;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.traits.AuthTrait;
+import software.amazon.smithy.model.validation.ValidationEvent;
+import software.amazon.smithy.rulesengine.aws.language.functions.EndpointAuthUtils;
+import software.amazon.smithy.rulesengine.language.Endpoint;
+import software.amazon.smithy.rulesengine.language.syntax.Identifier;
+import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
+import software.amazon.smithy.utils.Pair;
+import software.amazon.smithy.utils.SmithyInternalApi;
+
+/**
+ * Emit diff validation for SigV4 migration in the {@code @smithy.rules#endpointRuleSet} trait.
+ *
+ * Specifically, SigV4 ({@code aws.auth#sigv4}) to SigV4A ({@code aws.auth#sigv4a}) due to a subset of credentials
+ * usable with SigV4 that are not usable with SigV4A.
+ *
+ * @see AWS Authentication Traits
+ * @see Endpoint {@code authSchemes} list property
+ */
+@SmithyInternalApi
+public final class EndpointSigV4Migration extends AbstractDiffEvaluator {
+ private static final Identifier ID_NAME = Identifier.of("name");
+
+ @Override
+ public List evaluate(Differences differences) {
+ List events = new ArrayList<>();
+
+ Model oldModel = differences.getOldModel();
+ ServiceIndex oldServiceIndex = ServiceIndex.of(oldModel);
+ Model newModel = differences.getNewModel();
+ ServiceIndex newServiceIndex = ServiceIndex.of(newModel);
+
+ // Validate Service effective auth schemes
+ List> serviceChanges = differences
+ .changedShapes(ServiceShape.class)
+ .collect(Collectors.toList());
+ for (ChangedShape change : serviceChanges) {
+ ServiceShape oldServiceShape = change.getOldShape();
+ ServiceShape newServiceShape = change.getNewShape();
+
+ if (!oldServiceShape.hasTrait(EndpointRuleSetTrait.ID)
+ || !newServiceShape.hasTrait(EndpointRuleSetTrait.ID)) {
+ continue;
+ }
+
+ Optional> endpointRuleSetOpt =
+ change.getChangedTrait(EndpointRuleSetTrait.class);
+ Optional> authOpt =
+ change.getChangedTrait(AuthTrait.class);
+ List oldModeledAuthSchemes = getModeledAuthSchemes(oldServiceIndex, oldServiceShape);
+ List newModeledAuthSchemes = getModeledAuthSchemes(newServiceIndex, newServiceShape);
+ // Validate diffs for changes to `@smithy.rules#endpointRuleSet` and `@auth` and effective auth schemes
+ if (!endpointRuleSetOpt.isPresent()
+ && !authOpt.isPresent()
+ // Check modeled auth schemes since they could change without the `@auth` trait present
+ && oldModeledAuthSchemes.equals(newModeledAuthSchemes)) {
+ continue;
+ }
+
+ EndpointRuleSetTrait oldErs = oldServiceShape.expectTrait(EndpointRuleSetTrait.class);
+ Map oldEndpoints = EndpointPathCollector.from(oldErs).collect();
+
+ EndpointRuleSetTrait newErs = newServiceShape.expectTrait(EndpointRuleSetTrait.class);
+ Map newEndpoints = EndpointPathCollector.from(newErs).collect();
+
+ // JSON path -> Endpoint entries that exist in both the old and new model and are changed
+ Map> changedEndpoints = newEndpoints.entrySet().stream()
+ .filter(e -> oldEndpoints.containsKey(e.getKey()))
+ .map(e -> new SimpleEntry>(
+ e.getKey(),
+ Pair.of(oldEndpoints.get(e.getKey()), e.getValue())))
+ .filter(e -> !e.getValue().getLeft().equals(e.getValue().getRight()))
+ .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+ for (Entry> entry : changedEndpoints.entrySet()) {
+ String jsonPath = entry.getKey();
+ Endpoint oldEndpoint = entry.getValue().getLeft();
+ Endpoint newEndpoint = entry.getValue().getRight();
+ List oldAuthSchemes = getAuthSchemes(oldEndpoint, oldModeledAuthSchemes);
+ List newAuthSchemes = getAuthSchemes(newEndpoint, newModeledAuthSchemes);
+
+ boolean isOldSigV4Present = containsSigV4EquivalentAuthScheme(oldAuthSchemes);
+ boolean isOldSigV4APresent = containsSigV4AEquivalentAuthScheme(oldAuthSchemes);
+ boolean isNewSigV4Present = containsSigV4EquivalentAuthScheme(newAuthSchemes);
+ boolean isNewSigV4APresent = containsSigV4AEquivalentAuthScheme(newAuthSchemes);
+
+ boolean isSigV4Replaced = isOldSigV4Present && !isNewSigV4Present
+ && !isOldSigV4APresent && isNewSigV4APresent;
+ boolean isSigV4AReplaced = !isOldSigV4Present && isNewSigV4Present
+ && isOldSigV4APresent && !isNewSigV4APresent;
+ boolean noSigV4XRemoved = isOldSigV4Present && isNewSigV4Present
+ && isOldSigV4APresent && isNewSigV4APresent;
+ boolean isSigV4Added = !isOldSigV4Present && isNewSigV4Present
+ && isOldSigV4APresent && isNewSigV4APresent;
+ boolean isSigV4AAdded = isOldSigV4Present && isNewSigV4Present
+ && !isOldSigV4APresent && isNewSigV4APresent;
+ if (isSigV4Replaced) {
+ events.add(danger(
+ newServiceShape,
+ "The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` "
+ + "authentication scheme in the effective auth schemes for an endpoint in the "
+ + "`@smithy.rules#endpointRuleSet` trait applied to `" + newServiceShape.getId() + "` at: `"
+ + jsonPath + "`. "
+ + "Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` "
+ + "authentication scheme directly is not backward compatible since not all credentials usable "
+ + "by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' "
+ + "authentication."));
+ } else if (isSigV4AReplaced) {
+ events.add(danger(
+ newServiceShape,
+ "The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` "
+ + "authentication scheme in the effective auth schemes for an endpoint in the "
+ + "`@smithy.rules#endpointRuleSet` trait applied to `" + newServiceShape.getId() + "` at: `"
+ + jsonPath + "`. "
+ + "Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` "
+ + "authentication scheme directly may not be backward compatible if the signing scope was "
+ + "narrowed (typically from `*`)."));
+ } else if (noSigV4XRemoved) {
+ int oldSigV4Index = getIndexOfSigV4AuthScheme(oldAuthSchemes);
+ int oldSigV4aIndex = getIndexOfSigV4AAuthScheme(oldAuthSchemes);
+ int sigV4Index = getIndexOfSigV4AuthScheme(newAuthSchemes);
+ int sigV4aIndex = getIndexOfSigV4AAuthScheme(newAuthSchemes);
+ boolean isOldSigV4BeforeSigV4A = oldSigV4Index < oldSigV4aIndex;
+ boolean isSigV4BeforeSigV4A = sigV4Index < sigV4aIndex;
+ if (isOldSigV4BeforeSigV4A && !isSigV4BeforeSigV4A) {
+ events.add(danger(
+ newServiceShape,
+ "The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` "
+ + "authentication scheme in the effective auth schemes for an endpoint in the "
+ + "`@smithy.rules#endpointRuleSet` trait applied to `" + newServiceShape.getId()
+ + "` at: `" + jsonPath + "`. "
+ + "Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` "
+ + "authentication scheme is not backward compatible since not all credentials usable by "
+ + "`aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing "
+ + "clients' authentication."));
+ }
+ if (!isOldSigV4BeforeSigV4A && isSigV4BeforeSigV4A) {
+ events.add(danger(
+ newServiceShape,
+ "The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` "
+ + "authentication scheme in the effective auth schemes for an endpoint in the "
+ + "`@smithy.rules#endpointRuleSet` trait applied to `" + newServiceShape.getId()
+ + "` at: `" + jsonPath + "`. "
+ + "Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` "
+ + "authentication scheme may not be backward compatible if the signing scope was narrowed "
+ + "(typically from `*`)."));
+ }
+ } else if (isSigV4Added) {
+ int sigV4Index = getIndexOfSigV4AuthScheme(newAuthSchemes);
+ int sigV4aIndex = getIndexOfSigV4AAuthScheme(newAuthSchemes);
+ boolean isSigV4AddedBeforeSigV4A = sigV4Index < sigV4aIndex;
+ if (isSigV4AddedBeforeSigV4A) {
+ events.add(danger(
+ newServiceShape,
+ "The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` "
+ + "authentication scheme in the effective auth schemes for an endpoint in the "
+ + "`@smithy.rules#endpointRuleSet` trait applied to `" + newServiceShape.getId()
+ + "` at: `" + jsonPath + "`. "
+ + "Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` "
+ + "authentication scheme may not be backward compatible if the signing scope was narrowed "
+ + "(typically from `*`)."));
+ }
+ } else if (isSigV4AAdded) {
+ int sigV4Index = getIndexOfSigV4AuthScheme(newAuthSchemes);
+ int sigV4aIndex = getIndexOfSigV4AAuthScheme(newAuthSchemes);
+ boolean isSigV4AAddedBeforeSigV4 = sigV4aIndex < sigV4Index;
+ if (isSigV4AAddedBeforeSigV4) {
+ events.add(danger(
+ newServiceShape,
+ "The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` "
+ + "authentication scheme in the effective auth schemes for an endpoint in the "
+ + "`@smithy.rules#endpointRuleSet` trait applied to `" + newServiceShape.getId()
+ + "` at: `" + jsonPath + "`. "
+ + "Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` "
+ + "authentication scheme is not backward compatible since not all credentials usable by "
+ + "`aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' "
+ + "authentication."));
+ }
+ }
+ }
+ }
+
+ return events;
+ }
+
+ private static List getAuthSchemes(Endpoint endpoint, List modeledAuthSchemes) {
+ List endpointAuthSchemes = endpoint.getEndpointAuthSchemes().stream()
+ .map(a -> a.get(ID_NAME).asStringLiteral().get().expectLiteral())
+ .collect(Collectors.toList());
+ return endpointAuthSchemes.size() == 0
+ ? modeledAuthSchemes
+ : endpointAuthSchemes;
+ }
+
+ private static List getModeledAuthSchemes(ServiceIndex serviceIndex, ServiceShape serviceShape) {
+ return serviceIndex.getEffectiveAuthSchemes(serviceShape).keySet().stream()
+ .map(ShapeId::toString)
+ .collect(Collectors.toList());
+ }
+
+ private static boolean containsSigV4EquivalentAuthScheme(List authSchemes) {
+ return getIndexOfSigV4AuthScheme(authSchemes) != -1;
+ }
+
+ private static boolean containsSigV4AEquivalentAuthScheme(List authSchemes) {
+ return getIndexOfSigV4AAuthScheme(authSchemes) != -1;
+ }
+
+ private static int getIndexOfSigV4AuthScheme(List authSchemes) {
+ return getIndexOfAuthScheme(authSchemes, EndpointAuthUtils::isSigV4EquivalentAuthScheme);
+ }
+
+ private static int getIndexOfSigV4AAuthScheme(List authSchemes) {
+ return getIndexOfAuthScheme(authSchemes, EndpointAuthUtils::isSigV4AEquivalentAuthScheme);
+ }
+
+ private static int getIndexOfAuthScheme(List authSchemes, Predicate identifier) {
+ for (int i = 0; i < authSchemes.size(); i++) {
+ if (identifier.test(authSchemes.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/EndpointAuthUtils.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/EndpointAuthUtils.java
index 1c25ea94bcc..32544ee4aa5 100644
--- a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/EndpointAuthUtils.java
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/language/functions/EndpointAuthUtils.java
@@ -11,6 +11,8 @@
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
+import software.amazon.smithy.aws.traits.auth.SigV4ATrait;
+import software.amazon.smithy.aws.traits.auth.SigV4Trait;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.validation.Severity;
import software.amazon.smithy.model.validation.ValidationEvent;
@@ -26,15 +28,19 @@
* Utilities for constructing and validating AWS-specific authentication components for rule-sets.
*/
public final class EndpointAuthUtils {
- private static final String SIGV_4 = "sigv4";
- private static final String SIG_V4A = "sigv4a";
+ private static final String SIGV4 = "sigv4";
+ private static final String SIGV4_SHAPE_ID = SigV4Trait.ID.toString();
+ private static final String SIGV4_SUB = "sigv4-";
+ private static final String SIGV4_S3EXPRESS = "sigv4-s3express";
+ private static final String SIGV4A = "sigv4a";
+ private static final String SIGV4A_SHAPE_ID = SigV4ATrait.ID.toString();
private static final String SIGNING_NAME = "signingName";
private static final String SIGNING_REGION = "signingRegion";
private static final String SIGNING_REGION_SET = "signingRegionSet";
- private static final Identifier ID_SIGNING_NAME = Identifier.of("signingName");
- private static final Identifier ID_SIGNING_REGION = Identifier.of("signingRegion");
- private static final Identifier ID_SIGNING_REGION_SET = Identifier.of("signingRegionSet");
+ private static final Identifier ID_SIGNING_NAME = Identifier.of(SIGNING_NAME);
+ private static final Identifier ID_SIGNING_REGION = Identifier.of(SIGNING_REGION);
+ private static final Identifier ID_SIGNING_REGION_SET = Identifier.of(SIGNING_REGION_SET);
private static final Identifier ID_DISABLE_DOUBLE_ENCODING = Identifier.of("disableDoubleEncoding");
private static final Identifier ID_DISABLE_NORMALIZE_PATH = Identifier.of("disableNormalizePath");
@@ -49,7 +55,7 @@ private EndpointAuthUtils() {}
* @return the updated endpoint builder.
*/
public static Endpoint.Builder sigv4(Endpoint.Builder builder, Literal signingRegion, Literal signingService) {
- return builder.addAuthScheme(SIGV_4, MapUtils.of(
+ return builder.addAuthScheme(SIGV4, MapUtils.of(
SIGNING_NAME, signingService,
SIGNING_REGION, signingRegion));
}
@@ -67,17 +73,49 @@ public static Endpoint.Builder sigv4a(
List signingRegionSet,
Literal signingService
) {
- return builder.addAuthScheme(SIG_V4A, MapUtils.of(
+ return builder.addAuthScheme(SIGV4A, MapUtils.of(
SIGNING_NAME, signingService,
SIGNING_REGION_SET, Literal.tupleLiteral(signingRegionSet)));
}
+ /**
+ * Returns if a given auth scheme is equivalent to {@code aws.auth#sigv4}.
+ *
+ * @param authScheme name of the auth scheme.
+ * @return whether the auth scheme is equivalent to {@code aws.auth#sigv4}.
+ */
+ public static boolean isSigV4EquivalentAuthScheme(String authScheme) {
+ if (authScheme.equals(SIGV4) || authScheme.equals(SIGV4_SHAPE_ID)) {
+ return true;
+ }
+ if (authScheme.startsWith(SIGV4_SUB) && !authScheme.equals(SIGV4_S3EXPRESS)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns if a given auth scheme is equivalent to {@code aws.auth#sigv4a}.
+ *
+ * @param authScheme name of the auth scheme.
+ * @return whether the auth scheme is equivalent to {@code aws.auth#sigv4a}.
+ */
+ public static boolean isSigV4AEquivalentAuthScheme(String authScheme) {
+ if (authScheme.equals(SIGV4A) || authScheme.equals(SIGV4A_SHAPE_ID)) {
+ return true;
+ }
+ if (authScheme.equals(SIGV4_S3EXPRESS)) {
+ return true;
+ }
+ return false;
+ }
+
static final class SigV4SchemeValidator implements AuthSchemeValidator {
SigV4SchemeValidator() {}
@Override
public boolean test(String name) {
- return name.equals("sigv4");
+ return name.equals(SIGV4);
}
@Override
@@ -126,7 +164,7 @@ static final class SigV4aSchemeValidator implements AuthSchemeValidator {
@Override
public boolean test(String name) {
- return name.equals("sigv4a");
+ return name.equals(SIGV4A);
}
@Override
@@ -173,7 +211,7 @@ static final class SigV4SubSchemeValidator implements AuthSchemeValidator {
@Override
public boolean test(String name) {
- return name.startsWith("sigv4-");
+ return name.startsWith(SIGV4_SUB);
}
@Override
diff --git a/smithy-aws-endpoints/src/main/resources/META-INF/services/software.amazon.smithy.diff.DiffEvaluator b/smithy-aws-endpoints/src/main/resources/META-INF/services/software.amazon.smithy.diff.DiffEvaluator
new file mode 100644
index 00000000000..e16810eea84
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/resources/META-INF/services/software.amazon.smithy.diff.DiffEvaluator
@@ -0,0 +1 @@
+software.amazon.smithy.rulesengine.aws.diff.EndpointSigV4Migration
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/DiffTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/DiffTest.java
new file mode 100644
index 00000000000..36a0a1d6bd8
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/DiffTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws;
+
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.smithy.diff.ModelDiff;
+import software.amazon.smithy.diff.testrunner.SmithyDiffTestCase;
+import software.amazon.smithy.diff.testrunner.SmithyDiffTestSuite;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.SourceLocation;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.validation.Severity;
+import software.amazon.smithy.model.validation.ValidationEvent;
+import software.amazon.smithy.utils.IoUtils;
+import software.amazon.smithy.utils.Pair;
+
+public class DiffTest {
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("source")
+ public void testRunner(String filename, Callable callable) throws Exception {
+ callable.call();
+ }
+
+ public static Stream> source() {
+ return SmithyDiffTestSuite.defaultParameterizedTestSource(DiffTest.class);
+ }
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.a.smithy
new file mode 100644
index 00000000000..ca42804e545
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.a.smithy
@@ -0,0 +1,28 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Add.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Remove.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use smithy.rules#endpointRuleSet
+
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: []
+)
+service Service2 {}
+
+service Service3 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.b.smithy
new file mode 100644
index 00000000000..cf6f413d0d1
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.b.smithy
@@ -0,0 +1,28 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Add.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Remove.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use smithy.rules#endpointRuleSet
+
+service Service1 {}
+
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: []
+)
+service Service3 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/either-or-both-no-endpointRuleSet.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.a.smithy
new file mode 100644
index 00000000000..dbec9b9d77a
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.a.smithy
@@ -0,0 +1,74 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Add.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Remove.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "example"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "example"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service2 {}
+
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service3 {}
+
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.b.smithy
new file mode 100644
index 00000000000..71844773b7a
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.b.smithy
@@ -0,0 +1,74 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Add.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Remove.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "example"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service1 {}
+
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "example"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service3 {}
+
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/no-old-sigv4.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.a.smithy
new file mode 100644
index 00000000000..0cca3ba92c8
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.a.smithy
@@ -0,0 +1,159 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.b.smithy
new file mode 100644
index 00000000000..226fa83a059
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.b.smithy
@@ -0,0 +1,178 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-after-sigv4a-migration.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.a.smithy
new file mode 100644
index 00000000000..26184bb539a
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.a.smithy
@@ -0,0 +1,183 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.b.smithy
new file mode 100644
index 00000000000..ea80dd82fd8
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.b.smithy
@@ -0,0 +1,202 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: { hello: "world" }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.events
new file mode 100644
index 00000000000..70b9b43e194
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-before-sigv4a-migration.events
@@ -0,0 +1,11 @@
+[DANGER] ns.foo#Service1: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service1` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service2: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service2` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service3: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service3` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service4: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service4` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service5: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service5` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service7: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service7` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.a.smithy
new file mode 100644
index 00000000000..3b2b090c028
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.a.smithy
@@ -0,0 +1,116 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.b.smithy
new file mode 100644
index 00000000000..0eb05adcd90
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.b.smithy
@@ -0,0 +1,114 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.events
new file mode 100644
index 00000000000..81f3a779daa
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-replace-sigv4a-migration.events
@@ -0,0 +1,5 @@
+[DANGER] ns.foo#Service1: The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service1` at: `/rules/0/endpoint`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service2: The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service2` at: `/rules/0/endpoint`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service3: The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service3` at: `/rules/0/endpoint`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.a.smithy
new file mode 100644
index 00000000000..ac3a78168b7
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.a.smithy
@@ -0,0 +1,210 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@httpBearerAuth
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@httpBearerAuth
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.b.smithy
new file mode 100644
index 00000000000..29f88f1a4be
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.b.smithy
@@ -0,0 +1,212 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "example"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "example"
+ }
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "example"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, httpBearerAuth, sigv4a])
+@httpBearerAuth
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: { hello: "world" }
+ }
+ }
+ ]
+)
+@auth([sigv4, httpBearerAuth, sigv4a])
+@httpBearerAuth
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "example"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-in-order.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.a.smithy
new file mode 100644
index 00000000000..daa9b730bb3
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.a.smithy
@@ -0,0 +1,226 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Add.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service8")
+@sigv4a(name: "service8")
+service Service8 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.b.smithy
new file mode 100644
index 00000000000..4cad3982b97
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.b.smithy
@@ -0,0 +1,226 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Add.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service8")
+@sigv4a(name: "service8")
+service Service8 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.events
new file mode 100644
index 00000000000..b7bac41d2c7
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4-sigv4a-swap-order.events
@@ -0,0 +1,11 @@
+[DANGER] ns.foo#Service1: The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service1` at: `/rules/0/endpoint`. Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service2: The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service2` at: `/rules/0/endpoint`. Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service3: The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service3` at: `/rules/0/endpoint`. Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service5: The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service5` at: `/rules/0/endpoint`. Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service6: The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service6` at: `/rules/0/endpoint`. Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service7: The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service7` at: `/rules/0/endpoint`. Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | EndpointSigV4Migration
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.a.smithy
new file mode 100644
index 00000000000..4c2b91b2249
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.a.smithy
@@ -0,0 +1,156 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.b.smithy
new file mode 100644
index 00000000000..fc289c1c289
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.b.smithy
@@ -0,0 +1,178 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4, sigv4a])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-after-sigv4-migration.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.a.smithy
new file mode 100644
index 00000000000..cffe9fd9ef1
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.a.smithy
@@ -0,0 +1,180 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.b.smithy
new file mode 100644
index 00000000000..f6288ff77d1
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.b.smithy
@@ -0,0 +1,202 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service5")
+@sigv4a(name: "service5")
+service Service5 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service6")
+@sigv4a(name: "service6")
+service Service6 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: { hello: "world" }
+ }
+ }
+ ]
+)
+@auth([sigv4a, sigv4])
+@sigv4(name: "service7")
+@sigv4a(name: "service7")
+service Service7 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.events
new file mode 100644
index 00000000000..cafd3b9f587
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-before-sigv4-migration.events
@@ -0,0 +1,11 @@
+[DANGER] ns.foo#Service1: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service1` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service2: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service2` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service3: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service3` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service4: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service4` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service5: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service5` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service7: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service7` at: `/rules/0/endpoint`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.a.smithy
new file mode 100644
index 00000000000..6ebc522285d
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.a.smithy
@@ -0,0 +1,114 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.b.smithy
new file mode 100644
index 00000000000..b221414978e
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.b.smithy
@@ -0,0 +1,116 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+ {
+ id: "SigV4Migration"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4a"
+ signingRegionSet: ["*"]
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {}
+ }
+ }
+ ]
+)
+@auth([sigv4a])
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.events
new file mode 100644
index 00000000000..2b31c4e0afa
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/sigv4a-replace-sigv4-migration.events
@@ -0,0 +1,5 @@
+[DANGER] ns.foo#Service1: The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service1` at: `/rules/0/endpoint`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service2: The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service2` at: `/rules/0/endpoint`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
+-----
+[DANGER] ns.foo#Service3: The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for an endpoint in the `@smithy.rules#endpointRuleSet` trait applied to `ns.foo#Service3` at: `/rules/0/endpoint`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | EndpointSigV4Migration
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.a.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.a.smithy
new file mode 100644
index 00000000000..0ec0391bc30
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.a.smithy
@@ -0,0 +1,84 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@httpBearerAuth
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4])
+@httpBearerAuth
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@auth([sigv4])
+@httpBearerAuth
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@auth([sigv4])
+@httpBearerAuth
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.b.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.b.smithy
new file mode 100644
index 00000000000..f89b193fdf2
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.b.smithy
@@ -0,0 +1,90 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+ {
+ id: "ModifiedTrait.Update.smithy.rules#endpointRuleSet"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+use smithy.rules#endpointRuleSet
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "example"
+ }
+ {
+ name: "sigv4"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, httpBearerAuth])
+@httpBearerAuth
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {}
+
+@endpointRuleSet(
+ version: "1.0"
+ serviceId: "service"
+ parameters: {}
+ rules: [
+ {
+ type: "endpoint"
+ conditions: []
+ endpoint: {
+ url: "https://abc.service.com"
+ properties: {
+ authSchemes: [
+ {
+ name: "sigv4"
+ }
+ {
+ name: "example"
+ }
+ ]
+ }
+ }
+ }
+ ]
+)
+@auth([sigv4, httpBearerAuth])
+@httpBearerAuth
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {}
+
+@auth([sigv4, httpBearerAuth])
+@httpBearerAuth
+@sigv4(name: "service3")
+@sigv4a(name: "service3")
+service Service3 {}
+
+@auth([httpBearerAuth, sigv4])
+@httpBearerAuth
+@sigv4(name: "service4")
+@sigv4a(name: "service4")
+service Service4 {}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.events b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/diffs/with-sigv4-no-migration.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-traits/build.gradle b/smithy-aws-traits/build.gradle
index 151dd98dbcc..af2ac03b00e 100644
--- a/smithy-aws-traits/build.gradle
+++ b/smithy-aws-traits/build.gradle
@@ -21,5 +21,6 @@ ext {
}
dependencies {
+ api project(":smithy-diff")
api project(":smithy-model")
}
diff --git a/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/auth/diff/SigV4Migration.java b/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/auth/diff/SigV4Migration.java
new file mode 100644
index 00000000000..890c9fda867
--- /dev/null
+++ b/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/auth/diff/SigV4Migration.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.aws.traits.auth.diff;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import software.amazon.smithy.aws.traits.auth.SigV4ATrait;
+import software.amazon.smithy.aws.traits.auth.SigV4Trait;
+import software.amazon.smithy.diff.ChangedShape;
+import software.amazon.smithy.diff.Differences;
+import software.amazon.smithy.diff.evaluators.AbstractDiffEvaluator;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.knowledge.ServiceIndex;
+import software.amazon.smithy.model.knowledge.TopDownIndex;
+import software.amazon.smithy.model.shapes.OperationShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.validation.ValidationEvent;
+import software.amazon.smithy.utils.SmithyInternalApi;
+
+/**
+ * Emit diff validation for SigV4 migration in the {@code @auth} trait.
+ *
+ * Specifically, SigV4 ({@code aws.auth#sigv4}) to SigV4A ({@code aws.auth#sigv4a}) due to a subset of credentials
+ * usable with SigV4 that are not usable with SigV4A.
+ *
+ * @see AWS Authentication Traits
+ */
+@SmithyInternalApi
+public final class SigV4Migration extends AbstractDiffEvaluator {
+ @Override
+ public List evaluate(Differences differences) {
+ List events = new ArrayList<>();
+
+ Model oldModel = differences.getOldModel();
+ ServiceIndex oldServiceIndex = ServiceIndex.of(oldModel);
+ Model newModel = differences.getNewModel();
+ ServiceIndex newServiceIndex = ServiceIndex.of(newModel);
+
+ // Validate Service effective auth schemes
+ List> serviceChanges = differences
+ .changedShapes(ServiceShape.class)
+ .collect(Collectors.toList());
+ for (ChangedShape change : serviceChanges) {
+ ServiceShape oldServiceShape = change.getOldShape();
+ List oldAuthSchemes = oldServiceIndex
+ .getEffectiveAuthSchemes(oldServiceShape)
+ .keySet()
+ .stream()
+ .collect(Collectors.toList());
+ ServiceShape newServiceShape = change.getNewShape();
+ List newAuthSchemes = newServiceIndex
+ .getEffectiveAuthSchemes(newServiceShape)
+ .keySet()
+ .stream()
+ .collect(Collectors.toList());
+ validateMigration(
+ newServiceShape,
+ oldAuthSchemes,
+ newAuthSchemes,
+ events);
+ }
+
+ Map> operationToContainedServiceBindings =
+ computeOperationToContainedServiceBindings(newModel);
+ List> operationChanges = differences
+ .changedShapes(OperationShape.class)
+ .collect(Collectors.toList());
+ for (ChangedShape change : operationChanges) {
+ OperationShape newOperationShape = change.getNewShape();
+ Set newOperationServiceBindings = operationToContainedServiceBindings.get(newOperationShape);
+ if (newOperationServiceBindings == null) {
+ continue;
+ }
+ // Validate Operation effective auth schemes
+ for (ServiceShape newServiceShape : newOperationServiceBindings) {
+ ServiceShape oldServiceShape = oldModel.expectShape(newServiceShape.getId(), ServiceShape.class);
+ OperationShape oldOperationShape = change.getOldShape();
+ List oldAuthSchemes = oldServiceIndex
+ .getEffectiveAuthSchemes(oldServiceShape, oldOperationShape)
+ .keySet()
+ .stream()
+ .collect(Collectors.toList());
+ List newAuthSchemes = newServiceIndex
+ .getEffectiveAuthSchemes(newServiceShape, newOperationShape)
+ .keySet()
+ .stream()
+ .collect(Collectors.toList());
+ validateMigration(
+ newOperationShape,
+ oldAuthSchemes,
+ newAuthSchemes,
+ events);
+ }
+ }
+
+ return events;
+ }
+
+ private void validateMigration(
+ Shape shape,
+ List oldAuthSchemes,
+ List newAuthSchemes,
+ List events
+ ) {
+ boolean isOldSigV4Present = oldAuthSchemes.contains(SigV4Trait.ID);
+ boolean isOldSigV4APresent = oldAuthSchemes.contains(SigV4ATrait.ID);
+ boolean isNewSigV4Present = newAuthSchemes.contains(SigV4Trait.ID);
+ boolean isNewSigV4APresent = newAuthSchemes.contains(SigV4ATrait.ID);
+
+ boolean isSigV4Replaced = isOldSigV4Present && !isNewSigV4Present && !isOldSigV4APresent && isNewSigV4APresent;
+ boolean isSigV4AReplaced = !isOldSigV4Present && isNewSigV4Present && isOldSigV4APresent && !isNewSigV4APresent;
+ boolean noSigV4XRemoved = isOldSigV4Present && isNewSigV4Present && isOldSigV4APresent && isNewSigV4APresent;
+ boolean isSigV4Added = !isOldSigV4Present && isNewSigV4Present && isOldSigV4APresent && isNewSigV4APresent;
+ boolean isSigV4AAdded = isOldSigV4Present && isNewSigV4Present && !isOldSigV4APresent && isNewSigV4APresent;
+ if (isSigV4Replaced) {
+ events.add(danger(
+ shape,
+ "The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` authentication "
+ + "scheme in the effective auth schemes for `" + shape.getId() + "`. "
+ + "Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication "
+ + "scheme directly is not backward compatible since not all credentials usable by `aws.auth#sigv4` are "
+ + "compatible with `aws.auth#sigv4a`, and can break existing clients' authentication."));
+ } else if (isSigV4AReplaced) {
+ events.add(danger(
+ shape,
+ "The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` authentication "
+ + "scheme in the effective auth schemes for `" + shape.getId() + "`. "
+ + "Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication "
+ + "scheme directly may not be backward compatible if the signing scope was narrowed (typically from "
+ + "`*`)."));
+ } else if (noSigV4XRemoved) {
+ int oldSigV4Index = oldAuthSchemes.indexOf(SigV4Trait.ID);
+ int oldSigV4aIndex = oldAuthSchemes.indexOf(SigV4ATrait.ID);
+ int sigV4Index = newAuthSchemes.indexOf(SigV4Trait.ID);
+ int sigV4aIndex = newAuthSchemes.indexOf(SigV4ATrait.ID);
+ boolean isOldSigV4BeforeSigV4A = oldSigV4Index < oldSigV4aIndex;
+ boolean isSigV4BeforeSigV4A = sigV4Index < sigV4aIndex;
+ if (isOldSigV4BeforeSigV4A && !isSigV4BeforeSigV4A) {
+ events.add(danger(
+ shape,
+ "The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` authentication "
+ + "scheme in the effective auth schemes for `" + shape.getId() + "`. "
+ + "Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` authentication "
+ + "scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are "
+ + "compatible with `aws.auth#sigv4a`, and can break existing clients' authentication."));
+ }
+ if (!isOldSigV4BeforeSigV4A && isSigV4BeforeSigV4A) {
+ events.add(danger(
+ shape,
+ "The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` authentication "
+ + "scheme in the effective auth schemes for `" + shape.getId() + "`. "
+ + "Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` authentication "
+ + "scheme may not be backward compatible if the signing scope was narrowed (typically from `*`)."));
+ }
+ } else if (isSigV4Added) {
+ int sigV4Index = newAuthSchemes.indexOf(SigV4Trait.ID);
+ int sigV4aIndex = newAuthSchemes.indexOf(SigV4ATrait.ID);
+ boolean isSigV4AddedBeforeSigV4A = sigV4Index < sigV4aIndex;
+ if (isSigV4AddedBeforeSigV4A) {
+ events.add(danger(
+ shape,
+ "The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication "
+ + "scheme in the effective auth schemes for `" + shape.getId() + "`. "
+ + "Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` "
+ + "authentication scheme may not be backward compatible if the signing scope was narrowed "
+ + "(typically from `*`)."));
+ }
+ } else if (isSigV4AAdded) {
+ int sigV4Index = newAuthSchemes.indexOf(SigV4Trait.ID);
+ int sigV4aIndex = newAuthSchemes.indexOf(SigV4ATrait.ID);
+ boolean isSigV4AAddedBeforeSigV4 = sigV4aIndex < sigV4Index;
+ if (isSigV4AAddedBeforeSigV4) {
+ events.add(danger(
+ shape,
+ "The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication "
+ + "scheme in the effective auth schemes for `" + shape.getId() + "`. "
+ + "Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` "
+ + "authentication scheme is not backward compatible since not all credentials usable by "
+ + "`aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' "
+ + "authentication."));
+ }
+ }
+ }
+
+ private static Map> computeOperationToContainedServiceBindings(Model model) {
+ Map> operationToContainedServiceBindings = new HashMap<>();
+ TopDownIndex topDownIndex = TopDownIndex.of(model);
+ for (OperationShape operationShape : model.getOperationShapes()) {
+ Set operationEntry = operationToContainedServiceBindings
+ .getOrDefault(operationShape, new HashSet());
+ for (ServiceShape serviceShape : model.getServiceShapes()) {
+ if (topDownIndex.getContainedOperations(serviceShape).contains(operationShape)) {
+ operationEntry.add(serviceShape);
+ }
+ }
+ operationToContainedServiceBindings.put(operationShape, operationEntry);
+ }
+ return operationToContainedServiceBindings;
+ }
+}
diff --git a/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.diff.DiffEvaluator b/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.diff.DiffEvaluator
new file mode 100644
index 00000000000..5bbacbffb26
--- /dev/null
+++ b/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.diff.DiffEvaluator
@@ -0,0 +1 @@
+software.amazon.smithy.aws.traits.auth.diff.SigV4Migration
diff --git a/smithy-aws-traits/src/test/java/software/amazon/smithy/aws/traits/DiffTest.java b/smithy-aws-traits/src/test/java/software/amazon/smithy/aws/traits/DiffTest.java
new file mode 100644
index 00000000000..e001bff254c
--- /dev/null
+++ b/smithy-aws-traits/src/test/java/software/amazon/smithy/aws/traits/DiffTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.aws.traits;
+
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.smithy.diff.ModelDiff;
+import software.amazon.smithy.diff.testrunner.SmithyDiffTestCase;
+import software.amazon.smithy.diff.testrunner.SmithyDiffTestSuite;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.SourceLocation;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.validation.Severity;
+import software.amazon.smithy.model.validation.ValidationEvent;
+import software.amazon.smithy.utils.IoUtils;
+import software.amazon.smithy.utils.Pair;
+
+public class DiffTest {
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("source")
+ public void testRunner(String filename, Callable callable) throws Exception {
+ callable.call();
+ }
+
+ public static Stream> source() {
+ return SmithyDiffTestSuite.defaultParameterizedTestSource(DiffTest.class);
+ }
+}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.a.smithy
new file mode 100644
index 00000000000..e75d0177d05
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.a.smithy
@@ -0,0 +1,21 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+@auth([])
+@httpBearerAuth
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.b.smithy
new file mode 100644
index 00000000000..a2815d8d157
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.b.smithy
@@ -0,0 +1,21 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+@auth([httpBearerAuth])
+@httpBearerAuth
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([httpBearerAuth])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/no-old-sigv4.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.a.smithy
new file mode 100644
index 00000000000..8514baee527
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.a.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.b.smithy
new file mode 100644
index 00000000000..028d92375db
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.b.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a, sigv4])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4a, sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-after-sigv4a-migration.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.a.smithy
new file mode 100644
index 00000000000..8514baee527
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.a.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.b.smithy
new file mode 100644
index 00000000000..d6ad5d01fa1
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.b.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4, sigv4a])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4, sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.events
new file mode 100644
index 00000000000..ed584854cfb
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-before-sigv4a-migration.events
@@ -0,0 +1,3 @@
+[DANGER] ns.foo#Service: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for `ns.foo#Service`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | SigV4Migration
+-----
+[DANGER] ns.foo#Operation: The `aws.auth#sigv4` authentication scheme was added before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for `ns.foo#Operation`. Adding the `aws.auth#sigv4` authentication scheme before an existing `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | SigV4Migration
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.a.smithy
new file mode 100644
index 00000000000..8514baee527
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.a.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.b.smithy
new file mode 100644
index 00000000000..84b82e83db9
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.b.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.events
new file mode 100644
index 00000000000..6340e3e5437
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-replace-sigv4a-migration.events
@@ -0,0 +1,3 @@
+[DANGER] ns.foo#Service: The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` authentication scheme in the effective auth schemes for `ns.foo#Service`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly may not be backward compatible if the signing scope was narrowed (typically from `*`). | SigV4Migration
+-----
+[DANGER] ns.foo#Operation: The `aws.auth#sigv4a` authentication scheme was replaced by the `aws.auth#sigv4` authentication scheme in the effective auth schemes for `ns.foo#Operation`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly may not be backward compatible if the signing scope was narrowed (typically from `*`). | SigV4Migration
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.a.smithy
new file mode 100644
index 00000000000..5c797829d9a
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.a.smithy
@@ -0,0 +1,26 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([httpBearerAuth, sigv4, sigv4a])
+@httpBearerAuth
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([httpBearerAuth, sigv4, sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.b.smithy
new file mode 100644
index 00000000000..6b7dca49d53
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.b.smithy
@@ -0,0 +1,26 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4, httpBearerAuth, sigv4a])
+@httpBearerAuth
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4, httpBearerAuth, sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-in-order.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.a.smithy
new file mode 100644
index 00000000000..200a2690afb
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.a.smithy
@@ -0,0 +1,37 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4, sigv4a])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {
+ operations: [
+ Operation1
+ ]
+}
+
+@auth([sigv4, sigv4a])
+operation Operation1 {}
+
+@auth([sigv4a, sigv4])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {
+ operations: [
+ Operation2
+ ]
+}
+
+@auth([sigv4a, sigv4])
+operation Operation2 {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.b.smithy
new file mode 100644
index 00000000000..ec607fa19db
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.b.smithy
@@ -0,0 +1,37 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a, sigv4])
+@sigv4(name: "service1")
+@sigv4a(name: "service1")
+service Service1 {
+ operations: [
+ Operation1
+ ]
+}
+
+@auth([sigv4a, sigv4])
+operation Operation1 {}
+
+@auth([sigv4, sigv4a])
+@sigv4(name: "service2")
+@sigv4a(name: "service2")
+service Service2 {
+ operations: [
+ Operation2
+ ]
+}
+
+@auth([sigv4, sigv4a])
+operation Operation2 {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.events
new file mode 100644
index 00000000000..57888ae1188
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4-sigv4a-swap-order.events
@@ -0,0 +1,7 @@
+[DANGER] ns.foo#Service1: The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for `ns.foo#Service1`. Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | SigV4Migration
+-----
+[DANGER] ns.foo#Operation1: The `aws.auth#sigv4a` authentication scheme was moved before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for `ns.foo#Operation1`. Moving the `aws.auth#sigv4a` authentication scheme before the `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | SigV4Migration
+-----
+[DANGER] ns.foo#Service2: The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for `ns.foo#Service2`. Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | SigV4Migration
+-----
+[DANGER] ns.foo#Operation2: The `aws.auth#sigv4` authentication scheme was moved before the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for `ns.foo#Operation2`. Moving the `aws.auth#sigv4` authentication scheme before the `aws.auth#sigv4a` authentication scheme may not be backward compatible if the signing scope was narrowed (typically from `*`). | SigV4Migration
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.a.smithy
new file mode 100644
index 00000000000..84b82e83db9
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.a.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.b.smithy
new file mode 100644
index 00000000000..d6ad5d01fa1
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.b.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4, sigv4a])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4, sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-after-sigv4-migration.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.a.smithy
new file mode 100644
index 00000000000..84b82e83db9
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.a.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.b.smithy
new file mode 100644
index 00000000000..028d92375db
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.b.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a, sigv4])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4a, sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.events
new file mode 100644
index 00000000000..dab1a613028
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-before-sigv4-migration.events
@@ -0,0 +1,3 @@
+[DANGER] ns.foo#Service: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for `ns.foo#Service`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | SigV4Migration
+-----
+[DANGER] ns.foo#Operation: The `aws.auth#sigv4a` authentication scheme was added before the `aws.auth#sigv4` authentication scheme in the effective auth schemes for `ns.foo#Operation`. Adding the `aws.auth#sigv4a` authentication scheme before an existing `aws.auth#sigv4` authentication scheme is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | SigV4Migration
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.a.smithy
new file mode 100644
index 00000000000..84b82e83db9
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.a.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.b.smithy
new file mode 100644
index 00000000000..8514baee527
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.b.smithy
@@ -0,0 +1,25 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+use aws.auth#sigv4a
+
+@auth([sigv4a])
+@sigv4(name: "service")
+@sigv4a(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4a])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.events
new file mode 100644
index 00000000000..9b81ece18a8
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/sigv4a-replace-sigv4-migration.events
@@ -0,0 +1,3 @@
+[DANGER] ns.foo#Service: The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for `ns.foo#Service`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | SigV4Migration
+-----
+[DANGER] ns.foo#Operation: The `aws.auth#sigv4` authentication scheme was replaced by the `aws.auth#sigv4a` authentication scheme in the effective auth schemes for `ns.foo#Operation`. Replacing the `aws.auth#sigv4` authentication scheme with the `aws.auth#sigv4a` authentication scheme directly is not backward compatible since not all credentials usable by `aws.auth#sigv4` are compatible with `aws.auth#sigv4a`, and can break existing clients' authentication. | SigV4Migration
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.a.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.a.smithy
new file mode 100644
index 00000000000..df9bb9120ad
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.a.smithy
@@ -0,0 +1,24 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+
+@auth([sigv4])
+@httpBearerAuth
+@sigv4(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.b.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.b.smithy
new file mode 100644
index 00000000000..31d687fc96b
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.b.smithy
@@ -0,0 +1,24 @@
+$version: "2.0"
+
+metadata suppressions = [
+ {
+ id: "ModifiedTrait.Update.smithy.api#auth"
+ namespace: "ns.foo"
+ }
+]
+
+namespace ns.foo
+
+use aws.auth#sigv4
+
+@auth([httpBearerAuth, sigv4])
+@httpBearerAuth
+@sigv4(name: "service")
+service Service {
+ operations: [
+ Operation
+ ]
+}
+
+@auth([httpBearerAuth, sigv4])
+operation Operation {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.events b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/diffs/with-sigv4-no-migration.events
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/Endpoint.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/Endpoint.java
index dbaa282119b..0f0d3010fda 100644
--- a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/Endpoint.java
+++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/language/Endpoint.java
@@ -12,8 +12,11 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Optional;
import java.util.TreeMap;
+import java.util.stream.Collectors;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.ArrayNode;
@@ -42,6 +45,8 @@ public final class Endpoint implements FromSourceLocation, ToNode, ToSmithyBuild
private static final String URL = "url";
private static final String PROPERTIES = "properties";
private static final String HEADERS = "headers";
+ private static final Identifier ID_AUTH_SCHEMES = Identifier.of("authSchemes");
+ private static final Identifier ID_NAME = Identifier.of("name");
private final Map> headers;
private final Map properties;
@@ -57,12 +62,12 @@ private Endpoint(Builder builder) {
List authSchemes = new ArrayList<>();
for (Map.Entry> authScheme : builder.authSchemes.get().entrySet()) {
Map base = new TreeMap<>(Comparator.comparing(Identifier::toString));
- base.put(Identifier.of("name"), Literal.of(authScheme.getKey().toString()));
+ base.put(ID_NAME, Literal.of(authScheme.getKey().toString()));
base.putAll(authScheme.getValue());
authSchemes.add(Literal.recordLiteral(base));
}
if (!authSchemes.isEmpty()) {
- builder.putProperty(Identifier.of("authSchemes"), Literal.tupleLiteral(authSchemes));
+ builder.putProperty(ID_AUTH_SCHEMES, Literal.tupleLiteral(authSchemes));
}
this.properties = builder.properties.copy();
@@ -139,6 +144,19 @@ public Map getProperties() {
return properties;
}
+ /**
+ * Get the endpoint {@code authSchemes} property as a map of {@link Identifier} to {@link Literal} values.
+ *
+ * @return the list of endpoint {@code authSchemes}.
+ */
+ public List