diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTrait.java b/smithy-model/src/main/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTrait.java index e32e616f87b..d0c65443ddc 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTrait.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTrait.java @@ -15,7 +15,6 @@ package software.amazon.smithy.model.traits; -import java.util.Locale; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; import software.amazon.smithy.model.shapes.ShapeId; @@ -26,7 +25,7 @@ * An HTTP-specific authentication scheme that sends an arbitrary * API key in a header or query string parameter. */ -public final class HttpApiKeyAuthTrait extends BooleanTrait implements ToSmithyBuilder { +public final class HttpApiKeyAuthTrait extends AbstractTrait implements ToSmithyBuilder { public static final ShapeId ID = ShapeId.from("smithy.api#httpApiKeyAuth"); @@ -55,16 +54,40 @@ public Builder toBuilder() { .in(getIn()); } + @Override + protected Node createNode() { + return Node.objectNodeBuilder() + .withMember("name", getName()) + .withMember("in", getIn().toString()) + .build(); + } + public static Builder builder() { return new Builder(); } public enum Location { - HEADER, QUERY; + HEADER("header"), + QUERY("query"); + + private final String serialized; + + Location(String serialized) { + this.serialized = serialized; + } + + static Location from(String value) { + for (Location location : values()) { + if (location.serialized.equals(value)) { + return location; + } + } + throw new IllegalArgumentException("Invalid location type: " + value); + } @Override public String toString() { - return super.toString().toLowerCase(Locale.ENGLISH); + return serialized; } } @@ -78,7 +101,7 @@ public Trait createTrait(ShapeId target, Node value) { ObjectNode objectNode = value.expectObjectNode(); Builder builder = builder().sourceLocation(value.getSourceLocation()); builder.name(objectNode.expectStringMember("name").getValue()); - builder.in(Location.valueOf(objectNode.expectStringMember("in").expectOneOf("header", "query"))); + builder.in(Location.from(objectNode.expectStringMember("in").expectOneOf("header", "query"))); return builder.build(); } } diff --git a/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index bb2421785e0..364d8042e03 100644 --- a/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -52,4 +52,5 @@ software.amazon.smithy.model.traits.AuthDefinitionTrait$Provider software.amazon.smithy.model.traits.HttpBasicAuthTrait$Provider software.amazon.smithy.model.traits.HttpBearerAuthTrait$Provider software.amazon.smithy.model.traits.HttpDigestAuthTrait$Provider +software.amazon.smithy.model.traits.HttpApiKeyAuthTrait$Provider software.amazon.smithy.model.traits.OptionalAuthTrait$Provider diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTraitTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTraitTest.java new file mode 100644 index 00000000000..d2b73ee8ba3 --- /dev/null +++ b/smithy-model/src/test/java/software/amazon/smithy/model/traits/HttpApiKeyAuthTraitTest.java @@ -0,0 +1,65 @@ +/* + * 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.model.traits; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Optional; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.ShapeId; + +public class HttpApiKeyAuthTraitTest { + @Test + public void loadsTraitWithHeader() { + TraitFactory provider = TraitFactory.createServiceFactory(); + ObjectNode node = Node.objectNode() + .withMember("name", "X-Foo") + .withMember("in", "header"); + Optional trait = provider.createTrait( + HttpApiKeyAuthTrait.ID, ShapeId.from("ns.qux#foo"), node); + assertTrue(trait.isPresent()); + assertThat(trait.get(), instanceOf(HttpApiKeyAuthTrait.class)); + HttpApiKeyAuthTrait auth = (HttpApiKeyAuthTrait) trait.get(); + + assertThat(auth.getName(), equalTo("X-Foo")); + assertThat(auth.getIn(), equalTo(HttpApiKeyAuthTrait.Location.HEADER)); + assertThat(auth.toNode(), equalTo(node)); + assertThat(auth.toBuilder().build(), equalTo(auth)); + } + + @Test + public void loadsTraitWithQuery() { + TraitFactory provider = TraitFactory.createServiceFactory(); + ObjectNode node = Node.objectNode() + .withMember("name", "blerg") + .withMember("in", "query"); + Optional trait = provider.createTrait( + HttpApiKeyAuthTrait.ID, ShapeId.from("ns.qux#foo"), node); + assertTrue(trait.isPresent()); + assertThat(trait.get(), instanceOf(HttpApiKeyAuthTrait.class)); + HttpApiKeyAuthTrait auth = (HttpApiKeyAuthTrait) trait.get(); + + assertThat(auth.getName(), equalTo("blerg")); + assertThat(auth.getIn(), equalTo(HttpApiKeyAuthTrait.Location.QUERY)); + assertThat(auth.toNode(), equalTo(node)); + assertThat(auth.toBuilder().build(), equalTo(auth)); + } +} diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/CoreExtension.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/CoreExtension.java index 1e4d5046ab8..67ece38de56 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/CoreExtension.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/CoreExtension.java @@ -25,11 +25,11 @@ import software.amazon.smithy.openapi.fromsmithy.mappers.RemoveUnusedComponents; import software.amazon.smithy.openapi.fromsmithy.mappers.UnsupportedTraits; import software.amazon.smithy.openapi.fromsmithy.protocols.AwsRestJson1Protocol; -import software.amazon.smithy.openapi.fromsmithy.security.AwsV4; -import software.amazon.smithy.openapi.fromsmithy.security.HttpBasic; -import software.amazon.smithy.openapi.fromsmithy.security.HttpBearer; -import software.amazon.smithy.openapi.fromsmithy.security.HttpDigest; -import software.amazon.smithy.openapi.fromsmithy.security.XApiKey; +import software.amazon.smithy.openapi.fromsmithy.security.AwsV4Converter; +import software.amazon.smithy.openapi.fromsmithy.security.HttpApiKeyAuthConverter; +import software.amazon.smithy.openapi.fromsmithy.security.HttpBasicConverter; +import software.amazon.smithy.openapi.fromsmithy.security.HttpBearerConverter; +import software.amazon.smithy.openapi.fromsmithy.security.HttpDigestConverter; import software.amazon.smithy.utils.ListUtils; /** @@ -39,11 +39,11 @@ public final class CoreExtension implements Smithy2OpenApiExtension { @Override public List> getSecuritySchemeConverters() { return ListUtils.of( - new HttpBasic(), - new HttpBearer(), - new HttpDigest(), - new AwsV4(), - new XApiKey() + new HttpBasicConverter(), + new HttpBearerConverter(), + new HttpDigestConverter(), + new AwsV4Converter(), + new HttpApiKeyAuthConverter() ); } diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4Converter.java similarity index 96% rename from smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4.java rename to smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4Converter.java index f2f6eb68724..397efb0785a 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4Converter.java @@ -27,7 +27,7 @@ /** * Adds AWS signature version in a way that"s compatible with AWS API Gateway. */ -public final class AwsV4 implements SecuritySchemeConverter { +public final class AwsV4Converter implements SecuritySchemeConverter { private static final String AUTH_HEADER = "Authorization"; private static final Set REQUEST_HEADERS = SetUtils.of( AUTH_HEADER, "Date", "X-Amz-Date", "X-Amz-Target", "X-Amz-Security-Token"); diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/XApiKey.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpApiKeyAuthConverter.java similarity index 87% rename from smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/XApiKey.java rename to smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpApiKeyAuthConverter.java index d154a5d86ca..bf2c43230e9 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/XApiKey.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpApiKeyAuthConverter.java @@ -27,9 +27,8 @@ *

This is compatible with Amazon API Gateway API key authorization. * * @see API Gateway documentation - * TODO: Implement and rename */ -public final class XApiKey implements SecuritySchemeConverter { +public final class HttpApiKeyAuthConverter implements SecuritySchemeConverter { @Override public Class getAuthSchemeType() { return HttpApiKeyAuthTrait.class; @@ -39,9 +38,8 @@ public Class getAuthSchemeType() { public SecurityScheme createSecurityScheme(Context context, HttpApiKeyAuthTrait trait) { return SecurityScheme.builder() .type("apiKey") - .in("header") - .name("X-Api-Key") - .description("X-Api-Key authentication") + .name(trait.getName()) + .in(trait.getIn().toString()) .build(); } } diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasic.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicConverter.java similarity index 93% rename from smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasic.java rename to smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicConverter.java index 00a0f7ad8d7..9cb0678ef49 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasic.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicConverter.java @@ -24,7 +24,7 @@ /** * Applies Basic HTTP auth. */ -public final class HttpBasic implements SecuritySchemeConverter { +public final class HttpBasicConverter implements SecuritySchemeConverter { @Override public Class getAuthSchemeType() { return HttpBasicAuthTrait.class; diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBearer.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBearerConverter.java similarity index 93% rename from smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBearer.java rename to smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBearerConverter.java index 6a515416fcd..5d93a7845bc 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBearer.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBearerConverter.java @@ -24,7 +24,7 @@ /** * Uses the Bearer scheme of the Authentication header. */ -public final class HttpBearer implements SecuritySchemeConverter { +public final class HttpBearerConverter implements SecuritySchemeConverter { @Override public Class getAuthSchemeType() { return HttpBearerAuthTrait.class; diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigest.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestConverter.java similarity index 93% rename from smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigest.java rename to smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestConverter.java index 14af678b5ea..9bfb58e0f1d 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigest.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestConverter.java @@ -24,7 +24,7 @@ /** * Applies Digest HTTP auth. */ -public final class HttpDigest implements SecuritySchemeConverter { +public final class HttpDigestConverter implements SecuritySchemeConverter { @Override public Class getAuthSchemeType() { return HttpDigestAuthTrait.class; diff --git a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4Test.java b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4ConverterTest.java similarity index 96% rename from smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4Test.java rename to smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4ConverterTest.java index 417a1ca8f70..5b17a185e2f 100644 --- a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4Test.java +++ b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/AwsV4ConverterTest.java @@ -8,7 +8,7 @@ import software.amazon.smithy.openapi.model.OpenApi; import software.amazon.smithy.utils.IoUtils; -public class AwsV4Test { +public class AwsV4ConverterTest { @Test public void addsAwsV4() { Model model = Model.assembler() diff --git a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpApiKeyAuthConverterTest.java b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpApiKeyAuthConverterTest.java new file mode 100644 index 00000000000..32e60ca59a7 --- /dev/null +++ b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpApiKeyAuthConverterTest.java @@ -0,0 +1,25 @@ +package software.amazon.smithy.openapi.fromsmithy.security; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.openapi.fromsmithy.OpenApiConverter; +import software.amazon.smithy.openapi.model.OpenApi; +import software.amazon.smithy.utils.IoUtils; + +public class HttpApiKeyAuthConverterTest { + @Test + public void addsCustomApiKeyAuth() { + Model model = Model.assembler() + .addImport(getClass().getResource("http-api-key-security.json")) + .discoverModels() + .assemble() + .unwrap(); + OpenApi result = OpenApiConverter.create().convert(model, ShapeId.from("smithy.example#Service")); + Node expectedNode = Node.parse(IoUtils.toUtf8String( + getClass().getResourceAsStream("http-api-key-security.openapi.json"))); + + Node.assertEquals(result, expectedNode); + } +} diff --git a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicTest.java b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicConverterTest.java similarity index 96% rename from smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicTest.java rename to smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicConverterTest.java index e57cb9627dc..a2690a202aa 100644 --- a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicTest.java +++ b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpBasicConverterTest.java @@ -8,7 +8,7 @@ import software.amazon.smithy.openapi.model.OpenApi; import software.amazon.smithy.utils.IoUtils; -public class HttpBasicTest { +public class HttpBasicConverterTest { @Test public void addsHttpBasicAuth() { Model model = Model.assembler() diff --git a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestTest.java b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestConverterTest.java similarity index 96% rename from smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestTest.java rename to smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestConverterTest.java index adbe7b14ada..1321c40561a 100644 --- a/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestTest.java +++ b/smithy-openapi/src/test/java/software/amazon/smithy/openapi/fromsmithy/security/HttpDigestConverterTest.java @@ -8,7 +8,7 @@ import software.amazon.smithy.openapi.model.OpenApi; import software.amazon.smithy.utils.IoUtils; -public class HttpDigestTest { +public class HttpDigestConverterTest { @Test public void addsHttpDigestAuth() { Model model = Model.assembler() diff --git a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/security/http-api-key-security.json b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/security/http-api-key-security.json new file mode 100644 index 00000000000..2066a1684d1 --- /dev/null +++ b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/security/http-api-key-security.json @@ -0,0 +1,30 @@ +{ + "smithy": "0.5.0", + "shapes": { + "smithy.example#Service": { + "type": "service", + "version": "2006-03-01", + "operations": [ + { + "target": "smithy.example#Operation1" + } + ], + "traits": { + "aws.protocols#restJson1": true, + "smithy.api#httpApiKeyAuth": { + "name": "X-Api-Key", + "in": "header" + } + } + }, + "smithy.example#Operation1": { + "type": "operation", + "traits": { + "smithy.api#http": { + "uri": "/", + "method": "GET" + } + } + } + } +} diff --git a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/security/http-api-key-security.openapi.json b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/security/http-api-key-security.openapi.json new file mode 100644 index 00000000000..75d8a40462b --- /dev/null +++ b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/security/http-api-key-security.openapi.json @@ -0,0 +1,33 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Service", + "version": "2006-03-01" + }, + "paths": { + "/": { + "get": { + "operationId": "Operation1", + "responses": { + "200": { + "description": "Operation1 response" + } + } + } + } + }, + "components": { + "securitySchemes": { + "smithy.api#httpApiKeyAuth": { + "type": "apiKey", + "name": "X-Api-Key", + "in": "header" + } + } + }, + "security": [ + { + "smithy.api#httpApiKeyAuth": [ ] + } + ] +}