Skip to content

Commit

Permalink
Merge pull request #2203 from Mashape/feat/cors-multiple-origins
Browse files Browse the repository at this point in the history
feat(cors) allow for multiple origins
  • Loading branch information
thibaultcha authored Mar 15, 2017
2 parents b16a5cc + 76d0f12 commit d9f752b
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 33 deletions.
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

0 comments on commit d9f752b

Please sign in to comment.