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

Add plugin key-token. #13955

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
75 changes: 75 additions & 0 deletions kong/plugins/key-token/access.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
local http = require "kong.plugins.key-token.auth_service"
local error = error


local _M = {}


function _M:execute(plugin_conf)
-- Here is the main logic
-- 1. check if the client has the auth_key in the header.
-- 1.1 if user has no auth_key, return 412
-- 1.2 if user has auth_key use the key to auth authentication server
-- 2.1 if the JWT token is valid then go to 4
-- 2.2 request the authentication server with the auth_key
-- 2.2.1 authentication server reply 200 with the JWT token is the auth_key is valid,
-- 2.2.2 authentication server reply 403 reject the request if the auth_key is invalid
-- 3 cache the JWT token
-- 4.1 if none 200, return immediately, no access to the upstream
-- 4.2 if 200, access the upstream with the Authentication header
local auth_key = kong.request.get_header(plugin_conf.request_key_name)
local auth_server = plugin_conf.auth_server
local ttl = plugin_conf.ttl
local cached_token, err = self:get_cached_token(auth_key, auth_server, ttl)
if err then
kong.log.err("Failed to acquire token associates with the key. Error: " .. err)
return
end

if cached_token then
self:inject_token_to_service_header(cached_token)
return
end

end


function _M:get_cached_token(auth_key, auth_server, ttl)
-- return the cached token if it is not out of life. or else return nil
local cache = kong.cache
local credential_cache_key = kong.db.keyauth_credentials:cache_key(auth_key)
local credential, err = cache:get(credential_cache_key, { resurrent_ttl = ttl }, load_auth_token, auth_key, auth_server)
if err then
return nil, err
else
return credential, nil
end
end


function _M:inject_token_to_service_header(token)
kong.service.request.set_header("Authorizaion", "Bearer " .. token)
end


function _M:save_token_to_cache(auth_key, token)
end

function load_auth_token(auth_key, auth_server)
-- Maybe to set TIMEOUT variable to control the timeout?
local auth_headers = { auth_key = auth_key }
local body, status_code, headers, status_text = http.request {
url = auth_server,
headers = auth_headers,
}

kong.log.debug("return code from auth service " .. status_code)
if status_code == 200 then
return body, nil
else
return nil, error("Auth server return non 200 code " .. status_code)
end
end


return _M
18 changes: 18 additions & 0 deletions kong/plugins/key-token/auth_service.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local http = require "socket.http"


local default_payload = "123456"
local _M = {}

function _M.request(req)
kong.log.inspect(req)
if string.find(req.url, "localhost") then
-- mock the http request
return default_payload, 200, req.headers, "200 OK"
else
return http.request(req)
end
end


return _M
17 changes: 17 additions & 0 deletions kong/plugins/key-token/handler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
local access = require "kong.plugins.key-token.access"


local plugin = {
PRIORITY = 1255, -- Execute before key-auth
VERSION = "0.1.0", -- The initial version
}


-- runs in the 'access_by_lua_block'
function plugin:access(plugin_conf)
access:execute(plugin_conf)
end --]]


-- return our plugin object
return plugin
40 changes: 40 additions & 0 deletions kong/plugins/key-token/schema.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
local typedefs = require "kong.db.schema.typedefs"


local PLUGIN_NAME = "key-token"


local schema = {
name = PLUGIN_NAME,
fields = {
-- the 'fields' array is the top-level entry with fields defined by Kong
{ consumer = typedefs.no_consumer }, -- this plugin cannot be configured on a consumer (typical for auth plugins)
{ protocols = typedefs.protocols_http },
{ config = {
-- The 'config' record is the custom part of the plugin schema
type = "record",
fields = {
-- a standard defined field (typedef), with some customizations
{ request_key_name = typedefs.header_name {
description = "The header name that is used to send to backend authentication service.",
type = "string",
required = true,
default = "auth_key" }, },
{ auth_server = typedefs.url {
description = "The authenticaiton/authorization service URL. please note that 'localhost' is reserved for integration test.",
required = true,
default = "http://auth_server.com" }, },
{ ttl = {
description = "TTL for cached token from auth server.",
type = "integer",
default = 600,
required = true,
gt = 0, }, },
},
entity_checks = { },
},
},
},
}

return schema
86 changes: 86 additions & 0 deletions spec/02-integration/23-key-token_plugins/01-integration_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
local helpers = require "spec.helpers"


local PLUGIN_NAME = "key-token"


for _, strategy in helpers.all_strategies() do if strategy ~= "cassandra" then
describe(PLUGIN_NAME .. ": (access) [#" .. strategy .. "]", function()
local client

lazy_setup(function()

local bp = helpers.get_db_utils(strategy == "off" and "postgres" or strategy, nil, { PLUGIN_NAME })

-- Inject a test route. No need to create a service, there is a default
-- service which will echo the request.
local route_auth_server = bp.routes:insert({
hosts = { "auth_server.com" },
})

local route_upstream = bp.routes:insert({
hosts = { "resource1.com" },
})
-- add the plugin to test to the route we created
bp.plugins:insert {
name = PLUGIN_NAME,
route = { id = route_auth_server.id },
config = {},
}

bp.plugins:insert {
name = PLUGIN_NAME,
route = { id = route_upstream.id },
config = {auth_server = "http://localhost:9001"},
}
-- start kong
assert(helpers.start_kong({
-- set the strategy
database = strategy,
-- use the custom test template to create a local mock server
nginx_conf = "spec/fixtures/custom_nginx.template",
-- make sure our plugin gets loaded
plugins = "bundled," .. PLUGIN_NAME,
-- write & load declarative config, only if 'strategy=off'
declarative_config = strategy == "off" and helpers.make_yaml_file() or nil,
}))
end)

lazy_teardown(function()
helpers.stop_kong(nil, true)
end)

before_each(function()
client = helpers.proxy_client()
end)

after_each(function()
if client then client:close() end
end)



describe("request", function()
it("gets a auth key header", function()
local r = client:get("/request", {
headers = {
host = "resource1.com",
auth_key = "123456"
}
})
-- validate that the request succeeded, response status 200
assert.response(r).has.status(200)
-- now check the request (as echoed by the mock backend) to have the header
local header_value = assert.request(r).has.header("auth_key")
-- validate the value of that header
assert.equal("123456", header_value)
-- The mock server returns 123456. The ideal auth server would issue JWT token
local auth_token = assert.request(r).has.header("Authorizaion")
assert.equal("Bearer 123456", auth_token)
end)
end)


end)

end end
36 changes: 36 additions & 0 deletions spec/03-plugins/46-key-token/01-unit_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
local PLUGIN_NAME = "key-token"


-- helper function to validate data against a schema
local validate do
local validate_entity = require("spec.helpers").validate_plugin_config_schema
local plugin_schema = require("kong.plugins."..PLUGIN_NAME..".schema")

function validate(data)
return validate_entity(data, plugin_schema)
end
end


describe(PLUGIN_NAME .. ": (schema)", function()


it("accepts request key, auth server and ttl", function()
local ok, err = validate({
request_key_name = "My-Request-Header",
auth_server = "http://my-auth-service/",
ttl = 300
})
assert.is_nil(err)
assert.is_truthy(ok)
end)


it("accepts default configs", function()
local ok, err = validate({ })
assert.is_nil(err)
assert.is_truthy(ok)
end)


end)