Skip to content

Commit 40c6069

Browse files
committed
First import of User Pool Authorizers support to address issue #24
1 parent d1a8ff2 commit 40c6069

File tree

7 files changed

+406
-9
lines changed

7 files changed

+406
-9
lines changed

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayAuthorizerContext.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,57 @@
1212
*/
1313
package com.amazonaws.serverless.proxy.internal.model;
1414

15+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
16+
import com.fasterxml.jackson.annotation.JsonAnySetter;
17+
1518
import java.util.HashMap;
19+
import java.util.Map;
20+
1621

1722
/**
18-
* Custom authorizer context object for the API Gateway request context.
23+
* Context object used for custom authorizers and Cognito User Pool authorizers.
24+
* <p>
25+
* Custom authorizers populate the <code>principalId</code> field. All other custom values
26+
* returned by the authorizer are accessible via the <code>getContextValue</code> method.
27+
* </p>
28+
* <p>
29+
* Cognito User Pool authorizers populate the <pre>claims</pre> object.
30+
* </p>
1931
*/
20-
public class ApiGatewayAuthorizerContext extends HashMap<String, String> {
32+
public class ApiGatewayAuthorizerContext {
33+
34+
private Map<String, String> contextProperties = new HashMap<>();
35+
private String principalId;
36+
private CognitoAuthorizerClaims claims;
2137

2238
//-------------------------------------------------------------
2339
// Methods - Getter/Setter
2440
//-------------------------------------------------------------
2541

2642
public String getPrincipalId() {
27-
return get("principalId");
43+
return principalId;
2844
}
2945

30-
3146
public void setPrincipalId(String principalId) {
32-
put("principalId", principalId);
47+
this.principalId = principalId;
3348
}
3449

50+
@JsonAnyGetter
3551
public String getContextValue(String key) {
36-
return get(key);
52+
return contextProperties.get(key);
53+
}
54+
55+
@JsonAnySetter
56+
public void setContextValue(String key, String value) {
57+
contextProperties.put(key, value);
58+
}
59+
60+
61+
public CognitoAuthorizerClaims getClaims() {
62+
return claims;
63+
}
64+
65+
public void setClaims(CognitoAuthorizerClaims claims) {
66+
this.claims = claims;
3767
}
3868
}

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/AwsProxyRequest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import java.util.Map;
1818

1919
/**
20-
* Default implementation of the request object from an API GAteway AWS_PROXY integration
20+
* Default implementation of the request object from an API Gateway AWS_PROXY integration
2121
*/
2222
public class AwsProxyRequest {
2323

@@ -152,7 +152,7 @@ public boolean isBase64Encoded() {
152152
}
153153

154154

155-
public void setBase64Encoded(boolean base64Encoded) {
155+
public void setIsBase64Encoded(boolean base64Encoded) {
156156
isBase64Encoded = base64Encoded;
157157
}
158158
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0/
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
package com.amazonaws.serverless.proxy.internal.model;
14+
15+
16+
import com.fasterxml.jackson.annotation.JsonProperty;
17+
18+
import java.time.ZonedDateTime;
19+
import java.time.format.DateTimeFormatter;
20+
21+
22+
/**
23+
* This object represents the claims property in the authorizer context of a request. The claims object is normally populated
24+
* by a Cognito User Pool authorizer and contains the following fields:
25+
* <pre>
26+
* "claims": {
27+
* "sub": "42df3b02-29f1-4779-a3e5-eff92ff280b2",
28+
* "aud": "2k3no2j1rjjbqaskc4bk0ub29b",
29+
* "email_verified": "true",
30+
* "token_use": "id",
31+
* "auth_time": "1492467169",
32+
* "iss": "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_Adx5ZHePg",
33+
* "cognito:username": "sapessi",
34+
* "exp": "Mon Apr 17 23:12:49 UTC 2017",
35+
* "iat": "Mon Apr 17 22:12:49 UTC 2017",
36+
* "email": "[email protected]"
37+
* }
38+
* </pre>
39+
*/
40+
public class CognitoAuthorizerClaims {
41+
// Mon Apr 17 23:12:49 UTC 2017
42+
static final DateTimeFormatter TOKEN_DATE_FORMATTER = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy");
43+
44+
@JsonProperty(value = "sub")
45+
private String subject;
46+
@JsonProperty(value = "aud")
47+
private String audience;
48+
@JsonProperty(value = "iss")
49+
private String issuer;
50+
@JsonProperty(value = "token_use")
51+
private String tokenUse;
52+
@JsonProperty(value = "cognito:username")
53+
private String username;
54+
private String email;
55+
@JsonProperty(value = "email_verified")
56+
private boolean emailVerified;
57+
@JsonProperty(value = "auth_time")
58+
private Long authTime;
59+
private String exp;
60+
private String iat;
61+
62+
private String getSubject() { return this.subject; }
63+
64+
public void setSubject(String subject) {
65+
this.subject = subject;
66+
}
67+
68+
69+
public String getAudience() {
70+
return audience;
71+
}
72+
73+
74+
public void setAudience(String audience) {
75+
this.audience = audience;
76+
}
77+
78+
79+
public String getIssuer() {
80+
return issuer;
81+
}
82+
83+
84+
public void setIssuer(String issuer) {
85+
this.issuer = issuer;
86+
}
87+
88+
89+
public String getTokenUse() {
90+
return tokenUse;
91+
}
92+
93+
94+
public void setTokenUse(String tokenUse) {
95+
this.tokenUse = tokenUse;
96+
}
97+
98+
99+
public String getUsername() {
100+
return username;
101+
}
102+
103+
104+
public void setUsername(String username) {
105+
this.username = username;
106+
}
107+
108+
109+
public String getEmail() {
110+
return email;
111+
}
112+
113+
114+
public void setEmail(String email) {
115+
this.email = email;
116+
}
117+
118+
119+
public boolean isEmailVerified() {
120+
return emailVerified;
121+
}
122+
123+
124+
public void setEmailVerified(boolean emailVerified) {
125+
this.emailVerified = emailVerified;
126+
}
127+
128+
129+
public Long getAuthTime() {
130+
return authTime;
131+
}
132+
133+
134+
public void setAuthTime(Long authTime) {
135+
this.authTime = authTime;
136+
}
137+
138+
139+
public String getExp() {
140+
return exp;
141+
}
142+
143+
144+
public void setExp(String expiration) {
145+
this.exp = expiration;
146+
}
147+
148+
public ZonedDateTime getExpirationTime() {
149+
return ZonedDateTime.from(TOKEN_DATE_FORMATTER.parse(getExp()));
150+
}
151+
152+
153+
public String getIat() {
154+
return iat;
155+
}
156+
157+
158+
public void setIat(String issuedAt) {
159+
this.iat = issuedAt;
160+
}
161+
162+
public ZonedDateTime getIssueTime() {
163+
return ZonedDateTime.from((TOKEN_DATE_FORMATTER.parse(getIat())));
164+
}
165+
}

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ protected List<Map.Entry<String, String>> parseHeaderValue(String headerValue) {
280280
if (headerValue == null) {
281281
return values;
282282
}
283+
int entryCounter = 1;
283284
for (String kv : headerValue.split(HEADER_VALUE_SEPARATOR)) {
284285
String[] kvSplit = kv.split(HEADER_KEY_VALUE_SEPARATOR);
285286

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import javax.ws.rs.core.HttpHeaders;
2323
import javax.ws.rs.core.MediaType;
24+
25+
import java.io.File;
26+
import java.io.IOException;
2427
import java.util.HashMap;
2528

2629
/**
@@ -156,7 +159,7 @@ public AwsProxyRequestBuilder authorizerContextValue(String key, String value) {
156159
if (this.request.getRequestContext().getAuthorizer() == null) {
157160
this.request.getRequestContext().setAuthorizer(new ApiGatewayAuthorizerContext());
158161
}
159-
this.request.getRequestContext().getAuthorizer().put(key, value);
162+
this.request.getRequestContext().getAuthorizer().setContextValue(key, value);
160163
return this;
161164
}
162165

@@ -209,6 +212,18 @@ public AwsProxyRequestBuilder serverName(String serverName) {
209212
return this;
210213
}
211214

215+
public AwsProxyRequestBuilder fromJsonString(String jsonContent)
216+
throws IOException {
217+
request = mapper.readValue(jsonContent, AwsProxyRequest.class);
218+
return this;
219+
}
220+
221+
public AwsProxyRequestBuilder fromJsonPath(String filePath)
222+
throws IOException {
223+
request = mapper.readValue(new File(filePath), AwsProxyRequest.class);
224+
return this;
225+
}
226+
212227
public AwsProxyRequest build() {
213228
return this.request;
214229
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.amazonaws.serverless.proxy.internal.model;
2+
3+
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
4+
5+
import org.junit.Test;
6+
7+
import java.io.IOException;
8+
9+
import static org.junit.Assert.*;
10+
11+
public class ApiGatewayAuthorizerContextTest {
12+
private static final String FIELD_NAME_1 = "CUSTOM_FIELD_1";
13+
private static final String FIELD_NAME_2 = "CUSTOM_FIELD_2";
14+
private static final String FIELD_VALUE_1 = "VALUE_1";
15+
private static final String FIELD_VALUE_2 = "VALUE_2";
16+
private static final String PRINCIPAL = "xxxxx";
17+
18+
private static final String AUTHORIZER_REQUEST = "{\n"
19+
+ " \"resource\": \"/restaurants\",\n"
20+
+ " \"path\": \"/restaurants\",\n"
21+
+ " \"httpMethod\": \"GET\",\n"
22+
+ " \"headers\": {\n"
23+
+ " \"Accept\": \"*/*\",\n"
24+
+ " \"Authorization\": \"eyJraWQiOiJKSm9VQUtrRThcL3NTU3Rwa3dPZTFWN2dvak1xS0k1NU8zTzB4WVgwMGNRdz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI0MmRmM2IwMi0yOWYxLTQ3NzktYTNlNS1lZmY5MmZmMjgwYjIiLCJhdWQiOiIyazNubzJqMXJqamJxYXNrYzRiazB1YjI5YiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTQ5MjQ2NzE2OSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMi5hbWF6b25hd3MuY29tXC91cy1lYXN0LTJfQWR4NVpIZVBnIiwiY29nbml0bzp1c2VybmFtZSI6InNhcGVzc2kiLCJleHAiOjE0OTI0NzA3NjksImlhdCI6MTQ5MjQ2NzE2OSwiZW1haWwiOiJidWxpYW5pc0BhbWF6b24uY29tIn0.aTODUMNib_pQhad1aWTHrlz7kwA5QkcvZptcbLFY5BuNqpr9zsK14EhHRvmvflK4MMQaxCE5Cxa9joR9g-HCmmF1usZhXO4Q2iyEWcBk0whjn3CnC55k6yEuMv6y9krts0YHSamsRkhW7wnCpuLmk2KgzHTfyt6oQ1qbg9QE8l9LRhjCHLnujlLIQaG9p9UfJVf-uGSg1k_bCyzl48lqkc7LDwqDZCHXGf1RYRQLg5jphXF_tjByDk_0t9Ah7pX2nFwl0SUz74enG8emq58g4pemeVekb9Mw0wyD-B5TWeGVs_nvmC3q4jgxMyJy3Xq4Ggd9qSgIN_Khdg3Q26F2bA\",\n"
25+
+ " \"CloudFront-Forwarded-Proto\": \"https\"\n"
26+
+ " },\n"
27+
+ " \"queryStringParameters\": null,\n"
28+
+ " \"pathParameters\": null,\n"
29+
+ " \"stageVariables\": null,\n"
30+
+ " \"requestContext\": {\n"
31+
+ " \"accountId\": \"XXXXXXXXXXXXXX\",\n"
32+
+ " \"resourceId\": \"xxxxx\",\n"
33+
+ " \"stage\": \"dev\",\n"
34+
+ " \"authorizer\": {\n"
35+
+ " \"principalId\": \"" + PRINCIPAL + "\",\n"
36+
+ " \"" + FIELD_NAME_1 + "\": \"" + FIELD_VALUE_1 + "\","
37+
+ " \"" + FIELD_NAME_2 + "\": \"" + FIELD_VALUE_2 + "\""
38+
+ " },\n"
39+
+ " \"requestId\": \"ad0a33ba-23bc-11e7-9b7d-235a67eb05bd\",\n"
40+
+ " \"identity\": {\n"
41+
+ " \"cognitoIdentityPoolId\": null,\n"
42+
+ " \"accountId\": null,\n"
43+
+ " \"cognitoIdentityId\": null,\n"
44+
+ " \"caller\": null,\n"
45+
+ " \"apiKey\": null,\n"
46+
+ " \"sourceIp\": \"54.240.196.171\",\n"
47+
+ " \"accessKey\": null,\n"
48+
+ " \"cognitoAuthenticationType\": null,\n"
49+
+ " \"cognitoAuthenticationProvider\": null,\n"
50+
+ " \"userArn\": null,\n"
51+
+ " \"userAgent\": \"PostmanRuntime/3.0.1\",\n"
52+
+ " \"user\": null\n"
53+
+ " },\n"
54+
+ " \"resourcePath\": \"/restaurants\",\n"
55+
+ " \"httpMethod\": \"GET\",\n"
56+
+ " \"apiId\": \"xxxxxxxx\"\n"
57+
+ " },\n"
58+
+ " \"body\": null,\n"
59+
+ " \"isBase64Encoded\": false\n"
60+
+ "}";
61+
62+
@Test
63+
public void authorizerContext_serialize_customValues() {
64+
try {
65+
AwsProxyRequest req = new AwsProxyRequestBuilder().fromJsonString(AUTHORIZER_REQUEST).build();
66+
67+
assertNotNull(req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_1));
68+
assertNotNull(req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_2));
69+
assertEquals(FIELD_VALUE_1, req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_1));
70+
assertEquals(FIELD_VALUE_2, req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_2));
71+
assertEquals(PRINCIPAL, req.getRequestContext().getAuthorizer().getPrincipalId());
72+
assertNull(req.getRequestContext().getAuthorizer().getContextValue("principalId"));
73+
} catch (IOException e) {
74+
e.printStackTrace();
75+
fail();
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)