62 lines
2.0 KiB
Ruby
62 lines
2.0 KiB
Ruby
class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base
|
|
delegate :optionize, to: Kamal::Utils
|
|
|
|
private
|
|
def login(account)
|
|
unless loggedin?(account)
|
|
`op signin #{to_options(account: account, force: true, raw: true)}`.tap do
|
|
raise RuntimeError, "Failed to login to 1Password" unless $?.success?
|
|
end
|
|
end
|
|
end
|
|
|
|
def loggedin?(account)
|
|
`op account get --account #{account.shellescape}`
|
|
$?.success?
|
|
end
|
|
|
|
def fetch_from_vault(secrets, account:, session:)
|
|
{}.tap do |results|
|
|
vaults_items_fields(secrets).map do |vault, items|
|
|
items.each do |item, fields|
|
|
fields_json = JSON.parse(op_item_get(vault, item, fields, account: account, session: session))
|
|
fields_json = [ fields_json ] if fields.one?
|
|
|
|
fields_json.each do |field_json|
|
|
# The reference is in the form `op://vault/item/field[/field]`
|
|
field = field_json["reference"].delete_prefix("op://").delete_suffix("/password")
|
|
results[field] = field_json["value"]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def to_options(**options)
|
|
optionize(options.compact).join(" ")
|
|
end
|
|
|
|
def vaults_items_fields(secrets)
|
|
{}.tap do |vaults|
|
|
secrets.each do |secret|
|
|
secret = secret.delete_prefix("op://")
|
|
vault, item, *fields = secret.split("/")
|
|
fields << "password" if fields.empty?
|
|
|
|
vaults[vault] ||= {}
|
|
vaults[vault][item] ||= []
|
|
vaults[vault][item] << fields.join(".")
|
|
end
|
|
end
|
|
end
|
|
|
|
def op_item_get(vault, item, fields, account:, session:)
|
|
labels = fields.map { |field| "label=#{field}" }.join(",")
|
|
options = to_options(vault: vault, fields: labels, format: "json", account: account, session: session.presence)
|
|
|
|
`op item get #{item.shellescape} #{options}`.tap do
|
|
raise RuntimeError, "Could not read #{fields.join(", ")} from #{item} in the #{vault} 1Password vault" unless $?.success?
|
|
end
|
|
end
|
|
end
|