Skip to content

Commit

Permalink
feat(db) move ssl_cert schema to new db
Browse files Browse the repository at this point in the history
  • Loading branch information
kikito authored and bungle committed May 2, 2018
1 parent da4e36b commit a6dee10
Show file tree
Hide file tree
Showing 28 changed files with 1,205 additions and 2,725 deletions.
6 changes: 4 additions & 2 deletions kong-0.13.1-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ build = {
["kong.dao.schemas.plugins"] = "kong/dao/schemas/plugins.lua",
["kong.dao.schemas.upstreams"] = "kong/dao/schemas/upstreams.lua",
["kong.dao.schemas.targets"] = "kong/dao/schemas/targets.lua",
["kong.dao.schemas.ssl_certificates"] = "kong/dao/schemas/ssl_certificates.lua",
["kong.dao.schemas.ssl_servers_names"] = "kong/dao/schemas/ssl_servers_names.lua",
["kong.dao.db"] = "kong/dao/db/init.lua",
["kong.dao.db.cassandra"] = "kong/dao/db/cassandra.lua",
["kong.dao.db.postgres"] = "kong/dao/db/postgres.lua",
Expand All @@ -127,9 +125,13 @@ build = {
["kong.db"] = "kong/db/init.lua",
["kong.db.errors"] = "kong/db/errors.lua",
["kong.db.dao"] = "kong/db/dao/init.lua",
["kong.db.dao.certificates"] = "kong/db/dao/certificates.lua",
["kong.db.dao.snis"] = "kong/db/dao/snis.lua",
["kong.db.schema"] = "kong/db/schema/init.lua",
["kong.db.schema.entities.routes"] = "kong/db/schema/entities/routes.lua",
["kong.db.schema.entities.services"] = "kong/db/schema/entities/services.lua",
["kong.db.schema.entities.certificates"] = "kong/db/schema/entities/certificates.lua",
["kong.db.schema.entities.snis"] = "kong/db/schema/entities/snis.lua",
["kong.db.schema.entity"] = "kong/db/schema/entity.lua",
["kong.db.schema.metaschema"] = "kong/db/schema/metaschema.lua",
["kong.db.schema.typedefs"] = "kong/db/schema/typedefs.lua",
Expand Down
3 changes: 1 addition & 2 deletions kong/api/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,7 @@ ngx.log(ngx.DEBUG, "Loading Admin API endpoints")


-- Load core routes
for _, v in ipairs({"kong", "apis", "consumers", "plugins", "cache",
"certificates", "snis", "upstreams"}) do
for _, v in ipairs({"kong", "apis", "consumers", "plugins", "cache", "upstreams"}) do
local routes = require("kong.api.routes." .. v)
attach_routes(routes)
end
Expand Down
323 changes: 25 additions & 298 deletions kong/api/routes/certificates.lua
Original file line number Diff line number Diff line change
@@ -1,317 +1,44 @@
local crud = require "kong.api.crud_helpers"
local utils = require "kong.tools.utils"
local cjson = require "cjson"
local endpoints = require "kong.api.endpoints"
local utils = require "kong.tools.utils"
local responses = require "kong.tools.responses"


local function create_certificate(self, dao_factory, helpers)
local snis
if type(self.params.snis) == "string" then
snis = utils.split(self.params.snis, ",")
end

self.params.snis = nil

if snis then
-- dont add the certificate or any snis if we have an SNI conflict
-- its fairly inefficient that we have to loop twice over the datastore
-- but no support for OR queries means we gotsta!
local snis_in_request = {}

for _, sni in ipairs(snis) do
if snis_in_request[sni] then
return helpers.responses.send_HTTP_CONFLICT("duplicate SNI in " ..
"request: " .. sni)
end

local cnt, err = dao_factory.ssl_servers_names:count {
name = sni,
}
if err then
return helpers.yield_error(err)
end

if cnt > 0 then
-- Note: it could be that the SNI is not associated with any
-- certificate, but we don't handle this case. (for PostgreSQL
-- only, as C* requires a cert_id for its partition key).
return helpers.responses.send_HTTP_CONFLICT("SNI already exists: " ..
sni)
end

snis_in_request[sni] = true
end
end

local ssl_cert, err = dao_factory.ssl_certificates:insert(self.params)
if err then
return helpers.yield_error(err)
end

ssl_cert.snis = setmetatable({}, cjson.empty_array_mt)

-- insert SNIs if given

if snis then
for i, sni in ipairs(snis) do
local ssl_server_name = {
name = sni,
ssl_certificate_id = ssl_cert.id,
}

local row, err = dao_factory.ssl_servers_names:insert(ssl_server_name)
if err then
return helpers.yield_error(err)
end

ssl_cert.snis[i] = row.name
local function get_cert_id_from_sni(self, db, helpers)
local id = self.params.certificates
if not utils.is_valid_uuid(id) then
local sni, _, err_t = db.snis:select_by_name(id)
if err_t then
return endpoints.handle_error(err_t)
end
end

return helpers.responses.send_HTTP_CREATED(ssl_cert)
end


local function update_certificate(self, dao_factory, helpers)
-- check if exists
local ssl_cert, err = dao_factory.ssl_certificates:find {
id = self.params.id
}
if err then
return helpers.yield_error(err)
end

if not ssl_cert then
return helpers.responses.send_HTTP_NOT_FOUND()
end

local snis
if type(self.params.snis) == "string" then
snis = utils.split(self.params.snis, ",")

elseif self.params.snis == ngx.null then
snis = {}
end

self.params.snis = nil

local snis_in_request = {} -- check for duplicate snis in the request
local snis_in_db = {} -- avoid db insert if sni is already present in db

-- if snis field present
-- 1. no duplicate snis should be present in the request
-- 2. check if any sni in the request is using a cert
-- other than the one being updated

if snis then
for _, sni in ipairs(snis) do
if snis_in_request[sni] then
return helpers.responses.send_HTTP_CONFLICT("duplicate SNI in " ..
"request: " .. sni)
end

local sni_in_db, err = dao_factory.ssl_servers_names:find {
name = sni,
}
if err then
return helpers.yield_error(err)
end

if sni_in_db then
if sni_in_db.ssl_certificate_id ~= ssl_cert.id then
return helpers.responses.send_HTTP_CONFLICT(
"SNI '" .. sni .. "' already associated with existing " ..
"certificate (" .. sni_in_db.ssl_certificate_id .. ")"
)
end

snis_in_db[sni] = true
end

snis_in_request[sni] = true
end
end

local old_snis, err = dao_factory.ssl_servers_names:find_all {
ssl_certificate_id = ssl_cert.id,
}
if err then
return helpers.yield_error(err)
end

-- update certificate if necessary
if self.params.key or self.params.cert then
self.params.created_at = ssl_cert.created_at

ssl_cert, err = dao_factory.ssl_certificates:update(self.params, {
id = self.params.id,
}, { full = true })
if err then
return helpers.yield_error(err)
end
end

ssl_cert.snis = setmetatable({}, cjson.empty_array_mt)

if not snis then
for i = 1, #old_snis do
ssl_cert.snis[i] = old_snis[i].name
if not sni then
responses.send_HTTP_NOT_FOUND("SNI not found")
end

return helpers.responses.send_HTTP_OK(ssl_cert)
self.params.certificates = sni.certificate.id
end

-- insert/delete SNIs into db if snis field was present in the request
for i, sni in ipairs(snis) do
if not snis_in_db[sni] then
local ssl_server_name = {
name = sni,
ssl_certificate_id = ssl_cert.id,
}

local _, err = dao_factory.ssl_servers_names:insert(ssl_server_name)
if err then
return helpers.yield_error(err)
end
end

ssl_cert.snis[i] = sni
end

-- delete snis which should no longer use ssl_cert
for i = 1, #old_snis do
if not snis_in_request[old_snis[i].name] then
-- ignoring error
-- if we want to return an error here
-- to return 4xx here, the current transaction needs to be
-- rolled back else we risk an invalid state and confusing
-- the user
dao_factory.ssl_servers_names:delete({
name = old_snis[i].name,
})
end
end

return helpers.responses.send_HTTP_OK(ssl_cert)
end


return {
["/certificates/"] = {
POST = function(self, dao_factory, helpers)
create_certificate(self, dao_factory, helpers)
end,
["/certificates/:certificates"] = {
before = get_cert_id_from_sni,

-- override to include the snis list when getting an individual certificate
GET = function(self, db, helpers)
local pk = { id = self.params.certificates }

GET = function(self, dao_factory, helpers)
local ssl_certificates, err = dao_factory.ssl_certificates:find_all()
if err then
return helpers.yield_error(err)
local cert, _, err_t = db.certificates:select_with_name_list(pk)
if err_t then
return endpoints.handle_error(err_t)
end

for i = 1, #ssl_certificates do
local rows, err = dao_factory.ssl_servers_names:find_all {
ssl_certificate_id = ssl_certificates[i].id
}
if err then
return helpers.yield_error(err)
end

ssl_certificates[i].snis = setmetatable({}, cjson.empty_array_mt)

for j = 1, #rows do
ssl_certificates[i].snis[j] = rows[j].name
end
end

return helpers.responses.send_HTTP_OK({
data = #ssl_certificates > 0 and ssl_certificates or cjson.empty_array,
total = #ssl_certificates,
})
end,


PUT = function(self, dao_factory, helpers)
-- no id present, behaviour should be same as POST
if not self.params.id then
create_certificate(self, dao_factory, helpers)
return -- avoid tail call
end

-- id present in body
update_certificate(self, dao_factory, helpers)
return helpers.responses.send_HTTP_OK(cert)
end,
},


["/certificates/:sni_or_uuid"] = {
before = function(self, dao_factory, helpers)
if utils.is_valid_uuid(self.params.sni_or_uuid) then
self.ssl_certificate_id = self.params.sni_or_uuid

else
-- get requested SNI

local row, err = dao_factory.ssl_servers_names:find {
name = self.params.sni_or_uuid
}
if err then
return helpers.yield_error(err)
end

if not row then
return helpers.responses.send_HTTP_NOT_FOUND()
end

-- cache certificate row id

self.ssl_certificate_id = row.ssl_certificate_id
end

self.params.sni_or_uuid = nil
end,


GET = function(self, dao_factory, helpers)
local row, err = dao_factory.ssl_certificates:find {
id = self.ssl_certificate_id
}
if err then
return helpers.yield_error(err)
end

if not row then
return helpers.responses.send_HTTP_NOT_FOUND()
end

-- add list of other SNIs for this certificate

row.snis = setmetatable({}, cjson.empty_array_mt)

local rows, err = dao_factory.ssl_servers_names:find_all {
ssl_certificate_id = self.ssl_certificate_id
}
if err then
return helpers.yield_error(err)
end

for i = 1, #rows do
row.snis[i] = rows[i].name
end

return helpers.responses.send_HTTP_OK(row)
end,


PATCH = function(self, dao_factory, helpers)
self.params.id = self.ssl_certificate_id
update_certificate(self, dao_factory, helpers)
end,


DELETE = function(self, dao_factory, helpers)
return crud.delete({
id = self.ssl_certificate_id
}, dao_factory.ssl_certificates)
end,
}
["/certificates/:certificates/snis"] = {
before = get_cert_id_from_sni,
},
}

Loading

0 comments on commit a6dee10

Please sign in to comment.