local helpers = require "spec.helpers" -- initializes 'kong' global for vaults local conf_loader = require "kong.conf_loader" local is_ref_test_map = { ["{vault://x/x}"] = true, ["{vault://x/xx}"] = true, ["{vault://xx/xx-x}"] = true, ["{vault://xxx/xx-x}"] = true, ["{vault://;/xx-x}"] = true, ["vault:/xx-x}"] = false, ["{vault:/xx-x"] = false, ["vault:/xx-x"] = false, ["{valut:/xx-x}"] = false, ["{vault:/xx-x}"] = false, } local get_test_map = { ["{vault://env/test_secrets}"] = { k = "TEST_SECRETS", v = "test_value" }, ["{vault://env/test}"] = { k = "TEST", v = "" }, } describe("Vault PDK", function() local vaults local is_reference local parse_reference local dereference local try local update before_each(function() local conf = assert(conf_loader(nil, { vaults = "bundled", plugins = "bundled", })) local kong_global = require "kong.global" _G.kong = kong_global.new() kong_global.init_pdk(kong, conf) is_reference = _G.kong.vault.is_reference parse_reference = _G.kong.vault.parse_reference dereference = _G.kong.vault.get try = _G.kong.vault.try update = _G.kong.vault.update vaults = {} for vault in pairs(conf.loaded_vaults) do local init = require("kong.vaults." .. vault) table.insert(vaults, init) end end) it("test init", function() local res, err = parse_reference("{vault://env/test}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test", res.resource) assert.is_nil(res.key) assert.is_nil(res.version) end) it("test init path with only slashes does not work", function() local res, err = parse_reference("{vault://env}") assert.is_nil(res) assert.equal("reference url is missing path [{vault://env}]", err) local res, err = parse_reference("{vault://env/}") assert.is_nil(res) assert.equal("reference url has empty path [{vault://env/}]", err) local res, err = parse_reference("{vault://env/////}") assert.is_nil(res) assert.equal("reference url has invalid path [{vault://env/////}]", err) end) it("test init nested/path", function() local res, err = parse_reference("{vault://env/test-secret/test-key}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test-secret", res.resource) assert.is_equal("test-key", res.key) assert.is_nil(res.version) end) it("test init nested/path is url decoded", function() local res, err = parse_reference("{vault://env/test%3Asecret/test%3Akey}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test:secret", res.resource) assert.is_equal("test:key", res.key) assert.is_nil(res.version) end) it("test init nested/path ignores consecutive slashes", function() local res, err = parse_reference("{vault://env//////test-secret//////test-key}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test-secret", res.resource) assert.is_equal("test-key", res.key) assert.is_nil(res.version) end) it("test init nested/path ending with slash", function() local res, err = parse_reference("{vault://env/test-secret/test-key/}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test-secret/test-key", res.resource) assert.is_nil(res.key) assert.is_nil(res.version) end) it("test init nested/path ending with slash ignores consecutive slashes", function() local res, err = parse_reference("{vault://env//////test-secret//////test-key//////}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test-secret/test-key", res.resource) assert.is_nil(res.key) assert.is_nil(res.version) end) it("test init opts", function() local res, err = parse_reference("{vault://env/test?opt1=val1}") assert.is_nil(err) assert.is_same({ opt1 = "val1" }, res.config) assert.is_equal("env", res.name) assert.is_equal(res.resource, "test") assert.is_nil(res.key) assert.is_nil(res.version) end) it("test init multiple opts", function() local res, err = parse_reference("{vault://env/test?opt1=val1&opt2=val2}") assert.is_nil(err) assert.is_same({ opt1 = "val1", opt2 = "val2" }, res.config) assert.is_equal("env", res.name) assert.is_equal("test", res.resource) assert.is_nil(res.key) assert.is_nil(res.version) end) it("test init version", function() local res, err = parse_reference("{vault://env/test#1}") assert.is_nil(err) assert.is_nil(res.config) assert.is_equal("env", res.name) assert.is_equal("test", res.resource) assert.is_nil(res.key) assert.equal(1, res.version) end) it("ensure that every vault has a VERSION and a `get` field", function() for _, vault in ipairs(vaults) do assert.not_nil(vault.get) assert.not_nil(vault.VERSION) end end) for ref, exp in pairs(is_ref_test_map) do it("test is_reference [" .. ref .. "] -> " .. tostring(exp), function() assert.is_equal(exp, is_reference(ref)) end) end for ref, cfg in pairs(get_test_map) do it("test get [" .. ref .. "] -> " .. tostring(cfg.k), function() finally(function() helpers.unsetenv(cfg.k) end) helpers.setenv(cfg.k, cfg.v) local ret, err = dereference(ref) assert.is_nil(err) assert.is_equal(cfg.v, ret) end) end describe("try function", function() local called before_each(function() called = 0 end) it("calls the callback only once when successful", function() local ok, err = try(function() called = called + 1 return true end) assert.is_nil(err) assert.True(ok) assert.equal(1, called) end) it("calls the callback only once when no options", function() local ok, err = try(function() called = called + 1 return nil, "failed" end) assert.equal("failed", err) assert.is_nil(ok) assert.equal(1, called) end) it("calls the callback only once when no refs", function() local ok, err = try(function() called = called + 1 return nil, "failed" end, {}) assert.equal("failed", err) assert.is_nil(ok) assert.equal(1, called) end) it("calls the callback only once when refs is empty", function() local ok, err = try(function() called = called + 1 return nil, "failed" end, { ["$refs"] = {} }) assert.equal("failed", err) assert.is_nil(ok) assert.equal(1, called) end) it("calls the callback twice when current credentials doesn't work", function() finally(function() helpers.unsetenv("CREDENTIALS") end) helpers.setenv("CREDENTIALS", '{"username":"jane","password":"qwerty"}') local options = { username = "john", password = "secret", ["$refs"] = { username = "{vault://env/credentials/username}", password = "{vault://env/credentials/password}", }, } local callback = function(options) called = called + 1 if options.username ~= "jane" or options.password ~= "qwerty" then return nil, "failed" end return true end local ok, err = try(callback, options) assert.is_nil(err) assert.True(ok) assert.equal(2, called) assert.equal("jane", options.username) assert.equal("qwerty", options.password) assert.equal("{vault://env/credentials/username}", options["$refs"].username) assert.equal("{vault://env/credentials/password}", options["$refs"].password) -- updates values before first call from caches called = 0 options = { username = "john", password = "secret", ["$refs"] = { username = "{vault://env/credentials/username}", password = "{vault://env/credentials/password}", }, } local ok, err = try(callback, options) assert.is_nil(err) assert.True(ok) assert.equal(1, called) assert.equal("jane", options.username) assert.equal("qwerty", options.password) assert.equal("{vault://env/credentials/username}", options["$refs"].username) assert.equal("{vault://env/credentials/password}", options["$refs"].password) end) end) describe("update function", function() it("sets values to empty string on failure", function() finally(function() helpers.unsetenv("CREDENTIALS") end) helpers.setenv("CREDENTIALS", '{"username":"jane","password":"qwerty"}') -- warmup cache dereference("{vault://env/credentials/username}") dereference("{vault://env/credentials/password}") local config = { str_found = "found", str_not_found = "not found", str_not_found_2 = "not found", arr_found = { "found", "found", "found", "found", "found" }, arr_hole = { "found", "found", "not found", "found", "found" }, arr_not_found = { "found", "not found", "not found", "not found", "found" }, map_found = { nil, "found", a = "found", b = "found", c = "found", d = "found", }, map_not_found = { nil, "found", a = "found", b = "found", c = "found", d = "found", }, ["$refs"] = { str_found = "{vault://env/credentials/username}", str_not_found = "{vault://env/not-found}", str_not_found_2 = "{vault://env/credentials/not-found}", arr_found = { nil, "{vault://env/credentials/username}", "{vault://env/credentials/password}", "{vault://env/credentials/username}", }, arr_hole = { nil, "{vault://env/credentials/username}", "{vault://env/credentials/not-found}", "{vault://env/credentials/username}", }, arr_not_found = { nil, "{vault://env/not-found}", "{vault://env/credentials/not-found}", "{vault://env/not-found}", }, map_found = { a = "{vault://env/credentials/username}", b = "{vault://env/credentials/password}", c = "{vault://env/credentials/username}", }, map_not_found = { a = "{vault://env/not-found}", b = "{vault://env/credentials/not-found}", c = "{vault://env/not-found}", } }, sub = { str_found = "found", str_not_found = "not found", str_not_found_2 = "not found", arr_found = { "found", "found", "found", "found", "found" }, arr_hole = { "found", "found", "not found", "found", "found" }, arr_not_found = { "found", "not found", "not found", "not found", "found" }, map_found = { nil, "found", a = "found", b = "found", c = "found", d = "found", }, map_not_found = { nil, "found", a = "found", b = "found", c = "found", d = "found", }, ["$refs"] = { str_found = "{vault://env/credentials/username}", str_not_found = "{vault://env/not-found}", str_not_found_2 = "{vault://env/credentials/not-found}", arr_found = { nil, "{vault://env/credentials/username}", "{vault://env/credentials/password}", "{vault://env/credentials/username}", }, arr_hole = { nil, "{vault://env/credentials/username}", "{vault://env/credentials/not-found}", "{vault://env/credentials/username}", }, arr_not_found = { nil, "{vault://env/not-found}", "{vault://env/credentials/not-found}", "{vault://env/not-found}", }, map_found = { a = "{vault://env/credentials/username}", b = "{vault://env/credentials/password}", c = "{vault://env/credentials/username}", }, map_not_found = { a = "{vault://env/not-found}", b = "{vault://env/credentials/not-found}", c = "{vault://env/not-found}", } }, }, } local updated_cfg = update(config) assert.equal(config, updated_cfg) for _, cfg in ipairs({ config, config.sub }) do assert.equal("jane", cfg.str_found) assert.equal("", cfg.str_not_found) assert.equal("", cfg.str_not_found_2) assert.same({ "found", "jane", "qwerty", "jane", "found" }, cfg.arr_found) assert.same({ "found", "jane", "", "jane", "found" }, cfg.arr_hole) assert.same({ "found", "", "", "", "found" }, cfg.arr_not_found) assert.same({ nil, "found", a = "jane", b = "qwerty", c = "jane", d = "found", }, cfg.map_found) assert.same({ nil, "found", a = "", b = "", c = "", d = "found", }, cfg.map_not_found) end end) end) end)