kong/spec/02-integration/04-admin_api/06-certificates_routes_spec.lua (1,103 lines of code) (raw):

local ssl_fixtures = require "spec.fixtures.ssl" local helpers = require "spec.helpers" local cjson = require "cjson" local utils = require "kong.tools.utils" local Errors = require "kong.db.errors" local function it_content_types(title, fn) local test_form_encoded = fn("application/x-www-form-urlencoded") local test_multipart = fn("multipart/form-data") local test_json = fn("application/json") it(title .. " with application/www-form-urlencoded", test_form_encoded) it(title .. " with multipart/form-data", test_multipart) it(title .. " with application/json", test_json) end local get_name do local n = 0 get_name = function() n = n + 1 return string.format("name%04d.test", n) end end for _, strategy in helpers.each_strategy() do describe("Admin API: #" .. strategy, function() local client local function add_certificate() local n1 = get_name() local n2 = get_name() local names = { n1, n2 } local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = names, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(201, res) local certificate = cjson.decode(body) return certificate, names end local function add_certificate_with_alt_cert() local n1 = get_name() local n2 = get_name() local names = { n1, n2 } local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, cert_alt = ssl_fixtures.cert_ecdsa, key_alt = ssl_fixtures.key_ecdsa, snis = names, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(201, res) local certificate = cjson.decode(body) return certificate, names end local function get_certificates() local res = client:get("/certificates") local body = assert.res_status(200, res) return cjson.decode(body) end local bp, db before_each(function() client = assert(helpers.admin_client()) end) after_each(function() if client then client:close() end end) lazy_setup(function() bp, db = helpers.get_db_utils(strategy, { "certificates", "snis", }) assert(helpers.start_kong({ database = strategy, })) end) lazy_teardown(function() helpers.stop_kong() end) describe("/certificates", function() describe("GET", function() it("retrieves all certificates with snis", function() local my_snis = {} for i = 1, 150 do table.insert(my_snis, string.format("my-sni-%03d.test", i)) end local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = my_snis, }, headers = { ["Content-Type"] = "application/json" }, }) assert.res_status(201, res) local json = get_certificates() assert.equal(1, #json.data) assert.is_string(json.data[1].cert) assert.is_string(json.data[1].key) assert.equal(cjson.null, json.data[1].cert_alt) assert.equal(cjson.null, json.data[1].key_alt) assert.same(my_snis, json.data[1].snis) end) end) describe("POST", function() it("returns a conflict when duplicated snis are present in the request", function() local n1 = get_name() local n2 = get_name() local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1, n2, n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.equals("schema violation (snis: " .. n1 .. " is duplicated)", json.message) -- make sure we didnt add the certificate, or any snis local json = get_certificates() for _, data in ipairs(json.data) do for _, sni in ipairs(data.snis) do assert.not_equal(n1, sni) assert.not_equal(n2, sni) end end end) it("returns a conflict when a pre-existing sni is detected", function() local n1 = get_name() local n2 = get_name() local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) assert.res_status(201, res) local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1, n2 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.matches("snis: " .. n1 .. " already associated with existing certificate", json.message) -- make sure we didnt add the certificate, or any snis local json = get_certificates() for _, data in ipairs(json.data) do for _, sni in ipairs(data.snis) do assert.not_equal(n2, sni) end end end) it_content_types("creates a certificate and returns it with the snis pseudo-property", function(content_type) return function() local n1 = get_name() local n2 = get_name() local body if content_type == "multipart/form-data" then body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, ["snis[1]"] = n1, ["snis[2]"] = n2, } elseif content_type == "application/x-www-form-urlencoded" then body = { cert = require "socket.url".escape(ssl_fixtures.cert), key = require "socket.url".escape(ssl_fixtures.key), snis = { n1, n2 } } else body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1, n2 } } end local res = client:post("/certificates", { body = body, headers = { ["Content-Type"] = content_type }, }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.is_string(json.cert) assert.is_string(json.key) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.same({ n1, n2 }, json.snis) end end) it_content_types("returns snis as [] when none is set", function(content_type) return function() local body if content_type == "application/x-www-form-urlencoded" then body = { cert = require "socket.url".escape(ssl_fixtures.cert), key = require "socket.url".escape(ssl_fixtures.key), } else body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, } end local res = client:post("/certificates", { body = body, headers = { ["Content-Type"] = content_type }, }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.is_string(json.cert) assert.is_string(json.key) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.matches('"snis":[]', body, nil, true) end end) end) end) describe("/certificates/cert_id_or_sni", function() describe("GET", function() it("retrieves a certificate by id", function() local certificate, names = add_certificate() local res1 = client:get("/certificates/" .. certificate.id) local body1 = assert.res_status(200, res1) local json1 = cjson.decode(body1) assert.is_string(json1.cert) assert.is_string(json1.key) assert.equal(cjson.null, json1.cert_alt) assert.equal(cjson.null, json1.key_alt) assert.same(names, json1.snis) end) it("retrieves a certificate by id with alternate certificate", function() local certificate, names = add_certificate_with_alt_cert() local res1 = client:get("/certificates/" .. certificate.id) local body1 = assert.res_status(200, res1) local json1 = cjson.decode(body1) assert.is_string(json1.cert) assert.is_string(json1.key) assert.is_string(json1.cert_alt) assert.is_string(json1.key_alt) assert.same(names, json1.snis) end) it("retrieves a certificate by sni", function() local _, names = add_certificate() local res1 = client:get("/certificates/" .. names[1]) local body1 = assert.res_status(200, res1) local json1 = cjson.decode(body1) local res2 = client:get("/certificates/" .. names[2]) local body2 = assert.res_status(200, res2) local json2 = cjson.decode(body2) assert.is_string(json1.cert) assert.is_string(json1.key) assert.equal(cjson.null, json1.cert_alt) assert.equal(cjson.null, json1.key_alt) assert.same(names, json1.snis) assert.same(json1, json2) end) it("retrieves a certificate by sni with alternate certificate", function() local _, names = add_certificate_with_alt_cert() local res1 = client:get("/certificates/" .. names[1]) local body1 = assert.res_status(200, res1) local json1 = cjson.decode(body1) local res2 = client:get("/certificates/" .. names[2]) local body2 = assert.res_status(200, res2) local json2 = cjson.decode(body2) assert.is_string(json1.cert) assert.is_string(json1.key) assert.is_string(json1.cert_alt) assert.is_string(json1.key_alt) assert.same(names, json1.snis) assert.same(json1, json2) end) it("returns 404 for a random non-existing uuid", function() local res = client:get("/certificates/" .. utils.uuid()) assert.res_status(404, res) end) it("returns 404 for a random non-existing sni", function() local res = client:get("/certificates/doesntexist.com") assert.res_status(404, res) end) end) describe("PUT", function() it("creates if not found", function() local n1 = get_name() local id = utils.uuid() local res = client:put("/certificates/" .. id, { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(ssl_fixtures.cert, json.cert) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.same({ n1 }, json.snis) json.snis = nil local in_db = assert(db.certificates:select({ id = id }, { nulls = true })) assert.same(json, in_db) end) it("creates a new sni when provided in the url", function() local n1 = get_name() local n2 = get_name() local res = client:put("/certificates/" .. n1, { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n2 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(ssl_fixtures.cert, json.cert) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.same({ n1, n2 }, json.snis) json.snis = nil local in_db = assert(db.certificates:select({ id = json.id }, { nulls = true })) assert.same(json, in_db) end) it("creates a new sni when provided in the url (with sni duplicated in url and body)", function() local n1 = get_name() local n2 = get_name() local res = client:put("/certificates/" .. n1, { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1, n2 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(ssl_fixtures.cert, json.cert) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.same({ n1, n2 }, json.snis) json.snis = nil local in_db = assert(db.certificates:select({ id = json.id }, { nulls = true })) assert.same(json, in_db) end) it("upserts if found", function() local certificate = add_certificate() local res = client:put("/certificates/" .. certificate.id, { body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(ssl_fixtures.cert_alt, json.cert) assert.same(ssl_fixtures.key_alt, json.key) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.same({}, json.snis) json.snis = nil local in_db = assert(db.certificates:select({ id = certificate.id }, { nulls = true })) assert.same(json, in_db) end) it("upserts if found with alternate certificate", function() local certificate = add_certificate_with_alt_cert() local res = client:put("/certificates/" .. certificate.id, { body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, cert_alt = ssl_fixtures.cert_alt_ecdsa, key_alt = ssl_fixtures.key_alt_ecdsa, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(ssl_fixtures.cert_alt, json.cert) assert.same(ssl_fixtures.key_alt, json.key) assert.same(ssl_fixtures.cert_alt_ecdsa, json.cert_alt) assert.same(ssl_fixtures.key_alt_ecdsa, json.key_alt) assert.same({}, json.snis) json.snis = nil local in_db = assert(db.certificates:select({ id = certificate.id }, { nulls = true })) assert.same(json, in_db) end) it("handles invalid input", function() -- Missing params local res = client:put("/certificates/" .. utils.uuid(), { body = {}, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) assert.same({ code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "2 schema violations (cert: required field missing; key: required field missing)", fields = { cert = "required field missing", key = "required field missing", } }, cjson.decode(body)) end) it("handles invalid input with alternate certificate", function() -- Missing params local res = client:put("/certificates/" .. utils.uuid(), { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, cert_alt = ssl_fixtures.cert_ecdsa, }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) assert.same({ code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "schema violation (all or none of these fields must be set: 'cert_alt', 'key_alt')", fields = { ["@entity"] = { "all or none of these fields must be set: 'cert_alt', 'key_alt'" }, } }, cjson.decode(body)) end) it("handles mismatched keys/certificates", function() local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key_alt, }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) assert.same({ code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "schema violation (certificate does not match key)", fields = { ["@entity"] = { "certificate does not match key" }, } }, cjson.decode(body)) end) it("handles mismatched keys/certificates with alternate certificate", function() local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, cert_alt = ssl_fixtures.cert_ecdsa, key_alt = ssl_fixtures.key_alt_ecdsa, }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) assert.same({ code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "schema violation (alternative certificate does not match key)", fields = { ["@entity"] = { "alternative certificate does not match key" }, } }, cjson.decode(body)) end) it("errors on non-distinct certs", function() local res = client:post("/certificates", { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, cert_alt = ssl_fixtures.cert, key_alt = ssl_fixtures.key, }, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) assert.same({ code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "schema violation (certificate and alternative certificate need to have different type (e.g. RSA and ECDSA), the provided certificates were both of the same type)", fields = { ["@entity"] = { "certificate and alternative certificate need to have " .. "different type (e.g. RSA and ECDSA), the provided " .. "certificates were both of the same type" }, } }, cjson.decode(body)) end) end) describe("PATCH", function() it_content_types("updates a certificate by cert id", function(content_type) return function() local certificate = add_certificate() local body if content_type == "application/x-www-form-urlencoded" then body = { cert = require "socket.url".escape(ssl_fixtures.cert_alt), key = require "socket.url".escape(ssl_fixtures.key_alt), } else body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, } end local res = client:patch("/certificates/" .. certificate.id, { body = body, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(ssl_fixtures.cert_alt, json.cert) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) end end) it_content_types("updates a certificate by cert id with alternate certificate", function(content_type) return function() local certificate = add_certificate_with_alt_cert() local body if content_type == "application/x-www-form-urlencoded" then body = { cert = require "socket.url".escape(ssl_fixtures.cert_alt), key = require "socket.url".escape(ssl_fixtures.key_alt), cert_alt = require "socket.url".escape(ssl_fixtures.cert_alt_ecdsa), key_alt = require "socket.url".escape(ssl_fixtures.key_alt_ecdsa), } else body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, cert_alt = ssl_fixtures.cert_alt_ecdsa, key_alt = ssl_fixtures.key_alt_ecdsa, } end local res = client:patch("/certificates/" .. certificate.id, { body = body, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(ssl_fixtures.cert_alt, json.cert) assert.equal(ssl_fixtures.cert_alt_ecdsa, json.cert_alt) end end) it_content_types("update by id returns full certificate", function(content_type) return function() local certificate = add_certificate() local res = client:patch("/certificates/" .. certificate.id, { body = {}, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(certificate, json) end end) it_content_types("update by id returns full certificate with alternative certificate", function(content_type) return function() local certificate = add_certificate_with_alt_cert() local res = client:patch("/certificates/" .. certificate.id, { body = {}, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(certificate, json) end end) it_content_types("updates a certificate by sni", function(content_type) return function() local _, names = add_certificate() local body if content_type == "application/x-www-form-urlencoded" then body = { cert = require "socket.url".escape(ssl_fixtures.cert_alt), key = require "socket.url".escape(ssl_fixtures.key_alt), } else body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, } end local res = client:patch("/certificates/" .. names[1], { body = body, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(ssl_fixtures.cert_alt, json.cert) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) end end) it_content_types("updates a certificate by sni with alternate certificate", function(content_type) return function() local _, names = add_certificate_with_alt_cert() local body if content_type == "application/x-www-form-urlencoded" then body = { cert = require "socket.url".escape(ssl_fixtures.cert_alt), key = require "socket.url".escape(ssl_fixtures.key_alt), cert_alt = require "socket.url".escape(ssl_fixtures.cert_alt_ecdsa), key_alt = require "socket.url".escape(ssl_fixtures.key_alt_ecdsa), } else body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, cert_alt = ssl_fixtures.cert_alt_ecdsa, key_alt = ssl_fixtures.key_alt_ecdsa, } end local res = client:patch("/certificates/" .. names[1], { body = body, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(ssl_fixtures.cert_alt, json.cert) assert.equal(ssl_fixtures.key_alt, json.key) assert.equal(ssl_fixtures.cert_alt_ecdsa, json.cert_alt) assert.equal(ssl_fixtures.key_alt_ecdsa, json.key_alt) end end) it_content_types("update by sni returns full certificate", function(content_type) return function() local certificate, names = add_certificate() local res = client:patch("/certificates/" .. names[1], { body = {}, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(certificate, json) end end) it_content_types("update by sni returns full certificate with alternate certificate", function(content_type) return function() local certificate, names = add_certificate_with_alt_cert() local res = client:patch("/certificates/" .. names[1], { body = {}, headers = { ["Content-Type"] = content_type } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(certificate, json) end end) it("returns 404 for a random non-existing id", function() local n1 = get_name() local res = client:patch("/certificates/" .. utils.uuid(), { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, snis = { n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) assert.res_status(404, res) -- make sure we did not add any certificate or sni local json = get_certificates() for _, data in ipairs(json.data) do for _, sni in ipairs(data.snis) do assert.not_equal(n1, sni) end end end) it("updates snis associated with a certificate", function() local certificate = add_certificate() local n1 = get_name() local json_before = get_certificates() local res = client:patch("/certificates/" .. certificate.id, { body = { snis = { n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same({ n1 }, json.snis) -- make sure we did not add any certificate, and that the snis -- are correct local json = get_certificates() assert.equal(#json_before.data, #json.data) for i, data in ipairs(json.data) do if data.id == certificate.id then assert.same({ n1 }, data.snis) else assert.same(json_before.data[i].snis, data.snis) end end end) it("updates snis associated with a certificate with alternate certificate", function() local certificate = add_certificate_with_alt_cert() local n1 = get_name() local json_before = get_certificates() local res = client:patch("/certificates/" .. certificate.id, { body = { snis = { n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same({ n1 }, json.snis) -- make sure we did not add any certificate, and that the snis -- are correct local json = get_certificates() assert.equal(#json_before.data, #json.data) for i, data in ipairs(json.data) do if data.id == certificate.id then assert.same({ n1 }, data.snis) else assert.same(json_before.data[i].snis, data.snis) end end end) it("updates only the certificate if no snis are specified", function() local certificate, names = add_certificate() local json_before = get_certificates() local res = client:patch( "/certificates/" .. certificate.id, { body = { cert = ssl_fixtures.cert, key = ssl_fixtures.key, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) -- make sure certificate got updated and sni remains the same assert.same(names, json.snis) assert.same(ssl_fixtures.cert, json.cert) assert.same(ssl_fixtures.key, json.key) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) -- make sure the certificate got updated in DB res = client:get("/certificates/" .. certificate.id) body = assert.res_status(200, res) json = cjson.decode(body) assert.equal(ssl_fixtures.cert, json.cert) assert.equal(ssl_fixtures.key, json.key) assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) -- make sure we did not add any certificate or sni local json = get_certificates() assert.same(json_before, json) end) it("returns a conflict when duplicated snis are present in the request", function() local certificate = add_certificate() local json_before = get_certificates() local n1 = get_name() local res = client:patch("/certificates/" .. certificate.id, { body = { snis = { n1, n1 }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.equals("schema violation (snis: " .. n1 .. " is duplicated)", json.message) -- make sure we did not change certificates or snis local json = get_certificates() assert.same(json_before, json) end) it("returns a conflict when a pre-existing sni present in " .. "the request is associated with another certificate", function() local certificate = add_certificate() local certificate2, names2 = add_certificate() local json_before = get_certificates() local res = client:patch("/certificates/" .. certificate.id, { body = { snis = names2, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.equals("schema violation (snis: " .. names2[1] .. " already associated with " .. "existing certificate '" .. certificate2.id .. "')", json.message) -- make sure we did not add any certificate or sni local json = get_certificates() assert.same(json_before, json) end) it("deletes all snis from a certificate if snis field is JSON null", function() -- Note: we currently do not support unsetting a field with -- form-urlencoded requests. This depends on upcoming work -- to the Admin API. We here follow the road taken by: -- https://github.com/Kong/kong/pull/2700 local certificate = add_certificate() local json_before = get_certificates() local res = client:patch("/certificates/" .. certificate.id, { body = { snis = ngx.null, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(0, #json.snis) assert.matches('"snis":[]', body, nil, true) -- make sure we did not add any certificate and the sni was deleted local json = get_certificates() assert.equal(#json_before.data, #json.data) for i, data in ipairs(json.data) do if data.id == certificate.id then assert.same({}, data.snis) else assert.same(json_before.data[i].snis, data.snis) end end end) end) describe("DELETE", function() it("deletes a certificate and all related snis", function() local json_before = get_certificates() local _, names = add_certificate() local res = client:delete("/certificates/" .. names[1]) assert.res_status(204, res) local json = get_certificates() assert.same(json_before, json) end) it("deletes a certificate by id", function() local json_before = get_certificates() local certificate = add_certificate() local res = client:delete("/certificates/" .. certificate.id) assert.res_status(204, res) local json = get_certificates() assert.same(json_before, json) end) end) end) describe("/certificates/:certificate/snis", function() describe("POST", function() describe("errors", function() it("certificate doesn't exist", function() local res = client:post("/certificates/585e4c16-c656-11e6-8db9-5f512d8a12cd/snis", { body = { name = get_name(), }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(404, res) local json = cjson.decode(body) assert.same("Not found", json.message) end) end) it_content_types("creates a sni using a certificate id", function(content_type) return function() local certificate = add_certificate() local n1 = get_name() local res = client:post("/certificates/" .. certificate.id .. "/snis", { body = { name = n1, }, headers = { ["Content-Type"] = content_type }, }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(n1, json.name) assert.equal(certificate.id, json.certificate.id) end end) it_content_types("creates a sni using a sni to id the certificate", function(content_type) return function() local certificate, names = add_certificate() local n1 = get_name() local res = client:post("/certificates/" .. names[1] .. "/snis", { body = { name = n1, }, headers = { ["Content-Type"] = content_type }, }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(n1, json.name) assert.equal(certificate.id, json.certificate.id) end end) it("returns a conflict when an sni already exists", function() local certificate, names = add_certificate() local res = client:post("/certificates/" .. certificate.id .. "/snis", { body = { name = names[1], }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(409, res) local json = cjson.decode(body) assert.equals("unique constraint violation", json.name) end) end) describe("GET", function() it("retrieves a list of snis", function() local n1 = get_name() local certificate = bp.certificates:insert() bp.snis:insert { name = n1, certificate = certificate, } local res = client:get("/certificates/" .. certificate.id .. "/snis") local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(1, #json.data) assert.equal(n1, json.data[1].name) assert.equal(certificate.id, json.data[1].certificate.id) end) end) end) describe("/snis/:name", function() describe("wildcard snis", function() describe("POST", function() it("creates with prefix wildcard", function() local certificate = add_certificate() local n1 = get_name() local res = client:post("/snis", { body = { name = "*." .. n1, certificate = { id = certificate.id }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal("*." .. n1, json.name) assert.equal(certificate.id, json.certificate.id) end) it("creates with suffix wildcard", function() local certificate = add_certificate() local n1 = get_name() local res = client:post("/snis", { body = { name = n1 .. ".*", certificate = { id = certificate.id }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(201, res) local json = cjson.decode(body) assert.equal(n1 .. ".*", json.name) assert.equal(certificate.id, json.certificate.id) end) it("rejects invalid SNIs", function() local certificate = add_certificate() local res = client:post("/snis", { body = { name = "*.wildcard.*", certificate = { id = certificate.id }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(400, res) local json = cjson.decode(body) assert.equal("only one wildcard must be specified", json.fields.name) end) end) describe("GET", function() it("retrieves a wildcard SNI using the name", function() local certificate = add_certificate() bp.snis:insert({ name = "*.wildcard.com", certificate = { id = certificate.id }, }) local res = client:get("/snis/%2A.wildcard.com") local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("*.wildcard.com", json.name) end) end) end) describe("GET", function() it("retrieves a sni using the name", function() local certificate, names = add_certificate() local res = client:get("/snis/" .. names[1]) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(names[1], json.name) assert.equal(certificate.id, json.certificate.id) end) it("retrieves a sni using the id", function() local certificate = add_certificate() local n1 = get_name() local sni = bp.snis:insert({ name = n1, certificate = { id = certificate.id }, }) local res = client:get("/snis/" .. sni.id) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(n1, json.name) assert.equal(certificate.id, json.certificate.id) end) end) describe("PUT", function() it("creates if not found", function() local certificate = add_certificate() local n1 = get_name() local id = utils.uuid() local res = client:put("/snis/" .. id, { body = { certificate = { id = certificate.id }, name = n1, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(n1, json.name) local in_db = assert(db.snis:select({ id = id }, { nulls = true })) assert.same(json, in_db) end) it("updates if found", function() local certificate = add_certificate() local n1 = get_name() local sni = bp.snis:insert({ name = n1, certificate = { id = certificate.id }, }) local n2 = get_name() local res = client:put("/snis/" .. sni.id, { body = { name = n2, certificate = { id = certificate.id }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same(n2, json.name) local in_db = assert(db.snis:select({ id = sni.id }, { nulls = true })) assert.same(json, in_db) end) it("handles invalid input", function() -- Missing params local res = client:put("/snis/" .. utils.uuid(), { body = {}, headers = { ["Content-Type"] = "application/json" } }) local body = assert.res_status(400, res) assert.same({ code = Errors.codes.SCHEMA_VIOLATION, name = "schema violation", message = "2 schema violations (certificate: required field missing; name: required field missing)", fields = { certificate = "required field missing", name = "required field missing", } }, cjson.decode(body)) end) end) describe("PATCH", function() it("updates a sni", function() local certificate, names = add_certificate() local certificate_2 = bp.certificates:insert { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, } local res = client:patch("/snis/" .. names[1], { body = { certificate = { id = certificate_2.id }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(certificate_2.id, json.certificate.id) local res = client:get("/certificates/" .. certificate.id) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same({ names[2] }, json.snis) local res = client:get("/certificates/" .. certificate_2.id) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same({ names[1] }, json.snis) end) it("updates a sni with alternate certificate", function() local certificate, names = add_certificate() local certificate_2 = bp.certificates:insert { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt, cert_alt = ssl_fixtures.cert_alt_ecdsa, key_alt = ssl_fixtures.key_alt_ecdsa, } local res = client:patch("/snis/" .. names[1], { body = { certificate = { id = certificate_2.id }, }, headers = { ["Content-Type"] = "application/json" }, }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal(certificate_2.id, json.certificate.id) local res = client:get("/certificates/" .. certificate.id) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same({ names[2] }, json.snis) local res = client:get("/certificates/" .. certificate_2.id) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.same({ names[1] }, json.snis) end) end) describe("DELETE", function() it("deletes a sni", function() local certificate = add_certificate() local n1 = get_name() bp.snis:insert({ name = n1, certificate = { id = certificate.id }, }) local res = client:delete("/snis/" .. n1) assert.res_status(204, res) end) end) end) end) end