lib/zanzibar/actions/bundle.rb
require 'zanzibar/actions/base'
require 'zanzibar/error'
require 'zanzibar'
module Zanzibar
module Actions
# Download or verify the secrets in a Zanzifile
class Bundle < Base
##
# The settings defined in the Zanzifile
attr_accessor :settings
##
# The unresolved secrets from the Zanzifile
attr_accessor :remote_secrets
##
# The resolved secrets from the Zanzifile.resolved
attr_accessor :local_secrets
##
# Whether to disregard local secrets and re-download regardness
attr_accessor :update
##
# Our Zanzibar client
attr_accessor :zanzibar
##
# An action that will fetch secrets defined in a Zanzifile
def initialize(ui, options, args = {})
super(ui, options)
@update = args[:update]
end
##
# Coordinate downloading to secrets (or skipping ones we already have)
def run
ensure_zanzifile
load_required_secrets
ensure_secrets_path
validate_environment
load_resolved_secrets if resolved_file?
validate_local_secrets unless @update
run!
end
private
def run!
if need_secrets?
new_secrets = download_remote_secrets
update_resolved_file new_secrets
else
debug { 'No secrets to download...' }
end
end
def ensure_zanzifile
raise Error, NO_ZANZIFILE_ERROR unless File.exist? ZANZIFILE_NAME
debug { "#{ZANZIFILE_NAME} located..." }
end
def ensure_secrets_path
## Make sure the directory exists and that a .gitignore is there to ignore it
if @settings['secret_dir']
FileUtils.mkdir_p(@settings['secret_dir'])
unless File.exist? "#{@settings['secret_dir']}/.gitignore"
File.open("#{@settings['secret_dir']}/.gitignore", 'w') do |file|
file.puts '*'
file.puts '!.gitignore'
end
end
end
end
def resolved_file?
File.exist? RESOLVED_NAME
end
def load_required_secrets
zanzifile = YAML.load_file(ZANZIFILE_NAME)
@settings = zanzifile['settings'] || {}
@remote_secrets = zanzifile['secrets'] || {}
@local_secrets = {}
end
def validate_environment
return unless @settings.empty? || @remote_secrets.empty?
raise Error, INVALID_ZANZIFILE_ERROR
end
def load_resolved_secrets
@local_secrets = YAML.load_file RESOLVED_NAME
end
def need_secrets?
!@remote_secrets.empty?
end
def validate_local_secrets
@local_secrets.each do |key, secret|
if File.exist?(secret[:path]) && secret[:hash] == Digest::MD5.file(secret[:path]).hexdigest
debug { "#{key} found locally, skipping download..." }
@remote_secrets.delete key
end
end
end
def download_remote_secrets
args = @settings['ignore_ssl'] ? { ssl_verify_mode: :none } : {}
downloaded_secrets = {}
remote_secrets.each do |key, secret|
full_path = secret.key?('prefix') ? File.join(@settings['secret_dir'], secret['prefix']) : @settings['secret_dir']
downloaded_secrets[key] = download_one_secret(secret['id'], secret['label'], full_path,
args, secret_filename(secret))
debug { "Downloaded secret: #{key} to #{@settings['secret_dir']}..." }
end
downloaded_secrets
end
def download_one_secret(scrt_id, label, path, args, name = nil)
if label == 'Password'
path = zanzibar(args).get_username_and_password_and_save(scrt_id, path, name)
else
path = zanzibar(args).download_secret_file(scrt_id: scrt_id,
type: label,
path: path)
end
{ path: path, hash: Digest::MD5.file(path).hexdigest }
end
def update_resolved_file(new_secrets)
@local_secrets.merge! new_secrets
File.open(RESOLVED_NAME, 'w') do |out|
YAML.dump(@local_secrets, out)
end
debug { 'Updated resolved file...' }
end
def zanzibar(args)
@zanzibar ||= ::Zanzibar::Zanzibar.new(wsdl: @settings['wsdl'],
domain: @settings['domain'],
globals: args)
end
def secret_filename(secret)
secret['name'] || "#{secret['id']}_password"
end
end
end
end