Skip to content

Commit

Permalink
feat(core) global plugins (#1403)
Browse files Browse the repository at this point in the history
Closes #1369.
  • Loading branch information
subnetmarco authored Jul 28, 2016
1 parent ff5e183 commit 14d30d4
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 36 deletions.
30 changes: 19 additions & 11 deletions kong/core/plugins_iterator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,10 @@ local function load_plugin_configuration(api_id, consumer_id, plugin_name)
end

if #rows > 0 then
if consumer_id == nil then
for _, row in ipairs(rows) do
if row.consumer_id == nil then
return row
end
for _, row in ipairs(rows) do
if api_id == row.api_id and consumer_id == row.consumer_id then
return row
end
else
return rows[1]
end
else
-- insert a cached value to not trigger too many DB queries.
Expand Down Expand Up @@ -67,15 +63,27 @@ local function iter_plugins_for_req(loaded_plugins, access_or_cert_ctx)
if plugin and ctx.api then
-- load the plugin configuration in early phases
if access_or_cert_ctx then
ctx.plugins_for_request[plugin.name] = load_plugin_configuration(ctx.api.id, nil, plugin.name)

local plugin_configuration

-- Search API and Consumer specific, or consumer specific
local consumer_id = (ctx.authenticated_credential or empty).consumer_id
if consumer_id and not plugin.schema.no_consumer then
local consumer_plugin_configuration = load_plugin_configuration(ctx.api.id, consumer_id, plugin.name)
if consumer_plugin_configuration then
ctx.plugins_for_request[plugin.name] = consumer_plugin_configuration
plugin_configuration = load_plugin_configuration(ctx.api.id, consumer_id, plugin.name)
if not plugin_configuration then
plugin_configuration = load_plugin_configuration(nil, consumer_id, plugin.name)
end
end

if not plugin_configuration then
-- Search API specific, or global
plugin_configuration = load_plugin_configuration(ctx.api.id, nil, plugin.name)
if not plugin_configuration then
plugin_configuration = load_plugin_configuration(nil, nil, plugin.name)
end
end

ctx.plugins_for_request[plugin.name] = plugin_configuration
end

-- return the plugin configuration
Expand Down
1 change: 0 additions & 1 deletion kong/dao/schemas/plugins.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ return {
},
api_id = {
type = "id",
required = true,
foreign = "apis:id"
},
consumer_id = {
Expand Down
2 changes: 1 addition & 1 deletion kong/tools/database_cache.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function _M.consumer_key(id)
end

function _M.plugin_key(name, api_id, consumer_id)
return CACHE_KEYS.PLUGINS..":"..name..":"..api_id..(consumer_id and ":"..consumer_id or "")
return CACHE_KEYS.PLUGINS..":"..name..(api_id and ":"..api_id or "")..(consumer_id and ":"..consumer_id or "")
end

function _M.basicauth_credential_key(username)
Expand Down
113 changes: 90 additions & 23 deletions spec/02-integration/05-proxy/03-plugins_triggering_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,70 @@ local helpers = require "spec.helpers"
describe("Plugins triggering", function()
local client
setup(function()
local consumer = assert(helpers.dao.consumers:insert {
username = "bob"
local consumer1 = assert(helpers.dao.consumers:insert {
username = "consumer1"
})
local api = assert(helpers.dao.apis:insert {
name = "mockbin",
request_path = "/mockbin",
strip_request_path = true,
assert(helpers.dao.keyauth_credentials:insert {
key = "secret1",
consumer_id = consumer1.id
})
local consumer2 = assert(helpers.dao.consumers:insert {
username = "consumer2"
})
assert(helpers.dao.keyauth_credentials:insert {
key = "secret2",
consumer_id = consumer2.id
})

-- Global configuration
assert(helpers.dao.apis:insert {
request_host = "global1.com",
upstream_url = "http://mockbin.com"
})
assert(helpers.dao.plugins:insert {
name = "key-auth",
config = { }
})
assert(helpers.dao.plugins:insert {
name = "rate-limiting",
api_id = api.id,
config = {
hour = 1,
}
})

-- API Specific Configuration
local api1 = assert(helpers.dao.apis:insert {
request_host = "api1.com",
upstream_url = "http://mockbin.com"
})
assert(helpers.dao.plugins:insert {
name = "rate-limiting",
api_id = api.id,
consumer_id = consumer.id,
api_id = api1.id,
config = {
hour = 1,
hour = 2,
}
})

-- Consumer Specific Configuration
assert(helpers.dao.plugins:insert {
name = "rate-limiting",
consumer_id = consumer2.id,
config = {
hour = 3,
}
})

-- API and Consumer Configuration
local api2 = assert(helpers.dao.apis:insert {
request_host = "api2.com",
upstream_url = "http://mockbin.com"
})
assert(helpers.dao.plugins:insert {
name = "rate-limiting",
api_id = api2.id,
consumer_id = consumer2.id,
config = {
hour = 4,
}
})

Expand All @@ -39,23 +81,48 @@ describe("Plugins triggering", function()
helpers.clean_prefix()
end)

-- here have 2 rows in our plugins table, one with a
-- consumer_id column, the other without.
-- With Cassandra, it is not possible to have a WHERE clause
-- targetting specifically the null consumer_id row. Hence,
-- depending on Cassandra storage internals, the plugin iterator could
-- return the row that applies to a consumer, or the one that does
-- not, making this behavior non-deterministic.
-- the previous **hack** was to have a "nullified" uuid (0000s),
-- but since Postgres support, this hack has been removed. Instead,
-- the plugin iterator now manually filters the rows returned :(
-- this hack will only be used when Cassandra is our backend.
it("applies the correct plugin for a consumer", function()
it("checks global configuration without credentials", function()
local res = assert(client:send {
method = "GET",
path = "/status/200",
headers = { Host = "global1.com" }
})
assert.res_status(401, res)
end)
it("checks global api configuration", function()
local res = assert(client:send {
method = "GET",
path = "/mockbin/status/200"
path = "/status/200?apikey=secret1",
headers = { Host = "global1.com" }
})
assert.res_status(200, res)
assert.equal("1", res.headers["x-ratelimit-limit-hour"])
end)
it("checks api specific configuration", function()
local res = assert(client:send {
method = "GET",
path = "/status/200?apikey=secret1",
headers = { Host = "api1.com" }
})
assert.res_status(200, res)
assert.equal("2", res.headers["x-ratelimit-limit-hour"])
end)
it("checks global consumer configuration", function()
local res = assert(client:send {
method = "GET",
path = "/status/200?apikey=secret2",
headers = { Host = "global1.com" }
})
assert.res_status(200, res)
assert.equal("3", res.headers["x-ratelimit-limit-hour"])
end)
it("checks consumer specific configuration", function()
local res = assert(client:send {
method = "GET",
path = "/status/200?apikey=secret2",
headers = { Host = "api2.com" }
})
assert.res_status(200, res)
assert.equal("4", res.headers["x-ratelimit-limit-hour"])
end)
end)

0 comments on commit 14d30d4

Please sign in to comment.