From 41f228d620ee263c9795e08b6e270a756e06641a Mon Sep 17 00:00:00 2001 From: Michael Willemse Date: Sat, 9 Jun 2018 01:49:26 +0200 Subject: [PATCH 1/5] https://github.com/awslabs/serverless-application-model/issues/463: progress commit to support Access-Control-Allow-Credentials and Access-Control-Expose-Headers --- samtranslator/model/api/api_generator.py | 7 +- samtranslator/swagger/swagger.py | 29 +++++- tests/swagger/test_swagger.py | 124 ++++++++++++++++++++--- versions/2016-10-31.md | 6 ++ 4 files changed, 145 insertions(+), 21 deletions(-) diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index cb2a195ebe..0be254dbd1 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -11,9 +11,9 @@ from samtranslator.model.intrinsics import is_instrinsic _CORS_WILDCARD = "'*'" -CorsProperties = namedtuple("_CorsProperties", ["AllowMethods", "AllowHeaders", "AllowOrigin", "MaxAge"]) +CorsProperties = namedtuple("_CorsProperties", ["AllowMethods", "AllowHeaders", "AllowOrigin", "MaxAge", "ExposeHeaders", "AllowCredentials"]) # Default the Cors Properties to '*' wildcard. Other properties are actually Optional -CorsProperties.__new__.__defaults__ = (None, None, _CORS_WILDCARD, None) +CorsProperties.__new__.__defaults__ = (None, None, _CORS_WILDCARD, None, None, False) class ApiGenerator(object): @@ -206,7 +206,8 @@ def _add_cors(self): editor = SwaggerEditor(self.definition_body) for path in editor.iter_on_path(): editor.add_cors(path, properties.AllowOrigin, properties.AllowHeaders, properties.AllowMethods, - max_age=properties.MaxAge) + max_age=properties.MaxAge, exposed_headers=properties.ExposeHeaders, + allow_credentials=properties.AllowCredentials) # Assign the Swagger back to template self.definition_body = editor.swagger diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index eb49cdbf59..18e435c8c0 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -16,6 +16,7 @@ class SwaggerEditor(object): _X_APIGW_INTEGRATION = 'x-amazon-apigateway-integration' _X_ANY_METHOD = 'x-amazon-apigateway-any-method' + def __init__(self, doc): """ Initialize the class with a swagger dictionary. This class creates a copy of the Swagger and performs all @@ -117,7 +118,8 @@ def iter_on_path(self): for path, value in self.paths.items(): yield path - def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods=None, max_age=None): + def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods=None, max_age=None, + exposed_headers=None, allow_credentials=None): """ Add CORS configuration to this path. Specifically, we will add a OPTIONS response config to the Swagger that will return headers required for CORS. Since SAM uses aws_proxy integration, we cannot inject the headers @@ -138,6 +140,9 @@ def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods= Value can also be an intrinsic function dict. :param integer/dict max_age: Maximum duration to cache the CORS Preflight request. Value is set on Access-Control-Max-Age header. Value can also be an intrinsic function dict. + :param string/dict exposed_headers: Comma separated list of exposed headers. + Value can also be an instrinsic function dict. + :param bool/None allow_credentials: Flags whether request is allowed to contain credentials. :raises ValueError: When values for one of the allowed_* variables is empty """ @@ -155,15 +160,20 @@ def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods= # APIGW expects the value to be a "string expression". Hence wrap in another quote. Ex: "'GET,POST,DELETE'" allowed_methods = "'{}'".format(allowed_methods) + if allow_credentials is not True: + allow_credentials = False + # Add the Options method and the CORS response self.add_path(path, self._OPTIONS_METHOD) self.paths[path][self._OPTIONS_METHOD] = self._options_method_response_for_cors(allowed_origins, allowed_headers, allowed_methods, - max_age) + max_age, + exposed_headers, + allow_credentials) def _options_method_response_for_cors(self, allowed_origins, allowed_headers=None, allowed_methods=None, - max_age=None): + max_age=None, exposed_headers=None, allow_credentials=None): """ Returns a Swagger snippet containing configuration for OPTIONS HTTP Method to configure CORS. @@ -178,6 +188,9 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non Value can also be an intrinsic function dict. :param integer/dict max_age: Maximum duration to cache the CORS Preflight request. Value is set on Access-Control-Max-Age header. Value can also be an intrinsic function dict. + :param string/dict exposed_headers: Comma separated list of exposed headers. + Value can also be an instrinsic function dict. + :param bool allow_credentials: Flags whether request is allowed to contain credentials. :return dict: Dictionary containing Options method configuration for CORS """ @@ -186,6 +199,8 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non ALLOW_HEADERS = "Access-Control-Allow-Headers" ALLOW_METHODS = "Access-Control-Allow-Methods" MAX_AGE = "Access-Control-Max-Age" + EXPOSE_HEADERS = "Access-Control-Expose-Headers" + ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials" HEADER_RESPONSE = lambda x: "method.response.header."+x response_parameters = { @@ -216,6 +231,14 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non # MaxAge can be set to 0, which is a valid value. So explicitly check against None response_parameters[HEADER_RESPONSE(MAX_AGE)] = max_age response_headers[MAX_AGE] = {"type": "integer"} + if exposed_headers: + response_parameters[HEADER_RESPONSE(EXPOSE_HEADERS)] = exposed_headers + response_headers[EXPOSE_HEADERS] = {"type": "string"} + if allow_credentials is True: + # Allow-Credentials only has a valid value of true, it should be omitted otherwise. + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials + response_parameters[HEADER_RESPONSE(ALLOW_CREDENTIALS)] = 'true' + response_headers[ALLOW_CREDENTIALS] = {"type": "string"} return { "summary": "CORS support", diff --git a/tests/swagger/test_swagger.py b/tests/swagger/test_swagger.py index 9aa61a0b68..5a8c9db394 100644 --- a/tests/swagger/test_swagger.py +++ b/tests/swagger/test_swagger.py @@ -308,18 +308,23 @@ def test_must_add_options_to_new_path(self): allowed_headers = ["headers", "2"] allowed_methods = {"key": "methods"} max_age = 60 + exposed_headers = None + allow_credentials = False path = "/foo" expected = {"some cors": "return value"} self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, + allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, allowed_headers, allowed_methods, - max_age) + max_age, + exposed_headers, + allow_credentials) def test_must_skip_existing_path(self): path = "/withoptions" @@ -346,6 +351,9 @@ def test_must_work_for_optional_allowed_headers(self): allowed_headers = None # No Value allowed_methods = "methods" max_age = 60 + exposed_headers = None + allow_credentials = None + options_method_response_allow_credentials = False expected = {"some cors": "return value"} path = "/foo" @@ -353,14 +361,17 @@ def test_must_work_for_optional_allowed_headers(self): self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, + allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, allowed_headers, allowed_methods, - max_age) + max_age, + exposed_headers, + options_method_response_allow_credentials) def test_must_make_default_value_with_optional_allowed_methods(self): @@ -368,6 +379,9 @@ def test_must_make_default_value_with_optional_allowed_methods(self): allowed_headers = "headers" allowed_methods = None # No Value max_age = 60 + exposed_headers = None + allow_credentials = None + options_method_response_allow_credentials = False default_allow_methods_value = "some default value" default_allow_methods_value_with_quotes = "'{}'".format(default_allow_methods_value) @@ -380,7 +394,8 @@ def test_must_make_default_value_with_optional_allowed_methods(self): self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, + allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) @@ -389,7 +404,9 @@ def test_must_make_default_value_with_optional_allowed_methods(self): # Must be called with default value. # And value must be quoted default_allow_methods_value_with_quotes, - max_age) + max_age, + exposed_headers, + options_method_response_allow_credentials) class TestSwaggerEditor_options_method_response_for_cors(TestCase): @@ -400,6 +417,9 @@ def test_correct_value_is_returned(self): methods = {"a": "b"} origins = [1,2,3] max_age = 60 + exposed_headers = "x-cape" + allow_credentials = True + allow_credentials_value = "true" expected = { "summary": "CORS support", @@ -414,10 +434,12 @@ def test_correct_value_is_returned(self): "default": { "statusCode": "200", "responseParameters": { + "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, - "method.response.header.Access-Control-Max-Age": max_age + "method.response.header.Access-Control-Expose-Headers": exposed_headers, + "method.response.header.Access-Control-Max-Age": max_age, }, "responseTemplates": { "application/json": "{}\n" @@ -429,6 +451,9 @@ def test_correct_value_is_returned(self): "200": { "description": "Default response for CORS method", "headers": { + "Access-Control-Allow-Credentials": { + "type": "string" + }, "Access-Control-Allow-Headers": { "type": "string" }, @@ -438,6 +463,9 @@ def test_correct_value_is_returned(self): "Access-Control-Allow-Origin": { "type": "string" }, + "Access-Control-Expose-Headers": { + "type": "string" + }, "Access-Control-Max-Age": { "type": "integer" } @@ -446,30 +474,44 @@ def test_correct_value_is_returned(self): } } - actual = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors(origins, headers, methods, max_age) + actual = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors(origins, headers, + methods, max_age, + exposed_headers, + allow_credentials) self.assertEquals(expected, actual) def test_allow_headers_is_skipped_with_no_value(self): headers = None # No value methods = "methods" origins = "origins" + exposed_headers = "x-cape" + allow_credentials = True + allow_credentials_value = "true" expected = { + "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, "method.response.header.Access-Control-Allow-Methods": methods, - "method.response.header.Access-Control-Allow-Origin": origins + "method.response.header.Access-Control-Allow-Origin": origins, + "method.response.header.Access-Control-Expose-Headers": exposed_headers, } expected_headers = { + "Access-Control-Allow-Credentials": { + "type": "string" + }, "Access-Control-Allow-Methods": { "type": "string" }, "Access-Control-Allow-Origin": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" } } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods) + origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -479,14 +521,19 @@ def test_allow_methods_is_skipped_with_no_value(self): headers = "headers" methods = None # No value origins = "origins" + exposed_headers = "x-cape" + allow_credentials = True + allow_credentials_value = "true" expected = { + "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, "method.response.header.Access-Control-Allow-Headers": headers, - "method.response.header.Access-Control-Allow-Origin": origins + "method.response.header.Access-Control-Allow-Origin": origins, + "method.response.header.Access-Control-Expose-Headers": exposed_headers, } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods) + origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -495,6 +542,8 @@ def test_allow_origins_is_not_skipped_with_no_value(self): headers = None methods = None origins = None + exposed_headers = None + allow_credentials = False expected = { # We will ALWAYS set AllowOrigin. This is a minimum requirement for CORS @@ -502,7 +551,7 @@ def test_allow_origins_is_not_skipped_with_no_value(self): } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods) + origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -512,19 +561,64 @@ def test_max_age_can_be_set_to_zero(self): methods = "methods" origins = "origins" max_age = 0 + exposed_headers = "x-cape" + allow_credentials = True + allow_credentials_value = "true" expected = { + "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, - "method.response.header.Access-Control-Max-Age": max_age + "method.response.header.Access-Control-Max-Age": max_age, + "method.response.header.Access-Control-Expose-Headers": exposed_headers } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, max_age) + origins, headers, methods, max_age, exposed_headers, allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) + def test_expose_headers_is_skipped_with_no_value(self): + headers = "headers" + methods = "methods" + origins = "origins" + exposed_headers = None # No value + allow_credentials = True + allow_credentials_value = "true" + + expected = { + "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, + "method.response.header.Access-Control-Allow-Headers": headers, + "method.response.header.Access-Control-Allow-Methods": methods, + "method.response.header.Access-Control-Allow-Origin": origins, + } + + options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( + origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + + actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] + self.assertEquals(expected, actual) + + def test_allow_credentials_is_skipped_with_false_value(self): + headers = "headers" + methods = "methods" + origins = "origins" + exposed_headers = "x-cape" + allow_credentials = False + + expected = { + "method.response.header.Access-Control-Allow-Headers": headers, + "method.response.header.Access-Control-Allow-Methods": methods, + "method.response.header.Access-Control-Allow-Origin": origins, + "method.response.header.Access-Control-Expose-Headers": exposed_headers + } + + options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( + origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + + actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] + self.assertEquals(expected, actual) class TestSwaggerEditor_make_cors_allowed_methods_for_path(TestCase): diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index f29251d4a4..de17febe2e 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -625,6 +625,12 @@ Cors: MaxAge: Optional. String containing the number of seconds to cache CORS Preflight request. # For example, "'600'" will cache request for 600 seconds. Checkout [HTTP Spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age) for more details on this value + + ExposeHeaders: Optional. String of headers that can be exposed. + # For example, "'Content-Length,X-Revision'". Checkout [HTTP Spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) for more details on this value. + + AllowCredentials: Optional. Boolean indicating whether request is allowed to contain credentials. + # Header is omitted when false. Checkout [HTTP Spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials) for more details on this value. ``` > NOTE: HTTP spec requires the value of Allow properties to be a quoted string. So don't forget the additional quotes in the value. ie. "'www.example.com'" is correct whereas "www.example.com" is wrong From a972bd748af148bddafe453f6373271f46e80825 Mon Sep 17 00:00:00 2001 From: Michael Willemse Date: Sat, 9 Jun 2018 16:27:18 +0200 Subject: [PATCH 2/5] https://github.com/awslabs/serverless-application-model/issues/463: Set allow credentials output to "'true'" Added additional tests for the add_cors method --- samtranslator/swagger/swagger.py | 2 +- tests/swagger/test_swagger.py | 82 ++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index 18e435c8c0..0b6b751c34 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -237,7 +237,7 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non if allow_credentials is True: # Allow-Credentials only has a valid value of true, it should be omitted otherwise. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials - response_parameters[HEADER_RESPONSE(ALLOW_CREDENTIALS)] = 'true' + response_parameters[HEADER_RESPONSE(ALLOW_CREDENTIALS)] = "'true'" response_headers[ALLOW_CREDENTIALS] = {"type": "string"} return { diff --git a/tests/swagger/test_swagger.py b/tests/swagger/test_swagger.py index 5a8c9db394..808e1e86d2 100644 --- a/tests/swagger/test_swagger.py +++ b/tests/swagger/test_swagger.py @@ -8,6 +8,7 @@ _X_INTEGRATION = "x-amazon-apigateway-integration" _X_ANY_METHOD = 'x-amazon-apigateway-any-method' +_ALLOW_CREDENTALS_TRUE = "'true'" class TestSwaggerEditor_init(TestCase): @@ -308,8 +309,9 @@ def test_must_add_options_to_new_path(self): allowed_headers = ["headers", "2"] allowed_methods = {"key": "methods"} max_age = 60 - exposed_headers = None - allow_credentials = False + exposed_headers = {"headers", "2"} + allow_credentials = True + options_method_response_allow_credentials = True path = "/foo" expected = {"some cors": "return value"} @@ -324,7 +326,7 @@ def test_must_add_options_to_new_path(self): allowed_methods, max_age, exposed_headers, - allow_credentials) + options_method_response_allow_credentials) def test_must_skip_existing_path(self): path = "/withoptions" @@ -351,9 +353,9 @@ def test_must_work_for_optional_allowed_headers(self): allowed_headers = None # No Value allowed_methods = "methods" max_age = 60 - exposed_headers = None - allow_credentials = None - options_method_response_allow_credentials = False + exposed_headers = {"headers", "2"} + allow_credentials = True + options_method_response_allow_credentials = True expected = {"some cors": "return value"} path = "/foo" @@ -379,9 +381,9 @@ def test_must_make_default_value_with_optional_allowed_methods(self): allowed_headers = "headers" allowed_methods = None # No Value max_age = 60 - exposed_headers = None - allow_credentials = None - options_method_response_allow_credentials = False + exposed_headers = "headers" + allow_credentials = True + options_method_response_allow_credentials = True default_allow_methods_value = "some default value" default_allow_methods_value_with_quotes = "'{}'".format(default_allow_methods_value) @@ -407,6 +409,53 @@ def test_must_make_default_value_with_optional_allowed_methods(self): max_age, exposed_headers, options_method_response_allow_credentials) + def test_must_accept_none_exposed_headers(self): + allowed_origins = "origins" + allowed_headers = ["headers", "2"] + allowed_methods = {"key": "methods"} + max_age = 60 + exposed_headers = None + allow_credentials = True + options_method_response_allow_credentials = True + path = "/foo" + expected = {"some cors": "return value"} + + self.editor._options_method_response_for_cors = Mock() + self.editor._options_method_response_for_cors.return_value = expected + + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, + allow_credentials) + self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) + self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, + allowed_headers, + allowed_methods, + max_age, + exposed_headers, + options_method_response_allow_credentials) + + def test_must_accept_none_allow_credentials(self): + allowed_origins = "origins" + allowed_headers = ["headers", "2"] + allowed_methods = {"key": "methods"} + max_age = 60 + exposed_headers = {"headers", "2"} + allow_credentials = None + options_method_response_allow_credentials = False + path = "/foo" + expected = {"some cors": "return value"} + + self.editor._options_method_response_for_cors = Mock() + self.editor._options_method_response_for_cors.return_value = expected + + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, + allow_credentials) + self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) + self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, + allowed_headers, + allowed_methods, + max_age, + exposed_headers, + options_method_response_allow_credentials) class TestSwaggerEditor_options_method_response_for_cors(TestCase): @@ -419,7 +468,6 @@ def test_correct_value_is_returned(self): max_age = 60 exposed_headers = "x-cape" allow_credentials = True - allow_credentials_value = "true" expected = { "summary": "CORS support", @@ -434,7 +482,7 @@ def test_correct_value_is_returned(self): "default": { "statusCode": "200", "responseParameters": { - "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, + "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, @@ -486,10 +534,9 @@ def test_allow_headers_is_skipped_with_no_value(self): origins = "origins" exposed_headers = "x-cape" allow_credentials = True - allow_credentials_value = "true" expected = { - "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, + "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, "method.response.header.Access-Control-Expose-Headers": exposed_headers, @@ -523,10 +570,9 @@ def test_allow_methods_is_skipped_with_no_value(self): origins = "origins" exposed_headers = "x-cape" allow_credentials = True - allow_credentials_value = "true" expected = { - "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, + "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Origin": origins, "method.response.header.Access-Control-Expose-Headers": exposed_headers, @@ -563,10 +609,9 @@ def test_max_age_can_be_set_to_zero(self): max_age = 0 exposed_headers = "x-cape" allow_credentials = True - allow_credentials_value = "true" expected = { - "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, + "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, "method.response.header.Access-Control-Max-Age": max_age, @@ -585,10 +630,9 @@ def test_expose_headers_is_skipped_with_no_value(self): origins = "origins" exposed_headers = None # No value allow_credentials = True - allow_credentials_value = "true" expected = { - "method.response.header.Access-Control-Allow-Credentials": allow_credentials_value, + "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, From 44ccafe5032697aba79a2b0a377910eeeafed4e3 Mon Sep 17 00:00:00 2001 From: Michael Willemse Date: Mon, 11 Jun 2018 00:01:35 +0200 Subject: [PATCH 3/5] added test_translator.py input/output test cases for Allow-Credentials and Expose-Headers CORS implementation. --- tests/translator/input/api_with_cors.yaml | 2 + .../api_with_cors_and_only_credentials.yaml | 49 +++ ..._with_cors_and_only_credentials_false.yaml | 49 +++ ...api_with_cors_and_only_expose_headers.yaml | 70 +++ tests/translator/output/api_with_cors.json | 26 +- .../api_with_cors_and_only_credentials.json | 154 +++++++ ..._with_cors_and_only_credentials_false.json | 146 +++++++ ...api_with_cors_and_only_expose_headers.json | 389 +++++++++++++++++ .../output/aws-cn/api_with_cors.json | 26 +- .../api_with_cors_and_only_credentials.json | 162 +++++++ ..._with_cors_and_only_credentials_false.json | 154 +++++++ ...api_with_cors_and_only_expose_headers.json | 405 ++++++++++++++++++ .../output/aws-us-gov/api_with_cors.json | 26 +- .../api_with_cors_and_only_credentials.json | 162 +++++++ ..._with_cors_and_only_credentials_false.json | 154 +++++++ ...api_with_cors_and_only_expose_headers.json | 405 ++++++++++++++++++ tests/translator/test_translator.py | 3 + 17 files changed, 2367 insertions(+), 15 deletions(-) create mode 100644 tests/translator/input/api_with_cors_and_only_credentials.yaml create mode 100644 tests/translator/input/api_with_cors_and_only_credentials_false.yaml create mode 100644 tests/translator/input/api_with_cors_and_only_expose_headers.yaml create mode 100644 tests/translator/output/api_with_cors_and_only_credentials.json create mode 100644 tests/translator/output/api_with_cors_and_only_credentials_false.json create mode 100644 tests/translator/output/api_with_cors_and_only_expose_headers.json create mode 100644 tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json create mode 100644 tests/translator/output/aws-cn/api_with_cors_and_only_credentials_false.json create mode 100644 tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json create mode 100644 tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json create mode 100644 tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials_false.json create mode 100644 tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json diff --git a/tests/translator/input/api_with_cors.yaml b/tests/translator/input/api_with_cors.yaml index 0dc706076c..9fbae6bc99 100644 --- a/tests/translator/input/api_with_cors.yaml +++ b/tests/translator/input/api_with_cors.yaml @@ -66,3 +66,5 @@ Resources: AllowMethods: "methods" AllowHeaders: "headers" AllowOrigin: "origins" + ExposeHeaders: "headers" + AllowCredentials: true diff --git a/tests/translator/input/api_with_cors_and_only_credentials.yaml b/tests/translator/input/api_with_cors_and_only_credentials.yaml new file mode 100644 index 0000000000..d864ead1ca --- /dev/null +++ b/tests/translator/input/api_with_cors_and_only_credentials.yaml @@ -0,0 +1,49 @@ +Globals: + Api: + Cors: + AllowCredentials: true + +Resources: + + ExplicitApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionBody: { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + }, + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + + + diff --git a/tests/translator/input/api_with_cors_and_only_credentials_false.yaml b/tests/translator/input/api_with_cors_and_only_credentials_false.yaml new file mode 100644 index 0000000000..d04e949c3a --- /dev/null +++ b/tests/translator/input/api_with_cors_and_only_credentials_false.yaml @@ -0,0 +1,49 @@ +Globals: + Api: + Cors: + AllowCredentials: false + +Resources: + + ExplicitApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionBody: { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + }, + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + + + diff --git a/tests/translator/input/api_with_cors_and_only_expose_headers.yaml b/tests/translator/input/api_with_cors_and_only_expose_headers.yaml new file mode 100644 index 0000000000..6b2146df4a --- /dev/null +++ b/tests/translator/input/api_with_cors_and_only_expose_headers.yaml @@ -0,0 +1,70 @@ +Globals: + Api: + Cors: + + # If we skip AllowMethods, then SAM will auto generate a list of methods scoped to each path + ExposeHeaders: "headers" + +Resources: + ImplicitApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/member_portal.zip + Handler: index.gethtml + Runtime: nodejs4.3 + Events: + GetHtml: + Type: Api + Properties: + Path: / + Method: get + + PostHtml: + Type: Api + Properties: + Path: / + Method: post + + + ExplicitApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionBody: { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + }, + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + + + diff --git a/tests/translator/output/api_with_cors.json b/tests/translator/output/api_with_cors.json index 0791fc4f4f..f1a0cf5003 100644 --- a/tests/translator/output/api_with_cors.json +++ b/tests/translator/output/api_with_cors.json @@ -103,7 +103,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "ExplicitApiDeployment673f662a8e" + "Ref": "ExplicitApiDeploymente1a19134ca" }, "RestApiId": { "Ref": "ExplicitApi" @@ -160,7 +160,9 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", - "method.response.header.Access-Control-Allow-Headers": "headers" + "method.response.header.Access-Control-Allow-Headers": "headers", + "method.response.header.Access-Control-Expose-Headers": "headers", + "method.response.header.Access-Control-Allow-Credentials": "'true'" } } } @@ -180,6 +182,12 @@ }, "Access-Control-Allow-Methods": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" } }, "description": "Default response for CORS method" @@ -206,7 +214,9 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", - "method.response.header.Access-Control-Allow-Headers": "headers" + "method.response.header.Access-Control-Allow-Headers": "headers", + "method.response.header.Access-Control-Expose-Headers": "headers", + "method.response.header.Access-Control-Allow-Credentials": "'true'" } } } @@ -226,6 +236,12 @@ }, "Access-Control-Allow-Methods": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" } }, "description": "Default response for CORS method" @@ -272,13 +288,13 @@ } } }, - "ExplicitApiDeployment673f662a8e": { + "ExplicitApiDeploymente1a19134ca": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "ExplicitApi" }, - "Description": "RestApi deployment id: 673f662a8ed9f253ae8dacab409db551a72cd5b2", + "Description": "RestApi deployment id: e1a19134ca6f0d6afe2079a3ff60176078baa467", "StageName": "Stage" } }, diff --git a/tests/translator/output/api_with_cors_and_only_credentials.json b/tests/translator/output/api_with_cors_and_only_credentials.json new file mode 100644 index 0000000000..72d19110ce --- /dev/null +++ b/tests/translator/output/api_with_cors_and_only_credentials.json @@ -0,0 +1,154 @@ +{ + "Resources": { + "ExplicitApiDeploymentfb09e6b93d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: fb09e6b93d08b6b20bfd62ed420c23416f41280d", + "StageName": "Stage" + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentfb09e6b93d" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", + "method.response.header.Access-Control-Allow-Credentials": "'true'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Credentials": "'true'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_cors_and_only_credentials_false.json b/tests/translator/output/api_with_cors_and_only_credentials_false.json new file mode 100644 index 0000000000..4e1658dba8 --- /dev/null +++ b/tests/translator/output/api_with_cors_and_only_credentials_false.json @@ -0,0 +1,146 @@ +{ + "Resources": { + "ExplicitApiDeployment398246867a": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 398246867a6d5535e40b46e224e8998486a4b9eb", + "StageName": "Stage" + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeployment398246867a" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_cors_and_only_expose_headers.json b/tests/translator/output/api_with_cors_and_only_expose_headers.json new file mode 100644 index 0000000000..e4f12ff47a --- /dev/null +++ b/tests/translator/output/api_with_cors_and_only_expose_headers.json @@ -0,0 +1,389 @@ +{ + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Handler": "index.gethtml", + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs4.3", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ImplicitApiFunctionPostHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeployment2e88efef7e" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentb1d8a88486" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + } + }, + "ExplicitApiDeployment2e88efef7e": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 2e88efef7e1035a3538562fca85d22395cb8fb3f", + "StageName": "Stage" + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ImplicitApiFunctionPostHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ServerlessRestApiDeploymentb1d8a88486": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: b1d8a88486a429400e02f3eb2c8897a15bfd0dbf", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS,POST'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_cors.json b/tests/translator/output/aws-cn/api_with_cors.json index e3d3f3a3fa..fdd437e166 100644 --- a/tests/translator/output/aws-cn/api_with_cors.json +++ b/tests/translator/output/aws-cn/api_with_cors.json @@ -126,7 +126,9 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", - "method.response.header.Access-Control-Allow-Headers": "headers" + "method.response.header.Access-Control-Allow-Headers": "headers", + "method.response.header.Access-Control-Expose-Headers": "headers", + "method.response.header.Access-Control-Allow-Credentials": "'true'" } } } @@ -146,6 +148,12 @@ }, "Access-Control-Allow-Methods": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" } }, "description": "Default response for CORS method" @@ -172,7 +180,9 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", - "method.response.header.Access-Control-Allow-Headers": "headers" + "method.response.header.Access-Control-Allow-Headers": "headers", + "method.response.header.Access-Control-Expose-Headers": "headers", + "method.response.header.Access-Control-Allow-Credentials": "'true'" } } } @@ -192,6 +202,12 @@ }, "Access-Control-Allow-Methods": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" } }, "description": "Default response for CORS method" @@ -241,7 +257,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "ExplicitApiDeployment673f662a8e" + "Ref": "ExplicitApiDeploymente1a19134ca" }, "RestApiId": { "Ref": "ExplicitApi" @@ -270,13 +286,13 @@ } } }, - "ExplicitApiDeployment673f662a8e": { + "ExplicitApiDeploymente1a19134ca": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "ExplicitApi" }, - "Description": "RestApi deployment id: 673f662a8ed9f253ae8dacab409db551a72cd5b2", + "Description": "RestApi deployment id: e1a19134ca6f0d6afe2079a3ff60176078baa467", "StageName": "Stage" } }, diff --git a/tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json b/tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json new file mode 100644 index 0000000000..e5a56c7868 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json @@ -0,0 +1,162 @@ +{ + "Resources": { + "ExplicitApiDeploymentfb09e6b93d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: fb09e6b93d08b6b20bfd62ed420c23416f41280d", + "StageName": "Stage" + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentfb09e6b93d" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", + "method.response.header.Access-Control-Allow-Credentials": "'true'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Credentials": "'true'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_cors_and_only_credentials_false.json b/tests/translator/output/aws-cn/api_with_cors_and_only_credentials_false.json new file mode 100644 index 0000000000..06e79c17a3 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_cors_and_only_credentials_false.json @@ -0,0 +1,154 @@ +{ + "Resources": { + "ExplicitApiDeployment398246867a": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 398246867a6d5535e40b46e224e8998486a4b9eb", + "StageName": "Stage" + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeployment398246867a" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json b/tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json new file mode 100644 index 0000000000..af23c7cbb6 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json @@ -0,0 +1,405 @@ +{ + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Handler": "index.gethtml", + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs4.3", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ImplicitApiFunctionPostHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ServerlessRestApiDeploymentdbbf729d2b": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: dbbf729d2b5fe0b47621fb708a5dc6caddb9854b", + "StageName": "Stage" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentdbbf729d2b" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ExplicitApiDeployment2e88efef7e": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 2e88efef7e1035a3538562fca85d22395cb8fb3f", + "StageName": "Stage" + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ImplicitApiFunctionPostHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeployment2e88efef7e" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS,POST'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_cors.json b/tests/translator/output/aws-us-gov/api_with_cors.json index 3ddac18953..68af03d300 100644 --- a/tests/translator/output/aws-us-gov/api_with_cors.json +++ b/tests/translator/output/aws-us-gov/api_with_cors.json @@ -136,7 +136,9 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", - "method.response.header.Access-Control-Allow-Headers": "headers" + "method.response.header.Access-Control-Allow-Headers": "headers", + "method.response.header.Access-Control-Expose-Headers": "headers", + "method.response.header.Access-Control-Allow-Credentials": "'true'" } } } @@ -156,6 +158,12 @@ }, "Access-Control-Allow-Methods": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" } }, "description": "Default response for CORS method" @@ -182,7 +190,9 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", - "method.response.header.Access-Control-Allow-Headers": "headers" + "method.response.header.Access-Control-Allow-Headers": "headers", + "method.response.header.Access-Control-Expose-Headers": "headers", + "method.response.header.Access-Control-Allow-Credentials": "'true'" } } } @@ -202,6 +212,12 @@ }, "Access-Control-Allow-Methods": { "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" } }, "description": "Default response for CORS method" @@ -251,7 +267,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "ExplicitApiDeployment673f662a8e" + "Ref": "ExplicitApiDeploymente1a19134ca" }, "RestApiId": { "Ref": "ExplicitApi" @@ -280,13 +296,13 @@ } } }, - "ExplicitApiDeployment673f662a8e": { + "ExplicitApiDeploymente1a19134ca": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "ExplicitApi" }, - "Description": "RestApi deployment id: 673f662a8ed9f253ae8dacab409db551a72cd5b2", + "Description": "RestApi deployment id: e1a19134ca6f0d6afe2079a3ff60176078baa467", "StageName": "Stage" } }, diff --git a/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json b/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json new file mode 100644 index 0000000000..e5a56c7868 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json @@ -0,0 +1,162 @@ +{ + "Resources": { + "ExplicitApiDeploymentfb09e6b93d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: fb09e6b93d08b6b20bfd62ed420c23416f41280d", + "StageName": "Stage" + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeploymentfb09e6b93d" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", + "method.response.header.Access-Control-Allow-Credentials": "'true'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Credentials": "'true'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials_false.json b/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials_false.json new file mode 100644 index 0000000000..06e79c17a3 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials_false.json @@ -0,0 +1,154 @@ +{ + "Resources": { + "ExplicitApiDeployment398246867a": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 398246867a6d5535e40b46e224e8998486a4b9eb", + "StageName": "Stage" + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeployment398246867a" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json b/tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json new file mode 100644 index 0000000000..453c04f90b --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json @@ -0,0 +1,405 @@ +{ + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Handler": "index.gethtml", + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs4.3", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ServerlessRestApiDeploymentc020adc98a": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: c020adc98ab52b1180ff6477685a0d7b86ba6262", + "StageName": "Stage" + } + }, + "ImplicitApiFunctionPostHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ExplicitApiDeployment2e88efef7e" + }, + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentc020adc98a" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + } + }, + "/{proxy+}": { + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ExplicitApiDeployment2e88efef7e": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 2e88efef7e1035a3538562fca85d22395cb8fb3f", + "StageName": "Stage" + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ImplicitApiFunctionPostHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + }, + "options": { + "x-amazon-apigateway-integration": { + "type": "mock", + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{}\n" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS,POST'", + "method.response.header.Access-Control-Expose-Headers": "headers" + } + } + } + }, + "consumes": [ + "application/json" + ], + "summary": "CORS support", + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Expose-Headers": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + } + }, + "description": "Default response for CORS method" + } + }, + "produces": [ + "application/json" + ] + }, + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 40810b5b65..fb9ca151c5 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -60,6 +60,9 @@ class TestTranslatorEndToEnd(TestCase): 'api_with_cors_and_only_headers', 'api_with_cors_and_only_origins', 'api_with_cors_and_only_maxage', + 'api_with_cors_and_only_expose_headers', + 'api_with_cors_and_only_credentials', + 'api_with_cors_and_only_credentials_false', 'api_cache', 's3', 's3_create_remove', From d7581570d63a6c7a01b6083c7cbd66b0eead0cc4 Mon Sep 17 00:00:00 2001 From: Michael Willemse Date: Fri, 29 Jun 2018 01:34:11 +0200 Subject: [PATCH 4/5] Combining AllowCredentials with value true and AllowedOrigin with wildcard value ("'*'") now throws InvalidDocument --- samtranslator/model/api/api_generator.py | 7 +- ...credentials_true_with_wildcard_origin.yaml | 49 ++++++ ...entials_true_without_explicit_origin.yaml} | 0 .../api_with_cors_and_only_credentials.json | 154 ----------------- .../api_with_cors_and_only_credentials.json | 162 ------------------ .../api_with_cors_and_only_credentials.json | 162 ------------------ ...credentials_true_with_wildcard_origin.json | 8 + ...dentials_true_without_explicit_origin.json | 8 + tests/translator/test_translator.py | 3 +- 9 files changed, 73 insertions(+), 480 deletions(-) create mode 100644 tests/translator/input/error_cors_credentials_true_with_wildcard_origin.yaml rename tests/translator/input/{api_with_cors_and_only_credentials.yaml => error_cors_credentials_true_without_explicit_origin.yaml} (100%) delete mode 100644 tests/translator/output/api_with_cors_and_only_credentials.json delete mode 100644 tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json delete mode 100644 tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json create mode 100644 tests/translator/output/error_cors_credentials_true_with_wildcard_origin.json create mode 100644 tests/translator/output/error_cors_credentials_true_without_explicit_origin.json diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 0be254dbd1..8179ef27b6 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -12,7 +12,7 @@ _CORS_WILDCARD = "'*'" CorsProperties = namedtuple("_CorsProperties", ["AllowMethods", "AllowHeaders", "AllowOrigin", "MaxAge", "ExposeHeaders", "AllowCredentials"]) -# Default the Cors Properties to '*' wildcard. Other properties are actually Optional +# Default the Cors Properties to '*' wildcard and False AllowCredentials. Other properties are actually Optional CorsProperties.__new__.__defaults__ = (None, None, _CORS_WILDCARD, None, None, False) class ApiGenerator(object): @@ -203,6 +203,11 @@ def _add_cors(self): raise InvalidResourceException(self.logical_id, "Unable to add Cors configuration because " "'DefinitionBody' does not contain a valid Swagger") + if properties.AllowCredentials is True and properties.AllowOrigin == _CORS_WILDCARD: + raise InvalidResourceException(self.logical_id, "Unable to add Cors configuration because " + "'AllowCredentials' can not be true when " + "'AllowOrigin' is \"'*'\" or not set") + editor = SwaggerEditor(self.definition_body) for path in editor.iter_on_path(): editor.add_cors(path, properties.AllowOrigin, properties.AllowHeaders, properties.AllowMethods, diff --git a/tests/translator/input/error_cors_credentials_true_with_wildcard_origin.yaml b/tests/translator/input/error_cors_credentials_true_with_wildcard_origin.yaml new file mode 100644 index 0000000000..5bc77cd327 --- /dev/null +++ b/tests/translator/input/error_cors_credentials_true_with_wildcard_origin.yaml @@ -0,0 +1,49 @@ +Globals: + Api: + Cors: + AllowCredentials: true + AllowOrigin: "'*'" +Resources: + + ExplicitApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionBody: { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/add": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + }, + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + + + diff --git a/tests/translator/input/api_with_cors_and_only_credentials.yaml b/tests/translator/input/error_cors_credentials_true_without_explicit_origin.yaml similarity index 100% rename from tests/translator/input/api_with_cors_and_only_credentials.yaml rename to tests/translator/input/error_cors_credentials_true_without_explicit_origin.yaml diff --git a/tests/translator/output/api_with_cors_and_only_credentials.json b/tests/translator/output/api_with_cors_and_only_credentials.json deleted file mode 100644 index 72d19110ce..0000000000 --- a/tests/translator/output/api_with_cors_and_only_credentials.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "Resources": { - "ExplicitApiDeploymentfb09e6b93d": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ExplicitApi" - }, - "Description": "RestApi deployment id: fb09e6b93d08b6b20bfd62ed420c23416f41280d", - "StageName": "Stage" - } - }, - "ExplicitApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ExplicitApiDeploymentfb09e6b93d" - }, - "RestApiId": { - "Ref": "ExplicitApi" - }, - "StageName": "Prod" - } - }, - "ExplicitApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", - "method.response.header.Access-Control-Allow-Credentials": "'true'" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - } - }, - "/{proxy+}": { - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Allow-Credentials": "'true'" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - } - } - } - } -} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json b/tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json deleted file mode 100644 index e5a56c7868..0000000000 --- a/tests/translator/output/aws-cn/api_with_cors_and_only_credentials.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "Resources": { - "ExplicitApiDeploymentfb09e6b93d": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ExplicitApi" - }, - "Description": "RestApi deployment id: fb09e6b93d08b6b20bfd62ed420c23416f41280d", - "StageName": "Stage" - } - }, - "ExplicitApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ExplicitApiDeploymentfb09e6b93d" - }, - "RestApiId": { - "Ref": "ExplicitApi" - }, - "StageName": "Prod" - } - }, - "ExplicitApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", - "method.response.header.Access-Control-Allow-Credentials": "'true'" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - } - }, - "/{proxy+}": { - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Allow-Credentials": "'true'" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - }, - "EndpointConfiguration": { - "Types": [ - "REGIONAL" - ] - }, - "Parameters": { - "endpointConfigurationTypes": "REGIONAL" - } - } - } - } -} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json b/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json deleted file mode 100644 index e5a56c7868..0000000000 --- a/tests/translator/output/aws-us-gov/api_with_cors_and_only_credentials.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "Resources": { - "ExplicitApiDeploymentfb09e6b93d": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ExplicitApi" - }, - "Description": "RestApi deployment id: fb09e6b93d08b6b20bfd62ed420c23416f41280d", - "StageName": "Stage" - } - }, - "ExplicitApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ExplicitApiDeploymentfb09e6b93d" - }, - "RestApiId": { - "Ref": "ExplicitApi" - }, - "StageName": "Prod" - } - }, - "ExplicitApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", - "method.response.header.Access-Control-Allow-Credentials": "'true'" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - } - }, - "/{proxy+}": { - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Allow-Credentials": "'true'" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - }, - "EndpointConfiguration": { - "Types": [ - "REGIONAL" - ] - }, - "Parameters": { - "endpointConfigurationTypes": "REGIONAL" - } - } - } - } -} \ No newline at end of file diff --git a/tests/translator/output/error_cors_credentials_true_with_wildcard_origin.json b/tests/translator/output/error_cors_credentials_true_with_wildcard_origin.json new file mode 100644 index 0000000000..63932e749a --- /dev/null +++ b/tests/translator/output/error_cors_credentials_true_with_wildcard_origin.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [ExplicitApi] is invalid. Unable to add Cors configuration because 'AllowCredentials' can not be true when 'AllowOrigin' is '*' or not set" + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [ExplicitApi] is invalid. Unable to add Cors configuration because 'AllowCredentials' can not be true when 'AllowOrigin' is \"'*'\" or not set" +} diff --git a/tests/translator/output/error_cors_credentials_true_without_explicit_origin.json b/tests/translator/output/error_cors_credentials_true_without_explicit_origin.json new file mode 100644 index 0000000000..63932e749a --- /dev/null +++ b/tests/translator/output/error_cors_credentials_true_without_explicit_origin.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [ExplicitApi] is invalid. Unable to add Cors configuration because 'AllowCredentials' can not be true when 'AllowOrigin' is '*' or not set" + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [ExplicitApi] is invalid. Unable to add Cors configuration because 'AllowCredentials' can not be true when 'AllowOrigin' is \"'*'\" or not set" +} diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index fb9ca151c5..4573a3227e 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -61,7 +61,6 @@ class TestTranslatorEndToEnd(TestCase): 'api_with_cors_and_only_origins', 'api_with_cors_and_only_maxage', 'api_with_cors_and_only_expose_headers', - 'api_with_cors_and_only_credentials', 'api_with_cors_and_only_credentials_false', 'api_cache', 's3', @@ -153,6 +152,8 @@ def test_transform_success(self, testcase, partition_with_region): 'error_api_invalid_restapiid', 'error_cors_on_external_swagger', 'error_invalid_cors_dict', + 'error_cors_credentials_true_with_wildcard_origin', + 'error_cors_credentials_true_without_explicit_origin', 'error_function_invalid_codeuri', 'error_function_no_codeuri', 'error_function_no_handler', From 019997c3105e8ec460e6b374fec4e47ebaeeb293 Mon Sep 17 00:00:00 2001 From: Michael Willemse Date: Sat, 30 Jun 2018 00:36:41 +0200 Subject: [PATCH 5/5] Removed Expose Header functionality --- samtranslator/model/api/api_generator.py | 7 +- samtranslator/swagger/swagger.py | 13 +- tests/swagger/test_swagger.py | 91 +--- tests/translator/input/api_with_cors.yaml | 1 - ...api_with_cors_and_only_expose_headers.yaml | 70 --- tests/translator/output/api_with_cors.json | 14 +- ...api_with_cors_and_only_expose_headers.json | 389 ----------------- .../output/aws-cn/api_with_cors.json | 14 +- ...api_with_cors_and_only_expose_headers.json | 405 ------------------ .../output/aws-us-gov/api_with_cors.json | 14 +- ...api_with_cors_and_only_expose_headers.json | 405 ------------------ tests/translator/test_translator.py | 1 - versions/2016-10-31.md | 3 - 13 files changed, 23 insertions(+), 1404 deletions(-) delete mode 100644 tests/translator/input/api_with_cors_and_only_expose_headers.yaml delete mode 100644 tests/translator/output/api_with_cors_and_only_expose_headers.json delete mode 100644 tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json delete mode 100644 tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 8179ef27b6..2646570a9e 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -11,9 +11,9 @@ from samtranslator.model.intrinsics import is_instrinsic _CORS_WILDCARD = "'*'" -CorsProperties = namedtuple("_CorsProperties", ["AllowMethods", "AllowHeaders", "AllowOrigin", "MaxAge", "ExposeHeaders", "AllowCredentials"]) +CorsProperties = namedtuple("_CorsProperties", ["AllowMethods", "AllowHeaders", "AllowOrigin", "MaxAge", "AllowCredentials"]) # Default the Cors Properties to '*' wildcard and False AllowCredentials. Other properties are actually Optional -CorsProperties.__new__.__defaults__ = (None, None, _CORS_WILDCARD, None, None, False) +CorsProperties.__new__.__defaults__ = (None, None, _CORS_WILDCARD, None, False) class ApiGenerator(object): @@ -211,8 +211,7 @@ def _add_cors(self): editor = SwaggerEditor(self.definition_body) for path in editor.iter_on_path(): editor.add_cors(path, properties.AllowOrigin, properties.AllowHeaders, properties.AllowMethods, - max_age=properties.MaxAge, exposed_headers=properties.ExposeHeaders, - allow_credentials=properties.AllowCredentials) + max_age=properties.MaxAge, allow_credentials=properties.AllowCredentials) # Assign the Swagger back to template self.definition_body = editor.swagger diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index 0b6b751c34..b71ed4dfc3 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -119,7 +119,7 @@ def iter_on_path(self): yield path def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods=None, max_age=None, - exposed_headers=None, allow_credentials=None): + allow_credentials=None): """ Add CORS configuration to this path. Specifically, we will add a OPTIONS response config to the Swagger that will return headers required for CORS. Since SAM uses aws_proxy integration, we cannot inject the headers @@ -140,8 +140,6 @@ def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods= Value can also be an intrinsic function dict. :param integer/dict max_age: Maximum duration to cache the CORS Preflight request. Value is set on Access-Control-Max-Age header. Value can also be an intrinsic function dict. - :param string/dict exposed_headers: Comma separated list of exposed headers. - Value can also be an instrinsic function dict. :param bool/None allow_credentials: Flags whether request is allowed to contain credentials. :raises ValueError: When values for one of the allowed_* variables is empty """ @@ -169,11 +167,10 @@ def add_cors(self, path, allowed_origins, allowed_headers=None, allowed_methods= allowed_headers, allowed_methods, max_age, - exposed_headers, allow_credentials) def _options_method_response_for_cors(self, allowed_origins, allowed_headers=None, allowed_methods=None, - max_age=None, exposed_headers=None, allow_credentials=None): + max_age=None, allow_credentials=None): """ Returns a Swagger snippet containing configuration for OPTIONS HTTP Method to configure CORS. @@ -188,8 +185,6 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non Value can also be an intrinsic function dict. :param integer/dict max_age: Maximum duration to cache the CORS Preflight request. Value is set on Access-Control-Max-Age header. Value can also be an intrinsic function dict. - :param string/dict exposed_headers: Comma separated list of exposed headers. - Value can also be an instrinsic function dict. :param bool allow_credentials: Flags whether request is allowed to contain credentials. :return dict: Dictionary containing Options method configuration for CORS @@ -199,7 +194,6 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non ALLOW_HEADERS = "Access-Control-Allow-Headers" ALLOW_METHODS = "Access-Control-Allow-Methods" MAX_AGE = "Access-Control-Max-Age" - EXPOSE_HEADERS = "Access-Control-Expose-Headers" ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials" HEADER_RESPONSE = lambda x: "method.response.header."+x @@ -231,9 +225,6 @@ def _options_method_response_for_cors(self, allowed_origins, allowed_headers=Non # MaxAge can be set to 0, which is a valid value. So explicitly check against None response_parameters[HEADER_RESPONSE(MAX_AGE)] = max_age response_headers[MAX_AGE] = {"type": "integer"} - if exposed_headers: - response_parameters[HEADER_RESPONSE(EXPOSE_HEADERS)] = exposed_headers - response_headers[EXPOSE_HEADERS] = {"type": "string"} if allow_credentials is True: # Allow-Credentials only has a valid value of true, it should be omitted otherwise. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials diff --git a/tests/swagger/test_swagger.py b/tests/swagger/test_swagger.py index 808e1e86d2..9a6985bbc8 100644 --- a/tests/swagger/test_swagger.py +++ b/tests/swagger/test_swagger.py @@ -309,7 +309,6 @@ def test_must_add_options_to_new_path(self): allowed_headers = ["headers", "2"] allowed_methods = {"key": "methods"} max_age = 60 - exposed_headers = {"headers", "2"} allow_credentials = True options_method_response_allow_credentials = True path = "/foo" @@ -318,14 +317,12 @@ def test_must_add_options_to_new_path(self): self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, - allow_credentials) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, allowed_headers, allowed_methods, max_age, - exposed_headers, options_method_response_allow_credentials) def test_must_skip_existing_path(self): @@ -353,7 +350,6 @@ def test_must_work_for_optional_allowed_headers(self): allowed_headers = None # No Value allowed_methods = "methods" max_age = 60 - exposed_headers = {"headers", "2"} allow_credentials = True options_method_response_allow_credentials = True @@ -363,8 +359,7 @@ def test_must_work_for_optional_allowed_headers(self): self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, - allow_credentials) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) @@ -372,7 +367,6 @@ def test_must_work_for_optional_allowed_headers(self): allowed_headers, allowed_methods, max_age, - exposed_headers, options_method_response_allow_credentials) def test_must_make_default_value_with_optional_allowed_methods(self): @@ -381,7 +375,6 @@ def test_must_make_default_value_with_optional_allowed_methods(self): allowed_headers = "headers" allowed_methods = None # No Value max_age = 60 - exposed_headers = "headers" allow_credentials = True options_method_response_allow_credentials = True @@ -396,8 +389,7 @@ def test_must_make_default_value_with_optional_allowed_methods(self): self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, - allow_credentials) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) @@ -407,30 +399,6 @@ def test_must_make_default_value_with_optional_allowed_methods(self): # And value must be quoted default_allow_methods_value_with_quotes, max_age, - exposed_headers, - options_method_response_allow_credentials) - def test_must_accept_none_exposed_headers(self): - allowed_origins = "origins" - allowed_headers = ["headers", "2"] - allowed_methods = {"key": "methods"} - max_age = 60 - exposed_headers = None - allow_credentials = True - options_method_response_allow_credentials = True - path = "/foo" - expected = {"some cors": "return value"} - - self.editor._options_method_response_for_cors = Mock() - self.editor._options_method_response_for_cors.return_value = expected - - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, - allow_credentials) - self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) - self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, - allowed_headers, - allowed_methods, - max_age, - exposed_headers, options_method_response_allow_credentials) def test_must_accept_none_allow_credentials(self): @@ -438,7 +406,6 @@ def test_must_accept_none_allow_credentials(self): allowed_headers = ["headers", "2"] allowed_methods = {"key": "methods"} max_age = 60 - exposed_headers = {"headers", "2"} allow_credentials = None options_method_response_allow_credentials = False path = "/foo" @@ -447,14 +414,12 @@ def test_must_accept_none_allow_credentials(self): self.editor._options_method_response_for_cors = Mock() self.editor._options_method_response_for_cors.return_value = expected - self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, exposed_headers, - allow_credentials) + self.editor.add_cors(path, allowed_origins, allowed_headers, allowed_methods, max_age, allow_credentials) self.assertEquals(expected, self.editor.swagger["paths"][path]["options"]) self.editor._options_method_response_for_cors.assert_called_with(allowed_origins, allowed_headers, allowed_methods, max_age, - exposed_headers, options_method_response_allow_credentials) @@ -466,7 +431,6 @@ def test_correct_value_is_returned(self): methods = {"a": "b"} origins = [1,2,3] max_age = 60 - exposed_headers = "x-cape" allow_credentials = True expected = { @@ -486,7 +450,6 @@ def test_correct_value_is_returned(self): "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, - "method.response.header.Access-Control-Expose-Headers": exposed_headers, "method.response.header.Access-Control-Max-Age": max_age, }, "responseTemplates": { @@ -511,9 +474,6 @@ def test_correct_value_is_returned(self): "Access-Control-Allow-Origin": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Max-Age": { "type": "integer" } @@ -524,7 +484,6 @@ def test_correct_value_is_returned(self): actual = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors(origins, headers, methods, max_age, - exposed_headers, allow_credentials) self.assertEquals(expected, actual) @@ -532,14 +491,12 @@ def test_allow_headers_is_skipped_with_no_value(self): headers = None # No value methods = "methods" origins = "origins" - exposed_headers = "x-cape" allow_credentials = True expected = { "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, - "method.response.header.Access-Control-Expose-Headers": exposed_headers, } expected_headers = { @@ -551,14 +508,11 @@ def test_allow_headers_is_skipped_with_no_value(self): }, "Access-Control-Allow-Origin": { "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" } } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + origins, headers, methods, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -568,18 +522,16 @@ def test_allow_methods_is_skipped_with_no_value(self): headers = "headers" methods = None # No value origins = "origins" - exposed_headers = "x-cape" allow_credentials = True expected = { "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Origin": origins, - "method.response.header.Access-Control-Expose-Headers": exposed_headers, } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + origins, headers, methods, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -588,7 +540,6 @@ def test_allow_origins_is_not_skipped_with_no_value(self): headers = None methods = None origins = None - exposed_headers = None allow_credentials = False expected = { @@ -597,7 +548,7 @@ def test_allow_origins_is_not_skipped_with_no_value(self): } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + origins, headers, methods, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -607,7 +558,6 @@ def test_max_age_can_be_set_to_zero(self): methods = "methods" origins = "origins" max_age = 0 - exposed_headers = "x-cape" allow_credentials = True expected = { @@ -615,31 +565,10 @@ def test_max_age_can_be_set_to_zero(self): "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, "method.response.header.Access-Control-Max-Age": max_age, - "method.response.header.Access-Control-Expose-Headers": exposed_headers - } - - options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, max_age, exposed_headers, allow_credentials) - - actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] - self.assertEquals(expected, actual) - - def test_expose_headers_is_skipped_with_no_value(self): - headers = "headers" - methods = "methods" - origins = "origins" - exposed_headers = None # No value - allow_credentials = True - - expected = { - "method.response.header.Access-Control-Allow-Credentials": _ALLOW_CREDENTALS_TRUE, - "method.response.header.Access-Control-Allow-Headers": headers, - "method.response.header.Access-Control-Allow-Methods": methods, - "method.response.header.Access-Control-Allow-Origin": origins, } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + origins, headers, methods, max_age, allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) @@ -648,18 +577,16 @@ def test_allow_credentials_is_skipped_with_false_value(self): headers = "headers" methods = "methods" origins = "origins" - exposed_headers = "x-cape" allow_credentials = False expected = { "method.response.header.Access-Control-Allow-Headers": headers, "method.response.header.Access-Control-Allow-Methods": methods, "method.response.header.Access-Control-Allow-Origin": origins, - "method.response.header.Access-Control-Expose-Headers": exposed_headers } options_config = SwaggerEditor(SwaggerEditor.gen_skeleton())._options_method_response_for_cors( - origins, headers, methods, exposed_headers=exposed_headers, allow_credentials=allow_credentials) + origins, headers, methods, allow_credentials=allow_credentials) actual = options_config[_X_INTEGRATION]["responses"]["default"]["responseParameters"] self.assertEquals(expected, actual) diff --git a/tests/translator/input/api_with_cors.yaml b/tests/translator/input/api_with_cors.yaml index 9fbae6bc99..d7e93d58a6 100644 --- a/tests/translator/input/api_with_cors.yaml +++ b/tests/translator/input/api_with_cors.yaml @@ -66,5 +66,4 @@ Resources: AllowMethods: "methods" AllowHeaders: "headers" AllowOrigin: "origins" - ExposeHeaders: "headers" AllowCredentials: true diff --git a/tests/translator/input/api_with_cors_and_only_expose_headers.yaml b/tests/translator/input/api_with_cors_and_only_expose_headers.yaml deleted file mode 100644 index 6b2146df4a..0000000000 --- a/tests/translator/input/api_with_cors_and_only_expose_headers.yaml +++ /dev/null @@ -1,70 +0,0 @@ -Globals: - Api: - Cors: - - # If we skip AllowMethods, then SAM will auto generate a list of methods scoped to each path - ExposeHeaders: "headers" - -Resources: - ImplicitApiFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: s3://sam-demo-bucket/member_portal.zip - Handler: index.gethtml - Runtime: nodejs4.3 - Events: - GetHtml: - Type: Api - Properties: - Path: / - Method: get - - PostHtml: - Type: Api - Properties: - Path: / - Method: post - - - ExplicitApi: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - DefinitionBody: { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - }, - "/{proxy+}": { - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - } - - - diff --git a/tests/translator/output/api_with_cors.json b/tests/translator/output/api_with_cors.json index f1a0cf5003..65525b4cae 100644 --- a/tests/translator/output/api_with_cors.json +++ b/tests/translator/output/api_with_cors.json @@ -103,7 +103,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "ExplicitApiDeploymente1a19134ca" + "Ref": "ExplicitApiDeployment3a5a78688c" }, "RestApiId": { "Ref": "ExplicitApi" @@ -161,7 +161,6 @@ "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", "method.response.header.Access-Control-Allow-Headers": "headers", - "method.response.header.Access-Control-Expose-Headers": "headers", "method.response.header.Access-Control-Allow-Credentials": "'true'" } } @@ -183,9 +182,6 @@ "Access-Control-Allow-Methods": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Allow-Credentials": { "type": "string" } @@ -215,7 +211,6 @@ "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", "method.response.header.Access-Control-Allow-Headers": "headers", - "method.response.header.Access-Control-Expose-Headers": "headers", "method.response.header.Access-Control-Allow-Credentials": "'true'" } } @@ -237,9 +232,6 @@ "Access-Control-Allow-Methods": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Allow-Credentials": { "type": "string" } @@ -288,13 +280,13 @@ } } }, - "ExplicitApiDeploymente1a19134ca": { + "ExplicitApiDeployment3a5a78688c": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "ExplicitApi" }, - "Description": "RestApi deployment id: e1a19134ca6f0d6afe2079a3ff60176078baa467", + "Description": "RestApi deployment id: 3a5a78688c9bc377d53aa4153a11f0c4f6e7364c", "StageName": "Stage" } }, diff --git a/tests/translator/output/api_with_cors_and_only_expose_headers.json b/tests/translator/output/api_with_cors_and_only_expose_headers.json deleted file mode 100644 index e4f12ff47a..0000000000 --- a/tests/translator/output/api_with_cors_and_only_expose_headers.json +++ /dev/null @@ -1,389 +0,0 @@ -{ - "Resources": { - "ImplicitApiFunction": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "sam-demo-bucket", - "S3Key": "member_portal.zip" - }, - "Handler": "index.gethtml", - "Role": { - "Fn::GetAtt": [ - "ImplicitApiFunctionRole", - "Arn" - ] - }, - "Runtime": "nodejs4.3", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] - } - }, - "ImplicitApiFunctionRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } - } - ] - } - } - }, - "ImplicitApiFunctionGetHtmlPermissionTest": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ImplicitApiFunctionPostHtmlPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", - { - "__Stage__": "Prod", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ExplicitApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ExplicitApiDeployment2e88efef7e" - }, - "RestApiId": { - "Ref": "ExplicitApi" - }, - "StageName": "Prod" - } - }, - "ServerlessRestApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ServerlessRestApiDeploymentb1d8a88486" - }, - "RestApiId": { - "Ref": "ServerlessRestApi" - }, - "StageName": "Prod" - } - }, - "ExplicitApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - } - }, - "/{proxy+}": { - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - } - } - }, - "ExplicitApiDeployment2e88efef7e": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ExplicitApi" - }, - "Description": "RestApi deployment id: 2e88efef7e1035a3538562fca85d22395cb8fb3f", - "StageName": "Stage" - } - }, - "ImplicitApiFunctionGetHtmlPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", - { - "__Stage__": "Prod", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ImplicitApiFunctionPostHtmlPermissionTest": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ServerlessRestApiDeploymentb1d8a88486": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ServerlessRestApi" - }, - "Description": "RestApi deployment id: b1d8a88486a429400e02f3eb2c8897a15bfd0dbf", - "StageName": "Stage" - } - }, - "ServerlessRestApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS,POST'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "get": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - } - } - } - } -} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_cors.json b/tests/translator/output/aws-cn/api_with_cors.json index fdd437e166..9bdba92001 100644 --- a/tests/translator/output/aws-cn/api_with_cors.json +++ b/tests/translator/output/aws-cn/api_with_cors.json @@ -127,7 +127,6 @@ "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", "method.response.header.Access-Control-Allow-Headers": "headers", - "method.response.header.Access-Control-Expose-Headers": "headers", "method.response.header.Access-Control-Allow-Credentials": "'true'" } } @@ -149,9 +148,6 @@ "Access-Control-Allow-Methods": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Allow-Credentials": { "type": "string" } @@ -181,7 +177,6 @@ "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", "method.response.header.Access-Control-Allow-Headers": "headers", - "method.response.header.Access-Control-Expose-Headers": "headers", "method.response.header.Access-Control-Allow-Credentials": "'true'" } } @@ -203,9 +198,6 @@ "Access-Control-Allow-Methods": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Allow-Credentials": { "type": "string" } @@ -257,7 +249,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "ExplicitApiDeploymente1a19134ca" + "Ref": "ExplicitApiDeployment3a5a78688c" }, "RestApiId": { "Ref": "ExplicitApi" @@ -286,13 +278,13 @@ } } }, - "ExplicitApiDeploymente1a19134ca": { + "ExplicitApiDeployment3a5a78688c": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "ExplicitApi" }, - "Description": "RestApi deployment id: e1a19134ca6f0d6afe2079a3ff60176078baa467", + "Description": "RestApi deployment id: 3a5a78688c9bc377d53aa4153a11f0c4f6e7364c", "StageName": "Stage" } }, diff --git a/tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json b/tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json deleted file mode 100644 index af23c7cbb6..0000000000 --- a/tests/translator/output/aws-cn/api_with_cors_and_only_expose_headers.json +++ /dev/null @@ -1,405 +0,0 @@ -{ - "Resources": { - "ImplicitApiFunction": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "sam-demo-bucket", - "S3Key": "member_portal.zip" - }, - "Handler": "index.gethtml", - "Role": { - "Fn::GetAtt": [ - "ImplicitApiFunctionRole", - "Arn" - ] - }, - "Runtime": "nodejs4.3", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] - } - }, - "ImplicitApiFunctionRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "ManagedPolicyArns": [ - "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } - } - ] - } - } - }, - "ImplicitApiFunctionGetHtmlPermissionTest": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ImplicitApiFunctionPostHtmlPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", - { - "__Stage__": "Prod", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ServerlessRestApiDeploymentdbbf729d2b": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ServerlessRestApi" - }, - "Description": "RestApi deployment id: dbbf729d2b5fe0b47621fb708a5dc6caddb9854b", - "StageName": "Stage" - } - }, - "ServerlessRestApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ServerlessRestApiDeploymentdbbf729d2b" - }, - "RestApiId": { - "Ref": "ServerlessRestApi" - }, - "StageName": "Prod" - } - }, - "ExplicitApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - } - }, - "/{proxy+}": { - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - }, - "EndpointConfiguration": { - "Types": [ - "REGIONAL" - ] - }, - "Parameters": { - "endpointConfigurationTypes": "REGIONAL" - } - } - }, - "ExplicitApiDeployment2e88efef7e": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ExplicitApi" - }, - "Description": "RestApi deployment id: 2e88efef7e1035a3538562fca85d22395cb8fb3f", - "StageName": "Stage" - } - }, - "ImplicitApiFunctionGetHtmlPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", - { - "__Stage__": "Prod", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ImplicitApiFunctionPostHtmlPermissionTest": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ExplicitApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ExplicitApiDeployment2e88efef7e" - }, - "RestApiId": { - "Ref": "ExplicitApi" - }, - "StageName": "Prod" - } - }, - "ServerlessRestApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS,POST'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "get": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - }, - "EndpointConfiguration": { - "Types": [ - "REGIONAL" - ] - }, - "Parameters": { - "endpointConfigurationTypes": "REGIONAL" - } - } - } - } -} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_cors.json b/tests/translator/output/aws-us-gov/api_with_cors.json index 68af03d300..ccc64a1589 100644 --- a/tests/translator/output/aws-us-gov/api_with_cors.json +++ b/tests/translator/output/aws-us-gov/api_with_cors.json @@ -137,7 +137,6 @@ "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", "method.response.header.Access-Control-Allow-Headers": "headers", - "method.response.header.Access-Control-Expose-Headers": "headers", "method.response.header.Access-Control-Allow-Credentials": "'true'" } } @@ -159,9 +158,6 @@ "Access-Control-Allow-Methods": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Allow-Credentials": { "type": "string" } @@ -191,7 +187,6 @@ "method.response.header.Access-Control-Allow-Origin": "origins", "method.response.header.Access-Control-Allow-Methods": "methods", "method.response.header.Access-Control-Allow-Headers": "headers", - "method.response.header.Access-Control-Expose-Headers": "headers", "method.response.header.Access-Control-Allow-Credentials": "'true'" } } @@ -213,9 +208,6 @@ "Access-Control-Allow-Methods": { "type": "string" }, - "Access-Control-Expose-Headers": { - "type": "string" - }, "Access-Control-Allow-Credentials": { "type": "string" } @@ -267,7 +259,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "ExplicitApiDeploymente1a19134ca" + "Ref": "ExplicitApiDeployment3a5a78688c" }, "RestApiId": { "Ref": "ExplicitApi" @@ -296,13 +288,13 @@ } } }, - "ExplicitApiDeploymente1a19134ca": { + "ExplicitApiDeployment3a5a78688c": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "ExplicitApi" }, - "Description": "RestApi deployment id: e1a19134ca6f0d6afe2079a3ff60176078baa467", + "Description": "RestApi deployment id: 3a5a78688c9bc377d53aa4153a11f0c4f6e7364c", "StageName": "Stage" } }, diff --git a/tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json b/tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json deleted file mode 100644 index 453c04f90b..0000000000 --- a/tests/translator/output/aws-us-gov/api_with_cors_and_only_expose_headers.json +++ /dev/null @@ -1,405 +0,0 @@ -{ - "Resources": { - "ImplicitApiFunction": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "sam-demo-bucket", - "S3Key": "member_portal.zip" - }, - "Handler": "index.gethtml", - "Role": { - "Fn::GetAtt": [ - "ImplicitApiFunctionRole", - "Arn" - ] - }, - "Runtime": "nodejs4.3", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] - } - }, - "ImplicitApiFunctionRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "ManagedPolicyArns": [ - "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } - } - ] - } - } - }, - "ImplicitApiFunctionGetHtmlPermissionTest": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ServerlessRestApiDeploymentc020adc98a": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ServerlessRestApi" - }, - "Description": "RestApi deployment id: c020adc98ab52b1180ff6477685a0d7b86ba6262", - "StageName": "Stage" - } - }, - "ImplicitApiFunctionPostHtmlPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", - { - "__Stage__": "Prod", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ExplicitApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ExplicitApiDeployment2e88efef7e" - }, - "RestApiId": { - "Ref": "ExplicitApi" - }, - "StageName": "Prod" - } - }, - "ServerlessRestApiProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "ServerlessRestApiDeploymentc020adc98a" - }, - "RestApiId": { - "Ref": "ServerlessRestApi" - }, - "StageName": "Prod" - } - }, - "ExplicitApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/add": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,POST'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - } - }, - "/{proxy+}": { - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "x-amazon-apigateway-any-method": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - }, - "EndpointConfiguration": { - "Types": [ - "REGIONAL" - ] - }, - "Parameters": { - "endpointConfigurationTypes": "REGIONAL" - } - } - }, - "ExplicitApiDeployment2e88efef7e": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "ExplicitApi" - }, - "Description": "RestApi deployment id: 2e88efef7e1035a3538562fca85d22395cb8fb3f", - "StageName": "Stage" - } - }, - "ImplicitApiFunctionGetHtmlPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", - { - "__Stage__": "Prod", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ImplicitApiFunctionPostHtmlPermissionTest": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:invokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "ImplicitApiFunction" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "ServerlessRestApi" - } - } - ] - } - } - }, - "ServerlessRestApi": { - "Type": "AWS::ApiGateway::RestApi", - "Properties": { - "Body": { - "info": { - "version": "1.0", - "title": { - "Ref": "AWS::StackName" - } - }, - "paths": { - "/": { - "post": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - }, - "options": { - "x-amazon-apigateway-integration": { - "type": "mock", - "requestTemplates": { - "application/json": "{\n \"statusCode\" : 200\n}\n" - }, - "responses": { - "default": { - "statusCode": "200", - "responseTemplates": { - "application/json": "{}\n" - }, - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'", - "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS,POST'", - "method.response.header.Access-Control-Expose-Headers": "headers" - } - } - } - }, - "consumes": [ - "application/json" - ], - "summary": "CORS support", - "responses": { - "200": { - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Expose-Headers": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - } - }, - "description": "Default response for CORS method" - } - }, - "produces": [ - "application/json" - ] - }, - "get": { - "x-amazon-apigateway-integration": { - "httpMethod": "POST", - "type": "aws_proxy", - "uri": { - "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" - } - }, - "responses": {} - } - } - }, - "swagger": "2.0" - }, - "EndpointConfiguration": { - "Types": [ - "REGIONAL" - ] - }, - "Parameters": { - "endpointConfigurationTypes": "REGIONAL" - } - } - } - } -} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 4573a3227e..0e29a5c3dd 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -60,7 +60,6 @@ class TestTranslatorEndToEnd(TestCase): 'api_with_cors_and_only_headers', 'api_with_cors_and_only_origins', 'api_with_cors_and_only_maxage', - 'api_with_cors_and_only_expose_headers', 'api_with_cors_and_only_credentials_false', 'api_cache', 's3', diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index de17febe2e..dc1d032148 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -626,9 +626,6 @@ Cors: MaxAge: Optional. String containing the number of seconds to cache CORS Preflight request. # For example, "'600'" will cache request for 600 seconds. Checkout [HTTP Spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age) for more details on this value - ExposeHeaders: Optional. String of headers that can be exposed. - # For example, "'Content-Length,X-Revision'". Checkout [HTTP Spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) for more details on this value. - AllowCredentials: Optional. Boolean indicating whether request is allowed to contain credentials. # Header is omitted when false. Checkout [HTTP Spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials) for more details on this value. ```