From c8683a5c03a40901bd9276387ac800ff3dc3f3dd Mon Sep 17 00:00:00 2001 From: lhanjian Date: Tue, 14 Jan 2025 16:24:26 +0800 Subject: [PATCH 1/6] update --- kong/plugins/cors/handler.lua | 15 ++++++++++++--- kong/plugins/cors/schema.lua | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/kong/plugins/cors/handler.lua b/kong/plugins/cors/handler.lua index 3d62be388181..f0379f49524d 100644 --- a/kong/plugins/cors/handler.lua +++ b/kong/plugins/cors/handler.lua @@ -65,6 +65,11 @@ local function configure_origin(conf, header_filter) local n_origins = conf.origins ~= nil and #conf.origins or 0 local set_header = kong.response.set_header + local req_origin = kong.request.get_header("origin") + if not req_origin and conf.skip_cors_when_origin_is_empty then + return false + end + if n_origins == 0 or (n_origins == 1 and conf.origins[1] == "*") then set_header("Access-Control-Allow-Origin", "*") return true @@ -89,7 +94,6 @@ local function configure_origin(conf, header_filter) end end - local req_origin = kong.request.get_header("origin") if req_origin then local cached_domains = config_cache[conf] if not cached_domains then @@ -174,6 +178,11 @@ local function configure_credentials(conf, allow_all, header_filter) return end + local req_origin = kong.request.get_header("origin") + if not req_origin and conf.skip_cors_when_origin_is_empty then + return + end + if not allow_all then set_header("Access-Control-Allow-Credentials", true) return @@ -181,7 +190,6 @@ local function configure_credentials(conf, allow_all, header_filter) -- Access-Control-Allow-Origin is '*', must change it because ACAC cannot -- be 'true' if ACAO is '*'. - local req_origin = kong.request.get_header("origin") if req_origin then add_vary_header(header_filter) set_header("Access-Control-Allow-Origin", req_origin) @@ -250,7 +258,8 @@ function CorsHandler:header_filter(conf) local allow_all = configure_origin(conf, true) configure_credentials(conf, allow_all, true) - if conf.exposed_headers and #conf.exposed_headers > 0 then + local req_origin = kong.request.get_header("origin") + if req_origin and conf.exposed_headers and #conf.exposed_headers > 0 then kong.response.set_header("Access-Control-Expose-Headers", concat(conf.exposed_headers, ",")) end diff --git a/kong/plugins/cors/schema.lua b/kong/plugins/cors/schema.lua index 4910a321d085..3f5fbbf5b994 100644 --- a/kong/plugins/cors/schema.lua +++ b/kong/plugins/cors/schema.lua @@ -48,6 +48,7 @@ return { { credentials = { description = "Flag to determine whether the `Access-Control-Allow-Credentials` header should be sent with `true` as the value.", type = "boolean", required = true, default = false }, }, { private_network = { description = "Flag to determine whether the `Access-Control-Allow-Private-Network` header should be sent with `true` as the value.", type = "boolean", required = true, default = false }, }, { preflight_continue = { description = "A boolean value that instructs the plugin to proxy the `OPTIONS` preflight request to the Upstream service.", type = "boolean", required = true, default = false }, }, + { skip_cors_when_origin_is_empty = { description = "A boolean value that skip cors response headers when origin header of request is empty", type = "boolean", required = true, default = false }, }, }, }, }, }, } From 73079435dc221fdb1175ad27226d8cb36fb7cb0e Mon Sep 17 00:00:00 2001 From: lhanjian Date: Wed, 15 Jan 2025 10:53:37 +0800 Subject: [PATCH 2/6] update --- kong/plugins/cors/handler.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kong/plugins/cors/handler.lua b/kong/plugins/cors/handler.lua index f0379f49524d..ff2e9d40de1d 100644 --- a/kong/plugins/cors/handler.lua +++ b/kong/plugins/cors/handler.lua @@ -259,7 +259,10 @@ function CorsHandler:header_filter(conf) configure_credentials(conf, allow_all, true) local req_origin = kong.request.get_header("origin") - if req_origin and conf.exposed_headers and #conf.exposed_headers > 0 then + if not req_origin and conf.skip_cors_when_origin_is_empty then + return + end + if conf.exposed_headers and #conf.exposed_headers > 0 then kong.response.set_header("Access-Control-Expose-Headers", concat(conf.exposed_headers, ",")) end From e22e9daf241a3e4bc3ae038f408c9c778ffc8f43 Mon Sep 17 00:00:00 2001 From: lhanjian Date: Wed, 15 Jan 2025 15:38:53 +0800 Subject: [PATCH 3/6] update --- spec/03-plugins/13-cors/01-access_spec.lua | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/spec/03-plugins/13-cors/01-access_spec.lua b/spec/03-plugins/13-cors/01-access_spec.lua index cf6dce918179..4d46c2cc3ec0 100644 --- a/spec/03-plugins/13-cors/01-access_spec.lua +++ b/spec/03-plugins/13-cors/01-access_spec.lua @@ -291,6 +291,14 @@ for _, strategy in helpers.each_strategy() do hosts = { "cors14.test" }, }) + local route15 = bp.routes:insert({ + hosts = { "cors15.test" }, + }) + + local route16 = bp.routes:insert({ + hosts = { "cors16.test" }, + }) + local mock_upstream = bp.services:insert { host = helpers.mock_upstream_hostname, port = helpers.mock_upstream_port, @@ -464,6 +472,28 @@ for _, strategy in helpers.each_strategy() do } } + bp.plugins:insert { + name = "cors", + route = { id = route15.id }, + config = { + skip_cors_when_origin_is_empty = true, + origins = { "foo.bar" }, + exposed_headers = { "x-auth-token" }, + credentials = true + } + } + + bp.plugins:insert { + name = "cors", + route = { id = route16.id }, + config = { + skip_cors_when_origin_is_empty = false, + origins = { "foo.bar" }, + exposed_headers = { "x-auth-token" }, + credentials = true + } + } + bp.plugins:insert { name = "cors", route = { id = route_timeout.id }, @@ -1130,6 +1160,32 @@ for _, strategy in helpers.each_strategy() do assert.equal("true", res.headers["Access-Control-Allow-Credentials"]) assert.equal("disallowed-domain.test", json.headers["origin"]) end) + + it("when enable skip_cors_when_origin_is_empty, no ACAO", function() + local res = assert(proxy_client:send { + method = "GET", + headers = { + ["Host"] = "cors15.test", + } + }) + assert.res_status(200, res) + assert.is_nil(res.headers["Access-Control-Allow-Origin"]) + assert.is_nil(res.headers["Access-Control-Allow-Credentials"]) + assert.is_nil(res.headers["Access-Control-Expose-Headers"]) + end) + + it("when disable skip_cors_when_origin_is_empty, ACAO is returned", function() + local res = assert(proxy_client:send { + method = "GET", + headers = { + ["Host"] = "cors16.test", + } + }) + assert.res_status(200, res) + assert.equal("foo.bar", res.headers["Access-Control-Allow-Origin"] ) + assert.equal("true", res.headers["Access-Control-Allow-Credentials"]) + assert.equal("x-auth-token", res.headers["Access-Control-Expose-Headers"]) + end) end) end) end From bfdad0cdf5c057e2090060e362d256e891dd06ed Mon Sep 17 00:00:00 2001 From: lhanjian Date: Wed, 15 Jan 2025 15:55:58 +0800 Subject: [PATCH 4/6] update --- .../feat-cors-skip-return-acao-when-no-origin-in-request.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/unreleased/kong/feat-cors-skip-return-acao-when-no-origin-in-request.yml diff --git a/changelog/unreleased/kong/feat-cors-skip-return-acao-when-no-origin-in-request.yml b/changelog/unreleased/kong/feat-cors-skip-return-acao-when-no-origin-in-request.yml new file mode 100644 index 000000000000..228a515c6671 --- /dev/null +++ b/changelog/unreleased/kong/feat-cors-skip-return-acao-when-no-origin-in-request.yml @@ -0,0 +1,3 @@ +message: "**CORS**: add an option to skip return ACAO when request don't have Origin Header" +type: feature +scope: Plugin \ No newline at end of file From 9a6e2039b7d43a288479bde49db080b7dc3e8c70 Mon Sep 17 00:00:00 2001 From: lhanjian Date: Thu, 16 Jan 2025 11:20:26 +0800 Subject: [PATCH 5/6] update --- kong/plugins/cors/handler.lua | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/kong/plugins/cors/handler.lua b/kong/plugins/cors/handler.lua index ff2e9d40de1d..456c0c248a0f 100644 --- a/kong/plugins/cors/handler.lua +++ b/kong/plugins/cors/handler.lua @@ -65,11 +65,6 @@ local function configure_origin(conf, header_filter) local n_origins = conf.origins ~= nil and #conf.origins or 0 local set_header = kong.response.set_header - local req_origin = kong.request.get_header("origin") - if not req_origin and conf.skip_cors_when_origin_is_empty then - return false - end - if n_origins == 0 or (n_origins == 1 and conf.origins[1] == "*") then set_header("Access-Control-Allow-Origin", "*") return true @@ -94,6 +89,7 @@ local function configure_origin(conf, header_filter) end end + local req_origin = kong.request.get_header("origin") if req_origin then local cached_domains = config_cache[conf] if not cached_domains then @@ -178,11 +174,6 @@ local function configure_credentials(conf, allow_all, header_filter) return end - local req_origin = kong.request.get_header("origin") - if not req_origin and conf.skip_cors_when_origin_is_empty then - return - end - if not allow_all then set_header("Access-Control-Allow-Credentials", true) return @@ -190,6 +181,7 @@ local function configure_credentials(conf, allow_all, header_filter) -- Access-Control-Allow-Origin is '*', must change it because ACAC cannot -- be 'true' if ACAO is '*'. + local req_origin = kong.request.get_header("origin") if req_origin then add_vary_header(header_filter) set_header("Access-Control-Allow-Origin", req_origin) @@ -255,13 +247,14 @@ function CorsHandler:header_filter(conf) return end - local allow_all = configure_origin(conf, true) - configure_credentials(conf, allow_all, true) - local req_origin = kong.request.get_header("origin") if not req_origin and conf.skip_cors_when_origin_is_empty then return end + + local allow_all = configure_origin(conf, true) + configure_credentials(conf, allow_all, true) + if conf.exposed_headers and #conf.exposed_headers > 0 then kong.response.set_header("Access-Control-Expose-Headers", concat(conf.exposed_headers, ",")) From a531cfa6090b5ac4fbca60e111c598ab03ccd711 Mon Sep 17 00:00:00 2001 From: lhanjian Date: Thu, 16 Jan 2025 16:52:48 +0800 Subject: [PATCH 6/6] update --- kong/clustering/compat/removed_fields.lua | 5 ++++- spec/01-unit/19-hybrid/02-clustering_spec.lua | 1 + .../09-hybrid_mode/09-config-compat_spec.lua | 20 ++++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/kong/clustering/compat/removed_fields.lua b/kong/clustering/compat/removed_fields.lua index b52e215fec11..657ca2f22088 100644 --- a/kong/clustering/compat/removed_fields.lua +++ b/kong/clustering/compat/removed_fields.lua @@ -228,7 +228,10 @@ return { "queue.concurrency_limit", }, }, - [30010000000] = { + [3010000000] = { + cors = { + "skip_cors_when_origin_is_empty", + }, session = { "hash_subject", "store_metadata", diff --git a/spec/01-unit/19-hybrid/02-clustering_spec.lua b/spec/01-unit/19-hybrid/02-clustering_spec.lua index d2d54f10d83e..9d430d427b6c 100644 --- a/spec/01-unit/19-hybrid/02-clustering_spec.lua +++ b/spec/01-unit/19-hybrid/02-clustering_spec.lua @@ -7,6 +7,7 @@ describe("kong.clustering.compat.version", function() assert.equal(3000001000, version.string_to_number("3.0.1")) assert.equal(3000000000, version.string_to_number("3.0.0.0")) assert.equal(3000000001, version.string_to_number("3.0.0.1")) + assert.equal(3010000000, version.string_to_number("3.10.0.0")) assert.equal(333333333001, version.string_to_number("333.333.333.1")) assert.equal(333333333333, version.string_to_number("333.333.333.333")) end) diff --git a/spec/02-integration/09-hybrid_mode/09-config-compat_spec.lua b/spec/02-integration/09-hybrid_mode/09-config-compat_spec.lua index 07677fe45e8f..79e05ee24362 100644 --- a/spec/02-integration/09-hybrid_mode/09-config-compat_spec.lua +++ b/spec/02-integration/09-hybrid_mode/09-config-compat_spec.lua @@ -201,19 +201,27 @@ describe("CP/DP config compat transformations #" .. strategy, function() end) describe("compatibility test for cors plugin", function() - it("removes `config.private_network` before sending them to older(less than 3.5.0.0) DP nodes", function() + it("removes config.options before sending them to older DP nodes", function() local cors = admin.plugins:insert { name = "cors", enabled = true, config = { + -- [[ new fields 3.10.0 + skip_cors_when_origin_is_empty = false, + -- ]] -- [[ new fields 3.5.0 private_network = false -- ]] } } - assert.not_nil(cors.config.private_network) + assert.not_nil(cors.config.skip_cors_when_origin_is_empty) local expected_cors = cycle_aware_deep_copy(cors) + do_assert(uuid(), "3.10.0", expected_cors) + expected_cors.config.skip_cors_when_origin_is_empty = nil + + assert.not_nil(cors.config.private_network) + expected_cors = cycle_aware_deep_copy(expected_cors) expected_cors.config.private_network = nil do_assert(uuid(), "3.4.0", expected_cors) @@ -221,16 +229,22 @@ describe("CP/DP config compat transformations #" .. strategy, function() admin.plugins:remove({ id = cors.id }) end) - it("does not remove `config.private_network` from DP nodes that are already compatible", function() + it("does not remove config.options from DP nodes that are already compatible", function() local cors = admin.plugins:insert { name = "cors", enabled = true, config = { + -- [[ new fields 3.10.0 + skip_cors_when_origin_is_empty = false, + -- ]] -- [[ new fields 3.5.0 private_network = false -- ]] } } + do_assert(uuid(), "3.10.0", cors) + cors.config.skip_cors_when_origin_is_empty = nil + do_assert(uuid(), "3.5.0", cors) -- cleanup