diff --git a/kong/plugins/frontier/access.lua b/kong/plugins/frontier/access.lua index 6a96039..3985e74 100644 --- a/kong/plugins/frontier/access.lua +++ b/kong/plugins/frontier/access.lua @@ -196,7 +196,7 @@ local function check_request_permission(conf, cookies, bearer) end -local function append_claims_as_headers(conf, user_token) +local function append_claims_as_headers(conf, claims) local clear_header = kong.service.request.clear_header local set_header = kong.service.request.set_header @@ -215,14 +215,6 @@ local function append_claims_as_headers(conf, user_token) end end - local jwt, err = jwt_decoder.decode_token(user_token) - if err then - kong.log.warn("failed to decode token: ", err) - return fail_auth() - end - - local claims = jwt.claims - local claims_are_readable = type(claims) == "table" if not claims_are_readable then @@ -241,33 +233,21 @@ local function append_claims_as_headers(conf, user_token) end end -local function verify_organization_id_header(conf, user_token) - local request_organization_id = kong.request.get_header(conf.request_organization_id_header) +local function verify_organization_id_header(conf, claims, request_organization_id) + local org_ids = type(claims) == "table" and claims[frontier_org_ids_claim_key] or nil + local org_id_header_verified = false - if request_organization_id then - local jwt, err = jwt_decoder.decode_token(user_token) - if err then - kong.log.warn("failed to decode token: ", err) - return fail_auth() - end - - local claims = jwt.claims - local org_ids = type(claims) == "table" and claims[frontier_org_ids_claim_key] or nil - - local org_id_header_verified = false - - if type(org_ids) == "string" then - for word in string.gmatch(org_ids, '([^,]+)') do - if word == request_organization_id then - org_id_header_verified = true - end + if type(org_ids) == "string" then + for word in string.gmatch(org_ids, '([^,]+)') do + if word == request_organization_id then + org_id_header_verified = true end end + end - if not org_id_header_verified then - kong.log.info(conf.request_organization_id_header .. " header removed for request") - kong.service.request.clear_header(conf.request_organization_id_header) - end + if not org_id_header_verified then + kong.log.info(conf.request_organization_id_header .. " header removed for request") + kong.service.request.clear_header(conf.request_organization_id_header) end end @@ -294,12 +274,25 @@ function _M.run(conf) end end - if #conf.token_claims_to_append_as_headers > 0 then - append_claims_as_headers(conf, user_token) - end + local wants_claim_headers = #conf.token_claims_to_append_as_headers > 0 + local organization_id_to_verify = conf.verify_request_organization_id_header + and kong.request.get_header(conf.request_organization_id_header) or nil + + if wants_claim_headers or organization_id_to_verify then + local jwt, err = jwt_decoder.decode_token(user_token) + + if err then + kong.log.warn("failed to decode token: ", err) + return fail_auth() + end + + if wants_claim_headers then + append_claims_as_headers(conf, jwt.claims) + end - if conf.verify_request_organization_id_header then - verify_organization_id_header(conf, user_token) + if organization_id_to_verify then + verify_organization_id_header(conf, jwt.claims, organization_id_to_verify) + end end end end diff --git a/kong/plugins/spec/frontier-test/05-access_spec.lua b/kong/plugins/spec/frontier-test/05-access_spec.lua index cec43e4..f074286 100644 --- a/kong/plugins/spec/frontier-test/05-access_spec.lua +++ b/kong/plugins/spec/frontier-test/05-access_spec.lua @@ -41,7 +41,7 @@ end -- `answer` is either a token the server hands back in a 200, or a table -- describing the raw reply so the failure paths can be driven. local function run_plugin(conf, answer, request_headers) - local result = { set = {}, cleared = {}, status = nil, exit_headers = nil } + local result = { set = {}, cleared = {}, status = nil, exit_headers = nil, decodes = 0 } local reply, reply_err if type(answer) == "table" then @@ -115,6 +115,15 @@ local function run_plugin(conf, answer, request_headers) package.loaded["kong.plugins." .. PLUGIN_NAME .. "." .. mod] = nil end + -- wraps the real decoder so the number of reads of one token is countable + local real_decoder = require("kong.plugins." .. PLUGIN_NAME .. ".jwt_decoder") + package.loaded["kong.plugins." .. PLUGIN_NAME .. ".jwt_decoder"] = { + decode_token = function(token) + result.decodes = result.decodes + 1 + return real_decoder.decode_token(token) + end + } + local access = require("kong.plugins." .. PLUGIN_NAME .. ".access") local ok, err = pcall(access.run, conf) @@ -194,6 +203,51 @@ describe("Plugin: " .. PLUGIN_NAME .. " (access), ", function() end) end) + describe("reading the token", function() + local both_on = function() + local c = base_conf() + c.verify_request_organization_id_header = true + return c + end + + it("reads the token once when both header steps run", function() + local out = run_plugin(both_on(), token_with_payload('{"sub":"u1","org_ids":"o1"}'), + { ["x-organization-id"] = "o1" }) + + assert.is_nil(out.raised) + assert.equal(1, out.decodes) + end) + + it("reads it once for the claim headers alone", function() + local out = run_plugin(base_conf(), token_with_payload('{"sub":"u1"}'), {}) + + assert.is_nil(out.raised) + assert.equal(1, out.decodes) + end) + + it("reads it once for the organization check alone", function() + local c = both_on() + c.token_claims_to_append_as_headers = {} + + local out = run_plugin(c, token_with_payload('{"org_ids":"o1"}'), + { ["x-organization-id"] = "o1" }) + + assert.is_nil(out.raised) + assert.equal(1, out.decodes) + end) + + it("does not read it at all when nothing needs the claims", function() + local c = both_on() + c.token_claims_to_append_as_headers = {} + + -- the organization check is on but the client sent no header + local out = run_plugin(c, token_with_payload('{"sub":"u1"}'), {}) + + assert.is_nil(out.raised) + assert.equal(0, out.decodes) + end) + end) + describe("what the auth server answers", function() it("a 200 with no token in it gives a 401, not a 500", function() -- the one behaviour change in this work. it used to pass nil into