making zanzibar bin a thor app with bundler/librarian type capability
This commit is contained in:
380
lib/zanzibar.rb
380
lib/zanzibar.rb
@@ -1,190 +1,190 @@
|
||||
require 'zanzibar/version'
|
||||
require 'savon'
|
||||
require 'io/console'
|
||||
require 'fileutils'
|
||||
|
||||
module Zanzibar
|
||||
##
|
||||
# Class for interacting with Secret Server
|
||||
class Zanzibar
|
||||
##
|
||||
# @param args{:domain, :wsdl, :pwd, :username, :globals{}}
|
||||
|
||||
def initialize(args = {})
|
||||
if args[:username]
|
||||
@@username = args[:username]
|
||||
elsif ENV['ZANZIBAR_USER']
|
||||
@@username = ENV['ZANZIBAR_USER']
|
||||
else
|
||||
@@username = ENV['USER']
|
||||
end
|
||||
|
||||
if args[:wsdl]
|
||||
@@wsdl = args[:wsdl]
|
||||
else
|
||||
@@wsdl = get_wsdl_location
|
||||
end
|
||||
|
||||
if args[:pwd]
|
||||
@@password = args[:pwd]
|
||||
elsif ENV['ZANZIBAR_PASSWORD']
|
||||
@@password = ENV['ZANZIBAR_PASSWORD']
|
||||
else
|
||||
@@password = prompt_for_password
|
||||
end
|
||||
|
||||
if args[:domain]
|
||||
@@domain = args[:domain]
|
||||
else
|
||||
@@domain = prompt_for_domain
|
||||
end
|
||||
args[:globals] = {} unless args[:globals]
|
||||
init_client(args[:globals])
|
||||
end
|
||||
|
||||
def get_client_username
|
||||
@@username
|
||||
end
|
||||
|
||||
def get_client_password
|
||||
@@password
|
||||
end
|
||||
|
||||
## Initializes the Savon client class variable with the wdsl document location and optional global variables
|
||||
# @param globals{}, optional
|
||||
|
||||
def init_client(globals = {})
|
||||
globals = {} if globals.nil?
|
||||
@@client = Savon.client(globals) do
|
||||
wsdl @@wsdl
|
||||
end
|
||||
end
|
||||
|
||||
## Gets the user's password if none is provided in the constructor.
|
||||
# @return [String] the password for the current user
|
||||
|
||||
def prompt_for_password
|
||||
puts "Please enter password for #{@@username}:"
|
||||
STDIN.noecho(&:gets).chomp
|
||||
end
|
||||
|
||||
## Gets the wsdl document location if none is provided in the constructor
|
||||
# @return [String] the location of the WDSL document
|
||||
|
||||
def prompt_for_wsdl_location
|
||||
puts 'Enter the URL of the Secret Server WSDL:'
|
||||
STDIN.gets.chomp
|
||||
end
|
||||
|
||||
## Gets the domain of the Secret Server installation if none is provided in the constructor
|
||||
# @return [String] the domain of the secret server installation
|
||||
|
||||
def prompt_for_domain
|
||||
puts 'Enter the domain of your Secret Server:'
|
||||
STDIN.gets.chomp
|
||||
end
|
||||
|
||||
## Get an authentication token for interacting with Secret Server. These are only good for about 10 minutes so just get a new one each time.
|
||||
# Will raise an error if there is an issue with the authentication.
|
||||
# @return the authentication token for the current user.
|
||||
|
||||
def get_token
|
||||
response = @@client.call(:authenticate, message: { username: @@username, password: @@password, organization: '', domain: @@domain })
|
||||
.hash[:envelope][:body][:authenticate_response][:authenticate_result]
|
||||
fail "Error generating the authentication token for user #{@@username}: #{response[:errors][:string]}" if response[:errors]
|
||||
response[:token]
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error generating the authentiaton token for user #{@@username}: #{err}"
|
||||
end
|
||||
|
||||
## Get a secret returned as a hash
|
||||
# Will raise an error if there was an issue getting the secret
|
||||
# @param [Integer] the secret id
|
||||
# @return [Hash] the secret hash retrieved from the wsdl
|
||||
|
||||
def get_secret(scrt_id, token = nil)
|
||||
secret = @@client.call(:get_secret, message: { token: token || get_token, secretId: scrt_id }).hash[:envelope][:body][:get_secret_response][:get_secret_result]
|
||||
fail "There was an error getting secret #{scrt_id}: #{secret[:errors][:string]}" if secret[:errors]
|
||||
return secret
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error getting the secret with id #{scrt_id}: #{err}"
|
||||
end
|
||||
|
||||
## Retrieve a simple password from a secret
|
||||
# Will raise an error if there are any issues
|
||||
# @param [Integer] the secret id
|
||||
# @return [String] the password for the given secret
|
||||
|
||||
def get_password(scrt_id)
|
||||
secret = get_secret(scrt_id)
|
||||
secret_items = secret[:secret][:items][:secret_item]
|
||||
return get_secret_item_by_field_name(secret_items, 'Password')[:value]
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error getting the password for secret #{scrt_id}: #{err}"
|
||||
end
|
||||
|
||||
def write_secret_to_file(path, secret_response)
|
||||
File.open(File.join(path, secret_response[:file_name]), 'wb') do |file|
|
||||
file.puts Base64.decode64(secret_response[:file_attachment])
|
||||
end
|
||||
end
|
||||
|
||||
def get_secret_item_by_field_name(secret_items, field_name)
|
||||
secret_items.each do |item|
|
||||
return item if item[:field_name] == field_name
|
||||
end
|
||||
end
|
||||
|
||||
## Get the secret item id that relates to a key file or attachment.
|
||||
# Will raise on error
|
||||
# @param [Integer] the secret id
|
||||
# @param [String] the type of secret item to get, one of privatekey, publickey, attachment
|
||||
# @return [Integer] the secret item id
|
||||
|
||||
def get_scrt_item_id(scrt_id, type, token)
|
||||
secret = get_secret(scrt_id, token)
|
||||
secret_items = secret[:secret][:items][:secret_item]
|
||||
begin
|
||||
return get_secret_item_by_field_name(secret_items, type)[:id]
|
||||
rescue
|
||||
raise "Unknown type, #{type}."
|
||||
end
|
||||
end
|
||||
|
||||
## Downloads a file for a secret and places it where Zanzibar is running, or :path if specified
|
||||
# Raise on error
|
||||
# @param [Hash] args, :scrt_id, :type (one of "Private Key", "Public Key", "Attachment"), :scrt_item_id - optional, :path - optional
|
||||
|
||||
def download_secret_file(args = {})
|
||||
token = get_token
|
||||
FileUtils.mkdir_p(args[:path]) if args[:path]
|
||||
path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
|
||||
begin
|
||||
response = @@client.call(:download_file_attachment_by_item_id, message:
|
||||
{ token: token, secretId: args[:scrt_id], secretItemId: args[:scrt_item_id] || get_scrt_item_id(args[:scrt_id], args[:type], token) })
|
||||
.hash[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result]
|
||||
fail "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{response[:errors][:string]}" if response[:errors]
|
||||
write_secret_to_file(path, response)
|
||||
return File.join(path, response[:file_name])
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{err}"
|
||||
end
|
||||
end
|
||||
|
||||
## Methods to maintain backwards compatibility
|
||||
def download_private_key(args = {})
|
||||
args[:type] = 'Private Key'
|
||||
download_secret_file(args)
|
||||
end
|
||||
|
||||
def download_public_key(args = {})
|
||||
args[:type] = 'Public Key'
|
||||
download_secret_file(args)
|
||||
end
|
||||
|
||||
def download_attachment(args = {})
|
||||
args[:type] = 'Attachment'
|
||||
download_secret_file(args)
|
||||
end
|
||||
end
|
||||
end
|
||||
require 'zanzibar/version'
|
||||
require 'savon'
|
||||
require 'io/console'
|
||||
require 'fileutils'
|
||||
|
||||
module Zanzibar
|
||||
##
|
||||
# Class for interacting with Secret Server
|
||||
class Zanzibar
|
||||
##
|
||||
# @param args{:domain, :wsdl, :pwd, :username, :globals{}}
|
||||
|
||||
def initialize(args = {})
|
||||
if args[:username]
|
||||
@@username = args[:username]
|
||||
elsif ENV['ZANZIBAR_USER']
|
||||
@@username = ENV['ZANZIBAR_USER']
|
||||
else
|
||||
@@username = ENV['USER']
|
||||
end
|
||||
|
||||
if args[:wsdl]
|
||||
@@wsdl = args[:wsdl]
|
||||
else
|
||||
@@wsdl = get_wsdl_location
|
||||
end
|
||||
|
||||
if args[:pwd]
|
||||
@@password = args[:pwd]
|
||||
elsif ENV['ZANZIBAR_PASSWORD']
|
||||
@@password = ENV['ZANZIBAR_PASSWORD']
|
||||
else
|
||||
@@password = prompt_for_password
|
||||
end
|
||||
|
||||
if args[:domain]
|
||||
@@domain = args[:domain]
|
||||
else
|
||||
@@domain = prompt_for_domain
|
||||
end
|
||||
args[:globals] = {} unless args[:globals]
|
||||
init_client(args[:globals])
|
||||
end
|
||||
|
||||
def get_client_username
|
||||
@@username
|
||||
end
|
||||
|
||||
def get_client_password
|
||||
@@password
|
||||
end
|
||||
|
||||
## Initializes the Savon client class variable with the wdsl document location and optional global variables
|
||||
# @param globals{}, optional
|
||||
|
||||
def init_client(globals = {})
|
||||
globals = {} if globals.nil?
|
||||
@@client = Savon.client(globals) do
|
||||
wsdl @@wsdl
|
||||
end
|
||||
end
|
||||
|
||||
## Gets the user's password if none is provided in the constructor.
|
||||
# @return [String] the password for the current user
|
||||
|
||||
def prompt_for_password
|
||||
puts "Please enter password for #{@@username}:"
|
||||
STDIN.noecho(&:gets).chomp
|
||||
end
|
||||
|
||||
## Gets the wsdl document location if none is provided in the constructor
|
||||
# @return [String] the location of the WDSL document
|
||||
|
||||
def prompt_for_wsdl_location
|
||||
puts 'Enter the URL of the Secret Server WSDL:'
|
||||
STDIN.gets.chomp
|
||||
end
|
||||
|
||||
## Gets the domain of the Secret Server installation if none is provided in the constructor
|
||||
# @return [String] the domain of the secret server installation
|
||||
|
||||
def prompt_for_domain
|
||||
puts 'Enter the domain of your Secret Server:'
|
||||
STDIN.gets.chomp
|
||||
end
|
||||
|
||||
## Get an authentication token for interacting with Secret Server. These are only good for about 10 minutes so just get a new one each time.
|
||||
# Will raise an error if there is an issue with the authentication.
|
||||
# @return the authentication token for the current user.
|
||||
|
||||
def get_token
|
||||
response = @@client.call(:authenticate, message: { username: @@username, password: @@password, organization: '', domain: @@domain })
|
||||
.hash[:envelope][:body][:authenticate_response][:authenticate_result]
|
||||
fail "Error generating the authentication token for user #{@@username}: #{response[:errors][:string]}" if response[:errors]
|
||||
response[:token]
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error generating the authentiaton token for user #{@@username}: #{err}"
|
||||
end
|
||||
|
||||
## Get a secret returned as a hash
|
||||
# Will raise an error if there was an issue getting the secret
|
||||
# @param [Integer] the secret id
|
||||
# @return [Hash] the secret hash retrieved from the wsdl
|
||||
|
||||
def get_secret(scrt_id, token = nil)
|
||||
secret = @@client.call(:get_secret, message: { token: token || get_token, secretId: scrt_id }).hash[:envelope][:body][:get_secret_response][:get_secret_result]
|
||||
fail "There was an error getting secret #{scrt_id}: #{secret[:errors][:string]}" if secret[:errors]
|
||||
return secret
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error getting the secret with id #{scrt_id}: #{err}"
|
||||
end
|
||||
|
||||
## Retrieve a simple password from a secret
|
||||
# Will raise an error if there are any issues
|
||||
# @param [Integer] the secret id
|
||||
# @return [String] the password for the given secret
|
||||
|
||||
def get_password(scrt_id)
|
||||
secret = get_secret(scrt_id)
|
||||
secret_items = secret[:secret][:items][:secret_item]
|
||||
return get_secret_item_by_field_name(secret_items, 'Password')[:value]
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error getting the password for secret #{scrt_id}: #{err}"
|
||||
end
|
||||
|
||||
def write_secret_to_file(path, secret_response)
|
||||
File.open(File.join(path, secret_response[:file_name]), 'wb') do |file|
|
||||
file.puts Base64.decode64(secret_response[:file_attachment])
|
||||
end
|
||||
end
|
||||
|
||||
def get_secret_item_by_field_name(secret_items, field_name)
|
||||
secret_items.each do |item|
|
||||
return item if item[:field_name] == field_name
|
||||
end
|
||||
end
|
||||
|
||||
## Get the secret item id that relates to a key file or attachment.
|
||||
# Will raise on error
|
||||
# @param [Integer] the secret id
|
||||
# @param [String] the type of secret item to get, one of privatekey, publickey, attachment
|
||||
# @return [Integer] the secret item id
|
||||
|
||||
def get_scrt_item_id(scrt_id, type, token)
|
||||
secret = get_secret(scrt_id, token)
|
||||
secret_items = secret[:secret][:items][:secret_item]
|
||||
begin
|
||||
return get_secret_item_by_field_name(secret_items, type)[:id]
|
||||
rescue
|
||||
raise "Unknown type, #{type}."
|
||||
end
|
||||
end
|
||||
|
||||
## Downloads a file for a secret and places it where Zanzibar is running, or :path if specified
|
||||
# Raise on error
|
||||
# @param [Hash] args, :scrt_id, :type (one of "Private Key", "Public Key", "Attachment"), :scrt_item_id - optional, :path - optional
|
||||
|
||||
def download_secret_file(args = {})
|
||||
token = get_token
|
||||
FileUtils.mkdir_p(args[:path]) if args[:path]
|
||||
path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
|
||||
begin
|
||||
response = @@client.call(:download_file_attachment_by_item_id, message:
|
||||
{ token: token, secretId: args[:scrt_id], secretItemId: args[:scrt_item_id] || get_scrt_item_id(args[:scrt_id], args[:type], token) })
|
||||
.hash[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result]
|
||||
fail "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{response[:errors][:string]}" if response[:errors]
|
||||
write_secret_to_file(path, response)
|
||||
return File.join(path, response[:file_name])
|
||||
rescue Savon::Error => err
|
||||
raise "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{err}"
|
||||
end
|
||||
end
|
||||
|
||||
## Methods to maintain backwards compatibility
|
||||
def download_private_key(args = {})
|
||||
args[:type] = 'Private Key'
|
||||
download_secret_file(args)
|
||||
end
|
||||
|
||||
def download_public_key(args = {})
|
||||
args[:type] = 'Public Key'
|
||||
download_secret_file(args)
|
||||
end
|
||||
|
||||
def download_attachment(args = {})
|
||||
args[:type] = 'Attachment'
|
||||
download_secret_file(args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
3
lib/zanzibar/actions.rb
Normal file
3
lib/zanzibar/actions.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
require 'zanzibar/actions/init'
|
||||
require 'zanzibar/actions/bundle'
|
||||
require 'zanzibar/actions/get'
|
||||
27
lib/zanzibar/actions/base.rb
Normal file
27
lib/zanzibar/actions/base.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
module Zanzibar
|
||||
module Actions
|
||||
# Basic plumbing for all actions
|
||||
class Base
|
||||
attr_accessor :options
|
||||
private :options=
|
||||
|
||||
attr_accessor :logger
|
||||
private :logger=
|
||||
|
||||
def initialize(logger, options = {})
|
||||
self.logger = logger
|
||||
self.options = options
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def debug(*args, &block)
|
||||
logger.debug(*args, &block)
|
||||
end
|
||||
|
||||
def source_root
|
||||
@source_root ||= Pathname.new(File.expand_path('../../../../', __FILE__))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
119
lib/zanzibar/actions/bundle.rb
Normal file
119
lib/zanzibar/actions/bundle.rb
Normal file
@@ -0,0 +1,119 @@
|
||||
require 'zanzibar/actions/base'
|
||||
require 'zanzibar/error'
|
||||
require 'zanzibar'
|
||||
|
||||
module Zanzibar
|
||||
module Actions
|
||||
# Download or verify the secrets in a Zanzifile
|
||||
class Bundle < Base
|
||||
attr_accessor :settings
|
||||
attr_accessor :remote_secrets
|
||||
attr_accessor :local_secrets
|
||||
attr_accessor :update
|
||||
attr_accessor :zanzibar
|
||||
|
||||
def initialize(ui, options, args = {})
|
||||
super(ui, options)
|
||||
@update = args[:update]
|
||||
end
|
||||
|
||||
def run
|
||||
ensure_zanzifile
|
||||
load_required_secrets
|
||||
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
|
||||
fail Error, NO_ZANZIFILE_ERROR unless File.exist? ZANZIFILE_NAME
|
||||
debug { "#{ZANZIFILE_NAME} located..." }
|
||||
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?
|
||||
fail 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|
|
||||
downloaded_secrets[key] = download_one_secret(secret['id'],
|
||||
secret['label'],
|
||||
@settings['secret_dir'],
|
||||
args)
|
||||
|
||||
debug { "Downloaded secret: #{key} to #{path}..." }
|
||||
end
|
||||
|
||||
downloaded_secrets
|
||||
end
|
||||
|
||||
def download_one_secret(scrt_id, label, path, args)
|
||||
path = zanzibar(args).download_secret_file(scrt_id: scrt_id,
|
||||
type: label,
|
||||
path: path)
|
||||
|
||||
{ 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
|
||||
end
|
||||
end
|
||||
end
|
||||
61
lib/zanzibar/actions/get.rb
Normal file
61
lib/zanzibar/actions/get.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
require 'zanzibar/actions/base'
|
||||
require 'zanzibar/error'
|
||||
require 'zanzibar'
|
||||
require 'zanzibar/defaults'
|
||||
|
||||
module Zanzibar
|
||||
module Actions
|
||||
# Fetch a single secret
|
||||
class Get < Base
|
||||
attr_accessor :zanibar_options
|
||||
attr_accessor :scrt_id
|
||||
|
||||
def initialize(ui, options, scrt_id)
|
||||
super(ui, options)
|
||||
@scrt_id = scrt_id
|
||||
@zanzibar_options = {}
|
||||
end
|
||||
|
||||
def run
|
||||
construct_options
|
||||
ensure_options
|
||||
|
||||
fetch_secret(@scrt_id, options['filelabel'])
|
||||
end
|
||||
|
||||
def fetch_secret(scrt_id, label = nil)
|
||||
scrt = ::Zanzibar::Zanzibar.new(@zanzibar_options)
|
||||
|
||||
puts @zanzibar_options
|
||||
|
||||
if label
|
||||
scrt.download_secret_file(scrt_id: scrt_id,
|
||||
type: label)
|
||||
else
|
||||
scrt.get_password(scrt_id)
|
||||
end
|
||||
end
|
||||
|
||||
def construct_options
|
||||
@zanzibar_options[:wsdl] = construct_wsdl
|
||||
@zanzibar_options[:globals] = { ssl_verify_mode: :none } if options['ignoressl']
|
||||
@zanzibar_options[:domain] = options['domain']
|
||||
@zanzibar_options[:username] = options['username'] unless options['username'].nil?
|
||||
@zanzibar_options[:domain] = options['domain'] ? options['domain'] : 'local'
|
||||
end
|
||||
|
||||
def construct_wsdl
|
||||
if options['wsdl'].nil? && options['server']
|
||||
DEFAULT_WSDL % options['server']
|
||||
else
|
||||
options['wsdl']
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_options
|
||||
return if @zanzibar_options[:wsdl]
|
||||
fail Error, NO_WSDL_ERROR
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
40
lib/zanzibar/actions/init.rb
Normal file
40
lib/zanzibar/actions/init.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
require 'zanzibar/actions/base'
|
||||
require 'zanzibar/error'
|
||||
require 'ostruct'
|
||||
require 'erb'
|
||||
require 'zanzibar/defaults'
|
||||
|
||||
module Zanzibar
|
||||
module Actions
|
||||
# Create a new Zanzifile
|
||||
class Init < Base
|
||||
def run
|
||||
check_for_zanzifile
|
||||
write_template
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_for_zanzifile
|
||||
return unless File.exist?(ZANZIFILE_NAME) && !options['force']
|
||||
fail Error, ALREADY_EXISTS_ERROR
|
||||
end
|
||||
|
||||
def write_template
|
||||
template = TemplateRenderer.new(options)
|
||||
|
||||
File.open(ZANZIFILE_NAME, 'w') do |f|
|
||||
f.write template.render(File.read(source_root.join(TEMPLATE_NAME)))
|
||||
end
|
||||
end
|
||||
|
||||
# Allows us to easily feed our options hash
|
||||
# to an ERB
|
||||
class TemplateRenderer < OpenStruct
|
||||
def render(template)
|
||||
ERB.new(template).result(binding)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
120
lib/zanzibar/cli.rb
Normal file
120
lib/zanzibar/cli.rb
Normal file
@@ -0,0 +1,120 @@
|
||||
require 'thor'
|
||||
require 'thor/actions'
|
||||
require 'zanzibar/version'
|
||||
require 'zanzibar/cli'
|
||||
require 'zanzibar/ui'
|
||||
require 'zanzibar/actions'
|
||||
require 'zanzibar/error'
|
||||
require 'zanzibar/defaults'
|
||||
|
||||
module Zanzibar
|
||||
# The `zanzibar` binay/thor application main class
|
||||
class Cli < Thor
|
||||
include Thor::Actions
|
||||
|
||||
attr_accessor :ui
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
the_shell = (options['no-color'] ? Thor::Shell::Basic.new : shell)
|
||||
@ui = Shell.new(the_shell)
|
||||
@ui.be_quiet! if options['quiet']
|
||||
@ui.debug! if options['verbose']
|
||||
|
||||
debug_header
|
||||
end
|
||||
|
||||
desc 'version', 'Display your Zanzibar verion'
|
||||
def version
|
||||
say "#{APPLICATION_NAME} Version: #{VERSION}"
|
||||
end
|
||||
|
||||
desc 'init', "Create an empty #{ZANZIFILE_NAME} in the current directory."
|
||||
option 'verbose', type: :boolean, default: false, aliases: :v
|
||||
option 'wsdl', type: :string, aliases: :w,
|
||||
default: DEFAULT_WSDL % DEFAULT_SERVER,
|
||||
desc: 'The URI of the WSDL file for your Secret Server instance'
|
||||
option 'domain', type: :string, default: 'local', aliases: :d,
|
||||
desc: 'The logon domain for your Secret Server account'
|
||||
option 'force', type: :boolean, default: false, aliases: :f,
|
||||
desc: 'Recreate the Zanzifile if one already exists.'
|
||||
option 'secretdir', type: :string, default: 'secrets/', aliases: :s,
|
||||
desc: 'The directory to which secrets should be downloaded.'
|
||||
option 'ignoressl', type: :boolean, default: 'false', aliases: :k,
|
||||
desc: 'Don\'t check the SSL certificate of Secret Server'
|
||||
def init
|
||||
run_action { init! }
|
||||
end
|
||||
|
||||
desc 'bundle', "Fetch secrets declared in your #{ZANZIFILE_NAME}"
|
||||
option 'verbose', type: :boolean, default: false, aliases: :v
|
||||
def bundle
|
||||
run_action { bundle! }
|
||||
end
|
||||
|
||||
desc 'update', "Redownload all secrets in your #{ZANZIFILE_NAME}"
|
||||
option 'verbose', type: :boolean, default: false, aliases: :v
|
||||
def update
|
||||
run_action { update! }
|
||||
end
|
||||
|
||||
desc 'get SECRETID', 'Fetch a single SECRETID from Secret Server'
|
||||
option 'domain', type: :string, aliases: :d,
|
||||
desc: 'The logon domain to use when logging in.'
|
||||
option 'server', type: :string, aliases: :s,
|
||||
desc: 'The Secret Server hostname or IP'
|
||||
option 'wsdl', type: :string, aliases: :w,
|
||||
desc: 'Full path to the Secret Server WSDL'
|
||||
option 'ignoressl', type: :boolean, aliases: :k,
|
||||
desc: 'Don\'t verify Secret Server\'s SSL certificate'
|
||||
option 'filelabel', type: :string, aliases: :f,
|
||||
desc: 'Specify a file (by label) to download'
|
||||
option 'username', type: :string, aliases: :u
|
||||
option 'password', type: :string, aliases: :p
|
||||
def get(scrt_id)
|
||||
run_action { get! scrt_id }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def debug_header
|
||||
@ui.debug { "Running #{APPLICATION_NAME} in debug mode..." }
|
||||
@ui.debug { "Ruby Version: #{RUBY_VERSION}" }
|
||||
@ui.debug { "Ruby Platform: #{RUBY_PLATFORM}" }
|
||||
@ui.debug { "#{APPLICATION_NAME} Version: #{VERSION}" }
|
||||
end
|
||||
|
||||
# Run the specified action and rescue errors we
|
||||
# explicitly send back to format them
|
||||
def run_action(&_block)
|
||||
yield
|
||||
rescue ::Zanzibar::Error => e
|
||||
@ui.error e
|
||||
abort "Fatal error: #{e.message}"
|
||||
end
|
||||
|
||||
def init!
|
||||
say "Initializing a new #{ZANZIFILE_NAME} in the current directory..."
|
||||
Actions::Init.new(@ui, options).run
|
||||
say "Your #{ZANZIFILE_NAME} has been created!"
|
||||
say 'You should check the settings and add your secrets.'
|
||||
say 'Then run `zanzibar bundle` to fetch them.'
|
||||
end
|
||||
|
||||
def bundle!
|
||||
say "Checking for secrets declared in your #{ZANZIFILE_NAME}..."
|
||||
Actions::Bundle.new(@ui, options).run
|
||||
say 'Finished downloading secrets!'
|
||||
end
|
||||
|
||||
def update!
|
||||
say "Redownloading all secrets declared in your #{ZANZIFILE_NAME}..."
|
||||
Actions::Bundle.new(@ui, options, update: true).run
|
||||
say 'Finished downloading secrets!'
|
||||
end
|
||||
|
||||
def get!(scrt_id)
|
||||
say Actions::Get.new(@ui, options, scrt_id).run
|
||||
end
|
||||
end
|
||||
end
|
||||
14
lib/zanzibar/defaults.rb
Normal file
14
lib/zanzibar/defaults.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# Definitions for various strings used throughout the gem
|
||||
module Zanzibar
|
||||
APPLICATION_NAME = Pathname.new($PROGRAM_NAME).basename
|
||||
ZANZIFILE_NAME = 'Zanzifile'
|
||||
RESOLVED_NAME = 'Zanzifile.resolved'
|
||||
TEMPLATE_NAME = 'templates/Zanzifile.erb'
|
||||
DEFAULT_SERVER = 'secret.example.com'
|
||||
DEFAULT_WSDL = 'https://%s/webservices/sswebservice.asmx?wsdl'
|
||||
|
||||
ALREADY_EXISTS_ERROR = "#{ZANZIFILE_NAME} already exists! Aborting..."
|
||||
NO_WSDL_ERROR = 'Could not construct WSDL URL. Please provide either --server or --wsdl'
|
||||
NO_ZANZIFILE_ERROR = "You don't have a #{ZANZIFILE_NAME}! Run `#{APPLICATION_NAME} init` first!"
|
||||
INVALID_ZANZIFILE_ERROR = "Unable to load your #{ZANZIFILE_NAME}. Please ensure it is valid YAML."
|
||||
end
|
||||
6
lib/zanzibar/error.rb
Normal file
6
lib/zanzibar/error.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Zanzibar
|
||||
# A standard error with a different name
|
||||
# for identifying errors internal to zanzibar
|
||||
class Error < StandardError
|
||||
end
|
||||
end
|
||||
42
lib/zanzibar/ui.rb
Normal file
42
lib/zanzibar/ui.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
module Zanzibar
|
||||
# Prints messages out to stdout
|
||||
class Shell
|
||||
attr_writer :shell
|
||||
|
||||
def initialize(shell)
|
||||
@shell = shell
|
||||
@quiet = false
|
||||
@debug = ENV['DEBUG']
|
||||
end
|
||||
|
||||
def debug(message = nil)
|
||||
@shell.say(message || yield) if @debug && !@quiet
|
||||
end
|
||||
|
||||
def info(message = nil)
|
||||
@shell.say(message || yield) unless @quiet
|
||||
end
|
||||
|
||||
def confirm(message = nil)
|
||||
@shell.say(message || yield, :green) unless @quiet
|
||||
end
|
||||
|
||||
def warn(message = nil)
|
||||
@shell.say(message || yield, :yellow)
|
||||
end
|
||||
|
||||
def error(message = nil)
|
||||
@shell.say(message || yield, :red)
|
||||
end
|
||||
|
||||
def be_quiet!
|
||||
@quiet = true
|
||||
end
|
||||
|
||||
def debug!
|
||||
@debug = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,4 @@
|
||||
module Zanzibar
|
||||
VERSION = '0.1.13'
|
||||
end
|
||||
# The version of the gem
|
||||
module Zanzibar
|
||||
VERSION = '0.1.15'
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user