From 776832d10f7ad27dea7c6bb1926eea8dcfcea337 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Fri, 26 May 2017 15:21:20 +0200 Subject: [PATCH] Fix CORS origin config --- docs/providers/aws/events/apigateway.md | 3 +-- .../compile/events/apiGateway/lib/cors.js | 8 ++++++- .../events/apiGateway/lib/cors.test.js | 16 ++++++++----- .../apiGateway/lib/method/index.test.js | 5 ++-- .../apiGateway/lib/method/integration.js | 8 ++++++- .../events/apiGateway/lib/method/responses.js | 8 ++++++- .../compile/events/apiGateway/lib/validate.js | 11 +++++++++ .../events/apiGateway/lib/validate.test.js | 23 +++++++++++++++++++ 8 files changed, 68 insertions(+), 14 deletions(-) diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md index e4e5385b1..da85ecf36 100644 --- a/docs/providers/aws/events/apigateway.md +++ b/docs/providers/aws/events/apigateway.md @@ -115,8 +115,7 @@ functions: path: hello method: get cors: - origins: - - '*' + origin: '*' headers: - Content-Type - X-Amz-Date diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js index 3ea3f00f3..cb8a5fd91 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js @@ -12,8 +12,14 @@ module.exports = { const corsMethodLogicalId = this.provider.naming .getMethodLogicalId(resourceName, 'options'); + // TODO remove once "origins" config is deprecated + let origin = config.origin; + if (config.origins && config.origins.length) { + origin = config.origins.join(','); + } + const preflightHeaders = { - 'Access-Control-Allow-Origin': `'${config.origins.join(',')}'`, + 'Access-Control-Allow-Origin': `'${origin}'`, 'Access-Control-Allow-Headers': `'${config.headers.join(',')}'`, 'Access-Control-Allow-Methods': `'${config.methods.join(',')}'`, 'Access-Control-Allow-Credentials': `'${config.allowCredentials}'`, diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js index 6f06b7218..1b77b46a9 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js @@ -54,13 +54,13 @@ describe('#compileCors()', () => { it('should create preflight method for CORS enabled resource', () => { awsCompileApigEvents.validated.corsPreflight = { 'users/update': { - origins: ['*'], + origin: 'http://example.com', headers: ['*'], methods: ['OPTIONS', 'PUT'], allowCredentials: false, }, 'users/create': { - origins: ['*'], + origins: ['*', 'http://example.com'], headers: ['*'], methods: ['OPTIONS', 'POST'], allowCredentials: true, @@ -72,19 +72,20 @@ describe('#compileCors()', () => { allowCredentials: false, }, 'users/any': { - origins: ['*'], + origins: ['http://example.com'], headers: ['*'], methods: ['OPTIONS', 'ANY'], allowCredentials: false, }, }; return awsCompileApigEvents.compileCors().then(() => { + // users/create expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersCreateOptions .Properties.Integration.IntegrationResponses[0] .ResponseParameters['method.response.header.Access-Control-Allow-Origin'] - ).to.equal('\'*\''); + ).to.equal('\'*,http://example.com\''); expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate @@ -107,12 +108,13 @@ describe('#compileCors()', () => { .ResponseParameters['method.response.header.Access-Control-Allow-Credentials'] ).to.equal('\'true\''); + // users/update expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersUpdateOptions .Properties.Integration.IntegrationResponses[0] .ResponseParameters['method.response.header.Access-Control-Allow-Origin'] - ).to.equal('\'*\''); + ).to.equal('\'http://example.com\''); expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate @@ -128,6 +130,7 @@ describe('#compileCors()', () => { .ResponseParameters['method.response.header.Access-Control-Allow-Credentials'] ).to.equal('\'false\''); + // users/delete expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersDeleteOptions @@ -156,12 +159,13 @@ describe('#compileCors()', () => { .ResponseParameters['method.response.header.Access-Control-Allow-Credentials'] ).to.equal('\'false\''); + // users/any expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersAnyOptions .Properties.Integration.IntegrationResponses[0] .ResponseParameters['method.response.header.Access-Control-Allow-Origin'] - ).to.equal('\'*\''); + ).to.equal('\'http://example.com\''); expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js index da991486d..c81ed7edb 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js @@ -592,7 +592,7 @@ describe('#compileMethods()', () => { method: 'post', integration: 'AWS', cors: { - origins: ['*'], + origin: 'http://example.com', }, response: { statusCodes: { @@ -638,13 +638,12 @@ describe('#compileMethods()', () => { }, ]; return awsCompileApigEvents.compileMethods().then(() => { - // Check origin. expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersCreatePost.Properties .Integration.IntegrationResponses[0] .ResponseParameters['method.response.header.Access-Control-Allow-Origin'] - ).to.equal('\'*\''); + ).to.equal('\'http://example.com\''); // CORS not enabled! expect( diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js index fa67bb8ba..28aa0d7a7 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js @@ -102,8 +102,14 @@ module.exports = { const integrationResponseHeaders = []; if (http.cors) { + // TODO remove once "origins" config is deprecated + let origin = http.cors.origin; + if (http.cors.origins && http.cors.origins.length) { + origin = http.cors.origins.join(','); + } + _.merge(integrationResponseHeaders, { - 'Access-Control-Allow-Origin': `'${http.cors.origins.join(',')}'`, + 'Access-Control-Allow-Origin': `'${origin}'`, }); } diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js index c039b46fc..5d57455e1 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js @@ -12,8 +12,14 @@ module.exports = { const methodResponseHeaders = []; if (http.cors) { + // TODO remove once "origins" config is deprecated + let origin = http.cors.origin; + if (http.cors.origins && http.cors.origins.length) { + origin = http.cors.origins.join(','); + } + _.merge(methodResponseHeaders, { - 'Access-Control-Allow-Origin': `'${http.cors.origins.join(',')}'`, + 'Access-Control-Allow-Origin': `'${origin}'`, }); } diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js index 682502c1e..2cbbfe924 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js @@ -58,6 +58,7 @@ module.exports = { cors.headers = _.union(http.cors.headers, cors.headers); cors.methods = _.union(http.cors.methods, cors.methods); cors.origins = _.union(http.cors.origins, cors.origins); + cors.origin = http.cors.origin || '*'; cors.allowCredentials = cors.allowCredentials || http.cors.allowCredentials; corsPreflight[http.path] = cors; @@ -274,6 +275,7 @@ module.exports = { let cors = { origins: ['*'], + origin: '*', methods: ['OPTIONS'], headers, allowCredentials: false, @@ -284,6 +286,15 @@ module.exports = { cors.methods = cors.methods || []; cors.allowCredentials = Boolean(cors.allowCredentials); + if (cors.origins && cors.origin) { + const errorMessage = [ + 'You can only use "origin" or "origins",', + ' but not both at the same time to configure CORS.', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + } + if (cors.headers) { if (!Array.isArray(cors.headers)) { const errorMessage = [ diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js index fcbbb1e4d..7ead5365f 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js @@ -415,6 +415,28 @@ describe('#validate()', () => { expect(authorizer.identityValidationExpression).to.equal('foo'); }); + it('should throw an error if "origin" and "origins" CORS config is used', () => { + awsCompileApigEvents.serverless.service.functions = { + first: { + events: [ + { + http: { + method: 'POST', + path: '/foo/bar', + cors: { + origin: '*', + origins: ['*'], + }, + }, + }, + ], + }, + }; + + expect(() => awsCompileApigEvents.validate()) + .to.throw(Error, 'can only use'); + }); + it('should process cors defaults', () => { awsCompileApigEvents.serverless.service.functions = { first: { @@ -436,6 +458,7 @@ describe('#validate()', () => { headers: ['Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key', 'X-Amz-Security-Token', 'X-Amz-User-Agent'], methods: ['OPTIONS', 'POST'], + origin: '*', origins: ['*'], allowCredentials: false, });