Skip to content

Commit

Permalink
Adds security to individual operations
Browse files Browse the repository at this point in the history
We previously weren't adding security to individual operations if the
operation differend from the security of the operation differed from the
service because of an applied authorizer trait.
  • Loading branch information
mtdowling committed Mar 27, 2020
1 parent 3112bb6 commit 767167b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;
import software.amazon.smithy.aws.traits.apigateway.AuthorizerDefinition;
import software.amazon.smithy.aws.traits.apigateway.AuthorizerIndex;
import software.amazon.smithy.aws.traits.apigateway.AuthorizerTrait;
import software.amazon.smithy.aws.traits.apigateway.AuthorizersTrait;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
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.openapi.fromsmithy.Context;
Expand All @@ -32,7 +34,9 @@
import software.amazon.smithy.openapi.fromsmithy.mappers.RemoveUnusedComponents;
import software.amazon.smithy.openapi.model.ComponentsObject;
import software.amazon.smithy.openapi.model.OpenApi;
import software.amazon.smithy.openapi.model.OperationObject;
import software.amazon.smithy.openapi.model.SecurityScheme;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.MapUtils;

/**
Expand Down Expand Up @@ -79,6 +83,25 @@ public Map<String, List<String>> updateSecurity(
.orElse(requirement);
}

@Override
public OperationObject updateOperation(Context context, OperationShape shape, OperationObject operation) {
ServiceShape service = context.getService();
AuthorizerIndex authorizerIndex = context.getModel().getKnowledge(AuthorizerIndex.class);

// Get the resolved security schemes of the service and operation, and
// only add security if it's different than the service.
String serviceAuth = authorizerIndex.getAuthorizer(service).orElse(null);
String operationAuth = authorizerIndex.getAuthorizer(service, shape).orElse(null);

if (operationAuth == null || Objects.equals(operationAuth, serviceAuth)) {
return operation;
}

return operation.toBuilder()
.addSecurity(MapUtils.of(operationAuth, ListUtils.of()))
.build();
}

@Override
public OpenApi after(Context context, OpenApi openapi) {
return context.getService().getTrait(AuthorizersTrait.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
package software.amazon.smithy.aws.apigateway.openapi;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertFalse;

import java.util.Optional;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.Node;
Expand All @@ -28,6 +32,8 @@
import software.amazon.smithy.openapi.fromsmithy.OpenApiConverter;
import software.amazon.smithy.openapi.model.OpenApi;
import software.amazon.smithy.openapi.model.SecurityScheme;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.MapUtils;

public class AddAuthorizersTest {
@Test
Expand Down Expand Up @@ -95,4 +101,26 @@ public void addsCustomAuthType() {
assertThat(sigV4.getExtension("x-amazon-apigateway-authtype").get(), equalTo(Node.from("myCustomType")));
assertFalse(sigV4.getExtension("x-amazon-apigateway-authorizer").isPresent());
}

@Test
public void resolvesEffectiveAuthorizersForEachOperation() {
Model model = Model.assembler()
.discoverModels(getClass().getClassLoader())
.addImport(getClass().getResource("effective-authorizers.smithy"))
.assemble()
.unwrap();
OpenApi result = OpenApiConverter.create()
.classLoader(getClass().getClassLoader())
.convert(model, ShapeId.from("smithy.example#ServiceA"));

// The security of the service is just "foo".
assertThat(result.getSecurity(), contains(MapUtils.of("foo", ListUtils.of())));
// The "baz" and "foo" securitySchemes must be present.
assertThat(result.getComponents().getSecuritySchemes().keySet(), containsInAnyOrder("baz", "foo"));
// The security schemes of operationA must be empty.
assertThat(result.getPaths().get("/operationA").getGet().get().getSecurity(), is(Optional.empty()));
// The security schemes of operationB must be "baz".
assertThat(result.getPaths().get("/operationB").getGet().get().getSecurity(),
is(Optional.of(ListUtils.of(MapUtils.of("baz", ListUtils.of())))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace smithy.example

@protocols([{name: "aws.rest-json-1.1", auth: ["aws.v4"]}])
@aws.apigateway#authorizer("foo")
@aws.apigateway#authorizers(
foo: {scheme: "aws.v4", type: "aws", uri: "arn:foo"},
baz: {scheme: "aws.v4", type: "aws", uri: "arn:baz"})
service ServiceA {
version: "2019-06-17",
operations: [OperationA, OperationB]
}

// Inherits the authorizer of ServiceA
@http(method: "GET", uri: "/operationA")
operation OperationA {}

// Overrides the authorizer of ServiceA
@aws.apigateway#authorizer("baz")
@http(method: "GET", uri: "/operationB")
operation OperationB {}

0 comments on commit 767167b

Please sign in to comment.