aspera-cli 4.15.0 → 4.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +29 -3
- data/CHANGELOG.md +292 -228
- data/CONTRIBUTING.md +69 -18
- data/README.md +1102 -952
- data/bin/ascli +13 -31
- data/bin/asession +3 -1
- data/examples/dascli +2 -2
- data/lib/aspera/aoc.rb +28 -33
- data/lib/aspera/ascmd.rb +3 -6
- data/lib/aspera/assert.rb +45 -0
- data/lib/aspera/cli/extended_value.rb +5 -5
- data/lib/aspera/cli/formatter.rb +26 -13
- data/lib/aspera/cli/hints.rb +4 -3
- data/lib/aspera/cli/main.rb +16 -3
- data/lib/aspera/cli/manager.rb +45 -36
- data/lib/aspera/cli/plugin.rb +20 -13
- data/lib/aspera/cli/plugins/aoc.rb +103 -73
- data/lib/aspera/cli/plugins/ats.rb +4 -3
- data/lib/aspera/cli/plugins/config.rb +114 -119
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/faspex.rb +23 -19
- data/lib/aspera/cli/plugins/faspex5.rb +75 -43
- data/lib/aspera/cli/plugins/node.rb +28 -15
- data/lib/aspera/cli/plugins/orchestrator.rb +4 -2
- data/lib/aspera/cli/plugins/preview.rb +9 -7
- data/lib/aspera/cli/plugins/server.rb +6 -3
- data/lib/aspera/cli/plugins/shares.rb +30 -26
- data/lib/aspera/cli/sync_actions.rb +9 -9
- data/lib/aspera/cli/transfer_agent.rb +21 -14
- data/lib/aspera/cli/transfer_progress.rb +2 -3
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +13 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/coverage.rb +22 -0
- data/lib/aspera/data_repository.rb +33 -2
- data/lib/aspera/environment.rb +4 -2
- data/lib/aspera/fasp/{agent_aspera.rb → agent_alpha.rb} +29 -39
- data/lib/aspera/fasp/agent_base.rb +17 -7
- data/lib/aspera/fasp/agent_direct.rb +88 -84
- data/lib/aspera/fasp/agent_httpgw.rb +4 -3
- data/lib/aspera/fasp/agent_node.rb +3 -2
- data/lib/aspera/fasp/agent_trsdk.rb +79 -37
- data/lib/aspera/fasp/installation.rb +51 -12
- data/lib/aspera/fasp/management.rb +11 -6
- data/lib/aspera/fasp/parameters.rb +53 -47
- data/lib/aspera/fasp/resume_policy.rb +7 -5
- data/lib/aspera/fasp/sync.rb +273 -0
- data/lib/aspera/fasp/transfer_spec.rb +10 -8
- data/lib/aspera/fasp/uri.rb +2 -2
- data/lib/aspera/faspex_gw.rb +11 -8
- data/lib/aspera/faspex_postproc.rb +6 -5
- data/lib/aspera/id_generator.rb +3 -1
- data/lib/aspera/json_rpc.rb +10 -8
- data/lib/aspera/keychain/encrypted_hash.rb +46 -11
- data/lib/aspera/keychain/macos_security.rb +15 -13
- data/lib/aspera/log.rb +4 -3
- data/lib/aspera/nagios.rb +7 -2
- data/lib/aspera/node.rb +17 -16
- data/lib/aspera/node_simulator.rb +214 -0
- data/lib/aspera/oauth.rb +22 -19
- data/lib/aspera/persistency_action_once.rb +13 -14
- data/lib/aspera/persistency_folder.rb +3 -2
- data/lib/aspera/preview/file_types.rb +53 -267
- data/lib/aspera/preview/generator.rb +7 -5
- data/lib/aspera/preview/terminal.rb +14 -5
- data/lib/aspera/preview/utils.rb +8 -7
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +29 -13
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/rest_errors_aspera.rb +2 -0
- data/lib/aspera/secret_hider.rb +5 -2
- data/lib/aspera/ssh.rb +10 -8
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/web_server_simple.rb +2 -1
- data.tar.gz.sig +0 -0
- metadata +96 -45
- metadata.gz.sig +0 -0
- data/lib/aspera/sync.rb +0 -219
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# cspell:ignore initdemo genkey asperasoft
|
3
|
+
# cspell:ignore initdemo genkey pubkey asperasoft
|
4
4
|
require 'aspera/cli/basic_auth_plugin'
|
5
5
|
require 'aspera/cli/extended_value'
|
6
6
|
require 'aspera/cli/version'
|
@@ -19,9 +19,11 @@ require 'aspera/open_application'
|
|
19
19
|
require 'aspera/persistency_action_once'
|
20
20
|
require 'aspera/id_generator'
|
21
21
|
require 'aspera/persistency_folder'
|
22
|
+
require 'aspera/data_repository'
|
22
23
|
require 'aspera/line_logger'
|
23
24
|
require 'aspera/rest'
|
24
25
|
require 'aspera/log'
|
26
|
+
require 'aspera/assert'
|
25
27
|
require 'open3'
|
26
28
|
require 'date'
|
27
29
|
require 'erb'
|
@@ -35,11 +37,13 @@ module Aspera
|
|
35
37
|
ASPERA_HOME_FOLDER_NAME = '.aspera'
|
36
38
|
# default config file
|
37
39
|
DEFAULT_CONFIG_FILENAME = 'config.yaml'
|
40
|
+
DEFAULT_VAULT_FILENAME = 'vault.bin'
|
38
41
|
# reserved preset names
|
39
42
|
CONF_PRESET_CONFIG = 'config'
|
40
43
|
CONF_PRESET_VERSION = 'version'
|
41
|
-
|
44
|
+
CONF_PRESET_DEFAULTS = 'default'
|
42
45
|
CONF_PRESET_GLOBAL = 'global_common_defaults'
|
46
|
+
# special name to identify value of default
|
43
47
|
GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
|
44
48
|
CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
|
45
49
|
CONF_GLOBAL_SYM = :config
|
@@ -77,7 +81,7 @@ module Aspera
|
|
77
81
|
private_constant :DEFAULT_CONFIG_FILENAME,
|
78
82
|
:CONF_PRESET_CONFIG,
|
79
83
|
:CONF_PRESET_VERSION,
|
80
|
-
:
|
84
|
+
:CONF_PRESET_DEFAULTS,
|
81
85
|
:CONF_PRESET_GLOBAL,
|
82
86
|
:ASPERA_PLUGINS_FOLDERNAME,
|
83
87
|
:RUBY_FILE_EXT,
|
@@ -141,20 +145,22 @@ module Aspera
|
|
141
145
|
# return product family folder (~/.aspera)
|
142
146
|
def module_family_folder
|
143
147
|
user_home_folder = Dir.home
|
144
|
-
|
148
|
+
assert(Dir.exist?(user_home_folder), exception_class: Cli::Error){"Home folder does not exist: #{user_home_folder}. Check your user environment."}
|
145
149
|
return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
|
146
150
|
end
|
147
151
|
|
148
152
|
# return product config folder (~/.aspera/<name>)
|
149
153
|
def default_app_main_folder(app_name:)
|
150
|
-
|
154
|
+
assert_type(app_name, String)
|
155
|
+
assert(!app_name.empty?)
|
151
156
|
return File.join(module_family_folder, app_name)
|
152
157
|
end
|
153
158
|
end # self
|
154
159
|
|
155
160
|
def initialize(env, params)
|
156
|
-
|
157
|
-
|
161
|
+
assert_type(env, Hash)
|
162
|
+
assert_type(params, Hash)
|
163
|
+
assert(%i[gem help name version].eql?(params.keys.sort)){'missing param'}
|
158
164
|
# we need to defer parsing of options until we have the config file, so we can use @extend with @preset
|
159
165
|
super(env)
|
160
166
|
@info = params
|
@@ -168,6 +174,7 @@ module Aspera
|
|
168
174
|
@pac_exec = nil
|
169
175
|
@sdk_default_location = false
|
170
176
|
@option_insecure = false
|
177
|
+
@option_warn_insecure_cert = true
|
171
178
|
@option_ignore_cert_host_port = []
|
172
179
|
@option_http_options = {}
|
173
180
|
@ssl_warned_urls = []
|
@@ -233,7 +240,8 @@ module Aspera
|
|
233
240
|
options.declare(:notify_template, 'Email ERB template for notification of transfers')
|
234
241
|
# HTTP options
|
235
242
|
options.declare(:insecure, 'Do not validate any HTTPS certificate', values: :bool, handler: {o: self, m: :option_insecure}, default: :no)
|
236
|
-
options.declare(:ignore_certificate, '
|
243
|
+
options.declare(:ignore_certificate, 'Do not validate HTTPS certificate for these URLs', types: Array, handler: {o: self, m: :option_ignore_cert_host_port})
|
244
|
+
options.declare(:silent_insecure, 'Issue a warning if certificate is ignored', values: :bool, handler: {o: self, m: :option_warn_insecure_cert}, default: :yes)
|
237
245
|
options.declare(:cert_stores, 'List of folder with trusted certificates', types: [Array, String], handler: {o: self, m: :trusted_cert_locations})
|
238
246
|
options.declare(:http_options, 'Options for HTTP/S socket', types: Hash, handler: {o: self, m: :option_http_options}, default: {})
|
239
247
|
options.declare(:cache_tokens, 'Save and reuse Oauth tokens', values: :bool, handler: {o: self, m: :option_cache_tokens})
|
@@ -264,7 +272,7 @@ module Aspera
|
|
264
272
|
@pac_exec = Aspera::ProxyAutoConfig.new(pac_script).register_uri_generic unless pac_script.nil?
|
265
273
|
proxy_user_pass = options.get_option(:proxy_credentials)
|
266
274
|
if !proxy_user_pass.nil?
|
267
|
-
|
275
|
+
assert(proxy_user_pass.length.eql?(2), exception_class: Cli::BadArgument){"proxy_credentials shall have two elements (#{proxy_user_pass.length})"}
|
268
276
|
@proxy_credentials = {user: proxy_user_pass[0], pass: proxy_user_pass[1]}
|
269
277
|
@pac_exec.proxy_user = @proxy_credentials[:user]
|
270
278
|
@pac_exec.proxy_pass = @proxy_credentials[:pass]
|
@@ -280,7 +288,7 @@ module Aspera
|
|
280
288
|
Aspera::RestErrorsAspera.register_handlers
|
281
289
|
end
|
282
290
|
|
283
|
-
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_http_options
|
291
|
+
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_warn_insecure_cert, :option_http_options
|
284
292
|
attr_reader :option_ignore_cert_host_port, :progress_bar
|
285
293
|
|
286
294
|
def trusted_cert_locations=(path_list)
|
@@ -293,7 +301,7 @@ module Aspera
|
|
293
301
|
end
|
294
302
|
|
295
303
|
path_list.each do |path|
|
296
|
-
|
304
|
+
assert_type(path, String){'Expecting a String for cert location'}
|
297
305
|
Log.log.debug("Adding cert location: #{path}")
|
298
306
|
if path.eql?(ExtendedValue::DEF)
|
299
307
|
path = OpenSSL::X509::DEFAULT_CERT_DIR
|
@@ -325,22 +333,28 @@ module Aspera
|
|
325
333
|
def option_ignore_cert_host_port=(url_list)
|
326
334
|
url_list.each do |url|
|
327
335
|
uri = URI.parse(url)
|
336
|
+
raise "Expecting https scheme: #{url}" unless uri.scheme.eql?('https')
|
328
337
|
@option_ignore_cert_host_port.push([uri.host, uri.port].freeze)
|
329
338
|
end
|
330
339
|
end
|
331
340
|
|
332
341
|
def ignore_cert?(address, port)
|
333
342
|
endpoint = [address, port].freeze
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
343
|
+
ignore_cert = false
|
344
|
+
if @option_insecure || @option_ignore_cert_host_port.any?(endpoint)
|
345
|
+
ignore_cert = true
|
346
|
+
if @option_warn_insecure_cert
|
347
|
+
base_url = "https://#{address}:#{port}"
|
348
|
+
if !@ssl_warned_urls.include?(base_url)
|
349
|
+
formatter.display_message(
|
350
|
+
:error,
|
351
|
+
"#{Formatter::WARNING_FLASH} Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production.")
|
352
|
+
@ssl_warned_urls.push(base_url)
|
353
|
+
end
|
354
|
+
end
|
342
355
|
end
|
343
|
-
|
356
|
+
Log.log.debug{"ignore cert? #{endpoint} -> #{ignore_cert}"}
|
357
|
+
return ignore_cert
|
344
358
|
end
|
345
359
|
|
346
360
|
# called every time a new REST HTTP session is opened to set user-provided options
|
@@ -443,25 +457,25 @@ module Aspera
|
|
443
457
|
return nil
|
444
458
|
end
|
445
459
|
|
446
|
-
# get the default global preset, or
|
460
|
+
# get the default global preset, or set default one
|
447
461
|
def global_default_preset
|
448
|
-
|
449
|
-
if
|
450
|
-
|
451
|
-
set_preset_key(
|
462
|
+
result = get_plugin_default_config_name(CONF_GLOBAL_SYM)
|
463
|
+
if result.nil?
|
464
|
+
result = CONF_PRESET_GLOBAL
|
465
|
+
set_preset_key(CONF_PRESET_DEFAULTS, CONF_GLOBAL_SYM, result)
|
452
466
|
end
|
453
|
-
return
|
467
|
+
return result
|
454
468
|
end
|
455
469
|
|
456
470
|
def set_preset_key(preset, param_name, param_value)
|
457
|
-
|
471
|
+
assert_values(param_name.class, [String, Symbol]){'parameter'}
|
458
472
|
param_name = param_name.to_s
|
459
473
|
selected_preset = @config_presets[preset]
|
460
474
|
if selected_preset.nil?
|
461
475
|
Log.log.debug{"No such preset name: #{preset}, initializing"}
|
462
476
|
selected_preset = @config_presets[preset] = {}
|
463
477
|
end
|
464
|
-
|
478
|
+
assert_type(selected_preset, Hash){"#{preset}.#{param_name}"}
|
465
479
|
if selected_preset.key?(param_name)
|
466
480
|
if selected_preset[param_name].eql?(param_value)
|
467
481
|
Log.log.warn{"keeping same value for #{preset}: #{param_name}: #{param_value}"}
|
@@ -493,7 +507,7 @@ module Aspera
|
|
493
507
|
include_path = include_path.clone # avoid messing up if there are multiple branches
|
494
508
|
current = @config_presets
|
495
509
|
config_name.split(PRESET_DIG_SEPARATOR).each do |name|
|
496
|
-
|
510
|
+
assert_type(current, Hash, exception_class: Cli::Error){"sub key: #{include_path}"}
|
497
511
|
include_path.push(name)
|
498
512
|
current = current[name]
|
499
513
|
raise Cli::Error, "No such config preset: #{include_path}" if current.nil?
|
@@ -511,11 +525,10 @@ module Aspera
|
|
511
525
|
end
|
512
526
|
|
513
527
|
def option_plugin_folder=(value)
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
end
|
528
|
+
assert_values(value.class, [String, Array]){'plugin folder'}
|
529
|
+
value = [value] if value.is_a?(String)
|
530
|
+
assert(value.all?(String)){'plugin folder'}
|
531
|
+
value.each{|f|add_plugin_lookup_folder(f)}
|
519
532
|
end
|
520
533
|
|
521
534
|
def option_plugin_folder
|
@@ -558,15 +571,15 @@ module Aspera
|
|
558
571
|
end
|
559
572
|
files_to_copy = []
|
560
573
|
Log.log.debug{Log.dump('Available_presets', @config_presets)}
|
561
|
-
|
574
|
+
assert_type(@config_presets, Hash){'config file YAML'}
|
562
575
|
# check there is at least the config section
|
563
|
-
|
576
|
+
assert(@config_presets.key?(CONF_PRESET_CONFIG)){"Cannot find key: #{CONF_PRESET_CONFIG}"}
|
564
577
|
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]
|
565
578
|
raise 'No version found in config section.' if version.nil?
|
566
579
|
Log.log.debug{"conf version: #{version}"}
|
567
580
|
# VVV if there are any conversion needed, those happen here.
|
568
581
|
# fix bug in 4.4 (creating key "true" in "default" preset)
|
569
|
-
@config_presets[
|
582
|
+
@config_presets[CONF_PRESET_DEFAULTS].delete(true) if @config_presets[CONF_PRESET_DEFAULTS].is_a?(Hash)
|
570
583
|
# ^^^ Place new compatibility code before this line
|
571
584
|
# set version to current
|
572
585
|
@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = @info[:version]
|
@@ -646,13 +659,14 @@ module Aspera
|
|
646
659
|
next
|
647
660
|
end
|
648
661
|
next if detection_info.nil?
|
649
|
-
|
662
|
+
assert_type(detection_info, Hash)
|
663
|
+
assert_type(detection_info[:url], String) if detection_info.key?(:url)
|
650
664
|
app_name = detect_plugin_class.respond_to?(:application_name) ? detect_plugin_class.application_name : detect_plugin_class.name.split('::').last
|
651
665
|
# if there is a redirect, then the detector can override the url.
|
652
666
|
found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
|
653
667
|
end # loop
|
654
668
|
raise "No known application found at #{app_url}" if found_apps.empty?
|
655
|
-
|
669
|
+
assert(found_apps.all?{|a|a.keys.all?(Symbol)})
|
656
670
|
return found_apps
|
657
671
|
end
|
658
672
|
|
@@ -666,10 +680,10 @@ module Aspera
|
|
666
680
|
case command
|
667
681
|
when :list
|
668
682
|
return {type: :object_list, data: connect_versions, fields: %w[id title version]}
|
669
|
-
when :info
|
683
|
+
when :info
|
670
684
|
one_res.delete('links')
|
671
685
|
return {type: :single_object, data: one_res}
|
672
|
-
when :version
|
686
|
+
when :version
|
673
687
|
all_links = one_res['links']
|
674
688
|
command = options.get_next_command(%i[list download open])
|
675
689
|
if %i[download open].include?(command)
|
@@ -678,7 +692,7 @@ module Aspera
|
|
678
692
|
raise 'no such value' if one_link.nil?
|
679
693
|
end
|
680
694
|
case command
|
681
|
-
when :list
|
695
|
+
when :list
|
682
696
|
return {type: :object_list, data: all_links}
|
683
697
|
when :download
|
684
698
|
folder_dest = transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE)
|
@@ -706,49 +720,17 @@ module Aspera
|
|
706
720
|
formatter.display_status("ascp version: #{ascp_version}")
|
707
721
|
set_global_default(:ascp_path, ascp_path)
|
708
722
|
return Main.result_nothing
|
709
|
-
when :show
|
723
|
+
when :show
|
710
724
|
return {type: :status, data: Fasp::Installation.instance.path(:ascp)}
|
711
|
-
when :info
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
last_line = ''
|
716
|
-
while (line = stderr.gets)
|
717
|
-
line.chomp!
|
718
|
-
last_line = line
|
719
|
-
case line
|
720
|
-
when /^DBG Path ([^ ]+) (dir|file) +: (.*)$/
|
721
|
-
data[Regexp.last_match(1)] = Regexp.last_match(3)
|
722
|
-
when /^DBG Added module group:"(?<module>[^"]+)" name:"(?<scheme>[^"]+)", version:"(?<version>[^"]+)" interface:"(?<interface>[^"]+)"$/
|
723
|
-
c = Regexp.last_match.named_captures.symbolize_keys
|
724
|
-
data[c[:interface]] ||= {}
|
725
|
-
data[c[:interface]][c[:module]] ||= []
|
726
|
-
data[c[:interface]][c[:module]].push("#{c[:scheme]} v#{c[:version]}")
|
727
|
-
when %r{^DBG License result \(/license/(\S+)\): (.+)$}
|
728
|
-
data[Regexp.last_match(1)] = Regexp.last_match(2)
|
729
|
-
when /^LOG (.+) version ([0-9.]+)$/
|
730
|
-
data['product_name'] = Regexp.last_match(1)
|
731
|
-
data['product_version'] = Regexp.last_match(2)
|
732
|
-
when /^LOG Initializing FASP version ([^,]+),/
|
733
|
-
data['ascp_version'] = Regexp.last_match(1)
|
734
|
-
end
|
735
|
-
end
|
736
|
-
if !thread.value.exitstatus.eql?(1) && !data.key?('root')
|
737
|
-
raise last_line
|
738
|
-
end
|
739
|
-
end
|
740
|
-
# ascp's openssl directory
|
741
|
-
ascp_file = data['ascp']
|
742
|
-
File.binread(ascp_file).scan(/[\x20-\x7E]{4,}/) do |match|
|
743
|
-
if (m = match.match(/OPENSSLDIR.*"(.*)"/))
|
744
|
-
data['openssldir'] = m[1]
|
745
|
-
end
|
746
|
-
end if File.file?(ascp_file)
|
747
|
-
data['uuid'] = Fasp::Installation.instance.ssh_cert_uuid
|
748
|
-
# log is "-" no need to display
|
749
|
-
data.delete('log')
|
750
|
-
# show command line transfer spec
|
725
|
+
when :info
|
726
|
+
# collect info from ascp executable
|
727
|
+
data = Fasp::Installation.instance.ascp_info
|
728
|
+
# add command line transfer spec
|
751
729
|
data['ts'] = transfer.updated_ts
|
730
|
+
# add keys
|
731
|
+
DataRepository::ELEMENTS.each_with_object(data){|i, h|h[i.to_s] = DataRepository.instance.item(i)}
|
732
|
+
# declare those as secrets
|
733
|
+
SecretHider::ADDITIONAL_KEYS_TO_HIDE.push(*DataRepository::ELEMENTS.map(&:to_s))
|
752
734
|
return {type: :single_object, data: data}
|
753
735
|
when :products
|
754
736
|
command = options.get_next_command(%i[list use])
|
@@ -887,6 +869,7 @@ module Aspera
|
|
887
869
|
open
|
888
870
|
documentation
|
889
871
|
genkey
|
872
|
+
pubkey
|
890
873
|
remote_certificate
|
891
874
|
gem
|
892
875
|
plugins
|
@@ -925,6 +908,9 @@ module Aspera
|
|
925
908
|
private_key_length = options.get_next_argument('size in bits', mandatory: false, type: Integer, default: DEFAULT_PRIV_KEY_LENGTH)
|
926
909
|
self.class.generate_rsa_private_key(path: private_key_path, length: private_key_length)
|
927
910
|
return Main.result_status("Generated #{private_key_length} bit RSA key: #{private_key_path}")
|
911
|
+
when :pubkey # get pub key
|
912
|
+
private_key_pem = options.get_next_argument('private key PEM value')
|
913
|
+
return Main.result_status(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
|
928
914
|
when :remote_certificate
|
929
915
|
remote_url = options.get_next_argument('remote URL')
|
930
916
|
@option_insecure = true
|
@@ -984,8 +970,7 @@ module Aspera
|
|
984
970
|
return wizard_find(apps)
|
985
971
|
when :coffee
|
986
972
|
if OpenApplication.instance.url_method.eql?(:text)
|
987
|
-
|
988
|
-
return Main.result_status(Preview::Terminal.build(Rest.new(base_url: COFFEE_IMAGE).read('')[:http].body))
|
973
|
+
return Main.result_picture_in_terminal(options, Rest.new(base_url: COFFEE_IMAGE).read('')[:http].body)
|
989
974
|
end
|
990
975
|
OpenApplication.instance.uri(COFFEE_IMAGE)
|
991
976
|
return Main.result_nothing
|
@@ -1024,13 +1009,13 @@ module Aspera
|
|
1024
1009
|
'ssAP'.downcase.reverse + 'drow'.reverse => DEMO + ASPERA # cspell:disable-line
|
1025
1010
|
}
|
1026
1011
|
end
|
1027
|
-
@config_presets[
|
1028
|
-
if @config_presets[
|
1029
|
-
Log.log.warn{"Server default preset already set to: #{@config_presets[
|
1012
|
+
@config_presets[CONF_PRESET_DEFAULTS] ||= {}
|
1013
|
+
if @config_presets[CONF_PRESET_DEFAULTS].key?(SERVER_COMMAND)
|
1014
|
+
Log.log.warn{"Server default preset already set to: #{@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND]}"}
|
1030
1015
|
Log.log.warn{"Use #{DEMO_SERVER_PRESET} for demo: -P#{DEMO_SERVER_PRESET}"} unless
|
1031
|
-
DEMO_SERVER_PRESET.eql?(@config_presets[
|
1016
|
+
DEMO_SERVER_PRESET.eql?(@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND])
|
1032
1017
|
else
|
1033
|
-
@config_presets[
|
1018
|
+
@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND] = DEMO_SERVER_PRESET
|
1034
1019
|
Log.log.info{"Setting server default preset to : #{DEMO_SERVER_PRESET}"}
|
1035
1020
|
end
|
1036
1021
|
return Main.result_status('Done')
|
@@ -1041,9 +1026,9 @@ module Aspera
|
|
1041
1026
|
exception_class_name = options.get_next_argument('exception class name', mandatory: true)
|
1042
1027
|
exception_text = options.get_next_argument('exception text', mandatory: true)
|
1043
1028
|
exception_class = Object.const_get(exception_class_name)
|
1044
|
-
|
1029
|
+
assert(exception_class <= Exception){"#{exception_class} is not an exception: #{exception_class.class}"}
|
1045
1030
|
raise exception_class, exception_text
|
1046
|
-
else
|
1031
|
+
else error_unreachable_line
|
1047
1032
|
end
|
1048
1033
|
end
|
1049
1034
|
|
@@ -1064,7 +1049,7 @@ module Aspera
|
|
1064
1049
|
options.add_option_preset({url: wiz_url})
|
1065
1050
|
# instantiate plugin: command line options will be known and wizard can be called
|
1066
1051
|
wiz_plugin_class = self.class.plugin_class(identification[:product])
|
1067
|
-
|
1052
|
+
assert(wiz_plugin_class.respond_to?(:wizard), exception_class: Cli::BadArgument){"Detected: #{identification[:product]}, but this application has no wizard"}
|
1068
1053
|
# instantiate plugin: command line options will be known, e.g. private_key
|
1069
1054
|
plugin_instance = wiz_plugin_class.new(@agents)
|
1070
1055
|
wiz_params = {
|
@@ -1089,7 +1074,7 @@ module Aspera
|
|
1089
1074
|
formatter.display_status('Using existing key:')
|
1090
1075
|
else
|
1091
1076
|
formatter.display_status("Generating #{DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
|
1092
|
-
|
1077
|
+
self.class.generate_rsa_private_key(path: private_key_path)
|
1093
1078
|
formatter.display_status('Created key:')
|
1094
1079
|
end
|
1095
1080
|
formatter.display_status(private_key_path)
|
@@ -1102,7 +1087,7 @@ module Aspera
|
|
1102
1087
|
# finally, call the wizard
|
1103
1088
|
wizard_result = wiz_plugin_class.wizard(**wiz_params)
|
1104
1089
|
Log.log.debug{"wizard result: #{wizard_result}"}
|
1105
|
-
|
1090
|
+
assert(WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)){"missing or extra keys in wizard result: #{wizard_result.keys}"}
|
1106
1091
|
# get preset name from user or default
|
1107
1092
|
wiz_preset_name = options.get_option(:id)
|
1108
1093
|
if wiz_preset_name.nil?
|
@@ -1118,17 +1103,17 @@ module Aspera
|
|
1118
1103
|
# Write configuration file
|
1119
1104
|
formatter.display_status("Preparing preset: #{wiz_preset_name}")
|
1120
1105
|
# init defaults if necessary
|
1121
|
-
@config_presets[
|
1106
|
+
@config_presets[CONF_PRESET_DEFAULTS] ||= {}
|
1122
1107
|
option_override = options.get_option(:override, mandatory: true)
|
1123
1108
|
raise Cli::Error, "A default configuration already exists for plugin '#{identification[:product]}' (use --override=yes or --default=no)" \
|
1124
|
-
if !option_override && options.get_option(:default, mandatory: true) && @config_presets[
|
1109
|
+
if !option_override && options.get_option(:default, mandatory: true) && @config_presets[CONF_PRESET_DEFAULTS].key?(identification[:product])
|
1125
1110
|
raise Cli::Error, "Preset already exists: #{wiz_preset_name} (use --override=yes or --id=<name>)" \
|
1126
1111
|
if !option_override && @config_presets.key?(wiz_preset_name)
|
1127
1112
|
@config_presets[wiz_preset_name] = wizard_result[:preset_value].stringify_keys
|
1128
1113
|
test_args = wizard_result[:test_args]
|
1129
1114
|
if options.get_option(:default, mandatory: true)
|
1130
1115
|
formatter.display_status("Setting config preset as default for #{identification[:product]}")
|
1131
|
-
@config_presets[
|
1116
|
+
@config_presets[CONF_PRESET_DEFAULTS][identification[:product].to_s] = wiz_preset_name
|
1132
1117
|
else
|
1133
1118
|
test_args = "-P#{wiz_preset_name} #{test_args}"
|
1134
1119
|
end
|
@@ -1155,7 +1140,7 @@ module Aspera
|
|
1155
1140
|
smtp[:domain] ||= smtp[:from_email].gsub(/^.*@/, '') if smtp.key?(:from_email)
|
1156
1141
|
# check minimum required
|
1157
1142
|
%i[server port domain].each do |n|
|
1158
|
-
|
1143
|
+
assert(smtp.key?(n)){"Missing mandatory smtp parameter: #{n}"}
|
1159
1144
|
end
|
1160
1145
|
Log.log.debug{"smtp=#{smtp}"}
|
1161
1146
|
return smtp
|
@@ -1169,7 +1154,7 @@ module Aspera
|
|
1169
1154
|
values[:from_name] ||= mail_conf[:from_name]
|
1170
1155
|
values[:from_email] ||= mail_conf[:from_email]
|
1171
1156
|
%i[from_name from_email].each do |n|
|
1172
|
-
|
1157
|
+
assert(values.key?(n)){"Missing email parameter: #{n}"}
|
1173
1158
|
end
|
1174
1159
|
start_options = [mail_conf[:domain]]
|
1175
1160
|
start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
|
@@ -1177,7 +1162,7 @@ module Aspera
|
|
1177
1162
|
template_binding = Environment.empty_binding
|
1178
1163
|
# add variables to binding
|
1179
1164
|
values.each do |k, v|
|
1180
|
-
|
1165
|
+
assert_type(k, Symbol)
|
1181
1166
|
template_binding.local_variable_set(k, v)
|
1182
1167
|
end
|
1183
1168
|
# execute template
|
@@ -1211,14 +1196,14 @@ module Aspera
|
|
1211
1196
|
# returns [String] name if config_presets has default
|
1212
1197
|
# returns nil if there is no config or bypass default params
|
1213
1198
|
def get_plugin_default_config_name(plugin_name_sym)
|
1214
|
-
|
1199
|
+
assert(!@config_presets.nil?){'config_presets shall be defined'}
|
1215
1200
|
if !@use_plugin_defaults
|
1216
1201
|
Log.log.debug('skip default config')
|
1217
1202
|
return nil
|
1218
1203
|
end
|
1219
|
-
if @config_presets.key?(
|
1220
|
-
@config_presets[
|
1221
|
-
default_config_name = @config_presets[
|
1204
|
+
if @config_presets.key?(CONF_PRESET_DEFAULTS) &&
|
1205
|
+
@config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name_sym.to_s)
|
1206
|
+
default_config_name = @config_presets[CONF_PRESET_DEFAULTS][plugin_name_sym.to_s]
|
1222
1207
|
if !@config_presets.key?(default_config_name)
|
1223
1208
|
Log.log.error do
|
1224
1209
|
"Default config name [#{default_config_name}] specified for plugin [#{plugin_name_sym}], but it does not exist in config file.\n" \
|
@@ -1235,16 +1220,17 @@ module Aspera
|
|
1235
1220
|
# TODO: delete: ALLOWED_KEYS = %i[password username description].freeze
|
1236
1221
|
# @return [Hash] result of execution of vault command
|
1237
1222
|
def execute_vault
|
1238
|
-
command = options.get_next_command(%i[list show create delete password])
|
1223
|
+
command = options.get_next_command(%i[info list show create delete password])
|
1239
1224
|
case command
|
1225
|
+
when :info
|
1226
|
+
return {type: :single_object, data: vault_info}
|
1240
1227
|
when :list
|
1241
1228
|
return {type: :object_list, data: vault.list}
|
1242
1229
|
when :show
|
1243
1230
|
return {type: :single_object, data: vault.get(label: options.get_next_argument('label'))}
|
1244
1231
|
when :create
|
1245
|
-
label = options.get_next_argument('label')
|
1246
|
-
info = options.get_next_argument('info Hash
|
1247
|
-
raise 'info must be Hash' unless info.is_a?(Hash)
|
1232
|
+
label = options.get_next_argument('label', type: String)
|
1233
|
+
info = options.get_next_argument('info', type: Hash)
|
1248
1234
|
info = info.symbolize_keys
|
1249
1235
|
info[:label] = label
|
1250
1236
|
vault.set(info)
|
@@ -1253,7 +1239,7 @@ module Aspera
|
|
1253
1239
|
vault.delete(label: options.get_next_argument('label'))
|
1254
1240
|
return Main.result_status('Password deleted')
|
1255
1241
|
when :password
|
1256
|
-
|
1242
|
+
assert(vault.respond_to?(:password=)){'Vault does not support password change'}
|
1257
1243
|
new_password = options.get_next_argument('new_password')
|
1258
1244
|
vault.password = new_password
|
1259
1245
|
vault.save
|
@@ -1272,27 +1258,36 @@ module Aspera
|
|
1272
1258
|
return value
|
1273
1259
|
end
|
1274
1260
|
|
1261
|
+
def vault_info
|
1262
|
+
info = options.get_option(:vault) || {}
|
1263
|
+
info = info.symbolize_keys
|
1264
|
+
info[:type] ||= 'file'
|
1265
|
+
info[:name] ||= (info[:type].eql?('file') ? DEFAULT_VAULT_FILENAME : PROGRAM_NAME)
|
1266
|
+
assert(info.keys.sort == %i[name type]) {"vault info shall have exactly keys 'type' and 'name'"}
|
1267
|
+
assert(info.values.all?(String)){'vault info shall have only string values'}
|
1268
|
+
info[:password] = options.get_option(:vault_password, mandatory: true)
|
1269
|
+
return info
|
1270
|
+
end
|
1271
|
+
|
1275
1272
|
# @return [Object] vault, from options or cache
|
1276
1273
|
def vault
|
1277
1274
|
if @vault.nil?
|
1278
|
-
|
1279
|
-
|
1280
|
-
vault_type = vault_info['type'] || 'file'
|
1281
|
-
vault_name = vault_info['name'] || (vault_type.eql?('file') ? 'vault.bin' : PROGRAM_NAME)
|
1282
|
-
case vault_type
|
1275
|
+
info = vault_info
|
1276
|
+
case info[:type]
|
1283
1277
|
when 'file'
|
1284
1278
|
# absolute_path? introduced in ruby 2.7
|
1285
|
-
|
1286
|
-
|
1279
|
+
@vault = Keychain::EncryptedHash.new(
|
1280
|
+
info[:name].eql?(File.absolute_path(info[:name])) ? info[:name] : File.join(@main_folder, info[:name]),
|
1281
|
+
info[:password])
|
1287
1282
|
when 'system'
|
1288
1283
|
case Environment.os
|
1289
1284
|
when Environment::OS_X
|
1290
|
-
@vault = Keychain::MacosSystem.new(
|
1285
|
+
@vault = Keychain::MacosSystem.new(info[:name], info[:password])
|
1291
1286
|
else
|
1292
1287
|
raise 'not implemented for this OS'
|
1293
1288
|
end
|
1294
1289
|
else
|
1295
|
-
raise Cli::BadArgument, "Unknown vault type: #{
|
1290
|
+
raise Cli::BadArgument, "Unknown vault type: #{info[:type]}"
|
1296
1291
|
end
|
1297
1292
|
end
|
1298
1293
|
raise 'No vault defined' if @vault.nil?
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'aspera/cli/plugin'
|
4
4
|
require 'aspera/cli/plugins/node'
|
5
5
|
require 'aspera/cos_node'
|
6
|
+
require 'aspera/assert'
|
6
7
|
|
7
8
|
module Aspera
|
8
9
|
module Cli
|
@@ -30,8 +31,7 @@ module Aspera
|
|
30
31
|
# get service credentials, Hash, e.g. @json:@file:...
|
31
32
|
service_credentials = options.get_option(:service_credentials)
|
32
33
|
storage_endpoint = options.get_option(:endpoint)
|
33
|
-
|
34
|
-
raise Cli::BadArgument, 'endpoint and service_credentials are mutually exclusive' unless service_credentials.nil? || storage_endpoint.nil?
|
34
|
+
assert(service_credentials.nil? ^ storage_endpoint.nil?, exception_class: Cli::BadArgument){'endpoint and service_credentials are mutually exclusive'}
|
35
35
|
if service_credentials.nil?
|
36
36
|
service_api_key = options.get_option(:apikey, mandatory: true)
|
37
37
|
instance_id = options.get_option(:crn, mandatory: true)
|