Skip to content

Commit 477d3b0

Browse files
msailescarlzogh
authored andcommitted
Feat: Support for IAM Policy Responses for API Gateway REST APIs.
These are different to HTTP APIs responses as they include a required field for 'usageIdentifierKey'.
1 parent 752e012 commit 477d3b0

File tree

6 files changed

+236
-0
lines changed

6 files changed

+236
-0
lines changed

aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/IamPolicyResponse.java

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
import java.util.List;
1212
import java.util.Map;
1313

14+
/**
15+
* The IAM Policy Response required for API Gateway HTTP APIs
16+
*
17+
* https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html
18+
*
19+
*/
20+
1421
@Data
1522
@Builder(setterPrefix = "with")
1623
@NoArgsConstructor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.amazonaws.services.lambda.runtime.events;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.io.Serializable;
9+
import java.util.Collections;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
/**
15+
* The IAM Policy Response required for API Gateway REST APIs
16+
*
17+
* https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
18+
*
19+
*/
20+
21+
@Data
22+
@Builder(setterPrefix = "with")
23+
@NoArgsConstructor
24+
@AllArgsConstructor
25+
public class IamPolicyResponseV1 implements Serializable, Cloneable {
26+
27+
public static final String EXECUTE_API_INVOKE = "execute-api:Invoke";
28+
public static final String VERSION_2012_10_17 = "2012-10-17";
29+
public static final String ALLOW = "Allow";
30+
public static final String DENY = "Deny";
31+
32+
private String principalId;
33+
private PolicyDocument policyDocument;
34+
private Map<String, Object> context;
35+
private String usageIdentifierKey;
36+
37+
public Map<String, Object> getPolicyDocument() {
38+
Map<String, Object> serializablePolicy = new HashMap<>();
39+
serializablePolicy.put("Version", policyDocument.getVersion());
40+
41+
int numberOfStatements = policyDocument.getStatement().size();
42+
Map<String, Object>[] serializableStatementArray = new Map[numberOfStatements];
43+
for (int i = 0; i < numberOfStatements; i++) {
44+
Statement statement = policyDocument.getStatement().get(i);
45+
Map<String, Object> serializableStatement = new HashMap<>();
46+
serializableStatement.put("Effect", statement.getEffect());
47+
serializableStatement.put("Action", statement.getAction());
48+
serializableStatement.put("Resource", statement.getResource().toArray(new String[0]));
49+
serializableStatement.put("Condition", statement.getCondition());
50+
serializableStatementArray[i] = serializableStatement;
51+
}
52+
serializablePolicy.put("Statement", serializableStatementArray);
53+
return serializablePolicy;
54+
}
55+
56+
public static Statement allowStatement(String resource) {
57+
return Statement.builder()
58+
.withEffect(ALLOW)
59+
.withResource(Collections.singletonList(resource))
60+
.withAction(EXECUTE_API_INVOKE)
61+
.build();
62+
}
63+
64+
public static Statement denyStatement(String resource) {
65+
return Statement.builder()
66+
.withEffect(DENY)
67+
.withResource(Collections.singletonList(resource))
68+
.withAction(EXECUTE_API_INVOKE)
69+
.build();
70+
}
71+
72+
@Data
73+
@Builder(setterPrefix = "with")
74+
@NoArgsConstructor
75+
@AllArgsConstructor
76+
public static class PolicyDocument implements Serializable, Cloneable {
77+
78+
private String version;
79+
private List<Statement> statement;
80+
}
81+
82+
@Data
83+
@Builder(setterPrefix = "with")
84+
@NoArgsConstructor
85+
@AllArgsConstructor
86+
public static class Statement implements Serializable, Cloneable {
87+
88+
private String action;
89+
private String effect;
90+
private List<String> resource;
91+
private Map<String, Map<String, Object>> condition;
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.amazonaws.services.lambda.runtime.events;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.io.IOException;
8+
import java.nio.charset.StandardCharsets;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.nio.file.Paths;
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.ALLOW;
16+
import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.EXECUTE_API_INVOKE;
17+
import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.VERSION_2012_10_17;
18+
import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.allowStatement;
19+
import static com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1.denyStatement;
20+
import static java.util.Collections.singletonList;
21+
import static java.util.Collections.singletonMap;
22+
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
23+
24+
public class IamPolicyResponseV1Test {
25+
26+
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
27+
28+
@Test
29+
public void testAllowStatement() throws JsonProcessingException {
30+
IamPolicyResponseV1 iamPolicyResponse = IamPolicyResponseV1.builder()
31+
.withPrincipalId("me")
32+
.withPolicyDocument(IamPolicyResponseV1.PolicyDocument.builder()
33+
.withVersion(VERSION_2012_10_17)
34+
.withStatement(singletonList(allowStatement("arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*")))
35+
.build())
36+
.withUsageIdentifierKey("123ABC")
37+
.build();
38+
39+
String json = OBJECT_MAPPER.writeValueAsString(iamPolicyResponse);
40+
41+
assertThatJson(json).isEqualTo(readResource("iamPolicyV1Responses/allow.json"));
42+
}
43+
44+
@Test
45+
public void testDenyStatement() throws JsonProcessingException {
46+
IamPolicyResponseV1 iamPolicyResponse = IamPolicyResponseV1.builder()
47+
.withPrincipalId("me")
48+
.withPolicyDocument(IamPolicyResponseV1.PolicyDocument.builder()
49+
.withVersion(VERSION_2012_10_17)
50+
.withStatement(singletonList(denyStatement("arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*")))
51+
.build())
52+
.withUsageIdentifierKey("123ABC")
53+
.build();
54+
55+
String json = OBJECT_MAPPER.writeValueAsString(iamPolicyResponse);
56+
57+
assertThatJson(json).isEqualTo(readResource("iamPolicyV1Responses/deny.json"));
58+
}
59+
60+
@Test
61+
public void testStatementWithCondition() throws JsonProcessingException {
62+
Map<String, Map<String, Object>> conditions = new HashMap<>();
63+
conditions.put("DateGreaterThan", singletonMap("aws:TokenIssueTime", "2020-01-01T00:00:01Z"));
64+
65+
IamPolicyResponseV1 iamPolicyResponse = IamPolicyResponseV1.builder()
66+
.withPrincipalId("me")
67+
.withPolicyDocument(IamPolicyResponseV1.PolicyDocument.builder()
68+
.withVersion(VERSION_2012_10_17)
69+
.withStatement(singletonList(IamPolicyResponseV1.Statement.builder()
70+
.withAction(EXECUTE_API_INVOKE)
71+
.withEffect(ALLOW)
72+
.withResource(singletonList("arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"))
73+
.withCondition(conditions)
74+
.build()))
75+
.build())
76+
.withUsageIdentifierKey("123ABC")
77+
.build();
78+
79+
String json = OBJECT_MAPPER.writeValueAsString(iamPolicyResponse);
80+
81+
assertThatJson(json).isEqualTo(readResource("iamPolicyV1Responses/allow-with-condition.json"));
82+
}
83+
84+
private String readResource(String name) {
85+
Path filePath = Paths.get("src", "test", "resources", name);
86+
byte[] bytes = new byte[0];
87+
try {
88+
bytes = Files.readAllBytes(filePath);
89+
} catch (IOException e) {
90+
e.printStackTrace();
91+
}
92+
return new String(bytes, StandardCharsets.UTF_8);
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"principalId": "me",
3+
"policyDocument": {
4+
"Version": "2012-10-17",
5+
"Statement": [{
6+
"Action": "execute-api:Invoke",
7+
"Resource": ["arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"],
8+
"Effect": "Allow",
9+
"Condition": {"DateGreaterThan": {"aws:TokenIssueTime": "2020-01-01T00:00:01Z"}}
10+
}]
11+
},
12+
"context":null,
13+
"usageIdentifierKey": "123ABC"
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"principalId": "me",
3+
"policyDocument": {
4+
"Version": "2012-10-17",
5+
"Statement": [{
6+
"Action": "execute-api:Invoke",
7+
"Resource": ["arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"],
8+
"Effect": "Allow",
9+
"Condition": null
10+
}]
11+
},
12+
"context":null,
13+
"usageIdentifierKey": "123ABC"
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"principalId": "me",
3+
"policyDocument": {
4+
"Version": "2012-10-17",
5+
"Statement": [{
6+
"Action": "execute-api:Invoke",
7+
"Resource": ["arn:aws:execute-api:eu-west-1:123456789012:1234abc/$deafult/*/*"],
8+
"Effect": "Deny",
9+
"Condition": null
10+
}]
11+
},
12+
"context":null,
13+
"usageIdentifierKey": "123ABC"
14+
}

0 commit comments

Comments
 (0)