Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cors) allow for multiple origins #2203

Merged
merged 2 commits into from
Mar 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
## [Unreleased][unreleased]

### Added

- Plugins:
- cors: Support for configuring multiple Origin domains.
[#2203](https://github.com/Mashape/kong/pull/2203)

### Fixed

- Plugins:
- hmac: generate an HMAC secret value if none is provided.
- hmac: Generate an HMAC secret value if none is provided.
[#2158](https://github.com/Mashape/kong/pull/2158)


## [0.10.0] - 2016/03/07

Kong 0.10 is one of most significant releases to this day. It ships with
Expand Down
2 changes: 2 additions & 0 deletions kong-0.10.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ build = {

["kong.plugins.cors.handler"] = "kong/plugins/cors/handler.lua",
["kong.plugins.cors.schema"] = "kong/plugins/cors/schema.lua",
["kong.plugins.cors.migrations.cassandra"] = "kong/plugins/cors/migrations/cassandra.lua",
["kong.plugins.cors.migrations.postgres"] = "kong/plugins/cors/migrations/postgres.lua",

["kong.plugins.ip-restriction.handler"] = "kong/plugins/ip-restriction/handler.lua",
["kong.plugins.ip-restriction.schema"] = "kong/plugins/ip-restriction/schema.lua",
Expand Down
105 changes: 79 additions & 26 deletions kong/plugins/cors/handler.lua
Original file line number Diff line number Diff line change
@@ -1,88 +1,141 @@
local BasePlugin = require "kong.plugins.base_plugin"
local responses = require "kong.tools.responses"
local responses = require "kong.tools.responses"


local req_get_method = ngx.req.get_method
local re_find = ngx.re.find
local concat = table.concat
local tostring = tostring
local ipairs = ipairs


local CorsHandler = BasePlugin:extend()


CorsHandler.PRIORITY = 2000

local OPTIONS = "OPTIONS"

local function configure_origin(ngx, conf)
if conf.origin == nil then
if not conf.origins then
ngx.header["Access-Control-Allow-Origin"] = "*"
else
ngx.header["Access-Control-Allow-Origin"] = conf.origin
ngx.header["Vary"] = "Origin"
ngx.ctx.cors_allow_all = true
return
end

if #conf.origins == 1 then
if conf.origins[1] == "*" then
ngx.ctx.cors_allow_all = true

else
ngx.header["Vary"] = "Origin"
end

ngx.header["Access-Control-Allow-Origin"] = conf.origins[1]
return
end

local req_origin = ngx.var.http_origin
if req_origin then
for _, domain in ipairs(conf.origins) do
local from, _, err = re_find(req_origin,
[[\Q]] .. domain .. [[\E$]],
"jo")
if err then
ngx.log(ngx.ERR, "[cors] could not search for domain: ", err)
end

if from then
ngx.header["Access-Control-Allow-Origin"] = req_origin
ngx.header["Vary"] = "Origin"
return
end
end
end
end


local function configure_credentials(ngx, conf)
if conf.origin == nil or conf.origin == "*" then
if ngx.ctx.cors_allow_all then
ngx.header["Access-Control-Allow-Credentials"] = "false"

elseif conf.credentials then
ngx.header["Access-Control-Allow-Credentials"] = "true"
end
end

local function configure_headers(ngx, conf, headers)
if conf.headers == nil then
ngx.header["Access-Control-Allow-Headers"] = headers["access-control-request-headers"] or ""

local function configure_headers(ngx, conf)
if not conf.headers then
ngx.header["Access-Control-Allow-Headers"] = ngx.var["http_access_control_request_headers"] or ""

else
ngx.header["Access-Control-Allow-Headers"] = table.concat(conf.headers, ",")
ngx.header["Access-Control-Allow-Headers"] = concat(conf.headers, ",")
end
end


local function configure_exposed_headers(ngx, conf)
if conf.exposed_headers ~= nil then
ngx.header["Access-Control-Expose-Headers"] = table.concat(conf.exposed_headers, ",")
if conf.exposed_headers then
ngx.header["Access-Control-Expose-Headers"] = concat(conf.exposed_headers, ",")
end
end


local function configure_methods(ngx, conf)
if conf.methods == nil then
if not conf.methods then
ngx.header["Access-Control-Allow-Methods"] = "GET,HEAD,PUT,PATCH,POST,DELETE"

else
ngx.header["Access-Control-Allow-Methods"] = table.concat(conf.methods, ",")
ngx.header["Access-Control-Allow-Methods"] = concat(conf.methods, ",")
end
end


local function configure_max_age(ngx, conf)
if conf.max_age ~= nil then
if conf.max_age then
ngx.header["Access-Control-Max-Age"] = tostring(conf.max_age)
end
end


function CorsHandler:new()
CorsHandler.super.new(self, "cors")
end


function CorsHandler:access(conf)
CorsHandler.super.access(self)
if ngx.req.get_method() == OPTIONS then
CorsHandler.super.access(self)

if req_get_method() == "OPTIONS" then
if not conf.preflight_continue then
configure_origin(ngx, conf)
configure_credentials(ngx, conf)
configure_headers(ngx, conf, ngx.req.get_headers())
configure_headers(ngx, conf)
configure_methods(ngx, conf)
configure_max_age(ngx, conf)
ngx.ctx.skip_response_headers = true -- Don't add response headers because we already added them all
return responses.send_HTTP_NO_CONTENT()
else
-- Don't add any response header because we are delegating the preflight to the upstream API (conf.preflight_continue=true)

-- Don't add response headers because we already added them all
ngx.ctx.skip_response_headers = true

return responses.send_HTTP_NO_CONTENT()
end

-- Don't add any response header because we are delegating the preflight to
-- the upstream API (conf.preflight_continue=true)
ngx.ctx.skip_response_headers = true
end
end


function CorsHandler:header_filter(conf)
CorsHandler.super.header_filter(self)

if not ngx.ctx.skip_response_headers then
configure_origin(ngx, conf)
configure_credentials(ngx, conf)
configure_exposed_headers(ngx, conf)
end
end

return CorsHandler

return CorsHandler
29 changes: 29 additions & 0 deletions kong/plugins/cors/migrations/cassandra.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
return {
{
name = "2017-03-14_multiple_orgins",
up = function(db, _, dao)
local cjson = require "cjson"

local rows, err = db:query([[
SELECT * FROM plugins WHERE name = 'cors' ALLOW FILTERING
]])
if err then
return err
end

for _, row in ipairs(rows) do
local config = cjson.decode(row.config)

config.origins = { config.origin }
config.origin = nil

local _, err = db:query(string.format([[
UPDATE plugins SET config = '%s' WHERE name = 'cors' AND id = %s
]], cjson.encode(config), row.id))
if err then
return err
end
end
end,
}
}
27 changes: 27 additions & 0 deletions kong/plugins/cors/migrations/postgres.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
return {
{
name = "2017-03-14_multiple_orgins",
up = function(db)
local cjson = require "cjson"

local rows, err = db:query([[
SELECT * FROM plugins WHERE name = 'cors'
]])
if err then
return err
end

for _, row in ipairs(rows) do
row.config.origins = { row.config.origin }
row.config.origin = nil

local _, err = db:query(string.format([[
UPDATE plugins SET config = '%s' WHERE id = '%s'
]], cjson.encode(row.config), row.id))
if err then
return err
end
end
end,
}
}
4 changes: 2 additions & 2 deletions kong/plugins/cors/schema.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
return {
no_consumer = true,
fields = {
origin = { type = "string" },
origins = { type = "array" },
headers = { type = "array" },
exposed_headers = { type = "array" },
methods = { type = "array", enum = { "HEAD", "GET", "POST", "PUT", "PATCH", "DELETE" } },
max_age = { type = "number" },
credentials = { type = "boolean", default = false },
preflight_continue = { type = "boolean", default = false }
}
}
}
Loading