diff --git a/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponse.java b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponse.java index a511836c..e8d3b13d 100644 --- a/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponse.java +++ b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponse.java @@ -11,6 +11,13 @@ import java.util.List; import java.util.Map; +/** + * The IAM Policy Response required for API Gateway HTTP APIs + * + * https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html + * + */ + @Data @Builder(setterPrefix = "with") @NoArgsConstructor diff --git a/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponseV1.java b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponseV1.java new file mode 100644 index 00000000..a4316536 --- /dev/null +++ b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponseV1.java @@ -0,0 +1,93 @@ +package com.amazonaws.services.lambda.runtime.events; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The IAM Policy Response required for API Gateway REST APIs + * + * https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html + * + */ + +@Data +@Builder(setterPrefix = "with") +@NoArgsConstructor +@AllArgsConstructor +public class IamPolicyResponseV1 implements Serializable, Cloneable { + + public static final String EXECUTE_API_INVOKE = "execute-api:Invoke"; + public static final String VERSION_2012_10_17 = "2012-10-17"; + public static final String ALLOW = "Allow"; + public static final String DENY = "Deny"; + + private String principalId; + private PolicyDocument policyDocument; + private Map context; + private String usageIdentifierKey; + + public Map getPolicyDocument() { + Map serializablePolicy = new HashMap<>(); + serializablePolicy.put("Version", policyDocument.getVersion()); + + int numberOfStatements = policyDocument.getStatement().size(); + Map[] serializableStatementArray = new Map[numberOfStatements]; + for (int i = 0; i < numberOfStatements; i++) { + Statement statement = policyDocument.getStatement().get(i); + Map serializableStatement = new HashMap<>(); + serializableStatement.put("Effect", statement.getEffect()); + serializableStatement.put("Action", statement.getAction()); + serializableStatement.put("Resource", statement.getResource().toArray(new String[0])); + serializableStatement.put("Condition", statement.getCondition()); + serializableStatementArray[i] = serializableStatement; + } + serializablePolicy.put("Statement", serializableStatementArray); + return serializablePolicy; + } + + public static Statement allowStatement(String resource) { + return Statement.builder() + .withEffect(ALLOW) + .withResource(Collections.singletonList(resource)) + .withAction(EXECUTE_API_INVOKE) + .build(); + } + + public static Statement denyStatement(String resource) { + return Statement.builder() + .withEffect(DENY) + .withResource(Collections.singletonList(resource)) + .withAction(EXECUTE_API_INVOKE) + .build(); + } + + @Data + @Builder(setterPrefix = "with") + @NoArgsConstructor + @AllArgsConstructor + public static class PolicyDocument implements Serializable, Cloneable { + + private String version; + private List statement; + } + + @Data + @Builder(setterPrefix = "with") + @NoArgsConstructor + @AllArgsConstructor + public static class Statement implements Serializable, Cloneable { + + private String action; + private String effect; + private List resource; + private Map> condition; + } +} \ No newline at end of file diff --git a/aws-lambda-java-events/src/test/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponseV1Test.java b/aws-lambda-java-events/src/test/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponseV1Test.java new file mode 100644 index 00000000..9b966179 --- /dev/null +++ b/aws-lambda-java-events/src/test/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponseV1Test.java @@ -0,0 +1,94 @@ +package com.amazonaws.services.lambda.runtime.events; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.ALLOW; +import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.EXECUTE_API_INVOKE; +import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.VERSION_2012_10_17; +import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.allowStatement; +import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.denyStatement; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; + +public class IamPolicyResponseV1Test { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Test + public void testAllowStatement() throws JsonProcessingException { + IamPolicyResponseV1 iamPolicyResponse = IamPolicyResponseV1.builder() + .withPrincipalId("me") + .withPolicyDocument(IamPolicyResponseV1.PolicyDocument.builder() + .withVersion(VERSION_2012_10_17) + .withStatement(singletonList(allowStatement("arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"))) + .build()) + .withUsageIdentifierKey("123ABC") + .build(); + + String json = OBJECT_MAPPER.writeValueAsString(iamPolicyResponse); + + assertThatJson(json).isEqualTo(readResource("iamPolicyV1Responses/allow.json")); + } + + @Test + public void testDenyStatement() throws JsonProcessingException { + IamPolicyResponseV1 iamPolicyResponse = IamPolicyResponseV1.builder() + .withPrincipalId("me") + .withPolicyDocument(IamPolicyResponseV1.PolicyDocument.builder() + .withVersion(VERSION_2012_10_17) + .withStatement(singletonList(denyStatement("arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"))) + .build()) + .withUsageIdentifierKey("123ABC") + .build(); + + String json = OBJECT_MAPPER.writeValueAsString(iamPolicyResponse); + + assertThatJson(json).isEqualTo(readResource("iamPolicyV1Responses/deny.json")); + } + + @Test + public void testStatementWithCondition() throws JsonProcessingException { + Map> conditions = new HashMap<>(); + conditions.put("DateGreaterThan", singletonMap("aws:TokenIssueTime", "2020-01-01T00:00:01Z")); + + IamPolicyResponseV1 iamPolicyResponse = IamPolicyResponseV1.builder() + .withPrincipalId("me") + .withPolicyDocument(IamPolicyResponseV1.PolicyDocument.builder() + .withVersion(VERSION_2012_10_17) + .withStatement(singletonList(IamPolicyResponseV1.Statement.builder() + .withAction(EXECUTE_API_INVOKE) + .withEffect(ALLOW) + .withResource(singletonList("arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*")) + .withCondition(conditions) + .build())) + .build()) + .withUsageIdentifierKey("123ABC") + .build(); + + String json = OBJECT_MAPPER.writeValueAsString(iamPolicyResponse); + + assertThatJson(json).isEqualTo(readResource("iamPolicyV1Responses/allow-with-condition.json")); + } + + private String readResource(String name) { + Path filePath = Paths.get("src", "test", "resources", name); + byte[] bytes = new byte[0]; + try { + bytes = Files.readAllBytes(filePath); + } catch (IOException e) { + e.printStackTrace(); + } + return new String(bytes, StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/allow-with-condition.json b/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/allow-with-condition.json new file mode 100644 index 00000000..afebc54e --- /dev/null +++ b/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/allow-with-condition.json @@ -0,0 +1,14 @@ +{ + "principalId": "me", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": "execute-api:Invoke", + "Resource": ["arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"], + "Effect": "Allow", + "Condition": {"DateGreaterThan": {"aws:TokenIssueTime": "2020-01-01T00:00:01Z"}} + }] + }, + "context":null, + "usageIdentifierKey": "123ABC" +} \ No newline at end of file diff --git a/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/allow.json b/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/allow.json new file mode 100644 index 00000000..518f5baa --- /dev/null +++ b/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/allow.json @@ -0,0 +1,14 @@ +{ + "principalId": "me", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": "execute-api:Invoke", + "Resource": ["arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"], + "Effect": "Allow", + "Condition": null + }] + }, + "context":null, + "usageIdentifierKey": "123ABC" +} \ No newline at end of file diff --git a/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/deny.json b/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/deny.json new file mode 100644 index 00000000..d06e170c --- /dev/null +++ b/aws-lambda-java-events/src/test/resources/iamPolicyV1Responses/deny.json @@ -0,0 +1,14 @@ +{ + "principalId": "me", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": "execute-api:Invoke", + "Resource": ["arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"], + "Effect": "Deny", + "Condition": null + }] + }, + "context":null, + "usageIdentifierKey": "123ABC" +} \ No newline at end of file