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