Skip to content

Commit a49d907

Browse files
committed
Add support for AWS_AIM, Congnito and custom authorizer
1 parent d8f1463 commit a49d907

File tree

3 files changed

+478
-1
lines changed

3 files changed

+478
-1
lines changed

lib/deploy/events/apiGateway/methods.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ module.exports = {
202202
}
203203

204204
const authorizerLogicalId = this.provider.naming
205-
.getAuthorizerLogicalId(http.authorizer.name);
205+
.getAuthorizerLogicalId(http.authorizer.name || http.authorizer);
206206

207207
let authorizationType;
208208
const authorizerArn = http.authorizer.arn;

lib/deploy/events/apiGateway/validate.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22
const NOT_FOUND = -1;
33
const _ = require('lodash');
4+
const awsArnRegExs = require('../../../utils/arnRegularExpressions');
45

56
module.exports = {
67
httpValidate() {
@@ -15,6 +16,10 @@ module.exports = {
1516
http.path = this.getHttpPath(http, stateMachineName);
1617
http.method = this.getHttpMethod(http, stateMachineName);
1718

19+
if (http.authorizer) {
20+
http.authorizer = this.getAuthorizer(http, stateMachineName);
21+
}
22+
1823
if (http.cors) {
1924
http.cors = this.getCors(http);
2025

@@ -107,6 +112,123 @@ module.exports = {
107112
throw new this.serverless.classes.Error(errorMessage);
108113
},
109114

115+
getIntegration(http, stateMachineName) {
116+
if (http.integration) {
117+
// normalize the integration for further processing
118+
const normalizedIntegration = http.integration.toUpperCase().replace('-', '_');
119+
const allowedIntegrations = [
120+
'LAMBDA_PROXY', 'LAMBDA', 'AWS', 'AWS_PROXY', 'HTTP', 'HTTP_PROXY', 'MOCK',
121+
];
122+
123+
// check if the user has entered a non-valid integration
124+
if (allowedIntegrations.indexOf(normalizedIntegration) === NOT_FOUND) {
125+
const errorMessage = [
126+
`Invalid APIG integration "${http.integration}"`,
127+
` in function "${stateMachineName}".`,
128+
' Supported integrations are:',
129+
' lambda, lambda-proxy, aws, aws-proxy, http, http-proxy, mock.',
130+
].join('');
131+
throw new this.serverless.classes.Error(errorMessage);
132+
}
133+
if (normalizedIntegration === 'LAMBDA') {
134+
return 'AWS';
135+
} else if (normalizedIntegration === 'LAMBDA_PROXY') {
136+
return 'AWS_PROXY';
137+
}
138+
return normalizedIntegration;
139+
}
140+
return 'AWS_PROXY';
141+
},
142+
143+
getAuthorizer(http, functionName) {
144+
const authorizer = http.authorizer;
145+
146+
let type;
147+
let name;
148+
let arn;
149+
let identitySource;
150+
let resultTtlInSeconds;
151+
let identityValidationExpression;
152+
let claims;
153+
let authorizerId;
154+
155+
if (typeof authorizer === 'string') {
156+
if (authorizer.toUpperCase() === 'AWS_IAM') {
157+
type = 'AWS_IAM';
158+
} else if (authorizer.indexOf(':') === -1) {
159+
name = authorizer;
160+
arn = this.getLambdaArn(authorizer);
161+
} else {
162+
arn = authorizer;
163+
name = this.provider.naming.extractAuthorizerNameFromArn(arn);
164+
}
165+
} else if (typeof authorizer === 'object') {
166+
if (authorizer.type && authorizer.authorizerId) {
167+
type = authorizer.type;
168+
authorizerId = authorizer.authorizerId;
169+
} else if (authorizer.type && authorizer.type.toUpperCase() === 'AWS_IAM') {
170+
type = 'AWS_IAM';
171+
} else if (authorizer.arn) {
172+
arn = authorizer.arn;
173+
if (_.isString(authorizer.name)) {
174+
name = authorizer.name;
175+
} else {
176+
name = this.provider.naming.extractAuthorizerNameFromArn(arn);
177+
}
178+
} else if (authorizer.name) {
179+
name = authorizer.name;
180+
arn = this.getLambdaArn(name);
181+
} else {
182+
throw new this.serverless.classes.Error('Please provide either an authorizer name or ARN');
183+
}
184+
185+
if (!type) {
186+
type = authorizer.type;
187+
}
188+
189+
resultTtlInSeconds = Number.parseInt(authorizer.resultTtlInSeconds, 10);
190+
resultTtlInSeconds = Number.isNaN(resultTtlInSeconds) ? 300 : resultTtlInSeconds;
191+
claims = authorizer.claims || [];
192+
193+
identitySource = authorizer.identitySource;
194+
identityValidationExpression = authorizer.identityValidationExpression;
195+
} else {
196+
const errorMessage = [
197+
`authorizer property in function ${functionName} is not an object nor a string.`,
198+
' The correct format is: authorizer: functionName',
199+
' OR an object containing a name property.',
200+
' Please check the docs for more info.',
201+
].join('');
202+
throw new this.serverless.classes.Error(errorMessage);
203+
}
204+
205+
if (typeof identitySource === 'undefined') {
206+
identitySource = 'method.request.header.Authorization';
207+
}
208+
209+
const integration = this.getIntegration(http);
210+
if (integration === 'AWS_PROXY'
211+
&& typeof arn === 'string'
212+
&& awsArnRegExs.cognitoIdpArnExpr.test(arn)
213+
&& authorizer.claims) {
214+
const errorMessage = [
215+
'Cognito claims can only be filtered when using the lambda integration type',
216+
];
217+
throw new this.serverless.classes.Error(errorMessage);
218+
}
219+
220+
return {
221+
type,
222+
name,
223+
arn,
224+
authorizerId,
225+
resultTtlInSeconds,
226+
identitySource,
227+
identityValidationExpression,
228+
claims,
229+
};
230+
},
231+
110232
getCors(http) {
111233
const headers = [
112234
'Content-Type',
@@ -164,4 +286,10 @@ module.exports = {
164286

165287
return cors;
166288
},
289+
290+
getLambdaArn(name) {
291+
this.serverless.service.getFunction(name);
292+
const lambdaLogicalId = this.provider.naming.getLambdaLogicalId(name);
293+
return { 'Fn::GetAtt': [lambdaLogicalId, 'Arn'] };
294+
},
167295
};

0 commit comments

Comments
 (0)