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

Feature/global plugins #1369

Closed
wants to merge 2 commits into from
Closed
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
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 @@ -76,7 +76,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
115 changes: 90 additions & 25 deletions spec/02-integration/05-proxy/03-plugins_triggering_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,71 @@ describe("Plugins triggering", function()
local client
setup(function()
helpers.kill_all()
helpers.prepare_prefix()
helpers.dao:truncate_tables()

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 @@ -42,23 +82,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)
1 change: 0 additions & 1 deletion spec/helpers_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe("helpers: assertions and modifiers;", function()
upstream_url = "http://httpbin.org"
})

helpers.create_prefix()
assert(helpers.start_kong())
end)
teardown(function()
Expand Down