kong/spec/03-plugins/09-key-auth/01-api_spec.lua (564 lines of code) (raw):

local cjson = require "cjson" local helpers = require "spec.helpers" local utils = require "kong.tools.utils" for _, strategy in helpers.each_strategy() do describe("Plugin: key-auth (API) [#" .. strategy .. "]", function() local consumer local admin_client local bp local db local route1 local route2 lazy_setup(function() bp, db = helpers.get_db_utils(strategy, { "routes", "services", "plugins", "consumers", "keyauth_credentials", }) route1 = bp.routes:insert { hosts = { "keyauth1.test" }, } route2 = bp.routes:insert { hosts = { "keyauth2.test" }, } consumer = bp.consumers:insert({ username = "bob" }, { nulls = true }) assert(helpers.start_kong({ database = strategy, nginx_conf = "spec/fixtures/custom_nginx.template", })) admin_client = helpers.admin_client() end) lazy_teardown(function() if admin_client then admin_client:close() end helpers.stop_kong() end) describe("/consumers/:consumer/key-auth", function() describe("POST", function() after_each(function() db:truncate("keyauth_credentials") end) it("creates a key-auth credential with key", function() local res = assert(admin_client:send { method = "POST", path = "/consumers/bob/key-auth", body = { key = "1234" }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.equal("1234", json.key) end) it("creates a key-auth auto-generating a unique key", function() local res = assert(admin_client:send { method = "POST", path = "/consumers/bob/key-auth", body = {}, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.is_string(json.key) local first_key = json.key db:truncate("keyauth_credentials") local res = assert(admin_client:send { method = "POST", path = "/consumers/bob/key-auth", body = {}, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.is_string(json.key) assert.not_equal(first_key, json.key) end) it("creates a key-auth credential with tags", function() local res = assert(admin_client:send { method = "POST", path = "/consumers/bob/key-auth", body = { key = "keyauth-with-tags", tags = { "tag1", "tag2"}, }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.equal("tag1", json.tags[1]) assert.equal("tag2", json.tags[2]) end) it("creates a key-auth credential with a ttl", function() local res = assert(admin_client:send { method = "POST", path = "/consumers/bob/key-auth", body = { ttl = 1, }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.is_string(json.key) ngx.sleep(3) local id = json.id local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth/" .. id, }) assert.res_status(404, res) end) end) describe("GET", function() lazy_setup(function() for i = 1, 3 do assert(bp.keyauth_credentials:insert { consumer = { id = consumer.id } }) end end) lazy_teardown(function() db:truncate("keyauth_credentials") end) it("retrieves the first page", function() local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth" }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.is_table(json.data) assert.equal(3, #json.data) end) end) describe("GET #ttl", function() lazy_setup(function() for i = 1, 3 do bp.keyauth_credentials:insert({ consumer = { id = consumer.id }, }, { ttl = 10 }) end end) lazy_teardown(function() db:truncate("keyauth_credentials") end) it("entries contain ttl when specified", function() local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth" }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.is_table(json.data) for _, credential in ipairs(json.data) do assert.not_nil(credential.ttl) end end) end) end) describe("/consumers/:consumer/key-auth/:id", function() local credential before_each(function() db:truncate("keyauth_credentials") credential = bp.keyauth_credentials:insert { consumer = { id = consumer.id }, } end) describe("GET", function() it("retrieves key-auth credential by id", function() local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth/" .. credential.id }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(credential.id, json.id) end) it("retrieves key-auth credential by key", function() local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth/" .. credential.key }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(credential.id, json.id) end) it("retrieves credential by id only if the credential belongs to the specified consumer", function() assert(bp.consumers:insert { username = "alice" }) local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth/" .. credential.id }) assert.res_status(200, res) res = assert(admin_client:send { method = "GET", path = "/consumers/alice/key-auth/" .. credential.id }) assert.res_status(404, res) end) it("key-auth credential contains #ttl", function() local credential = bp.keyauth_credentials:insert({ consumer = { id = consumer.id }, }, { ttl = 10 }) local res = assert(admin_client:send { method = "GET", path = "/consumers/bob/key-auth/" .. credential.id }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(credential.id, json.id) assert.not_nil(json.ttl) end) end) describe("PUT", function() after_each(function() db:truncate("keyauth_credentials") end) it("creates a key-auth credential with key", function() local res = assert(admin_client:send { method = "PUT", path = "/consumers/bob/key-auth/1234", body = {}, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.equal("1234", json.key) end) it("creates a key-auth credential auto-generating the key", function() local res = assert(admin_client:send { method = "PUT", path = "/consumers/bob/key-auth/c16bbff7-5d0d-4a28-8127-1ee581898f11", body = {}, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(consumer.id, json.consumer.id) assert.is_string(json.key) end) end) describe("PATCH", function() it("updates a credential by id", function() local res = assert(admin_client:send { method = "PATCH", path = "/consumers/bob/key-auth/" .. credential.id, body = { key = "4321" }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("4321", json.key) end) it("updates a credential by key", function() local res = assert(admin_client:send { method = "PATCH", path = "/consumers/bob/key-auth/" .. credential.key, body = { key = "4321UPD" }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("4321UPD", json.key) end) describe("errors", function() it("handles invalid input", function() local res = assert(admin_client:send { method = "PATCH", path = "/consumers/bob/key-auth/" .. credential.id, body = { key = 123 }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.same({ key = "expected a string" }, json.fields) end) end) end) describe("DELETE", function() it("deletes a credential", function() local res = assert(admin_client:send { method = "DELETE", path = "/consumers/bob/key-auth/" .. credential.id, }) assert.res_status(204, res) end) describe("errors", function() it("returns 400 on invalid input", function() local res = assert(admin_client:send { method = "DELETE", path = "/consumers/bob/key-auth/blah" }) assert.res_status(404, res) end) it("returns 404 if not found", function() local res = assert(admin_client:send { method = "DELETE", path = "/consumers/bob/key-auth/00000000-0000-0000-0000-000000000000" }) assert.res_status(404, res) end) end) end) end) describe("/plugins for route", function() it("fails with invalid key_names", function() local key_name = "hello\\world" local res = assert(admin_client:send { method = "POST", path = "/plugins", body = { name = "key-auth", route = { id = route1.id }, config = { key_names = {key_name}, }, }, headers = { ["Content-Type"] = "application/json" } }) assert.response(res).has.status(400) local body = assert.response(res).has.jsonbody() assert.equal("bad header name 'hello\\world', allowed characters are A-Z, a-z, 0-9, '_', and '-'", body.fields.config.key_names[1]) end) it("succeeds with valid key_names", function() local key_name = "hello-world" local res = assert(admin_client:send { method = "POST", path = "/plugins", body = { route = { id = route2.id }, name = "key-auth", config = { key_names = {key_name}, }, }, headers = { ["Content-Type"] = "application/json" } }) assert.response(res).has.status(201) local body = assert.response(res).has.jsonbody() assert.equal(key_name, body.config.key_names[1]) end) end) describe("/key-auths", function() local consumer2 describe("GET", function() lazy_setup(function() db:truncate("keyauth_credentials") for i = 1, 3 do bp.keyauth_credentials:insert { consumer = { id = consumer.id }, } end consumer2 = bp.consumers:insert { username = "bob-the-buidler", } for i = 1, 3 do bp.keyauth_credentials:insert { consumer = { id = consumer2.id }, } end end) it("retrieves all the key-auths with trailing slash", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths/", }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.is_table(json.data) assert.equal(6, #json.data) end) it("retrieves all the key-auths without trailing slash", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths", }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.is_table(json.data) assert.equal(6, #json.data) end) it("paginates through the key-auths", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths?size=3", }) local body = assert.res_status(200, res) local json_1 = cjson.decode(body) assert.is_table(json_1.data) assert.equal(3, #json_1.data) res = assert(admin_client:send { method = "GET", path = "/key-auths", query = { size = 3, offset = json_1.offset, } }) body = assert.res_status(200, res) local json_2 = cjson.decode(body) assert.is_table(json_2.data) assert.equal(3, #json_2.data) assert.not_same(json_1.data, json_2.data) -- Disabled: on Cassandra, the last page still returns a -- next_page token, and thus, an offset proprty in the -- response of the Admin API. --assert.is_nil(json_2.offset) -- last page end) end) describe("POST", function() lazy_setup(function() db:truncate("keyauth_credentials") end) it("does not create key-auth credential when missing consumer", function() local res = assert(admin_client:send { method = "POST", path = "/key-auths", body = { key = "1234", }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.same("schema violation (consumer: required field missing)", json.message) end) it("creates key-auth credential", function() local res = assert(admin_client:send { method = "POST", path = "/key-auths", body = { key = "1234", consumer = { id = consumer.id } }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal("1234", json.key) end) end) end) describe("/key-auths/:credential_key_or_id", function() describe("PUT", function() lazy_setup(function() db:truncate("keyauth_credentials") end) it("does not create key-auth credential when missing consumer", function() local res = assert(admin_client:send { method = "PUT", path = "/key-auths/1234", body = { }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.same("schema violation (consumer: required field missing)", json.message) end) it("creates key-auth credential", function() local res = assert(admin_client:send { method = "PUT", path = "/key-auths/1234", body = { consumer = { id = consumer.id } }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("1234", json.key) end) end) end) describe("/key-auths/:credential_key_or_id/consumer", function() describe("GET", function() local credential lazy_setup(function() db:truncate("keyauth_credentials") credential = bp.keyauth_credentials:insert { consumer = { id = consumer.id }, } end) it("retrieve Consumer from a credential's id", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths/" .. credential.id .. "/consumer" }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(consumer, json) end) it("retrieve a Consumer from a credential's key", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths/" .. credential.key .. "/consumer" }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(consumer, json) end) it("returns 404 for a random non-existing id", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths/" .. utils.uuid() .. "/consumer" }) assert.res_status(404, res) end) it("returns 404 for a random non-existing key", function() local res = assert(admin_client:send { method = "GET", path = "/key-auths/" .. utils.random_string() .. "/consumer" }) assert.res_status(404, res) end) end) end) end) end