Skip to content

Commit

Permalink
Fix issue wit custom authorizers when using pseudo parameters plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Aleksander Dikanski committed Mar 16, 2018
1 parent e154c85 commit 708da49
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 8 deletions.
15 changes: 7 additions & 8 deletions lib/stackops/apiGateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,16 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
});

// Audjust authorizer Uri and name (stage variables are not allowed in Uris here)
const isExternalRefAuthorizerPredicate = part => _.startsWith(part, 'arn:aws:lambda') ||
(_.has(part, 'Fn::Sub') && _.startsWith(part['Fn::Sub'], 'arn:aws:lambda'));
_.forOwn(authorizers, (authorizer, name) => {
const authorizerType = _.get(authorizer, 'Properties.Type');
if (authorizerType === 'TOKEN' || authorizerType === 'REQUEST') {
const uriParts = authorizer.Properties.AuthorizerUri['Fn::Join'][1];
const isExternalRefAuthorizer = _.every(uriParts, part => !_.startsWith(part, 'arn:aws:lambda'));
if (isExternalRefAuthorizer) {
const funcIndex = _.findIndex(uriParts, part =>
_.has(part, 'Fn::GetAtt') || _.startsWith(part, 'arn:aws:lambda'));

// Use the SERVERLESS_ALIAS stage variable to determine the called function alias
uriParts.splice(funcIndex + 1, 0, ':${stageVariables.SERVERLESS_ALIAS}');
const isExternalRefAuthorizer = _.some(uriParts, isExternalRefAuthorizerPredicate);
if (!isExternalRefAuthorizer) {
const funcIndex = _.findIndex(uriParts, part => _.startsWith(part, '/invocations'));
uriParts.splice(funcIndex , 0, ':${stageVariables.SERVERLESS_ALIAS}');
}
}

Expand Down Expand Up @@ -269,7 +268,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
const functionName = _.replace(name, /LambdaPermissionApiGateway$/, '');
const versionName = _.find(_.keys(versions), version => _.startsWith(version, functionName));
const aliasName = _.find(_.keys(aliases), alias => _.startsWith(alias, functionName));
const isExternalRef = _.startsWith(permission.Properties.FunctionName, 'arn:aws:lambda');
const isExternalRef = isExternalRefAuthorizerPredicate(permission.Properties.FunctionName);

// Adjust references and alias permissions
if (!isExternalRef) {
Expand Down
85 changes: 85 additions & 0 deletions test/data/auth-stack-2.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,21 @@
}
}
},
"ApiGatewayResourceFunc2": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"PathPart": "func1",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodFunc1Get": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
Expand Down Expand Up @@ -258,6 +273,48 @@
},
"DependsOn": "TestauthApiGatewayAuthorizer"
},
"ApiGatewayMethodFunc2Get": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "GET",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceFunc2"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"AuthorizationType": "CUSTOM",
"AuthorizerId": {
"Ref": "PseudoParamCustomAuthApiGatewayAuthorizer"
},
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"Testfct1LambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
},
"DependsOn": "PseudoParamCustomAuthApiGatewayAuthorizer"
},
"TestauthApiGatewayAuthorizer": {
"Type": "AWS::ApiGateway::Authorizer",
"Properties": {
Expand Down Expand Up @@ -288,6 +345,34 @@
"Type": "TOKEN"
}
},
"PseudoParamCustomAuthApiGatewayAuthorizer": {
"Type": "AWS::ApiGateway::Authorizer",
"Properties": {
"AuthorizerResultTtlInSeconds": 0,
"IdentitySource": "method.request.header.Authorization",
"Name": "testauth",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"AuthorizerUri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::Sub": "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:custom-auth"
},
"/invocations"
]
]
},
"Type": "TOKEN"
}
},
"TestauthApiGatewayRequestAuthorizer": {
"Type": "AWS::ApiGateway::Authorizer",
"Properties": {
Expand Down
33 changes: 33 additions & 0 deletions test/stackops/apiGateway.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,39 @@ describe('API Gateway', () => {
]));

});

it('should support externally referenced custom authorizers with Pseudo Parameters', () => {
stackTemplate = _.cloneDeep(require('../data/auth-stack-2.json'));
const template = serverless.service.provider.compiledCloudFormationTemplate = stackTemplate;
const compiledAliasTemplate = serverless.service.provider.compiledCloudFormationAliasTemplate = aliasTemplate;
return expect(awsAlias.aliasHandleApiGateway({}, [], {})).to.be.fulfilled
.then(() => BbPromise.all([
expect(template)
.to.have.a.nested.property("Resources.ApiGatewayMethodFunc2Get.Properties.AuthorizerId")
.that.deep.equals({ Ref: "PseudoParamCustomAuthApiGatewayAuthorizermyAlias" }),
expect(template)
.to.have.a.nested.property("Resources.ApiGatewayMethodFunc2Get.DependsOn")
.that.equals("PseudoParamCustomAuthApiGatewayAuthorizermyAlias"),
expect(template)
.to.have.a.nested.property('Resources.PseudoParamCustomAuthApiGatewayAuthorizermyAlias.Properties.AuthorizerUri')
.that.deep.equals({
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::Sub": "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:custom-auth"
},
"/invocations"
]
]}),
]));

});

it('should transform string dependencies and references to authorizers', () => {
const template = serverless.service.provider.compiledCloudFormationTemplate = stackTemplate;
Expand Down

0 comments on commit 708da49

Please sign in to comment.