From 31f610c41599de23a7539181e024461fceea812f Mon Sep 17 00:00:00 2001 From: Jose Vitor Schneid Date: Tue, 27 Sep 2022 11:36:56 -0300 Subject: [PATCH 1/2] Add support for missing authorizer members This commit adds support for the `authorizerPayloadFormatVersion` and `enableSimpleResponses` members in `apiGateway#authorizers`. These members are used to properly define authorizers for HTTP APIs. --- docs/source-2.0/aws/amazon-apigateway.rst | 15 ++++ .../apigateway/openapi/AddAuthorizers.java | 4 ++ .../openapi/AddAuthorizersTest.java | 3 +- .../aws/apigateway/openapi/authorizers.json | 4 +- .../traits/AuthorizerDefinition.java | 55 ++++++++++++++- .../apigateway/traits/AuthorizersTrait.java | 2 +- .../traits/AuthorizersTraitValidator.java | 69 ++++++++++++++++--- .../META-INF/smithy/aws.apigateway.smithy | 16 +++++ .../traits/AuthorizersTraitTest.java | 17 +++++ 9 files changed, 170 insertions(+), 15 deletions(-) diff --git a/docs/source-2.0/aws/amazon-apigateway.rst b/docs/source-2.0/aws/amazon-apigateway.rst index 7eeac17c811..ecde85623be 100644 --- a/docs/source-2.0/aws/amazon-apigateway.rst +++ b/docs/source-2.0/aws/amazon-apigateway.rst @@ -164,6 +164,18 @@ An *authorizer* definition is a structure that supports the following members: authorization caching is disabled. If it is greater than 0, API Gateway will cache authorizer responses. If this field is not set, the default value is 300. The maximum value is 3600, or 1 hour. + * - authorizerPayloadFormatVersion + - ``string`` + - For HTTP APIs, specifies the format of the data that API Gateway + sends to a Lambda authorizer, and how API Gateway interprets the + response from Lambda. Supported values are ``1.0`` and ``2.0``. + For more information, see `Lambda Authorizers Payload Format`_. + * - enableSimpleResponses + - ``boolean`` + - For HTTP APIs, specifies whether a request authorizer returns a + Boolean value or an IAM policy. Supported only for authorizers + with an ``authorizerPayloadFormatVersion`` of 2.0. If enabled, the + Lambda authorizer function returns a Boolean value. .. code-block:: smithy @@ -188,6 +200,8 @@ An *authorizer* definition is a structure that supports the following members: identitySource: "mapping.expression" identityValidationExpression: "[A-Z]+" resultTtlInSeconds: 100 + authorizerPayloadFormatVersion: "2.0" + enableSimpleResponses: true } ) service Weather { @@ -871,3 +885,4 @@ integration response to two ``header`` parameters of the method response. .. _x-amazon-apigateway-api-key-source: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-api-key-source.html .. _IntegrationResponse: https://docs.aws.amazon.com/apigateway/api-reference/resource/integration-response/ .. _mapping templates: https://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html#models-mappings-mappings +.. _Lambda Authorizers Payload Format: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format diff --git a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizers.java b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizers.java index 12b93ded02f..b4a9a53547b 100644 --- a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizers.java +++ b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizers.java @@ -217,6 +217,10 @@ private SecurityScheme convertAuthScheme( .withOptionalMember("identitySource", authorizer.getIdentitySource().map(Node::from)) .withOptionalMember("authorizerResultTtlInSeconds", authorizer.getResultTtlInSeconds().map(Node::from)) + .withOptionalMember("authorizerPayloadFormatVersion", + authorizer.getAuthorizerPayloadFormatVersion().map(Node::from)) + .withOptionalMember("enableSimpleResponses", + authorizer.getEnableSimpleResponses().map(Node::from)) .build(); if (authorizerNode.size() != 0) { schemeBuilder.putExtension(EXTENSION_NAME, authorizerNode); diff --git a/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizersTest.java b/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizersTest.java index 61109e103e2..dfdb839f4ad 100644 --- a/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizersTest.java +++ b/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/AddAuthorizersTest.java @@ -22,7 +22,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -66,6 +65,8 @@ public void addsAuthorizers() { assertThat(authorizer.getStringMember("identitySource").get().getValue(), equalTo("mapping.expression")); assertThat(authorizer.getStringMember("identityValidationExpression").get().getValue(), equalTo("[A-Z]+")); assertThat(authorizer.getNumberMember("authorizerResultTtlInSeconds").get().getValue(), equalTo(100)); + assertThat(authorizer.getStringMember("authorizerPayloadFormatVersion").get().getValue(), equalTo("2.0")); + assertThat(authorizer.getBooleanMember("enableSimpleResponses").get().getValue(), equalTo(true)); } @Test diff --git a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/authorizers.json b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/authorizers.json index 99c4032d6f0..888ad2dddd7 100644 --- a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/authorizers.json +++ b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/authorizers.json @@ -18,7 +18,9 @@ "credentials": "arn:foo:bar", "identitySource": "mapping.expression", "identityValidationExpression": "[A-Z]+", - "resultTtlInSeconds": 100 + "resultTtlInSeconds": 100, + "authorizerPayloadFormatVersion": "2.0", + "enableSimpleResponses": true } } } diff --git a/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizerDefinition.java b/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizerDefinition.java index 0ad0732d3f3..41f674daa0b 100644 --- a/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizerDefinition.java +++ b/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizerDefinition.java @@ -42,6 +42,8 @@ public final class AuthorizerDefinition implements ToNode, ToSmithyBuilder getResultTtlInSeconds() { return Optional.ofNullable(resultTtlInSeconds); } + /** + * Gets the format of the payload returned by the authorizer. + * + * @return Returns payload type. + */ + public Optional getAuthorizerPayloadFormatVersion() { + return Optional.ofNullable(authorizerPayloadFormatVersion); + } + + /** + * Gets whether the authorizer returns simple responses. + * + * @return Returns true if authorizer returns a boolean, + * false if it returns an IAM policy. + */ + public Optional getEnableSimpleResponses() { + return Optional.ofNullable(enableSimpleResponses); + } + @Override public Builder toBuilder() { return builder() @@ -172,7 +195,9 @@ public Builder toBuilder() { .credentials(credentials) .identitySource(identitySource) .identityValidationExpression(identityValidationExpression) - .resultTtlInSeconds(resultTtlInSeconds); + .resultTtlInSeconds(resultTtlInSeconds) + .authorizerPayloadFormatVersion(authorizerPayloadFormatVersion) + .enableSimpleResponses(enableSimpleResponses); } @Override @@ -198,7 +223,9 @@ public boolean equals(Object o) { && Objects.equals(credentials, that.credentials) && Objects.equals(identitySource, that.identitySource) && Objects.equals(identityValidationExpression, that.identityValidationExpression) - && Objects.equals(resultTtlInSeconds, that.resultTtlInSeconds); + && Objects.equals(resultTtlInSeconds, that.resultTtlInSeconds) + && Objects.equals(authorizerPayloadFormatVersion, that.authorizerPayloadFormatVersion) + && Objects.equals(enableSimpleResponses, that.enableSimpleResponses); } @Override @@ -218,6 +245,8 @@ public static final class Builder implements SmithyBuilder private String identitySource; private String identityValidationExpression; private Integer resultTtlInSeconds; + private String authorizerPayloadFormatVersion; + private Boolean enableSimpleResponses; @Override public AuthorizerDefinition build() { @@ -333,5 +362,27 @@ public Builder resultTtlInSeconds(Integer resultTtlInSeconds) { this.resultTtlInSeconds = resultTtlInSeconds; return this; } + + /** + * Sets the format of the payload returned by the authorizer. + * + * @param authorizerPayloadFormatVersion format of the payload. + * @return Returns the builder. + */ + public Builder authorizerPayloadFormatVersion(String authorizerPayloadFormatVersion) { + this.authorizerPayloadFormatVersion = authorizerPayloadFormatVersion; + return this; + } + + /** + * Sets whether the authorizer returns simple responses. + * + * @param enableSimpleResponses defines if authorizer should return simple responses. + * @return Returns the builder. + */ + public Builder enableSimpleResponses(Boolean enableSimpleResponses) { + this.enableSimpleResponses = enableSimpleResponses; + return this; + } } } diff --git a/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTrait.java b/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTrait.java index 36fd48149da..ce9f6a1d17c 100644 --- a/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTrait.java +++ b/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTrait.java @@ -93,7 +93,7 @@ public Optional getAuthorizer(String name) { } /** - * Gets an immuatable map of authorizer names to their definitions. + * Gets an immutable map of authorizer names to their definitions. * * @return Returns the authorizers. */ diff --git a/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitValidator.java b/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitValidator.java index 31a96673f30..eacb3e9ead6 100644 --- a/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitValidator.java +++ b/smithy-aws-apigateway-traits/src/main/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitValidator.java @@ -15,8 +15,10 @@ package software.amazon.smithy.aws.apigateway.traits; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -27,30 +29,49 @@ import software.amazon.smithy.model.validation.AbstractValidator; import software.amazon.smithy.model.validation.ValidationEvent; import software.amazon.smithy.model.validation.ValidationUtils; -import software.amazon.smithy.utils.OptionalUtils; import software.amazon.smithy.utils.SmithyInternalApi; /** - * Each authorizer resolved within a service must use a scheme that - * matches one of the schemes of the protocols of the service. + * Validates if authorizers traits are well-defined. */ @SmithyInternalApi public final class AuthorizersTraitValidator extends AbstractValidator { @Override public List validate(Model model) { return model.shapes(ServiceShape.class) - .flatMap(service -> OptionalUtils.stream(validateService(model, service))) + .map(service -> validate(model, service)) + .flatMap(List::stream) .collect(Collectors.toList()); } - private Optional validateService(Model model, ServiceShape service) { + private List validate(Model model, ServiceShape service) { + Map authorizers = service.getTrait(AuthorizersTrait.class) + .map(AuthorizersTrait::getAuthorizers) + .orElseGet(HashMap::new); + + List validationEvents = new ArrayList<>(); + + Optional authSchemaValidation = + validateAuthSchema(authorizers, model, service); + authSchemaValidation.ifPresent(validationEvents::add); + + Optional enableSimpleResponsesValidation = + validateEnableSimpleResponsesConfig(authorizers, service); + enableSimpleResponsesValidation.ifPresent(validationEvents::add); + + return validationEvents; + } + + /** + * Each authorizer resolved within a service must use a scheme that + * matches one of the schemes of the protocols of the service. + */ + private Optional validateAuthSchema(Map authorizers, + Model model, + ServiceShape service) { Set authSchemes = ServiceIndex.of(model).getAuthSchemes(service).keySet(); - // Create a comma separated string of authorizer names to schemes. - String invalidMappings = service.getTrait(AuthorizersTrait.class) - .map(AuthorizersTrait::getAuthorizers) - .orElseGet(HashMap::new) - .entrySet().stream() + String invalidMappings = authorizers.entrySet().stream() .filter(entry -> !authSchemes.contains(entry.getValue().getScheme())) .map(entry -> entry.getKey() + " -> " + entry.getValue().getScheme()) .sorted() @@ -68,4 +89,32 @@ private Optional validateService(Model model, ServiceShape serv ValidationUtils.tickedList(authSchemes), invalidMappings))); } + + /** + * Each authorizer with the enableSimpleResponses member defined + * should have the authorizedPayloadFormatVersion member set to 2.0. + */ + private Optional validateEnableSimpleResponsesConfig(Map authorizers, + ServiceShape service) { + String invalidConfigs = authorizers.entrySet().stream() + .filter(entry -> entry.getValue().getEnableSimpleResponses().isPresent()) + .filter(entry -> entry.getValue().getAuthorizerPayloadFormatVersion().isPresent()) + .filter(entry -> !entry.getValue().getAuthorizerPayloadFormatVersion().get().equals("2.0")) + .map(Map.Entry::getKey) + .sorted() + .collect(Collectors.joining(", ")); + + if (invalidConfigs.isEmpty()) { + return Optional.empty(); + } + + AuthorizersTrait authorizersTrait = service.getTrait(AuthorizersTrait.class).get(); + return Optional.of(error(service, authorizersTrait, String.format( + "The enableSimpleResponses member of %s is only supported when authorizedPayloadFormatVersion " + + "is 2.0. The following authorizers are misconfigured: %s", + AuthorizersTrait.ID, + invalidConfigs + ))); + } + } diff --git a/smithy-aws-apigateway-traits/src/main/resources/META-INF/smithy/aws.apigateway.smithy b/smithy-aws-apigateway-traits/src/main/resources/META-INF/smithy/aws.apigateway.smithy index 1a4dfdeb28e..9cacc8c3a1e 100644 --- a/smithy-aws-apigateway-traits/src/main/resources/META-INF/smithy/aws.apigateway.smithy +++ b/smithy-aws-apigateway-traits/src/main/resources/META-INF/smithy/aws.apigateway.smithy @@ -177,6 +177,15 @@ structure AuthorizerDefinition { /// The number of seconds for which the resulting IAM policy is cached. resultTtlInSeconds: Integer + + /// Format version of the payload sent from API Gateway to the authorizer + /// and how API Gateway interprets the response. Used only by HTTP APIs. + authorizerPayloadFormatVersion: PayloadFormatVersion + + /// Specifies if the autorizer returns either a boolean or an IAM Policy. + /// If enabled, authorizer returns a boolean. Used only by HTTP APIs. + /// Only supported when authorizerPayloadFormatVersion is set to 2.0. + enableSimpleResponses: Boolean } /// Defines a response and specifies parameter mappings. @@ -336,3 +345,10 @@ enum PassThroughBehavior { /// request. NEVER = "never" } + +/// Defines the payloadFormatVersion used by authorizers +@private +enum PayloadFormatVersion { + V1_0 = "1.0" + V2_0 = "2.0" +} diff --git a/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java b/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java index e56be02df8d..ddb3fe30757 100644 --- a/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java +++ b/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + package software.amazon.smithy.aws.apigateway.traits; import static org.hamcrest.MatcherAssert.assertThat; @@ -25,6 +40,8 @@ public void registersTrait() { .withMember("identitySource", "mapping.expression") .withMember("identityValidationExpression", "[A-Z]+") .withMember("resultTtlInSeconds", 100) + .withMember("authorizerPayloadFormatVersion", "format.version") + .withMember("enableSimpleResponse", true) .build()) .build(); Trait trait = factory.createTrait(AuthorizersTrait.ID, id, node).get(); From 14f1171755ca7b4d4c938495a3d3f864d6f025da Mon Sep 17 00:00:00 2001 From: Jose Vitor Schneid Date: Fri, 30 Sep 2022 15:02:56 -0300 Subject: [PATCH 2/2] Fix copyright year in AuthorizersTraitTest Co-authored-by: Chase Coalwell <782571+srchase@users.noreply.github.com> --- .../smithy/aws/apigateway/traits/AuthorizersTraitTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java b/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java index ddb3fe30757..b40a9e8088e 100644 --- a/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java +++ b/smithy-aws-apigateway-traits/src/test/java/software/amazon/smithy/aws/apigateway/traits/AuthorizersTraitTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License.