Merge pull request #1570 from nickcoyne/bws-secrets
Request Bitwarden Secrets Manager secrets as JSON
This commit is contained in:
@@ -6,8 +6,8 @@ class Kamal::Secrets::Adapters::BitwardenSecretsManager < Kamal::Secrets::Adapte
|
|||||||
private
|
private
|
||||||
LIST_ALL_SELECTOR = "all"
|
LIST_ALL_SELECTOR = "all"
|
||||||
LIST_ALL_FROM_PROJECT_SUFFIX = "/all"
|
LIST_ALL_FROM_PROJECT_SUFFIX = "/all"
|
||||||
LIST_COMMAND = "secret list -o env"
|
LIST_COMMAND = "secret list"
|
||||||
GET_COMMAND = "secret get -o env"
|
GET_COMMAND = "secret get"
|
||||||
|
|
||||||
def fetch_secrets(secrets, from:, account:, session:)
|
def fetch_secrets(secrets, from:, account:, session:)
|
||||||
raise RuntimeError, "You must specify what to retrieve from Bitwarden Secrets Manager" if secrets.length == 0
|
raise RuntimeError, "You must specify what to retrieve from Bitwarden Secrets Manager" if secrets.length == 0
|
||||||
@@ -18,17 +18,17 @@ class Kamal::Secrets::Adapters::BitwardenSecretsManager < Kamal::Secrets::Adapte
|
|||||||
{}.tap do |results|
|
{}.tap do |results|
|
||||||
if command.nil?
|
if command.nil?
|
||||||
secrets.each do |secret_uuid|
|
secrets.each do |secret_uuid|
|
||||||
secret = run_command("#{GET_COMMAND} #{secret_uuid.shellescape}")
|
item_json = run_command("#{GET_COMMAND} #{secret_uuid.shellescape}")
|
||||||
raise RuntimeError, "Could not read #{secret_uuid} from Bitwarden Secrets Manager" unless $?.success?
|
raise RuntimeError, "Could not read #{secret_uuid} from Bitwarden Secrets Manager" unless $?.success?
|
||||||
key, value = parse_secret(secret)
|
item_json = JSON.parse(item_json)
|
||||||
results[key] = value
|
results[item_json["key"]] = item_json["value"]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
secrets = run_command(command)
|
items_json = run_command(command)
|
||||||
raise RuntimeError, "Could not read secrets from Bitwarden Secrets Manager" unless $?.success?
|
raise RuntimeError, "Could not read secrets from Bitwarden Secrets Manager" unless $?.success?
|
||||||
secrets.split("\n").each do |secret|
|
|
||||||
key, value = parse_secret(secret)
|
JSON.parse(items_json).each do |item_json|
|
||||||
results[key] = value
|
results[item_json["key"]] = item_json["value"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -45,12 +45,6 @@ class Kamal::Secrets::Adapters::BitwardenSecretsManager < Kamal::Secrets::Adapte
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_secret(secret)
|
|
||||||
key, value = secret.split("=", 2)
|
|
||||||
value = value.gsub(/^"|"$/, "")
|
|
||||||
[ key, value ]
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_command(command, session: nil)
|
def run_command(command, session: nil)
|
||||||
full_command = [ "bws", command ].join(" ")
|
full_command = [ "bws", command ].join(" ")
|
||||||
`#{full_command}`
|
`#{full_command}`
|
||||||
|
|||||||
@@ -15,57 +15,111 @@ class BitwardenSecretsManagerAdapterTest < SecretAdapterTestCase
|
|||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_login
|
stub_login
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bws secret list -o env")
|
.with("bws secret list")
|
||||||
.returns("KAMAL_REGISTRY_PASSWORD=\"some_password\"\nMY_OTHER_SECRET=\"my=weird\"secret\"")
|
.returns(<<~JSON)
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "KAMAL_REGISTRY_PASSWORD",
|
||||||
|
"value": "some_password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "MY_OTHER_SECRET",
|
||||||
|
"value": "my=wierd\\"secret"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
JSON
|
||||||
|
|
||||||
expected = '{"KAMAL_REGISTRY_PASSWORD":"some_password","MY_OTHER_SECRET":"my\=weird\"secret"}'
|
json = JSON.parse(shellunescape(run_command("fetch", "all")))
|
||||||
actual = shellunescape(run_command("fetch", "all"))
|
|
||||||
assert_equal expected, actual
|
expected_json = {
|
||||||
|
"KAMAL_REGISTRY_PASSWORD"=>"some_password",
|
||||||
|
"MY_OTHER_SECRET"=>"my=wierd\"secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch all with from" do
|
test "fetch all with from" do
|
||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_login
|
stub_login
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bws secret list -o env 82aeb5bd-6958-4a89-8197-eacab758acce")
|
.with("bws secret list 82aeb5bd-6958-4a89-8197-eacab758acce")
|
||||||
.returns("KAMAL_REGISTRY_PASSWORD=\"some_password\"\nMY_OTHER_SECRET=\"my=weird\"secret\"")
|
.returns(<<~JSON)
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "KAMAL_REGISTRY_PASSWORD",
|
||||||
|
"value": "some_password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "MY_OTHER_SECRET",
|
||||||
|
"value": "my=wierd\\"secret"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
JSON
|
||||||
|
|
||||||
expected = '{"KAMAL_REGISTRY_PASSWORD":"some_password","MY_OTHER_SECRET":"my\=weird\"secret"}'
|
json = JSON.parse(shellunescape(run_command("fetch", "all", "--from", "82aeb5bd-6958-4a89-8197-eacab758acce")))
|
||||||
actual = shellunescape(run_command("fetch", "all", "--from", "82aeb5bd-6958-4a89-8197-eacab758acce"))
|
|
||||||
assert_equal expected, actual
|
expected_json = {
|
||||||
|
"KAMAL_REGISTRY_PASSWORD"=>"some_password",
|
||||||
|
"MY_OTHER_SECRET"=>"my=wierd\"secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch item" do
|
test "fetch item" do
|
||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_login
|
stub_login
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bws secret get -o env 82aeb5bd-6958-4a89-8197-eacab758acce")
|
.with("bws secret get 82aeb5bd-6958-4a89-8197-eacab758acce")
|
||||||
.returns("KAMAL_REGISTRY_PASSWORD=\"some_password\"")
|
.returns(<<~JSON)
|
||||||
|
{
|
||||||
|
"key": "KAMAL_REGISTRY_PASSWORD",
|
||||||
|
"value": "some_password"
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
expected = '{"KAMAL_REGISTRY_PASSWORD":"some_password"}'
|
json = JSON.parse(shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce")))
|
||||||
actual = shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce"))
|
expected_json = {
|
||||||
assert_equal expected, actual
|
"KAMAL_REGISTRY_PASSWORD"=>"some_password"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with multiple items" do
|
test "fetch with multiple items" do
|
||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_login
|
stub_login
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bws secret get -o env 82aeb5bd-6958-4a89-8197-eacab758acce")
|
.with("bws secret get 82aeb5bd-6958-4a89-8197-eacab758acce")
|
||||||
.returns("KAMAL_REGISTRY_PASSWORD=\"some_password\"")
|
.returns(<<~JSON)
|
||||||
|
{
|
||||||
|
"key": "KAMAL_REGISTRY_PASSWORD",
|
||||||
|
"value": "some_password"
|
||||||
|
}
|
||||||
|
JSON
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bws secret get -o env 6f8cdf27-de2b-4c77-a35d-07df8050e332")
|
.with("bws secret get 6f8cdf27-de2b-4c77-a35d-07df8050e332")
|
||||||
.returns("MY_OTHER_SECRET=\"my=weird\"secret\"")
|
.returns(<<~JSON)
|
||||||
|
{
|
||||||
|
"key": "MY_OTHER_SECRET",
|
||||||
|
"value": "my=wierd\\"secret"
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
expected = '{"KAMAL_REGISTRY_PASSWORD":"some_password","MY_OTHER_SECRET":"my\=weird\"secret"}'
|
json = JSON.parse(shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce", "6f8cdf27-de2b-4c77-a35d-07df8050e332")))
|
||||||
actual = shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce", "6f8cdf27-de2b-4c77-a35d-07df8050e332"))
|
expected_json = {
|
||||||
assert_equal expected, actual
|
"KAMAL_REGISTRY_PASSWORD"=>"some_password",
|
||||||
|
"MY_OTHER_SECRET"=>"my=wierd\"secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch all empty" do
|
test "fetch all empty" do
|
||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_login
|
stub_login
|
||||||
stub_ticks_with("bws secret list -o env", succeed: false).returns("Error:\n0: Received error message from server")
|
stub_ticks_with("bws secret list", succeed: false).returns("Error:\n0: Received error message from server")
|
||||||
|
|
||||||
error = assert_raises RuntimeError do
|
error = assert_raises RuntimeError do
|
||||||
(shellunescape(run_command("fetch", "all")))
|
(shellunescape(run_command("fetch", "all")))
|
||||||
@@ -76,8 +130,8 @@ class BitwardenSecretsManagerAdapterTest < SecretAdapterTestCase
|
|||||||
test "fetch nonexistent item" do
|
test "fetch nonexistent item" do
|
||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_login
|
stub_login
|
||||||
stub_ticks_with("bws secret get -o env 82aeb5bd-6958-4a89-8197-eacab758acce", succeed: false)
|
stub_ticks_with("bws secret get 82aeb5bd-6958-4a89-8197-eacab758acce", succeed: false)
|
||||||
.returns("ERROR (RuntimeError): Could not read 82aeb5bd-6958-4a89-8197-eacab758acce from Bitwarden Secrets Manager")
|
.returns("Error:\n0: Received error message from server")
|
||||||
|
|
||||||
error = assert_raises RuntimeError do
|
error = assert_raises RuntimeError do
|
||||||
(shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce")))
|
(shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce")))
|
||||||
@@ -85,6 +139,26 @@ class BitwardenSecretsManagerAdapterTest < SecretAdapterTestCase
|
|||||||
assert_equal("Could not read 82aeb5bd-6958-4a89-8197-eacab758acce from Bitwarden Secrets Manager", error.message)
|
assert_equal("Could not read 82aeb5bd-6958-4a89-8197-eacab758acce from Bitwarden Secrets Manager", error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "fetch item with linebreak in value" do
|
||||||
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
|
stub_login
|
||||||
|
stub_ticks
|
||||||
|
.with("bws secret get 82aeb5bd-6958-4a89-8197-eacab758acce")
|
||||||
|
.returns(<<~JSON)
|
||||||
|
{
|
||||||
|
"key": "SSH_PRIVATE_KEY",
|
||||||
|
"value": "some_key\\nwith_linebreak"
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
json = JSON.parse(shellunescape(run_command("fetch", "82aeb5bd-6958-4a89-8197-eacab758acce")))
|
||||||
|
expected_json = {
|
||||||
|
"SSH_PRIVATE_KEY"=>"some_key\nwith_linebreak"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_json, json
|
||||||
|
end
|
||||||
|
|
||||||
test "fetch with no access token" do
|
test "fetch with no access token" do
|
||||||
stub_ticks.with("bws --version 2> /dev/null")
|
stub_ticks.with("bws --version 2> /dev/null")
|
||||||
stub_ticks_with("bws project list", succeed: false)
|
stub_ticks_with("bws project list", succeed: false)
|
||||||
|
|||||||
Reference in New Issue
Block a user