Skip to content

Commit

Permalink
Merge branch 'release/0.8.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
subnetmarco committed Jun 2, 2016
2 parents 7720935 + fbd5290 commit 33a8cab
Show file tree
Hide file tree
Showing 16 changed files with 316 additions and 48 deletions.
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
## [Unreleased][unreleased]

## [0.8.3] - 2016/06/01

This release includes some bugfixes:

### Changed

- Switched the log level of the "No nodes found in cluster" warning to `INFO`, that was printed when starting up the first Kong node in a new cluster.
- Kong now requires OpenResty `1.9.7.5`.

### Fixed

- New nodes are now properly registered into the `nodes` table when running on the same machine. [#1281](https://github.com/Mashape/kong/pull/1281)
- Fixed a failed error parsing on Postgres. [#1269](https://github.com/Mashape/kong/pull/1269)
- Plugins:
- Response Transformer: Slashes are now encoded properly, and fixed a bug that hang the execution of the plugin. [#1257](https://github.com/Mashape/kong/pull/1257) and [#1263](https://github.com/Mashape/kong/pull/1263)
- JWT: If a value for `algorithm` is missing, it's now `HS256` by default. This problem occured when migrating from older versions of Kong.
- OAuth 2.0: Fixed a Postgres problem that was preventing an application from being created, and fixed a check on the `redirect_uri` field. [#1264](https://github.com/Mashape/kong/pull/1264) and [#1267](https://github.com/Mashape/kong/issues/1267)

## [0.8.2] - 2016/05/25

This release includes bugfixes and minor updates:
Expand Down Expand Up @@ -589,7 +607,8 @@ First version running with Cassandra.
- CLI `bin/kong` script.
- Database migrations (using `db.lua`).

[unreleased]: https://github.com/mashape/kong/compare/0.8.2...next
[unreleased]: https://github.com/mashape/kong/compare/0.8.3...next
[0.8.3]: https://github.com/mashape/kong/compare/0.8.2...0.8.3
[0.8.2]: https://github.com/mashape/kong/compare/0.8.1...0.8.2
[0.8.1]: https://github.com/mashape/kong/compare/0.8.0...0.8.1
[0.8.0]: https://github.com/mashape/kong/compare/0.7.0...0.8.0
Expand Down
4 changes: 2 additions & 2 deletions kong-0.8.2-0.rockspec → kong-0.8.3-0.rockspec
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package = "kong"
version = "0.8.2-0"
version = "0.8.3-0"
supported_platforms = {"linux", "macosx"}
source = {
url = "git://github.com/Mashape/kong",
tag = "0.8.2"
tag = "0.8.3"
}
description = {
summary = "Kong is a scalable and customizable API Management Layer built on top of Nginx.",
Expand Down
14 changes: 10 additions & 4 deletions kong/cli/services/serf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ echo $COMMAND | ]]..luajit_path..[[
return false, res
end

-- Create the unique identifier if it doesn't exist
local _, err = cluster_utils.create_node_identifier(self._configuration)
if err then
return false, err
end

return true
end

Expand Down Expand Up @@ -112,7 +118,7 @@ function Serf:_autojoin(current_node_name)
return false, tostring(err)
else
if #nodes == 0 then
logger:warn("Cannot auto-join the cluster because no nodes were found")
logger:info("No other Kong nodes were found in the cluster")
else
-- Sort by newest to oldest (although by TTL would be a better sort)
table.sort(nodes, function(a, b)
Expand Down Expand Up @@ -144,7 +150,7 @@ function Serf:_add_node()
return false, err
end

local name = cluster_utils.get_node_name(self._configuration)
local name = cluster_utils.get_node_identifier(self._configuration)
local addr
for _, member in ipairs(members) do
if member.name == name then
Expand Down Expand Up @@ -178,7 +184,7 @@ function Serf:start()
return nil, err
end

local node_name = cluster_utils.get_node_name(self._configuration)
local node_name = cluster_utils.get_node_identifier(self._configuration)

-- Prepare arguments
local cmd_args = {
Expand Down Expand Up @@ -269,7 +275,7 @@ function Serf:stop()
-- Remove the node from the datastore.
-- This is useful when this is the only node running in the cluster.
self._dao_factory.nodes:delete({
name = cluster_utils.get_node_name(self._configuration)
name = cluster_utils.get_node_identifier(self._configuration)
})

-- Finally stop Serf
Expand Down
4 changes: 2 additions & 2 deletions kong/core/cluster.lua
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ local function async_autojoin(premature)
ngx.log(ngx.ERR, tostring(err))
elseif #members < 2 then
-- Trigger auto-join
local _, err = singletons.serf:_autojoin(cluster_utils.get_node_name(singletons.configuration))
local _, err = singletons.serf:_autojoin(cluster_utils.get_node_identifier(singletons.configuration))
if err then
ngx.log(ngx.ERR, tostring(err))
end
Expand Down Expand Up @@ -73,7 +73,7 @@ local function send_keepalive(premature)
local elapsed = lock:lock("keepalive")
if elapsed and elapsed == 0 then
-- Send keepalive
local node_name = cluster_utils.get_node_name(singletons.configuration)
local node_name = cluster_utils.get_node_identifier(singletons.configuration)
local nodes, err = singletons.dao.nodes:find_all {name = node_name}
if err then
ngx.log(ngx.ERR, tostring(err))
Expand Down
14 changes: 8 additions & 6 deletions kong/dao/postgres_db.lua
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,17 @@ local function parse_error(err_str)
local err
if string.find(err_str, "Key .* already exists") then
local col, value = string.match(err_str, "%((.+)%)=%((.+)%)")
err = Errors.unique {[col] = value}
if col then
err = Errors.unique {[col] = value}
end
elseif string.find(err_str, "violates foreign key constraint") then
local col, value = string.match(err_str, "%((.+)%)=%((.+)%)")
err = Errors.foreign {[col] = value}
else
err = Errors.db(err_str)
if col then
err = Errors.foreign {[col] = value}
end
end

return err
return err or Errors.db(err_str)
end

local function get_select_fields(schema)
Expand Down
2 changes: 1 addition & 1 deletion kong/meta.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
local version = setmetatable({
major = 0,
minor = 8,
patch = 2,
patch = 3,
--pre_release = "alpha"
}, {
__tostring = function(t)
Expand Down
22 changes: 7 additions & 15 deletions kong/plugins/oauth2/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ local url = require "socket.url"
local Multipart = require "multipart"
local string_find = string.find
local req_get_headers = ngx.req.get_headers
local check_https = utils.check_https

local _M = {}

Expand Down Expand Up @@ -83,17 +84,6 @@ local function get_redirect_uri(client_id)
return client and client.redirect_uri or nil, client
end

local HTTPS = "https"

local function is_https(conf)
local result = ngx.var.scheme:lower() == HTTPS
if not result and conf.accept_http_if_already_terminated then
local forwarded_proto_header = ngx.req.get_headers()["x-forwarded-proto"]
result = forwarded_proto_header and forwarded_proto_header:lower() == HTTPS
end
return result
end

local function retrieve_parameters()
ngx.req.read_body()
-- OAuth2 parameters could be in both the querystring or body
Expand Down Expand Up @@ -132,8 +122,9 @@ local function authorize(conf)
local state = parameters[STATE]
local allowed_redirect_uris, client, redirect_uri, parsed_redirect_uri

if not is_https(conf) then
response_params = {[ERROR] = "access_denied", error_description = "You must use HTTPS"}
local is_https, err = check_https(conf.accept_http_if_already_terminated)
if not is_https then
response_params = {[ERROR] = "access_denied", error_description = err or "You must use HTTPS"}
else
if conf.provision_key ~= parameters.provision_key then
response_params = {[ERROR] = "invalid_provision_key", error_description = "Invalid Kong provision_key"}
Expand Down Expand Up @@ -252,8 +243,9 @@ local function issue_token(conf)
local parameters = retrieve_parameters()
local state = parameters[STATE]

if not is_https(conf) then
response_params = {[ERROR] = "access_denied", error_description = "You must use HTTPS"}
local is_https, err = check_https(conf.accept_http_if_already_terminated)
if not is_https then
response_params = {[ERROR] = "access_denied", error_description = err or "You must use HTTPS"}
else
local grant_type = parameters[GRANT_TYPE]
if not (grant_type == GRANT_AUTHORIZATION_CODE or
Expand Down
3 changes: 3 additions & 0 deletions kong/plugins/oauth2/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ end

local function validate_uris(v, t, column)
if v then
if #v < 1 then
return false, "at least one URI is required"
end
for _, uri in ipairs(v) do
local parsed_uri = url.parse(uri)
if not (parsed_uri and parsed_uri.host and parsed_uri.scheme) then
Expand Down
7 changes: 6 additions & 1 deletion kong/plugins/response-transformer/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ local BasePlugin = require "kong.plugins.base_plugin"
local body_filter = require "kong.plugins.response-transformer.body_transformer"
local header_filter = require "kong.plugins.response-transformer.header_transformer"

local is_body_transform_set = header_filter.is_body_transform_set
local is_json_body = header_filter.is_json_body

local ResponseTransformerHandler = BasePlugin:extend()


function ResponseTransformerHandler:new()
ResponseTransformerHandler.super.new(self, "response-transformer")
end
Expand All @@ -20,7 +24,8 @@ end

function ResponseTransformerHandler:body_filter(conf)
ResponseTransformerHandler.super.body_filter(self)
if body_filter.is_json_body(ngx.header["content-type"]) then

if is_body_transform_set(conf) and is_json_body(ngx.header["content-type"]) then
local chunk, eof = ngx.arg[1], ngx.arg[2]
if eof then
local body = body_filter.transform_json_body(conf, ngx.ctx.buffer)
Expand Down
4 changes: 4 additions & 0 deletions kong/plugins/response-transformer/header_transformer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ local function is_body_transform_set(conf)
return #conf.add.json > 0 or #conf.remove.json > 0 or #conf.replace.json > 0 or #conf.append.json > 0
end

-- export utility functions
_M.is_json_body = is_json_body
_M.is_body_transform_set = is_body_transform_set

---
-- # Example:
-- ngx.headers = header_filter.transform_headers(conf, ngx.headers)
Expand Down
14 changes: 2 additions & 12 deletions kong/plugins/ssl/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@
local BasePlugin = require "kong.plugins.base_plugin"
local responses = require "kong.tools.responses"
local cache = require "kong.tools.database_cache"
local check_https = require("kong.tools.utils").check_https

local SSLHandler = BasePlugin:extend()

SSLHandler.PRIORITY = 3000

local HTTPS = "https"

local function is_https(conf)
local result = ngx.var.scheme:lower() == HTTPS
if not result and conf.accept_http_if_already_terminated then
local forwarded_proto_header = ngx.req.get_headers()["x-forwarded-proto"]
result = forwarded_proto_header and forwarded_proto_header:lower() == HTTPS
end
return result
end

function SSLHandler:new()
SSLHandler.super.new(self, "ssl")
end
Expand Down Expand Up @@ -50,7 +40,7 @@ end

function SSLHandler:access(conf)
SSLHandler.super.access(self)
if conf.only_https and not is_https(conf) then
if conf.only_https and not check_https(conf.accept_http_if_already_terminated) then
ngx.header["connection"] = { "Upgrade" }
ngx.header["upgrade"] = "TLS/1.0, HTTP/1.1"
return responses.send(426, {message="Please use HTTPS protocol"})
Expand Down
25 changes: 23 additions & 2 deletions kong/tools/cluster.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
local IO = require "kong.tools.io"
local utils = require "kong.tools.utils"
local singletons = require "kong.singletons"

local _M = {}

function _M.get_node_name(conf)
return utils.get_hostname().."_"..conf.cluster_listen
local IDENTIFIER = "serf.id"

function _M.get_node_identifier(conf)
local id = singletons.serf_id
if not id then
id = IO.read_file(IO.path:join(conf.nginx_working_dir, IDENTIFIER))
singletons.serf_id = id
end
return id
end

function _M.create_node_identifier(conf)
local path = IO.path:join(conf.nginx_working_dir, IDENTIFIER)
if not IO.file_exists(path) then
local id = utils.get_hostname().."_"..conf.cluster_listen.."_"..utils.random_string()
local _, err = IO.write_to_file(path, id)
if err then
return false, err
end
end
return true
end

return _M
29 changes: 29 additions & 0 deletions kong/tools/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,35 @@ function _M.encode_args(args, raw)
return table_concat(query, "&")
end

--- Checks whether a request is https or was originally https (but already terminated).
-- It will check in the current request (global `ngx` table). If the header `X-Forwarded-Proto` exists
-- with value `https` then it will also be considered as an https connection.
-- @param allow_terminated if truthy, the `X-Forwarded-Proto` header will be checked as well.
-- @return boolean or nil+error in case the header exists multiple times
_M.check_https = function(allow_terminated)
if ngx.var.scheme:lower() == "https" then
return true
end

if not allow_terminated then
return false
end

local forwarded_proto_header = ngx.req.get_headers()["x-forwarded-proto"]
if tostring(forwarded_proto_header):lower() == "https" then
return true
end

if type(forwarded_proto_header) == "table" then
-- we could use the first entry (lower security), or check the contents of each of them (slow). So for now defensive, and error
-- out on multiple entries for the x-forwarded-proto header.
return nil, "Only one X-Forwarded-Proto header allowed"
end

return false
end


--- Calculates a table size.
-- All entries both in array and hash part.
-- @param t The table to use
Expand Down
36 changes: 34 additions & 2 deletions spec/plugins/cors/access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe("CORS Plugin", function()
assert.are.equal(tostring(true), headers["access-control-allow-credentials"])
end)

it("should work with preflight_continue=true", function()
it("should work with preflight_continue=true and a duplicate header set by the API", function()
-- An OPTIONS preflight request with preflight_continue=true should have the same response as directly invoking the final API

local response, status, headers = http_client.options(PROXY_URL.."/headers", {}, {host = "cors3.com"})
Expand Down Expand Up @@ -131,7 +131,39 @@ describe("CORS Plugin", function()
assert.are.equal(tostring(23), headers["access-control-max-age"])

-- Any other request that's not a preflight request, should match our plugin configuration
local _, status, headers = http_client.get(PROXY_URL.."/get", {}, {host = "cors3.com"})
local _, status, headers = http_client.get(PROXY_URL.."/get", {}, {host = "cors4.com"})

assert.are.equal(200, status)
assert.are.equal("example.com", headers["access-control-allow-origin"])
assert.are.equal("x-auth-token", headers["access-control-expose-headers"])
assert.are.equal(tostring(true), headers["access-control-allow-credentials"])
end)

it("should work with preflight_continue=false and a duplicate header set by the API", function()
-- An OPTIONS preflight request with preflight_continue=false should be handled by Kong instead

local response, status, headers = http_client.options(PROXY_URL.."/headers", {}, {host = "cors4.com"})
local response2, status2, headers2 = http_client.options("http://httpbin.org/response-headers", {}, {host = "cors4.com"})

headers["via"] = nil
headers["x-kong-proxy-latency"] = nil
headers["x-kong-upstream-latency"] = nil
headers["date"] = nil
headers2["date"] = nil

assert.are.equal(response, response2)
assert.are_not.equal(status, status2)
assert.are_not.same(headers, headers2)

assert.are.equal("example.com", headers["access-control-allow-origin"])
assert.are.equal("GET", headers["access-control-allow-methods"])
assert.are.equal("origin,type,accepts", headers["access-control-allow-headers"])
assert.are.equal(nil, headers["access-control-expose-headers"])
assert.are.equal(tostring(true), headers["access-control-allow-credentials"])
assert.are.equal(tostring(23), headers["access-control-max-age"])

-- Any other request that's not a preflight request, should match our plugin configuration
local _, status, headers = http_client.get(PROXY_URL.."/response-headers", {["access-control-allow-origin"] = "*"}, {host = "cors4.com"})

assert.are.equal(200, status)
assert.are.equal("example.com", headers["access-control-allow-origin"])
Expand Down
Loading

0 comments on commit 33a8cab

Please sign in to comment.