Skip to content

Commit

Permalink
Fix httpApiKeyAuth trait
Browse files Browse the repository at this point in the history
This commit fixes the httpApiKeyAuth trait so that it loads/serializes
properly, it has tests, an it converts to OpenAPI properly. This commit
also normalizes the names of all of the OpenAPI security scheme
converters.
  • Loading branch information
mtdowling committed Feb 25, 2020
1 parent b1ce8ce commit 031e659
Show file tree
Hide file tree
Showing 15 changed files with 202 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<HttpApiKeyAuthTrait> {
public final class HttpApiKeyAuthTrait extends AbstractTrait implements ToSmithyBuilder<HttpApiKeyAuthTrait> {

public static final ShapeId ID = ShapeId.from("smithy.api#httpApiKeyAuth");

Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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> 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> 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -39,11 +39,11 @@ public final class CoreExtension implements Smithy2OpenApiExtension {
@Override
public List<SecuritySchemeConverter<? extends Trait>> getSecuritySchemeConverters() {
return ListUtils.of(
new HttpBasic(),
new HttpBearer(),
new HttpDigest(),
new AwsV4(),
new XApiKey()
new HttpBasicConverter(),
new HttpBearerConverter(),
new HttpDigestConverter(),
new AwsV4Converter(),
new HttpApiKeyAuthConverter()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
/**
* Adds AWS signature version in a way that"s compatible with AWS API Gateway.
*/
public final class AwsV4 implements SecuritySchemeConverter<SigV4Trait> {
public final class AwsV4Converter implements SecuritySchemeConverter<SigV4Trait> {
private static final String AUTH_HEADER = "Authorization";
private static final Set<String> REQUEST_HEADERS = SetUtils.of(
AUTH_HEADER, "Date", "X-Amz-Date", "X-Amz-Target", "X-Amz-Security-Token");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@
* <p>This is compatible with Amazon API Gateway API key authorization.
*
* @see <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-key-source.html">API Gateway documentation</a>
* TODO: Implement and rename
*/
public final class XApiKey implements SecuritySchemeConverter<HttpApiKeyAuthTrait> {
public final class HttpApiKeyAuthConverter implements SecuritySchemeConverter<HttpApiKeyAuthTrait> {
@Override
public Class<HttpApiKeyAuthTrait> getAuthSchemeType() {
return HttpApiKeyAuthTrait.class;
Expand All @@ -39,9 +38,8 @@ public Class<HttpApiKeyAuthTrait> getAuthSchemeType() {
public SecurityScheme createSecurityScheme(Context<? extends Trait> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
* Applies Basic HTTP auth.
*/
public final class HttpBasic implements SecuritySchemeConverter<HttpBasicAuthTrait> {
public final class HttpBasicConverter implements SecuritySchemeConverter<HttpBasicAuthTrait> {
@Override
public Class<HttpBasicAuthTrait> getAuthSchemeType() {
return HttpBasicAuthTrait.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
* Uses the Bearer scheme of the Authentication header.
*/
public final class HttpBearer implements SecuritySchemeConverter<HttpBearerAuthTrait> {
public final class HttpBearerConverter implements SecuritySchemeConverter<HttpBearerAuthTrait> {
@Override
public Class<HttpBearerAuthTrait> getAuthSchemeType() {
return HttpBearerAuthTrait.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
* Applies Digest HTTP auth.
*/
public final class HttpDigest implements SecuritySchemeConverter<HttpDigestAuthTrait> {
public final class HttpDigestConverter implements SecuritySchemeConverter<HttpDigestAuthTrait> {
@Override
public Class<HttpDigestAuthTrait> getAuthSchemeType() {
return HttpDigestAuthTrait.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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": [ ]
}
]
}

0 comments on commit 031e659

Please sign in to comment.