diff --git a/database/migrations/cassandra/2015-05-22-235608_plugins_fix.lua b/database/migrations/cassandra/2015-05-22-235608_plugins_fix.lua new file mode 100644 index 000000000000..993ea6a67fd5 --- /dev/null +++ b/database/migrations/cassandra/2015-05-22-235608_plugins_fix.lua @@ -0,0 +1,19 @@ +local Migration = { + name = "2015-05-22-235608_plugins_fix", + + up = function(options) + return [[ + CREATE INDEX IF NOT EXISTS ON keyauth_credentials(consumer_id); + CREATE INDEX IF NOT EXISTS ON basicauth_credentials(consumer_id); + ]] + end, + + down = function(options) + return [[ + DROP INDEX keyauth_credentials_consumer_id_idx; + DROP INDEX basicauth_credentials_consumer_id_idx; + ]] + end +} + +return Migration diff --git a/kong-0.3.0-1.rockspec b/kong-0.3.0-1.rockspec index 9b32e9badbc9..c5048ab0b4be 100644 --- a/kong-0.3.0-1.rockspec +++ b/kong-0.3.0-1.rockspec @@ -134,10 +134,11 @@ build = { ["kong.plugins.ssl.schema"] = "kong/plugins/ssl/schema.lua", ["kong.api.app"] = "kong/api/app.lua", + ["kong.api.crud_helpers"] = "kong/api/crud_helpers.lua", + ["kong.api.routes.kong"] = "kong/api/routes/kong.lua", ["kong.api.routes.apis"] = "kong/api/routes/apis.lua", ["kong.api.routes.consumers"] = "kong/api/routes/consumers.lua", ["kong.api.routes.plugins_configurations"] = "kong/api/routes/plugins_configurations.lua", - ["kong.api.routes.base_controller"] = "kong/api/routes/base_controller.lua" }, install = { conf = { "kong.yml" }, diff --git a/kong/api/app.lua b/kong/api/app.lua index 9b5f29950fc9..4b874463a032 100644 --- a/kong/api/app.lua +++ b/kong/api/app.lua @@ -78,6 +78,8 @@ local function parse_params(fn) end) end +app.parse_params = parse_params + app.default_route = function(self) local path = self.req.parsed_url.path:match("^(.*)/$") @@ -142,16 +144,16 @@ for _, v in ipairs({"kong", "apis", "consumers", "plugins_configurations"}) do end -- Loading plugins routes ---[[if configuration and configuration.plugins_available then +if configuration and configuration.plugins_available then for _, v in ipairs(configuration.plugins_available) do local loaded, mod = utils.load_module_if_exists("kong.plugins."..v..".api") if loaded then ngx.log(ngx.DEBUG, "Loading API endpoints for plugin: "..v) - mod() + attach_routes(mod) else ngx.log(ngx.DEBUG, "No API endpoints loaded for plugin: "..v) end end -end]] +end return app diff --git a/kong/api/crud_helpers.lua b/kong/api/crud_helpers.lua index 6681a347b707..40577b4dbed9 100644 --- a/kong/api/crud_helpers.lua +++ b/kong/api/crud_helpers.lua @@ -1,8 +1,45 @@ local responses = require "kong.tools.responses" +local validations = require "kong.dao.schemas_validation" local app_helpers = require "lapis.application" local _M = {} +function _M.find_api_by_name_or_id(self, dao_factory, helpers) + local fetch_keys = { + [validations.is_valid_uuid(self.params.name_or_id) and "id" or "name"] = self.params.name_or_id + } + self.params.name_or_id = nil + + -- TODO: make the base_dao more flexible so we can query find_one with key/values + -- https://github.com/Mashape/kong/issues/103 + local data, err = dao_factory.apis:find_by_keys(fetch_keys) + if err then + return helpers.yield_error(err) + end + + self.api = data[1] + if not self.api then + return helpers.responses.send_HTTP_NOT_FOUND() + end +end + +function _M.find_consumer_by_username_or_id(self, dao_factory, helpers) + local fetch_keys = { + [validations.is_valid_uuid(self.params.username_or_id) and "id" or "username"] = self.params.username_or_id + } + self.params.username_or_id = nil + + local data, err = dao_factory.consumers:find_by_keys(fetch_keys) + if err then + return helpers.yield_error(err) + end + + self.consumer = data[1] + if not self.consumer then + return helpers.responses.send_HTTP_NOT_FOUND() + end +end + function _M.paginated_set(self, dao_collection) local size = self.params.size and tonumber(self.params.size) or 100 local offset = self.params.offset and ngx.decode_base64(self.params.offset) or nil @@ -35,17 +72,17 @@ function _M.paginated_set(self, dao_collection) return responses.send_HTTP_OK(result, type(result) ~= "table") end -function _M.put(self, dao_collection) +function _M.put(params, dao_collection) local new_entity, err - if self.params.id then - new_entity, err = dao_collection:update(self.params) + if params.id then + new_entity, err = dao_collection:update(params) if not err and new_entity then return responses.send_HTTP_OK(new_entity) elseif not new_entity then return responses.send_HTTP_NOT_FOUND() end else - new_entity, err = dao_collection:insert(self.params) + new_entity, err = dao_collection:insert(params) if not err then return responses.send_HTTP_CREATED(new_entity) end @@ -56,8 +93,8 @@ function _M.put(self, dao_collection) end end -function _M.post(self, dao_collection) - local data, err = dao_collection:insert(self.params) +function _M.post(params, dao_collection) + local data, err = dao_collection:insert(params) if err then return app_helpers.yield_error(err) else diff --git a/kong/api/routes/apis.lua b/kong/api/routes/apis.lua index f097630bc97b..af93f0b7e296 100644 --- a/kong/api/routes/apis.lua +++ b/kong/api/routes/apis.lua @@ -1,43 +1,6 @@ -local validations = require "kong.dao.schemas" +local validations = require "kong.dao.schemas_validation" local crud = require "kong.api.crud_helpers" -local function find_api_by_name_or_id(self, dao_factory, helpers) - local fetch_keys = { - [validations.is_valid_uuid(self.params.name_or_id) and "id" or "name"] = self.params.name_or_id - } - self.params.name_or_id = nil - - -- TODO: make the base_dao more flexible so we can query find_one with key/values - -- https://github.com/Mashape/kong/issues/103 - local data, err = dao_factory.apis:find_by_keys(fetch_keys) - if err then - return helpers.yield_error(err) - end - - self.api = data[1] - if not self.api then - return helpers.responses.send_HTTP_NOT_FOUND() - end -end - -local function find_plugin_by_name_or_id(self, dao_factory, helpers) - local fetch_keys = { - api_id = self.api.id, - [validations.is_valid_uuid(self.params.plugin_name_or_id) and "id" or "name"] = self.params.plugin_name_or_id - } - self.params.plugin_name_or_id = nil - - local data, err = dao_factory.plugins_configurations:find_by_keys(fetch_keys) - if err then - return helpers.yield_error(err) - end - - self.plugin = data[1] - if not self.plugin then - return helpers.responses.send_HTTP_NOT_FOUND() - end -end - return { ["/apis/"] = { GET = function(self, dao_factory) @@ -45,16 +8,16 @@ return { end, PUT = function(self, dao_factory) - crud.put(self, dao_factory.apis) + crud.put(self.params, dao_factory.apis) end, POST = function(self, dao_factory) - crud.post(self, dao_factory.apis) + crud.post(self.params, dao_factory.apis) end }, ["/apis/:name_or_id"] = { - before = find_api_by_name_or_id, + before = crud.find_api_by_name_or_id, GET = function(self, dao_factory, helpers) return helpers.responses.send_HTTP_OK(self.api) @@ -72,7 +35,7 @@ return { ["/apis/:name_or_id/plugins/"] = { before = function(self, dao_factory, helpers) - find_api_by_name_or_id(self, dao_factory, helpers) + crud.find_api_by_name_or_id(self, dao_factory, helpers) self.params.api_id = self.api.id end, @@ -81,20 +44,34 @@ return { end, POST = function(self, dao_factory, helpers) - crud.post(self, dao_factory.plugins_configurations) + crud.post(self.params, dao_factory.plugins_configurations) end, PUT = function(self, dao_factory, helpers) - crud.put(self, dao_factory.plugins_configurations) + crud.put(self.params, dao_factory.plugins_configurations) end }, ["/apis/:name_or_id/plugins/:plugin_name_or_id"] = { before = function(self, dao_factory, helpers) - find_api_by_name_or_id(self, dao_factory, helpers) + crud.find_api_by_name_or_id(self, dao_factory, helpers) self.params.api_id = self.api.id - find_plugin_by_name_or_id(self, dao_factory, helpers) + local fetch_keys = { + api_id = self.api.id, + [validations.is_valid_uuid(self.params.plugin_name_or_id) and "id" or "name"] = self.params.plugin_name_or_id + } + self.params.plugin_name_or_id = nil + + local data, err = dao_factory.plugins_configurations:find_by_keys(fetch_keys) + if err then + return helpers.yield_error(err) + end + + self.plugin = data[1] + if not self.plugin then + return helpers.responses.send_HTTP_NOT_FOUND() + end end, GET = function(self, dao_factory, helpers) diff --git a/kong/api/routes/consumers.lua b/kong/api/routes/consumers.lua index 9b6edad3996b..610e0ffd4d3a 100644 --- a/kong/api/routes/consumers.lua +++ b/kong/api/routes/consumers.lua @@ -1,4 +1,3 @@ -local validations = require("kong.dao.schemas") local crud = require "kong.api.crud_helpers" return { @@ -8,30 +7,17 @@ return { end, PUT = function(self, dao_factory) - crud.put(self, dao_factory.consumers) + crud.put(self.params, dao_factory.consumers) end, POST = function(self, dao_factory) - crud.post(self, dao_factory.consumers) + crud.post(self.params, dao_factory.consumers) end }, ["/consumers/:username_or_id"] = { before = function(self, dao_factory, helpers) - local fetch_keys = { - [validations.is_valid_uuid(self.params.username_or_id) and "id" or "username"] = self.params.username_or_id - } - self.params.username_or_id = nil - - local data, err = dao_factory.consumers:find_by_keys(fetch_keys) - if err then - return helpers.yield_error(err) - end - - self.consumer = data[1] - if not self.consumer then - return helpers.responses.send_HTTP_NOT_FOUND() - end + crud.find_consumer_by_username_or_id(self, dao_factory, helpers) end, GET = function(self, dao_factory, helpers) diff --git a/kong/api/routes/plugins_configurations.lua b/kong/api/routes/plugins_configurations.lua index 6e46aeb93b66..418c5c898c93 100644 --- a/kong/api/routes/plugins_configurations.lua +++ b/kong/api/routes/plugins_configurations.lua @@ -7,11 +7,11 @@ return { end, PUT = function(self, dao_factory) - crud.put(self, dao_factory.plugins_configurations) + crud.put(self.params, dao_factory.plugins_configurations) end, POST = function(self, dao_factory) - crud.post(self, dao_factory.plugins_configurations) + crud.post(self.params, dao_factory.plugins_configurations) end }, diff --git a/kong/plugins/basicauth/api.lua b/kong/plugins/basicauth/api.lua index 67be66d7b1b7..d72a37cc7cd3 100644 --- a/kong/plugins/basicauth/api.lua +++ b/kong/plugins/basicauth/api.lua @@ -1,11 +1,51 @@ --- Copyright (C) Mashape, Inc. +local crud = require "kong.api.crud_helpers" -local BaseController = require "kong.api.routes.base_controller" +return { + ["/consumers/:username_or_id/basicauth/"] = { + before = function(self, dao_factory, helpers) + crud.find_consumer_by_username_or_id(self, dao_factory, helpers) + self.params.consumer_id = self.consumer.id + end, -local BasicAuthCredentials = BaseController:extend() + GET = function(self, dao_factory, helpers) + crud.paginated_set(self, dao_factory.basicauth_credentials) + end, -function BasicAuthCredentials:new() - BasicAuthCredentials.super.new(self, dao.basicauth_credentials, "basicauth_credentials") -end + PUT = function(self, dao_factory) + crud.put(self.params, dao_factory.basicauth_credentials) + end, -return BasicAuthCredentials + POST = function(self, dao_factory) + crud.post(self.params, dao_factory.basicauth_credentials) + end + }, + + ["/consumers/:username_or_id/basicauth/:id"] = { + before = function(self, dao_factory, helpers) + crud.find_consumer_by_username_or_id(self, dao_factory, helpers) + self.params.consumer_id = self.consumer.id + + local data, err = dao_factory.basicauth_credentials:find_by_keys({ id = self.params.id }) + if err then + return helpers.yield_error(err) + end + + self.credential = data[1] + if not self.credential then + return helpers.responses.send_HTTP_NOT_FOUND() + end + end, + + GET = function(self, dao_factory, helpers) + return helpers.responses.send_HTTP_OK(self.credential) + end, + + PATCH = function(self, dao_factory) + crud.patch(self.params, dao_factory.basicauth_credentials) + end, + + DELETE = function(self, dao_factory) + crud.delete(self.credential.id, dao_factory.basicauth_credentials) + end + } +} diff --git a/kong/plugins/keyauth/api.lua b/kong/plugins/keyauth/api.lua index a4c3eca1f94d..84aa16503c24 100644 --- a/kong/plugins/keyauth/api.lua +++ b/kong/plugins/keyauth/api.lua @@ -1,18 +1,51 @@ -local base_controller = require "kong.api.routes.base_controller" - -return function(lapis_app, dao_factory) - local inspect = require "inspect" - print(inspect(lapis_app)) - lapis_app:get("api/keyauth", "/apis/:name_or_id", function(self) - if is_valid_uuid(self.params.name_or_id) then - self.params.id = self.params.name_or_id - else - self.params.name = self.params.name_or_id +local crud = require "kong.api.crud_helpers" + +return { + ["/consumers/:username_or_id/keyauth/"] = { + before = function(self, dao_factory, helpers) + crud.find_consumer_by_username_or_id(self, dao_factory, helpers) + self.params.consumer_id = self.consumer.id + end, + + GET = function(self, dao_factory, helpers) + crud.paginated_set(self, dao_factory.keyauth_credentials) + end, + + PUT = function(self, dao_factory) + crud.put(self.params, dao_factory.keyauth_credentials) + end, + + POST = function(self, dao_factory) + crud.post(self.params, dao_factory.keyauth_credentials) end - self.params.name_or_id = nil + }, + + ["/consumers/:username_or_id/keyauth/:id"] = { + before = function(self, dao_factory, helpers) + crud.find_consumer_by_username_or_id(self, dao_factory, helpers) + self.params.consumer_id = self.consumer.id - base_controller.find_by_keys_paginated(self, dao_factory.apis) - end) + local data, err = dao_factory.keyauth_credentials:find_by_keys({ id = self.params.id }) + if err then + return helpers.yield_error(err) + end - base_controller(lapis_app, dao_factory.apis, "apis") -end + self.plugin = data[1] + if not self.plugin then + return helpers.responses.send_HTTP_NOT_FOUND() + end + end, + + GET = function(self, dao_factory, helpers) + return helpers.responses.send_HTTP_OK(self.plugin) + end, + + PATCH = function(self, dao_factory) + crud.patch(self.params, dao_factory.keyauth_credentials) + end, + + DELETE = function(self, dao_factory) + crud.delete(self.plugin.id, dao_factory.keyauth_credentials) + end + } +} diff --git a/spec/integration/admin_api/apis_routes_spec.lua b/spec/integration/admin_api/apis_routes_spec.lua index 852c398ecd20..49558a49c1b8 100644 --- a/spec/integration/admin_api/apis_routes_spec.lua +++ b/spec/integration/admin_api/apis_routes_spec.lua @@ -15,7 +15,6 @@ describe("Admin API", function() end) describe("/apis/", function() - local BASE_URL = spec_helper.API_URL.."/apis/" describe("POST", function() @@ -51,6 +50,7 @@ describe("Admin API", function() end) describe("PUT", function() + setup(function() spec_helper.drop_db() end) @@ -221,7 +221,7 @@ describe("Admin API", function() it("[FAILURE] should return proper errors", function() send_content_types(BASE_URL, "POST", {}, - 400, '{"name":"name is required","value":"value is required"}') + 400, '{"name":"name is required"}') end) it("[SUCCESS] should create a plugin configuration", function() @@ -252,7 +252,7 @@ describe("Admin API", function() it("[FAILURE] should return proper errors", function() send_content_types(BASE_URL, "PUT", {}, - 400, '{"name":"name is required","value":"value is required"}') + 400, '{"name":"name is required"}') end) it("[SUCCESS] should create and update", function() diff --git a/spec/plugins/basicauth/access_spec.lua b/spec/plugins/basicauth/access_spec.lua new file mode 100644 index 000000000000..1e074b8a0358 --- /dev/null +++ b/spec/plugins/basicauth/access_spec.lua @@ -0,0 +1,86 @@ +local spec_helper = require "spec.spec_helpers" +local http_client = require "kong.tools.http_client" +local cjson = require "cjson" + +local STUB_GET_URL = spec_helper.STUB_GET_URL +local STUB_POST_URL = spec_helper.STUB_POST_URL + +describe("Authentication Plugin", function() + + setup(function() + spec_helper.prepare_db() + spec_helper.insert_fixtures { + api = { + { name = "tests basicauth", public_dns = "basicauth.com", target_url = "http://mockbin.com" } + }, + consumer = { + { username = "basicauth_tests_consuser" } + }, + plugin_configuration = { + { name = "basicauth", value = {}, __api = 1 } + }, + basicauth_credential = { + { username = "username", password = "password", __consumer = 1 } + } + } + + spec_helper.start_kong() + end) + + teardown(function() + spec_helper.stop_kong() + end) + + describe("Basic Authentication", function() + + it("should return invalid credentials when the credential value is wrong", function() + local response, status = http_client.get(STUB_GET_URL, {}, {host = "basicauth.com", authorization = "asd"}) + local body = cjson.decode(response) + assert.are.equal(403, status) + assert.are.equal("Invalid authentication credentials", body.message) + end) + + it("should not pass when passing only the password", function() + local response, status = http_client.get(STUB_GET_URL, {}, {host = "basicauth.com", authorization = "Basic OmFwaWtleTEyMw=="}) + local body = cjson.decode(response) + assert.are.equal(403, status) + assert.are.equal("Invalid authentication credentials", body.message) + end) + + it("should not pass when passing only the username", function() + local response, status = http_client.get(STUB_GET_URL, {}, {host = "basicauth.com", authorization = "Basic dXNlcjEyMzo="}) + local body = cjson.decode(response) + assert.are.equal(403, status) + assert.are.equal("Invalid authentication credentials", body.message) + end) + + it("should return invalid credentials when the credential parameter name is wrong in GET", function() + local response, status = http_client.get(STUB_GET_URL, {}, {host = "basicauth.com", authorization123 = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) + local body = cjson.decode(response) + assert.are.equal(403, status) + assert.are.equal("Invalid authentication credentials", body.message) + end) + + it("should return invalid credentials when the credential parameter name is wrong in POST", function() + local response, status = http_client.post(STUB_POST_URL, {}, {host = "basicauth.com", authorization123 = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) + local body = cjson.decode(response) + assert.are.equal(403, status) + assert.are.equal("Invalid authentication credentials", body.message) + end) + + it("should pass with GET", function() + local response, status = http_client.get(STUB_GET_URL, {}, {host = "basicauth.com", authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) + assert.are.equal(200, status) + local parsed_response = cjson.decode(response) + assert.are.equal("Basic dXNlcm5hbWU6cGFzc3dvcmQ=", parsed_response.headers.authorization) + end) + + it("should pass with POST", function() + local response, status = http_client.post(STUB_POST_URL, {}, {host = "basicauth.com", authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) + assert.are.equal(200, status) + local parsed_response = cjson.decode(response) + assert.are.equal("Basic dXNlcm5hbWU6cGFzc3dvcmQ=", parsed_response.headers.authorization) + end) + + end) +end) diff --git a/spec/plugins/basicauth/api_spec.lua b/spec/plugins/basicauth/api_spec.lua new file mode 100644 index 000000000000..be23c3a468dc --- /dev/null +++ b/spec/plugins/basicauth/api_spec.lua @@ -0,0 +1,121 @@ +local json = require "cjson" +local http_client = require "kong.tools.http_client" +local spec_helper = require "spec.spec_helpers" + +describe("Basic Auth Credentials API", function() + local BASE_URL, credential, consumer + + setup(function() + spec_helper.prepare_db() + spec_helper.start_kong() + end) + + teardown(function() + spec_helper.stop_kong() + end) + + describe("/consumers/:consumer/basicauth/", function() + + setup(function() + local fixtures = spec_helper.insert_fixtures { + consumer = {{ username = "bob" }} + } + consumer = fixtures.consumer[1] + BASE_URL = spec_helper.API_URL.."/consumers/bob/basicauth/" + end) + + describe("POST", function() + + it("[SUCCESS] should create a basicauth credential", function() + local response, status = http_client.post(BASE_URL, { username = "bob", password = "1234" }) + assert.equal(201, status) + credential = json.decode(response) + assert.equal(consumer.id, credential.consumer_id) + end) + + it("[FAILURE] should return proper errors", function() + local response, status = http_client.post(BASE_URL, {}) + assert.equal(400, status) + assert.equal('{"username":"username is required"}\n', response) + end) + + end) + + describe("PUT", function() + setup(function() + spec_helper.get_env().dao_factory.basicauth_credentials:delete(credential.id) + end) + + it("[SUCCESS] should create and update", function() + local response, status = http_client.put(BASE_URL, { username = "bob", password = "1234" }) + assert.equal(201, status) + credential = json.decode(response) + assert.equal(consumer.id, credential.consumer_id) + end) + + it("[FAILURE] should return proper errors", function() + local response, status = http_client.put(BASE_URL, {}) + assert.equal(400, status) + assert.equal('{"username":"username is required"}\n', response) + end) + + end) + + describe("GET", function() + + it("should retrieve all", function() + local response, status = http_client.get(BASE_URL) + assert.equal(200, status) + local body = json.decode(response) + assert.equal(1, #(body.data)) + end) + + end) + end) + + describe("/consumers/:consumer/basicauth/:id", function() + + describe("GET", function() + + it("should retrieve by id", function() + local response, status = http_client.get(BASE_URL..credential.id) + assert.equal(200, status) + end) + + end) + + describe("PATCH", function() + + it("[SUCCESS] should update a credential", function() + local response, status = http_client.patch(BASE_URL..credential.id, { username = "alice" }) + assert.equal(200, status) + credential = json.decode(response) + assert.equal("alice", credential.username) + end) + + it("[FAILURE] should return proper errors", function() + local response, status = http_client.patch(BASE_URL..credential.id, { username = "" }) + assert.equal(400, status) + assert.equal('{"username":"username is not a string"}\n', response) + end) + + end) + + describe("DELETE", function() + + it("[FAILURE] should return proper errors", function() + local _, status = http_client.delete(BASE_URL.."blah") + assert.equal(400, status) + + _, status = http_client.delete(BASE_URL.."00000000-0000-0000-0000-000000000000") + assert.equal(404, status) + end) + + it("[SUCCESS] should delete a credential", function() + local _, status = http_client.delete(BASE_URL..credential.id) + assert.equal(204, status) + end) + + end) + end) +end) diff --git a/spec/plugins/authentication_spec.lua b/spec/plugins/keyauth/access_spec.lua similarity index 58% rename from spec/plugins/authentication_spec.lua rename to spec/plugins/keyauth/access_spec.lua index d96aaffa7084..f1cdbce84df4 100644 --- a/spec/plugins/authentication_spec.lua +++ b/spec/plugins/keyauth/access_spec.lua @@ -11,23 +11,18 @@ describe("Authentication Plugin", function() spec_helper.prepare_db() spec_helper.insert_fixtures { api = { - { name = "tests auth 1", public_dns = "test1.com", target_url = "http://mockbin.com" }, - { name = "tests auth 2", public_dns = "test2.com", target_url = "http://mockbin.com" }, - { name = "tests auth 3", public_dns = "test3.com", target_url = "http://mockbin.com" } + { name = "tests auth 1", public_dns = "keyauth1.com", target_url = "http://mockbin.com" }, + { name = "tests auth 2", public_dns = "keyauth2.com", target_url = "http://mockbin.com" } }, consumer = { { username = "auth_tests_consumer" } }, plugin_configuration = { { name = "keyauth", value = { key_names = { "apikey" }}, __api = 1 }, - { name = "basicauth", value = {}, __api = 2 }, - { name = "keyauth", value = {key_names = {"apikey"}, hide_credentials = true}, __api = 3 } + { name = "keyauth", value = {key_names = {"apikey"}, hide_credentials = true}, __api = 2 } }, keyauth_credential = { { key = "apikey123", __consumer = 1 } - }, - basicauth_credential = { - { username = "username", password = "password", __consumer = 1 } } } @@ -41,56 +36,56 @@ describe("Authentication Plugin", function() describe("Query Authentication", function() it("should return invalid credentials when the credential value is wrong", function() - local response, status = http_client.get(STUB_GET_URL, {apikey = "asd"}, {host = "test1.com"}) + local response, status = http_client.get(STUB_GET_URL, {apikey = "asd"}, {host = "keyauth1.com"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should return invalid credentials when the credential parameter name is wrong in GET", function() - local response, status = http_client.get(STUB_GET_URL, {apikey123 = "apikey123"}, {host = "test1.com"}) + local response, status = http_client.get(STUB_GET_URL, {apikey123 = "apikey123"}, {host = "keyauth1.com"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should return invalid credentials when the credential parameter name is wrong in POST", function() - local response, status = http_client.post(STUB_POST_URL, {apikey123 = "apikey123"}, {host = "test1.com"}) + local response, status = http_client.post(STUB_POST_URL, {apikey123 = "apikey123"}, {host = "keyauth1.com"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should pass with GET", function() - local response, status = http_client.get(STUB_GET_URL, {apikey = "apikey123"}, {host = "test1.com"}) + local response, status = http_client.get(STUB_GET_URL, {apikey = "apikey123"}, {host = "keyauth1.com"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.are.equal("apikey123", parsed_response.queryString.apikey) end) it("should pass with POST", function() - local response, status = http_client.post(STUB_POST_URL, {apikey = "apikey123"}, {host = "test1.com"}) + local response, status = http_client.post(STUB_POST_URL, {apikey = "apikey123"}, {host = "keyauth1.com"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.are.equal("apikey123", parsed_response.postData.params.apikey) end) it("should return invalid credentials when the credential parameter name is wrong in GET header", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test1.com", apikey123 = "apikey123"}) + local response, status = http_client.get(STUB_GET_URL, {}, {host = "keyauth1.com", apikey123 = "apikey123"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should return invalid credentials when the credential parameter name is wrong in POST header", function() - local response, status = http_client.post(STUB_POST_URL, {}, {host = "test1.com", apikey123 = "apikey123"}) + local response, status = http_client.post(STUB_POST_URL, {}, {host = "keyauth1.com", apikey123 = "apikey123"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should set right headers", function() - local response, status = http_client.post(STUB_POST_URL, {apikey = "apikey123"}, {host = "test1.com"}) + local response, status = http_client.post(STUB_POST_URL, {apikey = "apikey123"}, {host = "keyauth1.com"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.truthy(parsed_response.headers["x-consumer-id"]) @@ -101,7 +96,7 @@ describe("Authentication Plugin", function() describe("Hide credentials", function() it("should pass with POST and hide credentials", function() - local response, status = http_client.post(STUB_POST_URL, {apikey = "apikey123", wot = "wat"}, {host = "test3.com"}) + local response, status = http_client.post(STUB_POST_URL, {apikey = "apikey123", wot = "wat"}, {host = "keyauth2.com"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.falsy(parsed_response.postData.params.apikey) @@ -109,7 +104,7 @@ describe("Authentication Plugin", function() end) it("should pass with POST multipart and hide credentials", function() - local response, status = http_client.post_multipart(STUB_POST_URL, {apikey = "apikey123", wot = "wat"}, {host = "test3.com"}) + local response, status = http_client.post_multipart(STUB_POST_URL, {apikey = "apikey123", wot = "wat"}, {host = "keyauth2.com"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.falsy(parsed_response.postData.params.apikey) @@ -117,14 +112,14 @@ describe("Authentication Plugin", function() end) it("should pass with GET and hide credentials", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test3.com", apikey = "apikey123"}) + local response, status = http_client.get(STUB_GET_URL, {}, {host = "keyauth2.com", apikey = "apikey123"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.falsy(parsed_response.headers.apikey) end) it("should pass with GET and hide credentials and another param", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test3.com", apikey = "apikey123", foo = "bar"}) + local response, status = http_client.get(STUB_GET_URL, {}, {host = "keyauth2.com", apikey = "apikey123", foo = "bar"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.falsy(parsed_response.headers.apikey) @@ -132,14 +127,14 @@ describe("Authentication Plugin", function() end) it("should not pass with GET and hide credentials", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test3.com", apikey = "apikey123123"}) + local response, status = http_client.get(STUB_GET_URL, {}, {host = "keyauth2.com", apikey = "apikey123123"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should pass with GET and hide credentials and another param", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test3.com", apikey = "apikey123", wot = "wat"}) + local response, status = http_client.get(STUB_GET_URL, {}, {host = "keyauth2.com", apikey = "apikey123", wot = "wat"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.falsy(parsed_response.headers.apikey) @@ -147,82 +142,19 @@ describe("Authentication Plugin", function() end) it("should not pass with GET and hide credentials", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test3.com", apikey = "apikey123123"}) + local response, status = http_client.get(STUB_GET_URL, {}, {host = "keyauth2.com", apikey = "apikey123123"}) local body = cjson.decode(response) assert.are.equal(403, status) assert.are.equal("Invalid authentication credentials", body.message) end) it("should pass with GET and hide credentials in querystring", function() - local response, status = http_client.get(STUB_GET_URL, {apikey = "apikey123"}, {host = "test3.com"}) + local response, status = http_client.get(STUB_GET_URL, {apikey = "apikey123"}, {host = "keyauth2.com"}) assert.are.equal(200, status) local parsed_response = cjson.decode(response) assert.falsy(parsed_response.queryString.apikey) end) end) - - end) - - describe("Basic Authentication", function() - - it("should return invalid credentials when the credential value is wrong", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test2.com", authorization = "asd"}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - - it("should not pass when passing only the password", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test2.com", authorization = "Basic OmFwaWtleTEyMw=="}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - - it("should not pass when passing only the username", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test2.com", authorization = "Basic dXNlcjEyMzo="}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - - it("should return invalid credentials when the credential parameter name is wrong in GET", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test2.com", authorization123 = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - - it("should return invalid credentials when the credential parameter name is wrong in POST", function() - local response, status = http_client.post(STUB_POST_URL, {}, {host = "test2.com", authorization123 = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - - it("should pass with GET", function() - local response, status = http_client.get(STUB_GET_URL, {}, {host = "test2.com", authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) - assert.are.equal(200, status) - local parsed_response = cjson.decode(response) - assert.are.equal("Basic dXNlcm5hbWU6cGFzc3dvcmQ=", parsed_response.headers.authorization) - end) - - it("should pass with POST", function() - local response, status = http_client.post(STUB_POST_URL, {}, {host = "test2.com", authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) - assert.are.equal(200, status) - local parsed_response = cjson.decode(response) - assert.are.equal("Basic dXNlcm5hbWU6cGFzc3dvcmQ=", parsed_response.headers.authorization) - end) - - it("should set right headers", function() - local response, status = http_client.post(STUB_POST_URL, {}, {host = "test2.com", authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="}) - assert.are.equal(200, status) - local parsed_response = cjson.decode(response) - assert.truthy(parsed_response.headers["x-consumer-id"]) - assert.truthy(parsed_response.headers["x-consumer-username"]) - assert.are.equal("auth_tests_consumer", parsed_response.headers["x-consumer-username"]) - end) - end) end) diff --git a/spec/plugins/keyauth/api_spec.lua b/spec/plugins/keyauth/api_spec.lua new file mode 100644 index 000000000000..c6c8a9daa5a2 --- /dev/null +++ b/spec/plugins/keyauth/api_spec.lua @@ -0,0 +1,121 @@ +local json = require "cjson" +local http_client = require "kong.tools.http_client" +local spec_helper = require "spec.spec_helpers" + +describe("Basic Auth Credentials API", function() + local BASE_URL, credential, consumer + + setup(function() + spec_helper.prepare_db() + spec_helper.start_kong() + end) + + teardown(function() + spec_helper.stop_kong() + end) + + describe("/consumers/:consumer/keyauth/", function() + + setup(function() + local fixtures = spec_helper.insert_fixtures { + consumer = {{ username = "bob" }} + } + consumer = fixtures.consumer[1] + BASE_URL = spec_helper.API_URL.."/consumers/bob/keyauth/" + end) + + describe("POST", function() + + it("[SUCCESS] should create a keyauth credential", function() + local response, status = http_client.post(BASE_URL, { key = "1234" }) + assert.equal(201, status) + credential = json.decode(response) + assert.equal(consumer.id, credential.consumer_id) + end) + + it("[FAILURE] should return proper errors", function() + local response, status = http_client.post(BASE_URL, {}) + assert.equal(400, status) + assert.equal('{"key":"key is required"}\n', response) + end) + + end) + + describe("PUT", function() + setup(function() + spec_helper.get_env().dao_factory.keyauth_credentials:delete(credential.id) + end) + + it("[SUCCESS] should create and update", function() + local response, status = http_client.put(BASE_URL, { key = "1234" }) + assert.equal(201, status) + credential = json.decode(response) + assert.equal(consumer.id, credential.consumer_id) + end) + + it("[FAILURE] should return proper errors", function() + local response, status = http_client.put(BASE_URL, {}) + assert.equal(400, status) + assert.equal('{"key":"key is required"}\n', response) + end) + + end) + + describe("GET", function() + + it("should retrieve all", function() + local response, status = http_client.get(BASE_URL) + assert.equal(200, status) + local body = json.decode(response) + assert.equal(1, #(body.data)) + end) + + end) + end) + + describe("/consumers/:consumer/keyauth/:id", function() + + describe("GET", function() + + it("should retrieve by id", function() + local response, status = http_client.get(BASE_URL..credential.id) + assert.equal(200, status) + end) + + end) + + describe("PATCH", function() + + it("[SUCCESS] should update a credential", function() + local response, status = http_client.patch(BASE_URL..credential.id, { key = "4321" }) + assert.equal(200, status) + credential = json.decode(response) + assert.equal("4321", credential.key) + end) + + it("[FAILURE] should return proper errors", function() + local response, status = http_client.patch(BASE_URL..credential.id, { key = "" }) + assert.equal(400, status) + assert.equal('{"key":"key is not a string"}\n', response) + end) + + end) + + describe("DELETE", function() + + it("[FAILURE] should return proper errors", function() + local _, status = http_client.delete(BASE_URL.."blah") + assert.equal(400, status) + + _, status = http_client.delete(BASE_URL.."00000000-0000-0000-0000-000000000000") + assert.equal(404, status) + end) + + it("[SUCCESS] should delete a credential", function() + local _, status = http_client.delete(BASE_URL..credential.id) + assert.equal(204, status) + end) + + end) + end) +end) diff --git a/spec/plugins/request_transformer_spec.lua b/spec/plugins/request_transformer_spec.lua index f2855126cf11..a51d2763ecd6 100644 --- a/spec/plugins/request_transformer_spec.lua +++ b/spec/plugins/request_transformer_spec.lua @@ -5,7 +5,7 @@ local cjson = require "cjson" local STUB_GET_URL = spec_helper.STUB_GET_URL local STUB_POST_URL = spec_helper.STUB_POST_URL -describe("Request Transformer Plugin #proxy", function() +describe("Request Transformer Plugin", function() setup(function() spec_helper.prepare_db() diff --git a/spec/unit/base_controller_spec.lua b/spec/unit/api/app_spec.lua similarity index 85% rename from spec/unit/base_controller_spec.lua rename to spec/unit/api/app_spec.lua index 2af3bc35948c..1c0def88834b 100644 --- a/spec/unit/base_controller_spec.lua +++ b/spec/unit/api/app_spec.lua @@ -1,4 +1,4 @@ -local base_controller = require "kong.api.routes.base_controller" +local app = require "kong.api.app" require "kong.tools.ngx_stub" @@ -8,13 +8,13 @@ local stub = { params = { foo = "bar", number = 10, ["value.nested"] = 1, ["value.nested_2"] = 2 } } -describe("Base Controller", function() +describe("App", function() describe("#parse_params()", function() it("should normalize nested properties for parsed form-encoded parameters", function() -- Here Lapis already parsed the form-encoded parameters but we are normalizing -- the nested ones (with "." keys) - local f = base_controller.parse_params(function(stub) + local f = app.parse_params(function(stub) assert.are.same({ foo = "bar", number = 10, @@ -32,7 +32,7 @@ describe("Base Controller", function() ngx.req.get_body_data = function() return '{"foo":"bar","number":10,"value":{"nested":1,"nested_2":2}}' end stub.req.headers["Content-Type"] = "application/json; charset=utf-8" - local f = base_controller.parse_params(function(stub) + local f = app.parse_params(function(stub) assert.are.same({ foo = "bar", number = 10, @@ -49,7 +49,7 @@ describe("Base Controller", function() stub.params = { foo = "bar", number = 10, ["value.nested_1"] = 1, ["value.nested_2"] = 2, ["value.nested.sub-nested"] = "hi" } - local f = base_controller.parse_params(function(stub) + local f = app.parse_params(function(stub) assert.are.same({ foo = 'bar', number = 10, @@ -66,7 +66,7 @@ describe("Base Controller", function() it("should normalize nested properties when they are plain arrays", function() stub.params = { foo = "bar", number = 10, ["value.nested"] = {["1"]="hello", ["2"]="world"}} - local f = base_controller.parse_params(function(stub) + local f = app.parse_params(function(stub) assert.are.same({ foo = 'bar', number = 10, diff --git a/spec/unit/tools/migrations_spec.lua b/spec/unit/tools/migrations_spec.lua index db435e179e03..541d5f10bf46 100644 --- a/spec/unit/tools/migrations_spec.lua +++ b/spec/unit/tools/migrations_spec.lua @@ -109,7 +109,7 @@ describe("Migrations", function() assert.are.same(migrations_names[i], migration.name..".lua") end) - assert.are.same(3, i) + assert.are.same(4, i) assert.spy(env.dao_factory.migrations.get_migrations).was.called(1) assert.spy(env.dao_factory.execute_queries).was.called(#migrations_names-1) assert.spy(env.dao_factory.migrations.add_migration).was.called(#migrations_names-1)