From 06bbfedfa11a6b0171b881565f8327e4adb85f8a Mon Sep 17 00:00:00 2001 From: Frank Schmid Date: Mon, 29 May 2017 18:39:23 +0200 Subject: [PATCH 1/2] Set authorizer name and Uri --- lib/stackops/apiGateway.js | 51 +++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/lib/stackops/apiGateway.js b/lib/stackops/apiGateway.js index fd8deb6..a8ab3a7 100644 --- a/lib/stackops/apiGateway.js +++ b/lib/stackops/apiGateway.js @@ -101,13 +101,12 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac ['Properties.Principal', 'apigateway.amazonaws.com'])); const apiMethods = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Method' ])); - //const apiResources = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Resource' ])); + const authorizers = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Authorizer' ])); const aliases = _.assign({}, _.pickBy(aliasStack.Resources, [ 'Type', 'AWS::Lambda::Alias' ])); const versions = _.assign({}, _.pickBy(aliasStack.Resources, [ 'Type', 'AWS::Lambda::Version' ])); // Adjust method API and target function _.forOwn(apiMethods, (method, name) => { - // Relink to function alias in case we have a lambda endpoint if (_.includes([ 'AWS', 'AWS_PROXY' ], _.get(method, 'Properties.Integration.Type'))) { // For methods it is a bit tricky to find the related function name. There is no direct link. @@ -127,6 +126,25 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac stageStack.Resources[name] = method; }); + // Audjust authorizer Uri and name (stage variables are not allowed in Uris here) + _.forOwn(authorizers, (authorizer, name) => { + const uriParts = authorizer.Properties.AuthorizerUri['Fn::Join'][1]; + const funcIndex = _.findIndex(uriParts, part => _.has(part, 'Fn::GetAtt')); + + // Use the SERVERLESS_ALIAS stage variable to determine the called function alias + uriParts.splice(funcIndex + 1, 0, `:${this._alias}`); + + authorizer.Properties.Name = `${authorizer.Properties.Name}-${this._alias}`; + + // Check for user resource overrides + if (_.has(userResources.Resources, name)) { + _.merge(authorizer, userResources.Resources[name]); + delete userResources.Resources[name]; + } + + stageStack.Resources[name] = authorizer; + }); + // Adjust permission to reference the function aliases _.forOwn(apiLambdaPermissions, (permission, name) => { const functionName = _.replace(name, /LambdaPermissionApiGateway$/, ''); @@ -135,20 +153,23 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac // Adjust references and alias permissions permission.Properties.FunctionName = { Ref: aliasName }; - permission.Properties.SourceArn = { - 'Fn::Join': [ - '', - [ - 'arn:aws:execute-api:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':', - { 'Fn::ImportValue': `${stackName}-ApiGatewayRestApi` }, - '/*/*' + if (permission.Properties.SourceArn) { + // Authorizers do not set the SourceArn property + permission.Properties.SourceArn = { + 'Fn::Join': [ + '', + [ + 'arn:aws:execute-api:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':', + { 'Fn::ImportValue': `${stackName}-ApiGatewayRestApi` }, + '/*/*' + ] ] - ] - }; + }; + } // Add dependency on function version permission.DependsOn = [ versionName, aliasName ]; From 5756f32ef8223592e73741cec590ed9793590989 Mon Sep 17 00:00:00 2001 From: Frank Schmid Date: Mon, 29 May 2017 20:54:15 +0200 Subject: [PATCH 2/2] Added unit test --- test/stackops/init.test.js | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/stackops/init.test.js diff --git a/test/stackops/init.test.js b/test/stackops/init.test.js new file mode 100644 index 0000000..451257b --- /dev/null +++ b/test/stackops/init.test.js @@ -0,0 +1,69 @@ +'use strict'; +/** + * Unit tests for initialization. + */ + +const getInstalledPath = require('get-installed-path'); +const BbPromise = require('bluebird'); +const chai = require('chai'); +const sinon = require('sinon'); +const AWSAlias = require('../../index'); + +const serverlessPath = getInstalledPath.sync('serverless', { local: true }); +const AwsProvider = require(`${serverlessPath}/lib/plugins/aws/provider/awsProvider`); +const Serverless = require(`${serverlessPath}/lib/Serverless`); + +chai.use(require('chai-as-promised')); +chai.use(require('sinon-chai')); +const expect = chai.expect; + +describe('SNS Events', () => { + let serverless; + let options; + let awsAlias; + // Sinon and stubs for SLS CF access + let sandbox; + let logStub; + + before(() => { + sandbox = sinon.sandbox.create(); + }); + + beforeEach(() => { + options = { + alias: 'myAlias', + stage: 'myStage', + region: 'us-east-1', + }; + serverless = new Serverless(options); + serverless.setProvider('aws', new AwsProvider(serverless)); + serverless.cli = new serverless.classes.CLI(serverless); + serverless.service.service = 'testService'; + serverless.service.provider.compiledCloudFormationAliasTemplate = {}; + awsAlias = new AWSAlias(serverless, options); + + // Disable logging + logStub = sandbox.stub(serverless.cli, 'log'); + logStub.returns(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('#aliasInit()', () => { + it('should set alias flags', () => { + serverless.service.provider.compiledCloudFormationTemplate = require('../data/sls-stack-1.json'); + const aliasStack = serverless.service.provider.compiledCloudFormationAliasTemplate = require('../data/alias-stack-1.json'); + return expect(awsAlias.aliasInit({}, [], {})).to.be.fulfilled + .then(() => BbPromise.all([ + expect(aliasStack).to.have.property('Outputs') + .that.has.property('AliasFlags') + .that.deep.equals({ + Description: 'Alias flags.', + Value: { hasRole: false } + }) + ])); + }); + }); +});