aspera-cli 4.21.2 → 4.22.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +1 -1
  4. data/CHANGELOG.md +34 -16
  5. data/CONTRIBUTING.md +6 -10
  6. data/README.md +805 -574
  7. data/examples/get_proto_file.rb +1 -1
  8. data/lib/aspera/agent/base.rb +9 -5
  9. data/lib/aspera/agent/connect.rb +30 -28
  10. data/lib/aspera/agent/desktop.rb +29 -25
  11. data/lib/aspera/agent/direct.rb +137 -125
  12. data/lib/aspera/agent/httpgw.rb +22 -26
  13. data/lib/aspera/agent/node.rb +14 -11
  14. data/lib/aspera/agent/transferd.rb +6 -2
  15. data/lib/aspera/api/aoc.rb +6 -6
  16. data/lib/aspera/api/cos_node.rb +1 -1
  17. data/lib/aspera/api/httpgw.rb +7 -3
  18. data/lib/aspera/api/node.rb +6 -4
  19. data/lib/aspera/ascmd.rb +3 -3
  20. data/lib/aspera/ascp/installation.rb +15 -16
  21. data/lib/aspera/ascp/management.rb +1 -1
  22. data/lib/aspera/assert.rb +11 -2
  23. data/lib/aspera/cli/error.rb +2 -2
  24. data/lib/aspera/cli/extended_value.rb +38 -19
  25. data/lib/aspera/cli/formatter.rb +48 -48
  26. data/lib/aspera/cli/hints.rb +1 -1
  27. data/lib/aspera/cli/main.rb +190 -168
  28. data/lib/aspera/cli/manager.rb +15 -15
  29. data/lib/aspera/cli/plugin.rb +23 -20
  30. data/lib/aspera/cli/plugin_factory.rb +1 -1
  31. data/lib/aspera/cli/plugins/alee.rb +1 -1
  32. data/lib/aspera/cli/plugins/aoc.rb +144 -107
  33. data/lib/aspera/cli/plugins/ats.rb +19 -17
  34. data/lib/aspera/cli/plugins/config.rb +67 -83
  35. data/lib/aspera/cli/plugins/console.rb +5 -3
  36. data/lib/aspera/cli/plugins/faspex.rb +39 -35
  37. data/lib/aspera/cli/plugins/faspex5.rb +104 -80
  38. data/lib/aspera/cli/plugins/faspio.rb +13 -1
  39. data/lib/aspera/cli/plugins/httpgw.rb +13 -1
  40. data/lib/aspera/cli/plugins/node.rb +306 -179
  41. data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
  42. data/lib/aspera/cli/plugins/preview.rb +3 -3
  43. data/lib/aspera/cli/plugins/server.rb +6 -6
  44. data/lib/aspera/cli/plugins/shares.rb +5 -5
  45. data/lib/aspera/cli/sync_actions.rb +19 -18
  46. data/lib/aspera/cli/transfer_agent.rb +5 -5
  47. data/lib/aspera/cli/transfer_progress.rb +2 -2
  48. data/lib/aspera/cli/version.rb +1 -1
  49. data/lib/aspera/command_line_builder.rb +116 -95
  50. data/lib/aspera/coverage.rb +4 -3
  51. data/lib/aspera/environment.rb +6 -6
  52. data/lib/aspera/faspex_gw.rb +14 -14
  53. data/lib/aspera/faspex_postproc.rb +7 -6
  54. data/lib/aspera/hash_ext.rb +2 -2
  55. data/lib/aspera/json_rpc.rb +1 -1
  56. data/lib/aspera/keychain/encrypted_hash.rb +47 -34
  57. data/lib/aspera/keychain/factory.rb +41 -0
  58. data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
  59. data/lib/aspera/keychain/macos_security.rb +19 -11
  60. data/lib/aspera/log.rb +28 -34
  61. data/lib/aspera/nagios.rb +6 -6
  62. data/lib/aspera/node_simulator.rb +8 -8
  63. data/lib/aspera/oauth/base.rb +8 -6
  64. data/lib/aspera/oauth/factory.rb +5 -6
  65. data/lib/aspera/oauth/url_json.rb +6 -6
  66. data/lib/aspera/persistency_action_once.rb +6 -4
  67. data/lib/aspera/persistency_folder.rb +2 -2
  68. data/lib/aspera/preview/generator.rb +1 -1
  69. data/lib/aspera/preview/options.rb +16 -16
  70. data/lib/aspera/preview/terminal.rb +3 -3
  71. data/lib/aspera/preview/utils.rb +11 -13
  72. data/lib/aspera/products/connect.rb +1 -1
  73. data/lib/aspera/products/desktop.rb +1 -1
  74. data/lib/aspera/products/transferd.rb +1 -1
  75. data/lib/aspera/proxy_auto_config.rb +2 -2
  76. data/lib/aspera/rest.rb +52 -43
  77. data/lib/aspera/rest_errors_aspera.rb +1 -1
  78. data/lib/aspera/secret_hider.rb +5 -5
  79. data/lib/aspera/ssh.rb +4 -4
  80. data/lib/aspera/transfer/convert.rb +29 -0
  81. data/lib/aspera/transfer/error_info.rb +66 -66
  82. data/lib/aspera/transfer/parameters.rb +13 -68
  83. data/lib/aspera/transfer/spec.rb +5 -6
  84. data/lib/aspera/transfer/spec.schema.yaml +753 -0
  85. data/lib/aspera/transfer/spec_doc.rb +62 -0
  86. data/lib/aspera/transfer/sync.rb +23 -72
  87. data/lib/aspera/transfer/sync_instance.schema.yaml +13 -0
  88. data/lib/aspera/transfer/sync_session.schema.yaml +79 -0
  89. data/lib/aspera/transfer/uri.rb +6 -6
  90. data/lib/aspera/uri_reader.rb +1 -1
  91. data/lib/aspera/web_auth.rb +1 -1
  92. data/lib/aspera/web_server_simple.rb +53 -44
  93. data.tar.gz.sig +1 -2
  94. metadata +37 -4
  95. metadata.gz.sig +0 -0
  96. data/examples/build_package.sh +0 -28
  97. data/lib/aspera/transfer/spec.yaml +0 -718
@@ -14,8 +14,10 @@ module Aspera
14
14
  # Access Aspera Transfer Service
15
15
  # https://developer.ibm.com/aspera/docs/ats-api-reference/creating-ats-api-keys/
16
16
  class Ats < Cli::Plugin
17
+ # columns for list of cloud providers
17
18
  CLOUD_TABLE = %w[id name].freeze
18
- def initialize(**env)
19
+ private_constant :CLOUD_TABLE
20
+ def initialize(**_)
19
21
  super
20
22
  options.declare(:ibm_api_key, 'IBM API key, see https://cloud.ibm.com/iam/apikeys')
21
23
  options.declare(:instance, 'ATS instance in ibm cloud')
@@ -68,7 +70,7 @@ module Aspera
68
70
  when 'ibm-s3'
69
71
  server_data2 = nil
70
72
  if server_data.nil?
71
- server_data2 = @ats_api_pub.all_servers.find{|s|s['id'].eql?(params['transfer_server_id'])}
73
+ server_data2 = @ats_api_pub.all_servers.find{ |s| s['id'].eql?(params['transfer_server_id'])}
72
74
  raise "no such transfer server id: #{params['transfer_server_id']}" if server_data2.nil?
73
75
  else
74
76
  server_data2 = @ats_api_pub.all_servers.find do |s|
@@ -86,15 +88,15 @@ module Aspera
86
88
  end
87
89
  end
88
90
  res = ats_api_pub_v1.create('access_keys', params)
89
- return {type: :single_object, data: res}
91
+ return Main.result_single_object(res)
90
92
  # TODO : action : modify, with "PUT"
91
93
  when :list
92
94
  params = options.get_option(:params) || {'offset' => 0, 'max_results' => 1000}
93
95
  res = ats_api_pub_v1.read('access_keys', params)
94
- return {type: :object_list, data: res['data'], fields: ['name', 'id', 'created.at', 'modified.at']}
96
+ return Main.result_object_list(res['data'], fields: ['name', 'id', 'created.at', 'modified.at'])
95
97
  when :show
96
98
  res = ats_api_pub_v1.read("access_keys/#{access_key_id}")
97
- return {type: :single_object, data: res}
99
+ return Main.result_single_object(res)
98
100
  when :modify
99
101
  params = value_create_modify(command: command)
100
102
  params['id'] = access_key_id
@@ -103,13 +105,13 @@ module Aspera
103
105
  when :entitlement
104
106
  ak = ats_api_pub_v1.read("access_keys/#{access_key_id}")
105
107
  api_bss = Api::Alee.new(ak['license']['entitlement_id'], ak['license']['customer_id'])
106
- return {type: :single_object, data: api_bss.read('entitlement')}
108
+ return Main.result_single_object(api_bss.read('entitlement'))
107
109
  when :delete
108
110
  ats_api_pub_v1.delete("access_keys/#{access_key_id}")
109
111
  return Main.result_status("deleted #{access_key_id}")
110
112
  when :node
111
113
  ak_data = ats_api_pub_v1.read("access_keys/#{access_key_id}")
112
- server_data = @ats_api_pub.all_servers.find {|i| i['id'].start_with?(ak_data['transfer_server_id'])}
114
+ server_data = @ats_api_pub.all_servers.find{ |i| i['id'].start_with?(ak_data['transfer_server_id'])}
113
115
  raise Cli::Error, 'no such server found' if server_data.nil?
114
116
  node_url = server_data['transfer_setup_url']
115
117
  api_node = Api::Node.new(
@@ -130,7 +132,7 @@ module Aspera
130
132
  username: access_key_id,
131
133
  password: config.lookup_secret(url: ats_url, username: access_key_id)
132
134
  })
133
- return {type: :single_object, data: api_ak_auth.read('servers')}
135
+ return Main.result_single_object(api_ak_auth.read('servers'))
134
136
  else Aspera.error_unexpected_value(command)
135
137
  end
136
138
  end
@@ -139,18 +141,18 @@ module Aspera
139
141
  command = options.get_next_command(%i[clouds list show])
140
142
  case command
141
143
  when :clouds
142
- return {type: :object_list, data: @ats_api_pub.cloud_names.map { |k, v| CLOUD_TABLE.zip([k, v]).to_h }}
144
+ return Main.result_object_list(@ats_api_pub.cloud_names.map{ |k, v| CLOUD_TABLE.zip([k, v]).to_h})
143
145
  when :list
144
- return {type: :object_list, data: @ats_api_pub.all_servers, fields: %w[id cloud region]}
146
+ return Main.result_object_list(@ats_api_pub.all_servers, fields: %w[id cloud region])
145
147
  when :show
146
148
  if options.get_option(:cloud) || options.get_option(:region)
147
149
  server_data = server_by_cloud_region
148
150
  else
149
151
  server_id = instance_identifier
150
- server_data = @ats_api_pub.all_servers.find {|i| i['id'].eql?(server_id)}
152
+ server_data = @ats_api_pub.all_servers.find{ |i| i['id'].eql?(server_id)}
151
153
  raise 'no such server id' if server_data.nil?
152
154
  end
153
- return {type: :single_object, data: server_data}
155
+ return Main.result_single_object(server_data)
154
156
  end
155
157
  end
156
158
 
@@ -189,16 +191,16 @@ module Aspera
189
191
  when :instances
190
192
  instances = ats_ibm_api.read('instances')
191
193
  Log.log.warn{"more instances remaining: #{instances['remaining']}"} unless instances['remaining'].to_i.eql?(0)
192
- return {type: :value_list, data: instances['data'], name: 'instance'}
194
+ return Main.result_value_list(instances['data'], name: 'instance')
193
195
  when :create
194
196
  created_key = ats_ibm_api.create('api_keys', value_create_modify(command: command, default: {}))
195
- return {type: :single_object, data: created_key}
197
+ return Main.result_single_object(created_key)
196
198
  when :list # list known api keys in ATS (this require an api_key ...)
197
199
  res = ats_ibm_api.read('api_keys', {'offset' => 0, 'max_results' => 1000})
198
- return {type: :value_list, data: res['data'], name: 'ats_id'}
200
+ return Main.result_value_list(res['data'], name: 'ats_id')
199
201
  when :show # show one of api_key in ATS
200
202
  res = ats_ibm_api.read("api_keys/#{concerned_id}")
201
- return {type: :single_object, data: res}
203
+ return Main.result_single_object(res)
202
204
  when :delete
203
205
  ats_ibm_api.delete("api_keys/#{concerned_id}")
204
206
  return Main.result_status("deleted #{concerned_id}")
@@ -225,7 +227,7 @@ module Aspera
225
227
  return execute_action_api_key
226
228
  when :aws_trust_policy
227
229
  res = ats_api_pub_v1.read('aws/trustpolicy', {region: options.get_option(:region, mandatory: true)})
228
- return {type: :single_object, data: res}
230
+ return Main.result_single_object(res)
229
231
  else Aspera.error_unexpected_value(command)
230
232
  end
231
233
  end
@@ -13,6 +13,7 @@ require 'aspera/products/transferd'
13
13
  require 'aspera/transfer/error_info'
14
14
  require 'aspera/transfer/parameters'
15
15
  require 'aspera/transfer/spec'
16
+ require 'aspera/transfer/spec_doc'
16
17
  require 'aspera/keychain/macos_security'
17
18
  require 'aspera/proxy_auto_config'
18
19
  require 'aspera/environment'
@@ -39,7 +40,6 @@ module Aspera
39
40
  ASPERA_HOME_FOLDER_NAME = '.aspera'
40
41
  # default config file
41
42
  DEFAULT_CONFIG_FILENAME = 'config.yaml'
42
- DEFAULT_VAULT_FILENAME = 'vault.bin'
43
43
  # reserved preset names
44
44
  CONF_PRESET_CONFIG = 'config'
45
45
  CONF_PRESET_VERSION = 'version'
@@ -76,6 +76,7 @@ module Aspera
76
76
  # for testing only
77
77
  SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse) # cspell: disable-line
78
78
  CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
79
+ SMTP_CONF_PARAMS = %i[server tls ssl port domain username password from_name from_email].freeze
79
80
  private_constant :DEFAULT_CONFIG_FILENAME,
80
81
  :CONF_PRESET_CONFIG,
81
82
  :CONF_PRESET_VERSION,
@@ -97,7 +98,8 @@ module Aspera
97
98
  :SELF_SIGNED_CERT,
98
99
  :PERSISTENCY_FOLDER,
99
100
  :DEFAULT_PRIV_KEY_LENGTH,
100
- :CONF_OVERVIEW_KEYS
101
+ :CONF_OVERVIEW_KEYS,
102
+ :SMTP_CONF_PARAMS
101
103
 
102
104
  class << self
103
105
  def generate_rsa_private_key(path:, length: DEFAULT_PRIV_KEY_LENGTH)
@@ -142,13 +144,13 @@ module Aspera
142
144
  end
143
145
  end
144
146
 
145
- def initialize(**env)
147
+ def initialize(**_)
146
148
  # we need to defer parsing of options until we have the config file, so we can use @extend with @preset
147
149
  super
148
150
  @use_plugin_defaults = true
149
151
  @config_presets = nil
150
152
  @config_checksum_on_disk = nil
151
- @vault = nil
153
+ @vault_instance = nil
152
154
  @pac_exec = nil
153
155
  @sdk_default_location = false
154
156
  @option_insecure = false
@@ -172,8 +174,8 @@ module Aspera
172
174
  default: self.class.default_app_main_folder(app_name: Info::CMD_NAME))
173
175
  options.parse_options!
174
176
  Log.log.debug{"#{Info::CMD_NAME} folder: #{@main_folder}"}
175
- # data persistency manager, created by config plugin
176
- @persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
177
+ # data persistency manager, created by config plugin, set for global object
178
+ @broker.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
177
179
  # set folders for plugin lookup
178
180
  PluginFactory.instance.add_lookup_folder(self.class.gem_plugins_folder)
179
181
  PluginFactory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
@@ -186,19 +188,19 @@ module Aspera
186
188
  # read config file (set @config_presets)
187
189
  read_config_file
188
190
  # add preset handler (needed for smtp)
189
- ExtendedValue.instance.set_handler(EXTEND_PRESET, lambda{|v|preset_by_name(v)})
190
- ExtendedValue.instance.set_handler(EXTEND_VAULT, lambda{|v|vault_value(v)})
191
+ ExtendedValue.instance.set_handler(EXTEND_PRESET, lambda{ |v| preset_by_name(v)})
192
+ ExtendedValue.instance.set_handler(EXTEND_VAULT, lambda{ |v| vault_value(v)})
191
193
  # load defaults before it can be overridden
192
194
  add_plugin_default_preset(CONF_GLOBAL_SYM)
193
195
  # vault options
194
196
  options.declare(:secret, 'Secret for access keys')
195
- options.declare(:vault, 'Vault for secrets', types: Hash)
197
+ options.declare(:vault, 'Vault for secrets', types: Hash, default: {})
196
198
  options.declare(:vault_password, 'Vault password')
197
199
  options.parse_options!
198
200
  # declare generic plugin options only after handlers are declared
199
201
  Plugin.declare_generic_options(options)
200
202
  # configuration options
201
- options.declare(:no_default, 'Do not load default configuration for plugin', values: :none, short: 'N') { @use_plugin_defaults = false }
203
+ options.declare(:no_default, 'Do not load default configuration for plugin', values: :none, short: 'N'){@use_plugin_defaults = false}
202
204
  options.declare(:preset, 'Load the named option preset from current config file', short: 'P', handler: {o: self, m: :option_preset})
203
205
  options.declare(:version_check_days, 'Period in days to check new version (zero to disable)', coerce: Integer, default: DEFAULT_CHECK_NEW_VERSION_DAYS)
204
206
  options.declare(:plugin_folder, 'Folder where to find additional plugins', handler: {o: self, m: :option_plugin_folder})
@@ -261,8 +263,8 @@ module Aspera
261
263
  end
262
264
  RestParameters.instance.user_agent = Info::CMD_NAME
263
265
  RestParameters.instance.progress_bar = @progress_bar
264
- RestParameters.instance.session_cb = lambda{|http_session|update_http_session(http_session)}
265
- @option_http_options.keys.select{|i|RestParameters.instance.respond_to?(i)}.each do |k|
266
+ RestParameters.instance.session_cb = lambda{ |http_session| update_http_session(http_session)}
267
+ @option_http_options.keys.select{ |i| RestParameters.instance.respond_to?(i)}.each do |k|
266
268
  method = "#{k}=".to_sym
267
269
  RestParameters.instance.send(method, @option_http_options[k])
268
270
  @option_http_options.delete(k)
@@ -299,7 +301,7 @@ module Aspera
299
301
  paths_to_add = [OpenSSL::X509::DEFAULT_CERT_DIR]
300
302
  # JRuby cert file seems not to be PEM
301
303
  paths_to_add.push(OpenSSL::X509::DEFAULT_CERT_FILE) unless defined?(JRUBY_VERSION)
302
- paths_to_add.select!{|f|File.exist?(f)}
304
+ paths_to_add.select!{ |f| File.exist?(f)}
303
305
  elsif File.file?(path)
304
306
  @certificate_store.add_file(path)
305
307
  elsif File.directory?(path)
@@ -311,8 +313,8 @@ module Aspera
311
313
  pp = [File.realpath(p)]
312
314
  if File.directory?(p)
313
315
  pp = Dir.entries(p)
314
- .map{|e|File.realpath(File.join(p, e))}
315
- .select{|entry|File.file?(entry)}
316
+ .map{ |e| File.realpath(File.join(p, e))}
317
+ .select{ |entry| File.file?(entry)}
316
318
  end
317
319
  @certificate_paths.concat(pp)
318
320
  end
@@ -519,7 +521,7 @@ module Aspera
519
521
  Aspera.assert_values(value.class, [String, Array]){'plugin folder'}
520
522
  value = [value] if value.is_a?(String)
521
523
  Aspera.assert(value.all?(String)){'plugin folder'}
522
- value.each{|f|PluginFactory.instance.add_lookup_folder(f)}
524
+ value.each{ |f| PluginFactory.instance.add_lookup_folder(f)}
523
525
  end
524
526
 
525
527
  def option_plugin_folder
@@ -549,7 +551,7 @@ module Aspera
549
551
  # files search for configuration, by default the one given by user
550
552
  search_files = [@option_config_file]
551
553
  # find first existing file (or nil)
552
- conf_file_to_load = search_files.find{|f| File.exist?(f)}
554
+ conf_file_to_load = search_files.find{ |f| File.exist?(f)}
553
555
  # if no file found, create default config
554
556
  if conf_file_to_load.nil?
555
557
  Log.log.warn{"No config file found. New configuration file: #{@option_config_file}"}
@@ -632,7 +634,7 @@ module Aspera
632
634
  found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
633
635
  end
634
636
  raise "No known application found at #{app_url}" if found_apps.empty?
635
- Aspera.assert(found_apps.all?{|a|a.keys.all?(Symbol)})
637
+ Aspera.assert(found_apps.all?{ |a| a.keys.all?(Symbol)})
636
638
  return found_apps
637
639
  end
638
640
 
@@ -640,8 +642,8 @@ module Aspera
640
642
  command = options.get_next_command(%i[list info version])
641
643
  if %i[info version].include?(command)
642
644
  connect_id = options.get_next_argument('id or title')
643
- one_res = Products::Connect.instance.versions.find{|i|i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}
644
- raise Cli::NoSuchIdentifier.new(:connect, connect_id) if one_res.nil?
645
+ one_res = Products::Connect.instance.versions.find{ |i| i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}
646
+ raise Cli::BadIdentifier.new(:connect, connect_id) if one_res.nil?
645
647
  end
646
648
  case command
647
649
  when :list
@@ -654,7 +656,7 @@ module Aspera
654
656
  command = options.get_next_command(%i[list download open])
655
657
  if %i[download open].include?(command)
656
658
  link_title = options.get_next_argument('title or rel')
657
- one_link = all_links.find {|i| i['title'].eql?(link_title) || i['rel'].eql?(link_title)}
659
+ one_link = all_links.find{ |i| i['title'].eql?(link_title) || i['rel'].eql?(link_title)}
658
660
  raise "no such value: #{link_title}" if one_link.nil?
659
661
  end
660
662
  case command
@@ -673,7 +675,7 @@ module Aspera
673
675
  end
674
676
 
675
677
  def execute_action_ascp
676
- command = options.get_next_command(%i[connect use show products info install spec errors])
678
+ command = options.get_next_command(%i[connect use show products info install spec schema errors])
677
679
  case command
678
680
  when :connect
679
681
  return execute_connect_action
@@ -691,7 +693,7 @@ module Aspera
691
693
  # add command line transfer spec
692
694
  data['ts'] = transfer.updated_ts
693
695
  # add keys
694
- DataRepository::ELEMENTS.each_with_object(data){|i, h|h[i.to_s] = DataRepository.instance.item(i)}
696
+ DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
695
697
  # declare those as secrets
696
698
  SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
697
699
  return Main.result_single_object(data)
@@ -714,9 +716,15 @@ module Aspera
714
716
  return Main.result_status("Installed #{n} version #{v}")
715
717
  when :spec
716
718
  return Main.result_object_list(
717
- Transfer::Parameters.man_table(formatter),
718
- fields: [%w[name type], Transfer::Parameters::SUPPORTED_AGENTS_SHORT.map(&:to_s), %w[description]].flatten.freeze
719
+ Transfer::SpecDoc.man_table(formatter, cli: false),
720
+ fields: Transfer::SpecDoc::TABLE_COLUMNS.map(&:to_s)
719
721
  )
722
+ when :schema
723
+ schema = Transfer::Spec::SCHEMA.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
724
+ agent = options.get_next_argument('transfer agent name', mandatory: false)
725
+ schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent, v)} unless agent.nil?
726
+ schema['properties'] = schema['properties'].sort.to_h
727
+ return Main.result_single_object(schema)
720
728
  when :errors
721
729
  error_data = []
722
730
  Transfer::ERROR_INFO.each_pair do |code, prop|
@@ -754,7 +762,7 @@ module Aspera
754
762
  PRESET_EXIST_ACTIONS = %i[show delete get unset].freeze
755
763
  # require id
756
764
  PRESET_INSTANCE_ACTIONS = %i[initialize update ask set].concat(PRESET_EXIST_ACTIONS).freeze
757
- PRESET_ALL_ACTIONS = [PRESET_GBL_ACTIONS, PRESET_INSTANCE_ACTIONS].flatten.freeze
765
+ PRESET_ALL_ACTIONS = (PRESET_GBL_ACTIONS + PRESET_INSTANCE_ACTIONS).freeze
758
766
 
759
767
  def execute_preset(action: nil, name: nil)
760
768
  action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
@@ -786,7 +794,7 @@ module Aspera
786
794
  value = @config_presets[name][param_name]
787
795
  raise "no such option in preset #{name} : #{param_name}" if value.nil?
788
796
  case value
789
- when Numeric, String then return {type: :text, data: ExtendedValue.instance.evaluate(value.to_s)}
797
+ when Numeric, String then return Main.result_text(ExtendedValue.instance.evaluate(value.to_s))
790
798
  end
791
799
  return Main.result_single_object(value)
792
800
  when :unset
@@ -919,10 +927,10 @@ module Aspera
919
927
  when :only
920
928
  return Main.result_status(remote_chain.first.to_pem)
921
929
  when :name
922
- return Main.result_status(remote_chain.first.subject.to_a.find { |name, _, _| name == 'CN' }[1])
930
+ return Main.result_status(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
923
931
  end
924
932
  when :echo # display the content of a value given on command line
925
- return Formatter.auto_type(options.get_next_argument('value', validation: nil))
933
+ return Main.result_auto(options.get_next_argument('value', validation: nil))
926
934
  when :download
927
935
  file_url = options.get_next_argument('source URL').chomp
928
936
  file_dest = options.get_next_argument('file path', mandatory: false)
@@ -1051,9 +1059,10 @@ module Aspera
1051
1059
  else
1052
1060
  formatter.display_status('Multiple applications detected, please select from:')
1053
1061
  formatter.display_results(type: :object_list, data: apps, fields: %w[product url version])
1054
- answer = options.prompt_user_input_in_list('product', apps.map{|a|a[:product]})
1055
- apps.find{|a|a[:product].eql?(answer)}
1062
+ answer = options.prompt_user_input_in_list('product', apps.map{ |a| a[:product]})
1063
+ apps.find{ |a| a[:product].eql?(answer)}
1056
1064
  end
1065
+ wiz_preset_name = options.get_next_argument('preset name', default: '')
1057
1066
  Log.log.debug{Log.dump(:identification, identification)}
1058
1067
  wiz_url = identification[:url]
1059
1068
  formatter.display_status("Using: #{identification[:name]} at #{wiz_url}".bold)
@@ -1078,7 +1087,6 @@ module Aspera
1078
1087
  if private_key_path.nil?
1079
1088
  formatter.display_status('Please provide the path to your private RSA key, or nothing to generate one:')
1080
1089
  private_key_path = options.get_option(:key_path, mandatory: true).to_s
1081
- # private_key_path = File.expand_path(private_key_path)
1082
1090
  end
1083
1091
  # else generate path
1084
1092
  if private_key_path.empty?
@@ -1103,13 +1111,12 @@ module Aspera
1103
1111
  Log.log.debug{"wizard result: #{wizard_result}"}
1104
1112
  Aspera.assert(WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)){"missing or extra keys in wizard result: #{wizard_result.keys}"}
1105
1113
  # get preset name from user or default
1106
- wiz_preset_name = nil
1107
- if wiz_preset_name.nil?
1114
+ if wiz_preset_name.empty?
1108
1115
  elements = [
1109
1116
  identification[:product],
1110
1117
  URI.parse(wiz_url).host
1111
1118
  ]
1112
- elements.push(options.get_option(:username, mandatory: true)) unless wizard_result[:preset_value].key?(:link)
1119
+ elements.push(options.get_option(:username, mandatory: true)) unless wizard_result[:preset_value].key?(:link) rescue nil
1113
1120
  wiz_preset_name = elements.join('_').strip.downcase.gsub(/[^a-z0-9]/, '_').squeeze('_')
1114
1121
  end
1115
1122
  # test mode does not change conf file
@@ -1138,9 +1145,12 @@ module Aspera
1138
1145
  # @return [Hash] email server setting with defaults if not defined
1139
1146
  def email_settings
1140
1147
  smtp = options.get_option(:smtp, mandatory: true)
1141
- # change string keys into symbol keys
1148
+ # change keys from string into symbol
1142
1149
  smtp = smtp.symbolize_keys
1150
+ unsupported = smtp.keys - SMTP_CONF_PARAMS
1151
+ raise Cli::Error, "Unsupported SMTP parameter: #{unsupported.join(', ')}, use: #{SMTP_CONF_PARAMS.join(', ')}" unless unsupported.empty?
1143
1152
  # defaults
1153
+ # smtp[:ssl] = nil (false)
1144
1154
  smtp[:tls] = !smtp[:ssl] unless smtp.key?(:tls)
1145
1155
  smtp[:port] ||= if smtp[:tls]
1146
1156
  587
@@ -1169,8 +1179,8 @@ module Aspera
1169
1179
  mail_conf = email_settings
1170
1180
  values[:from_name] ||= mail_conf[:from_name]
1171
1181
  values[:from_email] ||= mail_conf[:from_email]
1172
- %i[from_name from_email].each do |n|
1173
- Aspera.assert(values.key?(n)){"Missing email parameter: #{n}"}
1182
+ %i[to from_email].each do |n|
1183
+ Aspera.assert_type(values[n], String){"Missing email parameter: #{n} in config"}
1174
1184
  end
1175
1185
  start_options = [mail_conf[:domain]]
1176
1186
  start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
@@ -1204,7 +1214,7 @@ module Aspera
1204
1214
  Environment.restrict_file_access(@main_folder)
1205
1215
  Log.log.info{"Writing #{@option_config_file}"}
1206
1216
  formatter.display_status('Saving config file.')
1207
- Environment.write_file_restricted(@option_config_file, force: true) {@config_presets.to_yaml}
1217
+ Environment.write_file_restricted(@option_config_file, force: true){@config_presets.to_yaml}
1208
1218
  @config_checksum_on_disk = current_checksum
1209
1219
  return true
1210
1220
  end
@@ -1234,15 +1244,15 @@ module Aspera
1234
1244
  return nil
1235
1245
  end
1236
1246
 
1237
- # TODO: delete: ALLOWED_KEYS = %i[password username description].freeze
1238
1247
  # @return [Hash] result of execution of vault command
1239
1248
  def execute_vault
1240
1249
  command = options.get_next_command(%i[info list show create delete password])
1241
1250
  case command
1242
1251
  when :info
1243
- return Main.result_single_object(vault_info)
1252
+ return Main.result_single_object(vault.info)
1244
1253
  when :list
1245
- return Main.result_object_list(vault.list, fields: %w(label url username password description))
1254
+ # , fields: %w(label url username password description)
1255
+ return Main.result_object_list(vault.list)
1246
1256
  when :show
1247
1257
  return Main.result_single_object(vault.get(label: options.get_next_argument('label')))
1248
1258
  when :create
@@ -1251,17 +1261,15 @@ module Aspera
1251
1261
  info = info.symbolize_keys
1252
1262
  info[:label] = label
1253
1263
  vault.set(info)
1254
- return Main.result_status('Password added')
1264
+ return Main.result_status('Secret added')
1255
1265
  when :delete
1256
1266
  label_to_delete = options.get_next_argument('label')
1257
1267
  vault.delete(label: label_to_delete)
1258
- return Main.result_status("Entry deleted: #{label_to_delete}")
1268
+ return Main.result_status("Secret deleted: #{label_to_delete}")
1259
1269
  when :password
1260
- Aspera.assert(vault.respond_to?(:password=)){'Vault does not support password change'}
1261
- new_password = options.get_next_argument('new_password')
1262
- vault.password = new_password
1263
- vault.save
1264
- return Main.result_status('Password updated')
1270
+ Aspera.assert(vault.respond_to?(:change_password)){'Vault does not support password change'}
1271
+ vault.change_password(options.get_next_argument('new_password'))
1272
+ return Main.result_status('Vault password updated')
1265
1273
  end
1266
1274
  end
1267
1275
 
@@ -1276,42 +1284,18 @@ module Aspera
1276
1284
  return value
1277
1285
  end
1278
1286
 
1279
- def vault_info
1280
- info = options.get_option(:vault) || {}
1281
- info = info.symbolize_keys
1282
- info[:type] ||= 'file'
1283
- info[:name] ||= (info[:type].eql?('file') ? DEFAULT_VAULT_FILENAME : Info::CMD_NAME)
1284
- Aspera.assert(info.keys.sort == %i[name type]) {"vault info shall have exactly keys 'type' and 'name'"}
1285
- Aspera.assert(info.values.all?(String)){'vault info shall have only string values'}
1286
- info[:password] = options.get_option(:vault_password, mandatory: true)
1287
- return info
1288
- end
1289
-
1290
1287
  # @return [Object] vault, from options or cache
1291
1288
  def vault
1292
- if @vault.nil?
1293
- info = vault_info
1294
- case info[:type]
1295
- when 'file'
1296
- # this module requires comilation, so it is optinal
1297
- require 'aspera/keychain/encrypted_hash'
1298
- # absolute_path? introduced in ruby 2.7
1299
- @vault = Keychain::EncryptedHash.new(
1300
- info[:name].eql?(File.absolute_path(info[:name])) ? info[:name] : File.join(@main_folder, info[:name]),
1301
- info[:password])
1302
- when 'system'
1303
- case Environment.os
1304
- when Environment::OS_MACOS
1305
- @vault = Keychain::MacosSystem.new(info[:name], info[:password])
1306
- else
1307
- raise 'not implemented for this OS'
1308
- end
1309
- else
1310
- raise Cli::BadArgument, "Unknown vault type: #{info[:type]}"
1311
- end
1312
- end
1313
- raise 'No vault defined' if @vault.nil?
1314
- @vault
1289
+ return @vault_instance unless @vault_instance.nil?
1290
+ info = options.get_option(:vault).symbolize_keys
1291
+ info[:type] ||= 'file'
1292
+ require 'aspera/keychain/factory'
1293
+ @vault_instance = Keychain::Factory.create(
1294
+ info,
1295
+ Info::CMD_NAME,
1296
+ @main_folder,
1297
+ options.get_option(:vault_password)
1298
+ )
1315
1299
  end
1316
1300
 
1317
1301
  def execute_test
@@ -40,7 +40,9 @@ module Aspera
40
40
  return nil
41
41
  end
42
42
 
43
- def wizard(object:, private_key_path: nil, pub_key_pem: nil)
43
+ # @param object [Plugin] An instance of this class
44
+ # @return [Hash] :preset_value, :test_args
45
+ def wizard(object:)
44
46
  options = object.options
45
47
  return {
46
48
  preset_value: {
@@ -84,11 +86,11 @@ module Aspera
84
86
  command = options.get_next_command(%i[list submit])
85
87
  case command
86
88
  when :list
87
- return {type: :object_list, data: api_console.read('smart_transfers')}
89
+ return Main.result_object_list(api_console.read('smart_transfers'))
88
90
  when :submit
89
91
  smart_id = options.get_next_argument('smart_id')
90
92
  params = options.get_next_argument('transfer parameters')
91
- return {type: :object_list, data: api_console.create("smart_transfers/#{smart_id}", params)}
93
+ return Main.result_object_list(api_console.create("smart_transfers/#{smart_id}", params))
92
94
  end
93
95
  when :current
94
96
  command = options.get_next_command([:list])