aspera-cli 4.15.0 → 4.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +29 -3
- data/CHANGELOG.md +375 -280
- data/CONTRIBUTING.md +71 -18
- data/README.md +1978 -1656
- data/bin/ascli +13 -31
- data/bin/asession +32 -22
- data/examples/dascli +2 -2
- data/lib/aspera/agent/alpha.rb +117 -0
- data/lib/aspera/agent/base.rb +61 -0
- data/lib/aspera/{fasp/agent_connect.rb → agent/connect.rb} +13 -11
- data/lib/aspera/{fasp/agent_direct.rb → agent/direct.rb} +116 -116
- data/lib/aspera/{fasp/agent_httpgw.rb → agent/httpgw.rb} +21 -19
- data/lib/aspera/{fasp/agent_node.rb → agent/node.rb} +21 -33
- data/lib/aspera/agent/trsdk.rb +188 -0
- data/lib/aspera/api/aoc.rb +586 -0
- data/lib/aspera/api/ats.rb +46 -0
- data/lib/aspera/api/cos_node.rb +95 -0
- data/lib/aspera/api/node.rb +344 -0
- data/lib/aspera/ascmd.rb +47 -14
- data/lib/aspera/{fasp → ascp}/installation.rb +54 -15
- data/lib/aspera/{fasp → ascp}/management.rb +14 -14
- data/lib/aspera/{fasp → ascp}/products.rb +1 -1
- data/lib/aspera/assert.rb +45 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +11 -10
- data/lib/aspera/cli/extended_value.rb +5 -5
- data/lib/aspera/cli/formatter.rb +27 -14
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/main.rb +49 -29
- data/lib/aspera/cli/manager.rb +46 -36
- data/lib/aspera/cli/plugin.rb +34 -20
- data/lib/aspera/cli/plugin_factory.rb +61 -0
- data/lib/aspera/cli/plugins/alee.rb +7 -7
- data/lib/aspera/cli/plugins/aoc.rb +168 -132
- data/lib/aspera/cli/plugins/ats.rb +33 -33
- data/lib/aspera/cli/plugins/bss.rb +3 -4
- data/lib/aspera/cli/plugins/config.rb +250 -272
- data/lib/aspera/cli/plugins/console.rb +8 -6
- data/lib/aspera/cli/plugins/cos.rb +20 -19
- data/lib/aspera/cli/plugins/faspex.rb +71 -60
- data/lib/aspera/cli/plugins/faspex5.rb +212 -133
- data/lib/aspera/cli/plugins/node.rb +83 -75
- data/lib/aspera/cli/plugins/orchestrator.rb +36 -44
- data/lib/aspera/cli/plugins/preview.rb +33 -31
- data/lib/aspera/cli/plugins/server.rb +33 -32
- data/lib/aspera/cli/plugins/shares.rb +39 -33
- data/lib/aspera/cli/sync_actions.rb +9 -9
- data/lib/aspera/cli/transfer_agent.rb +45 -25
- data/lib/aspera/cli/transfer_progress.rb +2 -3
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +5 -0
- data/lib/aspera/command_line_builder.rb +16 -14
- data/lib/aspera/coverage.rb +21 -0
- data/lib/aspera/data_repository.rb +33 -2
- data/lib/aspera/environment.rb +5 -4
- data/lib/aspera/faspex_gw.rb +13 -11
- data/lib/aspera/faspex_postproc.rb +6 -5
- data/lib/aspera/id_generator.rb +4 -2
- data/lib/aspera/json_rpc.rb +10 -8
- data/lib/aspera/keychain/encrypted_hash.rb +46 -11
- data/lib/aspera/keychain/macos_security.rb +29 -22
- data/lib/aspera/log.rb +5 -4
- data/lib/aspera/nagios.rb +7 -2
- data/lib/aspera/node_simulator.rb +213 -0
- data/lib/aspera/oauth/base.rb +143 -0
- data/lib/aspera/oauth/factory.rb +124 -0
- data/lib/aspera/oauth/generic.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +51 -0
- data/lib/aspera/oauth/url_json.rb +31 -0
- data/lib/aspera/oauth/web.rb +50 -0
- data/lib/aspera/oauth.rb +5 -328
- data/lib/aspera/open_application.rb +7 -7
- 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 +17 -7
- data/lib/aspera/preview/utils.rb +8 -7
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +187 -140
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/rest_errors_aspera.rb +5 -3
- data/lib/aspera/resumer.rb +77 -0
- data/lib/aspera/secret_hider.rb +5 -2
- data/lib/aspera/ssh.rb +15 -8
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/{fasp → transfer}/error.rb +3 -3
- data/lib/aspera/{fasp → transfer}/error_info.rb +1 -1
- data/lib/aspera/{fasp → transfer}/faux_file.rb +1 -1
- data/lib/aspera/{fasp → transfer}/parameters.rb +95 -120
- data/lib/aspera/{fasp/transfer_spec.rb → transfer/spec.rb} +23 -19
- data/lib/aspera/{fasp/parameters.yaml → transfer/spec.yaml} +4 -99
- data/lib/aspera/transfer/sync.rb +273 -0
- data/lib/aspera/{fasp → transfer}/uri.rb +10 -9
- data/lib/aspera/web_server_simple.rb +12 -3
- data.tar.gz.sig +0 -0
- metadata +92 -68
- metadata.gz.sig +0 -0
- data/lib/aspera/aoc.rb +0 -606
- data/lib/aspera/ats_api.rb +0 -47
- data/lib/aspera/cos_node.rb +0 -93
- data/lib/aspera/fasp/agent_aspera.rb +0 -126
- data/lib/aspera/fasp/agent_base.rb +0 -48
- data/lib/aspera/fasp/agent_trsdk.rb +0 -146
- data/lib/aspera/fasp/resume_policy.rb +0 -77
- data/lib/aspera/node.rb +0 -338
- data/lib/aspera/sync.rb +0 -219
@@ -1,17 +1,17 @@
|
|
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'
|
7
7
|
require 'aspera/cli/formatter'
|
8
8
|
require 'aspera/cli/info'
|
9
9
|
require 'aspera/cli/transfer_progress'
|
10
|
-
require 'aspera/
|
11
|
-
require 'aspera/
|
12
|
-
require 'aspera/
|
13
|
-
require 'aspera/
|
14
|
-
require 'aspera/
|
10
|
+
require 'aspera/ascp/installation'
|
11
|
+
require 'aspera/ascp/products'
|
12
|
+
require 'aspera/transfer/error_info'
|
13
|
+
require 'aspera/transfer/parameters'
|
14
|
+
require 'aspera/transfer/spec'
|
15
15
|
require 'aspera/keychain/encrypted_hash'
|
16
16
|
require 'aspera/keychain/macos_security'
|
17
17
|
require 'aspera/proxy_auto_config'
|
@@ -19,9 +19,12 @@ 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'
|
27
|
+
require 'aspera/oauth'
|
25
28
|
require 'open3'
|
26
29
|
require 'date'
|
27
30
|
require 'erb'
|
@@ -30,31 +33,31 @@ module Aspera
|
|
30
33
|
module Cli
|
31
34
|
module Plugins
|
32
35
|
# manage the CLI config file
|
33
|
-
class Config <
|
36
|
+
class Config < Cli::Plugin
|
34
37
|
# folder in $HOME for application files (config, cache)
|
35
38
|
ASPERA_HOME_FOLDER_NAME = '.aspera'
|
36
39
|
# default config file
|
37
40
|
DEFAULT_CONFIG_FILENAME = 'config.yaml'
|
41
|
+
DEFAULT_VAULT_FILENAME = 'vault.bin'
|
38
42
|
# reserved preset names
|
39
43
|
CONF_PRESET_CONFIG = 'config'
|
40
44
|
CONF_PRESET_VERSION = 'version'
|
41
|
-
|
45
|
+
CONF_PRESET_DEFAULTS = 'default'
|
42
46
|
CONF_PRESET_GLOBAL = 'global_common_defaults'
|
47
|
+
# special name to identify value of default
|
43
48
|
GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
|
44
|
-
CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
|
45
49
|
CONF_GLOBAL_SYM = :config
|
46
50
|
# folder containing custom plugins in user's config folder
|
47
51
|
ASPERA_PLUGINS_FOLDERNAME = 'plugins'
|
48
52
|
PERSISTENCY_FOLDER = 'persist_store'
|
49
|
-
RUBY_FILE_EXT = '.rb'
|
50
53
|
ASPERA = 'aspera'
|
51
54
|
SERVER_COMMAND = 'server'
|
52
55
|
APP_NAME_SDK = 'sdk'
|
53
56
|
CONNECT_WEB_URL = 'https://d3gcli72yxqn2z.cloudfront.net/connect'
|
54
57
|
CONNECT_VERSIONS = 'connectversions.js' # cspell: disable-line
|
55
58
|
TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/aspera_transfer_sdk'
|
56
|
-
|
57
|
-
|
59
|
+
DEMO_SERVER = 'demo'
|
60
|
+
DEMO_PRESET = 'demoserver' # cspell: disable-line
|
58
61
|
EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
|
59
62
|
From: <%=from_name%> <<%=from_email%>>
|
60
63
|
To: <<%=to%>>
|
@@ -77,14 +80,13 @@ module Aspera
|
|
77
80
|
private_constant :DEFAULT_CONFIG_FILENAME,
|
78
81
|
:CONF_PRESET_CONFIG,
|
79
82
|
:CONF_PRESET_VERSION,
|
80
|
-
:
|
83
|
+
:CONF_PRESET_DEFAULTS,
|
81
84
|
:CONF_PRESET_GLOBAL,
|
82
85
|
:ASPERA_PLUGINS_FOLDERNAME,
|
83
|
-
:RUBY_FILE_EXT,
|
84
86
|
:ASPERA,
|
85
|
-
:DEMO,
|
86
87
|
:TRANSFER_SDK_ARCHIVE_URL,
|
87
|
-
:
|
88
|
+
:DEMO_SERVER,
|
89
|
+
:DEMO_PRESET,
|
88
90
|
:EMAIL_TEST_TEMPLATE,
|
89
91
|
:EXTEND_PRESET,
|
90
92
|
:EXTEND_VAULT,
|
@@ -114,23 +116,11 @@ module Aspera
|
|
114
116
|
File.dirname(File.expand_path(__FILE__))
|
115
117
|
end
|
116
118
|
|
117
|
-
# name of englobing module
|
118
|
-
# @return "Aspera::Cli::Plugins"
|
119
|
-
def module_full_name
|
120
|
-
return Module.nesting[2].to_s
|
121
|
-
end
|
122
|
-
|
123
119
|
# @return main folder where code is, i.e. .../lib
|
124
120
|
# go up as many times as englobing modules (not counting class, as it is a file)
|
125
121
|
def gem_src_root
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
# instantiate a plugin
|
130
|
-
# plugins must be Capitalized
|
131
|
-
def plugin_class(plugin_name_sym)
|
132
|
-
# Module.nesting[2] is Aspera::Cli::Plugins
|
133
|
-
return Object.const_get("#{module_full_name}::#{plugin_name_sym.to_s.capitalize}")
|
122
|
+
# Module.nesting[2] is Cli::Plugins
|
123
|
+
File.expand_path(Module.nesting[2].to_s.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
|
134
124
|
end
|
135
125
|
|
136
126
|
# deep clone hash so that it does not get modified in case of display and secret hide
|
@@ -141,25 +131,25 @@ module Aspera
|
|
141
131
|
# return product family folder (~/.aspera)
|
142
132
|
def module_family_folder
|
143
133
|
user_home_folder = Dir.home
|
144
|
-
|
134
|
+
Aspera.assert(Dir.exist?(user_home_folder), exception_class: Cli::Error){"Home folder does not exist: #{user_home_folder}. Check your user environment."}
|
145
135
|
return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
|
146
136
|
end
|
147
137
|
|
148
138
|
# return product config folder (~/.aspera/<name>)
|
149
139
|
def default_app_main_folder(app_name:)
|
150
|
-
|
140
|
+
Aspera.assert_type(app_name, String)
|
141
|
+
Aspera.assert(!app_name.empty?)
|
151
142
|
return File.join(module_family_folder, app_name)
|
152
143
|
end
|
153
144
|
end # self
|
154
145
|
|
155
|
-
def initialize(env
|
156
|
-
raise 'Internal Error: env and params must be Hash' unless env.is_a?(Hash) && params.is_a?(Hash)
|
157
|
-
raise 'Internal Error: missing param' unless %i[gem help name version].eql?(params.keys.sort)
|
146
|
+
def initialize(gem:, name:, help:, version:, **env)
|
158
147
|
# we need to defer parsing of options until we have the config file, so we can use @extend with @preset
|
159
|
-
super(env)
|
160
|
-
@
|
161
|
-
@
|
162
|
-
@
|
148
|
+
super(**env)
|
149
|
+
@gem = gem
|
150
|
+
@name = name
|
151
|
+
@help = help
|
152
|
+
@version = version
|
163
153
|
@use_plugin_defaults = true
|
164
154
|
@config_presets = nil
|
165
155
|
@config_checksum_on_disk = nil
|
@@ -168,6 +158,7 @@ module Aspera
|
|
168
158
|
@pac_exec = nil
|
169
159
|
@sdk_default_location = false
|
170
160
|
@option_insecure = false
|
161
|
+
@option_warn_insecure_cert = true
|
171
162
|
@option_ignore_cert_host_port = []
|
172
163
|
@option_http_options = {}
|
173
164
|
@ssl_warned_urls = []
|
@@ -175,7 +166,9 @@ module Aspera
|
|
175
166
|
@proxy_credentials = nil
|
176
167
|
@main_folder = nil
|
177
168
|
@option_config_file = nil
|
169
|
+
# store is used for ruby https
|
178
170
|
@certificate_store = nil
|
171
|
+
# paths are used for ascp
|
179
172
|
@certificate_paths = nil
|
180
173
|
@progress_bar = nil
|
181
174
|
# option to set main folder
|
@@ -183,14 +176,14 @@ module Aspera
|
|
183
176
|
:home, 'Home folder for tool',
|
184
177
|
handler: {o: self, m: :main_folder},
|
185
178
|
types: String,
|
186
|
-
default: self.class.default_app_main_folder(app_name: @
|
179
|
+
default: self.class.default_app_main_folder(app_name: @name))
|
187
180
|
options.parse_options!
|
188
|
-
Log.log.debug{"#{@
|
189
|
-
# data persistency manager
|
190
|
-
|
181
|
+
Log.log.debug{"#{@name} folder: #{@main_folder}"}
|
182
|
+
# data persistency manager, created by config plugin
|
183
|
+
@persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
191
184
|
# set folders for plugin lookup
|
192
|
-
|
193
|
-
|
185
|
+
PluginFactory.instance.add_lookup_folder(self.class.gem_plugins_folder)
|
186
|
+
PluginFactory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
194
187
|
# option to set config file
|
195
188
|
options.declare(
|
196
189
|
:config_file, 'Path to YAML file with preset configuration',
|
@@ -222,10 +215,10 @@ module Aspera
|
|
222
215
|
options.declare(:test_mode, 'Wizard: skip private key check step', values: :bool, default: false)
|
223
216
|
options.declare(:key_path, 'Wizard: path to private key for JWT')
|
224
217
|
# Transfer SDK options
|
225
|
-
options.declare(:ascp_path, 'Path to ascp', handler: {o:
|
218
|
+
options.declare(:ascp_path, 'Path to ascp', handler: {o: Ascp::Installation.instance, m: :ascp_path})
|
226
219
|
options.declare(:use_product, 'Use ascp from specified product', handler: {o: self, m: :option_use_product})
|
227
220
|
options.declare(:sdk_url, 'URL to get SDK', default: TRANSFER_SDK_ARCHIVE_URL)
|
228
|
-
options.declare(:sdk_folder, 'SDK folder path', handler: {o:
|
221
|
+
options.declare(:sdk_folder, 'SDK folder path', handler: {o: Ascp::Installation.instance, m: :sdk_folder})
|
229
222
|
options.declare(:progress_bar, 'Display progress bar', values: :bool, default: Environment.terminal?)
|
230
223
|
# email options
|
231
224
|
options.declare(:smtp, 'SMTP configuration', types: Hash)
|
@@ -233,16 +226,17 @@ module Aspera
|
|
233
226
|
options.declare(:notify_template, 'Email ERB template for notification of transfers')
|
234
227
|
# HTTP options
|
235
228
|
options.declare(:insecure, 'Do not validate any HTTPS certificate', values: :bool, handler: {o: self, m: :option_insecure}, default: :no)
|
236
|
-
options.declare(:ignore_certificate, '
|
229
|
+
options.declare(:ignore_certificate, 'Do not validate HTTPS certificate for these URLs', types: Array, handler: {o: self, m: :option_ignore_cert_host_port})
|
230
|
+
options.declare(:silent_insecure, 'Issue a warning if certificate is ignored', values: :bool, handler: {o: self, m: :option_warn_insecure_cert}, default: :yes)
|
237
231
|
options.declare(:cert_stores, 'List of folder with trusted certificates', types: [Array, String], handler: {o: self, m: :trusted_cert_locations})
|
238
232
|
options.declare(:http_options, 'Options for HTTP/S socket', types: Hash, handler: {o: self, m: :option_http_options}, default: {})
|
239
|
-
options.declare(:cache_tokens, 'Save and reuse
|
233
|
+
options.declare(:cache_tokens, 'Save and reuse OAuth tokens', values: :bool, handler: {o: self, m: :option_cache_tokens})
|
240
234
|
options.declare(:fpac, 'Proxy auto configuration script')
|
241
235
|
options.declare(:proxy_credentials, 'HTTP proxy credentials (user and password)', types: Array)
|
242
236
|
options.parse_options!
|
243
237
|
@progress_bar = TransferProgress.new if options.get_option(:progress_bar)
|
244
238
|
# Check SDK folder is set or not, for compatibility, we check in two places
|
245
|
-
sdk_folder =
|
239
|
+
sdk_folder = Ascp::Installation.instance.sdk_folder rescue nil
|
246
240
|
if sdk_folder.nil?
|
247
241
|
@sdk_default_location = true
|
248
242
|
Log.log.debug('SDK folder is not set, checking default')
|
@@ -252,19 +246,19 @@ module Aspera
|
|
252
246
|
if !Dir.exist?(sdk_folder)
|
253
247
|
Log.log.debug{"not exists: #{sdk_folder}"}
|
254
248
|
# former location
|
255
|
-
former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: @
|
249
|
+
former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: @name), APP_NAME_SDK)
|
256
250
|
Log.log.debug{"checking: #{former_sdk_folder}"}
|
257
251
|
sdk_folder = former_sdk_folder if Dir.exist?(former_sdk_folder)
|
258
252
|
end
|
259
253
|
Log.log.debug{"using: #{sdk_folder}"}
|
260
|
-
|
254
|
+
Ascp::Installation.instance.sdk_folder = sdk_folder
|
261
255
|
end
|
262
256
|
pac_script = options.get_option(:fpac)
|
263
257
|
# create PAC executor
|
264
|
-
@pac_exec =
|
258
|
+
@pac_exec = ProxyAutoConfig.new(pac_script).register_uri_generic unless pac_script.nil?
|
265
259
|
proxy_user_pass = options.get_option(:proxy_credentials)
|
266
260
|
if !proxy_user_pass.nil?
|
267
|
-
|
261
|
+
Aspera.assert(proxy_user_pass.length.eql?(2), exception_class: Cli::BadArgument){"proxy_credentials shall have two elements (#{proxy_user_pass.length})"}
|
268
262
|
@proxy_credentials = {user: proxy_user_pass[0], pass: proxy_user_pass[1]}
|
269
263
|
@pac_exec.proxy_user = @proxy_credentials[:user]
|
270
264
|
@pac_exec.proxy_pass = @proxy_credentials[:pass]
|
@@ -273,34 +267,36 @@ module Aspera
|
|
273
267
|
user_agent: PROGRAM_NAME,
|
274
268
|
session_cb: lambda{|http_session|update_http_session(http_session)},
|
275
269
|
progress_bar: @progress_bar)
|
276
|
-
|
277
|
-
|
278
|
-
|
270
|
+
OAuth::Factory.instance.persist_mgr = persistency if @option_cache_tokens
|
271
|
+
Transfer::Parameters.file_list_folder = File.join(@main_folder, 'filelists') # cspell: disable-line
|
272
|
+
RestErrorAnalyzer.instance.log_file = File.join(@main_folder, 'rest_exceptions.log')
|
279
273
|
# register aspera REST call error handlers
|
280
|
-
|
274
|
+
RestErrorsAspera.register_handlers
|
281
275
|
end
|
282
276
|
|
283
|
-
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_http_options
|
277
|
+
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_warn_insecure_cert, :option_http_options
|
284
278
|
attr_reader :option_ignore_cert_host_port, :progress_bar
|
285
279
|
|
280
|
+
# add files, folders or default locations to the certificate store
|
286
281
|
def trusted_cert_locations=(path_list)
|
287
282
|
path_list = [path_list] unless path_list.is_a?(Array)
|
283
|
+
Aspera.assert_type(path_list, Array){'cert locations'}
|
288
284
|
if @certificate_store.nil?
|
289
285
|
Log.log.debug('Creating SSL Cert store')
|
290
286
|
@certificate_store = OpenSSL::X509::Store.new
|
291
|
-
@certificate_store.set_default_paths
|
292
287
|
@certificate_paths = []
|
293
288
|
end
|
294
289
|
|
295
290
|
path_list.each do |path|
|
296
|
-
|
297
|
-
|
291
|
+
Aspera.assert_type(path, String){'Expecting a String for certificate location'}
|
292
|
+
paths_to_add = [path]
|
293
|
+
Log.log.debug{"Adding cert location: #{path}"}
|
298
294
|
if path.eql?(ExtendedValue::DEF)
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
295
|
+
@certificate_store.set_default_paths
|
296
|
+
paths_to_add = [
|
297
|
+
OpenSSL::X509::DEFAULT_CERT_DIR,
|
298
|
+
OpenSSL::X509::DEFAULT_CERT_FILE
|
299
|
+
].select{|f|File.exist?(f)}
|
304
300
|
elsif File.file?(path)
|
305
301
|
@certificate_store.add_file(path)
|
306
302
|
elsif File.directory?(path)
|
@@ -308,39 +304,57 @@ module Aspera
|
|
308
304
|
else
|
309
305
|
raise "No such file or folder: #{path}"
|
310
306
|
end
|
311
|
-
|
307
|
+
paths_to_add.each do |p|
|
308
|
+
pp = [File.realpath(p)]
|
309
|
+
if File.directory?(p)
|
310
|
+
pp = Dir.entries(p)
|
311
|
+
.map{|e|File.realpath(File.join(p, e))}
|
312
|
+
.select{|entry|File.file?(entry)}
|
313
|
+
end
|
314
|
+
@certificate_paths.concat(pp)
|
315
|
+
end
|
312
316
|
end
|
317
|
+
@certificate_paths.uniq!
|
313
318
|
end
|
314
319
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
+
# returns only files
|
321
|
+
def trusted_cert_locations
|
322
|
+
locations = @certificate_paths
|
323
|
+
if locations.nil?
|
324
|
+
# compute default locations
|
325
|
+
self.trusted_cert_locations = ExtendedValue::DEF
|
326
|
+
locations = @certificate_paths
|
327
|
+
# restore defaults
|
328
|
+
@certificate_paths = @certificate_store = nil
|
320
329
|
end
|
321
|
-
locations = locations.select{|f|File.file?(f)} if files_only
|
322
330
|
return locations
|
323
331
|
end
|
324
332
|
|
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
|
@@ -370,7 +384,7 @@ module Aspera
|
|
370
384
|
def check_gem_version
|
371
385
|
latest_version =
|
372
386
|
begin
|
373
|
-
Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{@
|
387
|
+
Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{@gem}/latest.json")[:data]['version']
|
374
388
|
rescue StandardError
|
375
389
|
Log.log.warn('Could not retrieve latest gem version on rubygems.')
|
376
390
|
'0'
|
@@ -382,10 +396,10 @@ module Aspera
|
|
382
396
|
end
|
383
397
|
end
|
384
398
|
return {
|
385
|
-
name: @
|
386
|
-
current:
|
399
|
+
name: @gem,
|
400
|
+
current: Cli::VERSION,
|
387
401
|
latest: latest_version,
|
388
|
-
need_update: Gem::Version.new(
|
402
|
+
need_update: Gem::Version.new(Cli::VERSION) < Gem::Version.new(latest_version)
|
389
403
|
}
|
390
404
|
end
|
391
405
|
|
@@ -418,8 +432,8 @@ module Aspera
|
|
418
432
|
# retrieve structure from cloud (CDN) with all versions available
|
419
433
|
def connect_versions
|
420
434
|
if @connect_versions.nil?
|
421
|
-
api_connect_cdn = Rest.new(
|
422
|
-
javascript = api_connect_cdn.call(
|
435
|
+
api_connect_cdn = Rest.new(base_url: CONNECT_WEB_URL)
|
436
|
+
javascript = api_connect_cdn.call(operation: 'GET', subpath: CONNECT_VERSIONS)
|
423
437
|
# get result on one line
|
424
438
|
connect_versions_javascript = javascript[:http].body.gsub(/\r?\n\s*/, '')
|
425
439
|
Log.log.debug{"javascript=[\n#{connect_versions_javascript}\n]"}
|
@@ -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
|
+
Aspera.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
|
+
Aspera.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}"}
|
@@ -482,7 +496,7 @@ module Aspera
|
|
482
496
|
end
|
483
497
|
|
484
498
|
# $HOME/.aspera/`program_name`
|
485
|
-
attr_reader :gem_url
|
499
|
+
attr_reader :gem_url
|
486
500
|
attr_accessor :option_config_file
|
487
501
|
|
488
502
|
# @return the hash from name (also expands possible includes)
|
@@ -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
|
+
Aspera.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?
|
@@ -503,7 +517,7 @@ module Aspera
|
|
503
517
|
end
|
504
518
|
|
505
519
|
def option_use_product=(value)
|
506
|
-
|
520
|
+
Ascp::Installation.instance.use_ascp_from_product(value)
|
507
521
|
end
|
508
522
|
|
509
523
|
def option_use_product
|
@@ -511,15 +525,14 @@ module Aspera
|
|
511
525
|
end
|
512
526
|
|
513
527
|
def option_plugin_folder=(value)
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
end
|
528
|
+
Aspera.assert_values(value.class, [String, Array]){'plugin folder'}
|
529
|
+
value = [value] if value.is_a?(String)
|
530
|
+
Aspera.assert(value.all?(String)){'plugin folder'}
|
531
|
+
value.each{|f|PluginFactory.instance.add_lookup_folder(f)}
|
519
532
|
end
|
520
533
|
|
521
534
|
def option_plugin_folder
|
522
|
-
return
|
535
|
+
return PluginFactory.instance.lookup_folders
|
523
536
|
end
|
524
537
|
|
525
538
|
def option_preset; 'write-only option'; end
|
@@ -558,18 +571,18 @@ module Aspera
|
|
558
571
|
end
|
559
572
|
files_to_copy = []
|
560
573
|
Log.log.debug{Log.dump('Available_presets', @config_presets)}
|
561
|
-
|
574
|
+
Aspera.assert_type(@config_presets, Hash){'config file YAML'}
|
562
575
|
# check there is at least the config section
|
563
|
-
|
576
|
+
Aspera.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
|
-
@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = @
|
585
|
+
@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = @version
|
573
586
|
unless files_to_copy.empty?
|
574
587
|
Log.log.warn('Copying referenced files')
|
575
588
|
files_to_copy.each do |file|
|
@@ -584,7 +597,7 @@ module Aspera
|
|
584
597
|
Log.log.debug{"-> #{e.class.name} : #{e}"}
|
585
598
|
if File.exist?(@option_config_file)
|
586
599
|
# then there is a problem with that file.
|
587
|
-
new_name = "#{@option_config_file}.pre#{@
|
600
|
+
new_name = "#{@option_config_file}.pre#{@version}.manual_conversion_needed"
|
588
601
|
File.rename(@option_config_file, new_name)
|
589
602
|
Log.log.warn{"Renamed config file to #{new_name}."}
|
590
603
|
Log.log.warn('Manual Conversion is required. Next time, a new empty file will be created.')
|
@@ -592,33 +605,6 @@ module Aspera
|
|
592
605
|
raise Cli::Error, e.to_s
|
593
606
|
end
|
594
607
|
|
595
|
-
# find plugins in defined paths
|
596
|
-
def add_plugins_from_lookup_folders
|
597
|
-
@plugin_lookup_folders.each do |folder|
|
598
|
-
next unless File.directory?(folder)
|
599
|
-
# TODO: add gem root to load path ? and require short folder ?
|
600
|
-
# $LOAD_PATH.push(folder) if i[:add_path]
|
601
|
-
Dir.entries(folder).select{|file|file.end_with?(RUBY_FILE_EXT)}.each do |source|
|
602
|
-
add_plugin_info(File.join(folder, source))
|
603
|
-
end
|
604
|
-
end
|
605
|
-
end
|
606
|
-
|
607
|
-
def add_plugin_lookup_folder(folder)
|
608
|
-
@plugin_lookup_folders.unshift(folder)
|
609
|
-
end
|
610
|
-
|
611
|
-
def add_plugin_info(path)
|
612
|
-
raise "ERROR: plugin path must end with #{RUBY_FILE_EXT}" if !path.end_with?(RUBY_FILE_EXT)
|
613
|
-
plugin_symbol = File.basename(path, RUBY_FILE_EXT).to_sym
|
614
|
-
req = path.gsub(/#{RUBY_FILE_EXT}$/o, '')
|
615
|
-
if @plugins.key?(plugin_symbol)
|
616
|
-
Log.log.warn{"skipping plugin already registered: #{plugin_symbol}"}
|
617
|
-
return
|
618
|
-
end
|
619
|
-
@plugins[plugin_symbol] = {source: path, require_stanza: req}
|
620
|
-
end
|
621
|
-
|
622
608
|
# Find a plugin, and issue the "require"
|
623
609
|
# @return [Hash] plugin info: { product: , url:, version: }
|
624
610
|
def identify_plugins_for_url
|
@@ -626,33 +612,36 @@ module Aspera
|
|
626
612
|
check_only = options.get_next_argument('plugin name', mandatory: false)
|
627
613
|
check_only = check_only.to_sym unless check_only.nil?
|
628
614
|
found_apps = []
|
629
|
-
|
615
|
+
my_self_plugin_sym = self.class.name.split('::').last.downcase.to_sym
|
616
|
+
PluginFactory.instance.plugins.each do |plugin_name_sym, plugin_info|
|
630
617
|
# no detection for internal plugin
|
631
|
-
next if plugin_name_sym.eql?(
|
618
|
+
next if plugin_name_sym.eql?(my_self_plugin_sym)
|
632
619
|
next if check_only && !check_only.eql?(plugin_name_sym)
|
633
620
|
# load plugin class
|
634
621
|
require plugin_info[:require_stanza]
|
635
|
-
detect_plugin_class =
|
622
|
+
detect_plugin_class = PluginFactory.plugin_class(plugin_name_sym)
|
636
623
|
# requires detection method
|
637
624
|
next unless detect_plugin_class.respond_to?(:detect)
|
638
625
|
detection_info = nil
|
639
626
|
begin
|
627
|
+
Log.log.debug{"detecting #{plugin_name_sym} at #{app_url}"}
|
640
628
|
detection_info = detect_plugin_class.detect(app_url)
|
641
629
|
rescue OpenSSL::SSL::SSLError => e
|
642
630
|
Log.log.warn(e.message)
|
643
631
|
Log.log.warn('Use option --insecure=yes to allow unchecked certificate') if e.message.include?('cert')
|
644
632
|
rescue StandardError => e
|
645
|
-
Log.log.debug{"detect error: #{e}"}
|
633
|
+
Log.log.debug{"detect error: [#{e.class}] #{e}"}
|
646
634
|
next
|
647
635
|
end
|
648
636
|
next if detection_info.nil?
|
649
|
-
|
637
|
+
Aspera.assert_type(detection_info, Hash)
|
638
|
+
Aspera.assert_type(detection_info[:url], String) if detection_info.key?(:url)
|
650
639
|
app_name = detect_plugin_class.respond_to?(:application_name) ? detect_plugin_class.application_name : detect_plugin_class.name.split('::').last
|
651
640
|
# if there is a redirect, then the detector can override the url.
|
652
641
|
found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
|
653
642
|
end # loop
|
654
643
|
raise "No known application found at #{app_url}" if found_apps.empty?
|
655
|
-
|
644
|
+
Aspera.assert(found_apps.all?{|a|a.keys.all?(Symbol)})
|
656
645
|
return found_apps
|
657
646
|
end
|
658
647
|
|
@@ -666,10 +655,10 @@ module Aspera
|
|
666
655
|
case command
|
667
656
|
when :list
|
668
657
|
return {type: :object_list, data: connect_versions, fields: %w[id title version]}
|
669
|
-
when :info
|
658
|
+
when :info
|
670
659
|
one_res.delete('links')
|
671
660
|
return {type: :single_object, data: one_res}
|
672
|
-
when :version
|
661
|
+
when :version
|
673
662
|
all_links = one_res['links']
|
674
663
|
command = options.get_next_command(%i[list download open])
|
675
664
|
if %i[download open].include?(command)
|
@@ -678,15 +667,15 @@ module Aspera
|
|
678
667
|
raise 'no such value' if one_link.nil?
|
679
668
|
end
|
680
669
|
case command
|
681
|
-
when :list
|
670
|
+
when :list
|
682
671
|
return {type: :object_list, data: all_links}
|
683
672
|
when :download
|
684
|
-
folder_dest = transfer.destination_folder(
|
673
|
+
folder_dest = transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE)
|
685
674
|
# folder_dest=self.options.get_next_argument('destination folder')
|
686
|
-
api_connect_cdn = Rest.new(
|
675
|
+
api_connect_cdn = Rest.new(base_url: CONNECT_WEB_URL)
|
687
676
|
file_url = one_link['href']
|
688
677
|
filename = file_url.gsub(%r{.*/}, '')
|
689
|
-
api_connect_cdn.call(
|
678
|
+
api_connect_cdn.call(operation: 'GET', subpath: file_url, save_to_file: File.join(folder_dest, filename))
|
690
679
|
return Main.result_status("Downloaded: #{filename}")
|
691
680
|
when :open
|
692
681
|
OpenApplication.instance.uri(one_link['href'])
|
@@ -702,79 +691,47 @@ module Aspera
|
|
702
691
|
return execute_connect_action
|
703
692
|
when :use
|
704
693
|
ascp_path = options.get_next_argument('path to ascp')
|
705
|
-
ascp_version =
|
694
|
+
ascp_version = Ascp::Installation.instance.get_ascp_version(ascp_path)
|
706
695
|
formatter.display_status("ascp version: #{ascp_version}")
|
707
696
|
set_global_default(:ascp_path, ascp_path)
|
708
697
|
return Main.result_nothing
|
709
|
-
when :show
|
710
|
-
return {type: :status, data:
|
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
|
698
|
+
when :show
|
699
|
+
return {type: :status, data: Ascp::Installation.instance.path(:ascp)}
|
700
|
+
when :info
|
701
|
+
# collect info from ascp executable
|
702
|
+
data = Ascp::Installation.instance.ascp_info
|
703
|
+
# add command line transfer spec
|
751
704
|
data['ts'] = transfer.updated_ts
|
705
|
+
# add keys
|
706
|
+
DataRepository::ELEMENTS.each_with_object(data){|i, h|h[i.to_s] = DataRepository.instance.item(i)}
|
707
|
+
# declare those as secrets
|
708
|
+
SecretHider::ADDITIONAL_KEYS_TO_HIDE.push(*DataRepository::ELEMENTS.map(&:to_s))
|
752
709
|
return {type: :single_object, data: data}
|
753
710
|
when :products
|
754
711
|
command = options.get_next_command(%i[list use])
|
755
712
|
case command
|
756
713
|
when :list
|
757
|
-
return {type: :object_list, data:
|
714
|
+
return {type: :object_list, data: Ascp::Products.installed_products, fields: %w[name app_root]}
|
758
715
|
when :use
|
759
716
|
default_product = options.get_next_argument('product name')
|
760
|
-
|
761
|
-
set_global_default(:ascp_path,
|
717
|
+
Ascp::Installation.instance.use_ascp_from_product(default_product)
|
718
|
+
set_global_default(:ascp_path, Ascp::Installation.instance.path(:ascp))
|
762
719
|
return Main.result_nothing
|
763
720
|
end
|
764
721
|
when :install
|
765
722
|
# reset to default location, if older default was used
|
766
|
-
|
767
|
-
v =
|
723
|
+
Ascp::Installation.instance.sdk_folder = self.class.default_app_main_folder(app_name: APP_NAME_SDK) if @sdk_default_location
|
724
|
+
v = Ascp::Installation.instance.install_sdk(options.get_option(:sdk_url, mandatory: true))
|
768
725
|
return Main.result_status("Installed version #{v}")
|
769
726
|
when :spec
|
770
727
|
return {
|
771
728
|
type: :object_list,
|
772
|
-
data:
|
773
|
-
fields: [%w[name type],
|
729
|
+
data: Transfer::Parameters.man_table,
|
730
|
+
fields: [%w[name type], Transfer::Parameters::SUPPORTED_AGENTS_SHORT.map(&:to_s), %w[description]].flatten.freeze
|
774
731
|
}
|
775
732
|
when :errors
|
776
733
|
error_data = []
|
777
|
-
|
734
|
+
Transfer::ERROR_INFO.each_pair do |code, prop|
|
778
735
|
error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
|
779
736
|
end
|
780
737
|
return {type: :object_list, data: error_data}
|
@@ -887,6 +844,7 @@ module Aspera
|
|
887
844
|
open
|
888
845
|
documentation
|
889
846
|
genkey
|
847
|
+
pubkey
|
890
848
|
remote_certificate
|
891
849
|
gem
|
892
850
|
plugins
|
@@ -918,36 +876,45 @@ module Aspera
|
|
918
876
|
when :documentation
|
919
877
|
section = options.get_next_argument('private key file path', mandatory: false)
|
920
878
|
section = "##{section}" unless section.nil?
|
921
|
-
OpenApplication.instance.uri("#{@
|
879
|
+
OpenApplication.instance.uri("#{@help}#{section}")
|
922
880
|
return Main.result_nothing
|
923
881
|
when :genkey # generate new rsa key
|
924
882
|
private_key_path = options.get_next_argument('private key file path')
|
925
883
|
private_key_length = options.get_next_argument('size in bits', mandatory: false, type: Integer, default: DEFAULT_PRIV_KEY_LENGTH)
|
926
884
|
self.class.generate_rsa_private_key(path: private_key_path, length: private_key_length)
|
927
885
|
return Main.result_status("Generated #{private_key_length} bit RSA key: #{private_key_path}")
|
886
|
+
when :pubkey # get pub key
|
887
|
+
private_key_pem = options.get_next_argument('private key PEM value')
|
888
|
+
return Main.result_status(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
|
928
889
|
when :remote_certificate
|
890
|
+
cert_action = options.get_next_command(%i[chain only name])
|
929
891
|
remote_url = options.get_next_argument('remote URL')
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
892
|
+
remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
|
893
|
+
raise "No certificate found for #{remote_url}" unless remote_chain&.first
|
894
|
+
case cert_action
|
895
|
+
when :chain
|
896
|
+
return Main.result_status(remote_chain.map(&:to_pem).join("\n"))
|
897
|
+
when :only
|
898
|
+
return Main.result_status(remote_chain.first.to_pem)
|
899
|
+
when :name
|
900
|
+
return Main.result_status(remote_chain.first.subject.to_a.find { |name, _, _| name == 'CN' }[1])
|
901
|
+
end
|
935
902
|
when :echo # display the content of a value given on command line
|
936
903
|
return Formatter.auto_type(options.get_next_argument('value'))
|
937
904
|
when :flush_tokens
|
938
|
-
deleted_files =
|
905
|
+
deleted_files = OAuth::Factory.instance.flush_tokens
|
939
906
|
return {type: :value_list, data: deleted_files, name: 'file'}
|
940
907
|
when :plugins
|
941
908
|
case options.get_next_command(%i[list create])
|
942
909
|
when :list
|
943
910
|
result = []
|
944
|
-
|
911
|
+
PluginFactory.instance.plugins.each do |name, info|
|
945
912
|
require info[:require_stanza]
|
946
|
-
|
913
|
+
plugin_klass = PluginFactory.plugin_class(name)
|
947
914
|
result.push({
|
948
915
|
plugin: name,
|
949
|
-
detect: Formatter.tick(
|
950
|
-
wizard: Formatter.tick(
|
916
|
+
detect: Formatter.tick(plugin_klass.respond_to?(:detect)),
|
917
|
+
wizard: Formatter.tick(plugin_klass.respond_to?(:wizard)),
|
951
918
|
path: info[:source]
|
952
919
|
})
|
953
920
|
end
|
@@ -984,8 +951,7 @@ module Aspera
|
|
984
951
|
return wizard_find(apps)
|
985
952
|
when :coffee
|
986
953
|
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))
|
954
|
+
return Main.result_picture_in_terminal(options, Rest.new(base_url: COFFEE_IMAGE).read('')[:http].body)
|
989
955
|
end
|
990
956
|
OpenApplication.instance.uri(COFFEE_IMAGE)
|
991
957
|
return Main.result_nothing
|
@@ -994,8 +960,8 @@ module Aspera
|
|
994
960
|
when :gem
|
995
961
|
case options.get_next_command(%i[path version name])
|
996
962
|
when :path then return Main.result_status(self.class.gem_src_root)
|
997
|
-
when :version then return Main.result_status(
|
998
|
-
when :name then return Main.result_status(@
|
963
|
+
when :version then return Main.result_status(Cli::VERSION)
|
964
|
+
when :name then return Main.result_status(@gem)
|
999
965
|
end
|
1000
966
|
when :folder
|
1001
967
|
return Main.result_status(@main_folder)
|
@@ -1014,24 +980,24 @@ module Aspera
|
|
1014
980
|
when :check_update
|
1015
981
|
return {type: :single_object, data: check_gem_version}
|
1016
982
|
when :initdemo
|
1017
|
-
if @config_presets.key?(
|
1018
|
-
Log.log.warn{"Demo server preset already present: #{
|
983
|
+
if @config_presets.key?(DEMO_PRESET)
|
984
|
+
Log.log.warn{"Demo server preset already present: #{DEMO_PRESET}"}
|
1019
985
|
else
|
1020
|
-
Log.log.info{"Creating Demo server preset: #{
|
1021
|
-
@config_presets[
|
1022
|
-
'url' => "ssh://#{
|
986
|
+
Log.log.info{"Creating Demo server preset: #{DEMO_PRESET}"}
|
987
|
+
@config_presets[DEMO_PRESET] = {
|
988
|
+
'url' => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
|
1023
989
|
'username' => ASPERA,
|
1024
|
-
'ssAP'.downcase.reverse + 'drow'.reverse =>
|
990
|
+
'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
|
1025
991
|
}
|
1026
992
|
end
|
1027
|
-
@config_presets[
|
1028
|
-
if @config_presets[
|
1029
|
-
Log.log.warn{"Server default preset already set to: #{@config_presets[
|
1030
|
-
Log.log.warn{"Use #{
|
1031
|
-
|
993
|
+
@config_presets[CONF_PRESET_DEFAULTS] ||= {}
|
994
|
+
if @config_presets[CONF_PRESET_DEFAULTS].key?(SERVER_COMMAND)
|
995
|
+
Log.log.warn{"Server default preset already set to: #{@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND]}"}
|
996
|
+
Log.log.warn{"Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}"} unless
|
997
|
+
DEMO_PRESET.eql?(@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND])
|
1032
998
|
else
|
1033
|
-
@config_presets[
|
1034
|
-
Log.log.info{"Setting server default preset to : #{
|
999
|
+
@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
|
1000
|
+
Log.log.info{"Setting server default preset to : #{DEMO_PRESET}"}
|
1035
1001
|
end
|
1036
1002
|
return Main.result_status('Done')
|
1037
1003
|
when :vault then execute_vault
|
@@ -1041,9 +1007,9 @@ module Aspera
|
|
1041
1007
|
exception_class_name = options.get_next_argument('exception class name', mandatory: true)
|
1042
1008
|
exception_text = options.get_next_argument('exception text', mandatory: true)
|
1043
1009
|
exception_class = Object.const_get(exception_class_name)
|
1044
|
-
|
1010
|
+
Aspera.assert(exception_class <= Exception){"#{exception_class} is not an exception: #{exception_class.class}"}
|
1045
1011
|
raise exception_class, exception_text
|
1046
|
-
else
|
1012
|
+
else Aspera.error_unreachable_line
|
1047
1013
|
end
|
1048
1014
|
end
|
1049
1015
|
|
@@ -1058,15 +1024,17 @@ module Aspera
|
|
1058
1024
|
apps.find{|a|a[:product].eql?(answer)}
|
1059
1025
|
end
|
1060
1026
|
wiz_url = identification[:url]
|
1061
|
-
Log.log.debug{Log.dump(:identification, identification
|
1027
|
+
Log.log.debug{Log.dump(:identification, identification)}
|
1062
1028
|
formatter.display_status("Using: #{identification[:name]} at #{wiz_url}".bold)
|
1063
1029
|
# set url for instantiation of plugin
|
1064
1030
|
options.add_option_preset({url: wiz_url})
|
1065
1031
|
# instantiate plugin: command line options will be known and wizard can be called
|
1066
|
-
wiz_plugin_class =
|
1067
|
-
|
1032
|
+
wiz_plugin_class = PluginFactory.plugin_class(identification[:product])
|
1033
|
+
Aspera.assert(wiz_plugin_class.respond_to?(:wizard), exception_class: Cli::BadArgument) do
|
1034
|
+
"Detected: #{identification[:product]}, but this application has no wizard"
|
1035
|
+
end
|
1068
1036
|
# instantiate plugin: command line options will be known, e.g. private_key
|
1069
|
-
plugin_instance = wiz_plugin_class.new(
|
1037
|
+
plugin_instance = wiz_plugin_class.new(**init_params)
|
1070
1038
|
wiz_params = {
|
1071
1039
|
object: plugin_instance
|
1072
1040
|
}
|
@@ -1089,7 +1057,7 @@ module Aspera
|
|
1089
1057
|
formatter.display_status('Using existing key:')
|
1090
1058
|
else
|
1091
1059
|
formatter.display_status("Generating #{DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
|
1092
|
-
|
1060
|
+
self.class.generate_rsa_private_key(path: private_key_path)
|
1093
1061
|
formatter.display_status('Created key:')
|
1094
1062
|
end
|
1095
1063
|
formatter.display_status(private_key_path)
|
@@ -1102,7 +1070,7 @@ module Aspera
|
|
1102
1070
|
# finally, call the wizard
|
1103
1071
|
wizard_result = wiz_plugin_class.wizard(**wiz_params)
|
1104
1072
|
Log.log.debug{"wizard result: #{wizard_result}"}
|
1105
|
-
|
1073
|
+
Aspera.assert(WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)){"missing or extra keys in wizard result: #{wizard_result.keys}"}
|
1106
1074
|
# get preset name from user or default
|
1107
1075
|
wiz_preset_name = options.get_option(:id)
|
1108
1076
|
if wiz_preset_name.nil?
|
@@ -1118,22 +1086,22 @@ module Aspera
|
|
1118
1086
|
# Write configuration file
|
1119
1087
|
formatter.display_status("Preparing preset: #{wiz_preset_name}")
|
1120
1088
|
# init defaults if necessary
|
1121
|
-
@config_presets[
|
1089
|
+
@config_presets[CONF_PRESET_DEFAULTS] ||= {}
|
1122
1090
|
option_override = options.get_option(:override, mandatory: true)
|
1123
1091
|
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[
|
1092
|
+
if !option_override && options.get_option(:default, mandatory: true) && @config_presets[CONF_PRESET_DEFAULTS].key?(identification[:product])
|
1125
1093
|
raise Cli::Error, "Preset already exists: #{wiz_preset_name} (use --override=yes or --id=<name>)" \
|
1126
1094
|
if !option_override && @config_presets.key?(wiz_preset_name)
|
1127
1095
|
@config_presets[wiz_preset_name] = wizard_result[:preset_value].stringify_keys
|
1128
1096
|
test_args = wizard_result[:test_args]
|
1129
1097
|
if options.get_option(:default, mandatory: true)
|
1130
1098
|
formatter.display_status("Setting config preset as default for #{identification[:product]}")
|
1131
|
-
@config_presets[
|
1099
|
+
@config_presets[CONF_PRESET_DEFAULTS][identification[:product].to_s] = wiz_preset_name
|
1132
1100
|
else
|
1133
1101
|
test_args = "-P#{wiz_preset_name} #{test_args}"
|
1134
1102
|
end
|
1135
1103
|
# TODO: actually test the command
|
1136
|
-
return Main.result_status("You can test with:\n#{@
|
1104
|
+
return Main.result_status("You can test with:\n#{@name} #{identification[:product]} #{test_args}")
|
1137
1105
|
end
|
1138
1106
|
|
1139
1107
|
# @return [Hash] email server setting with defaults if not defined
|
@@ -1151,11 +1119,11 @@ module Aspera
|
|
1151
1119
|
25
|
1152
1120
|
end
|
1153
1121
|
smtp[:from_email] ||= smtp[:username] if smtp.key?(:username)
|
1154
|
-
smtp[:from_name] ||= smtp[:from_email].
|
1155
|
-
smtp[:domain] ||= smtp[:from_email].
|
1122
|
+
smtp[:from_name] ||= smtp[:from_email].sub(/@.*$/, '').gsub(/[^a-zA-Z]/, ' ').capitalize if smtp.key?(:username)
|
1123
|
+
smtp[:domain] ||= smtp[:from_email].sub(/^.*@/, '') if smtp.key?(:from_email)
|
1156
1124
|
# check minimum required
|
1157
1125
|
%i[server port domain].each do |n|
|
1158
|
-
|
1126
|
+
Aspera.assert(smtp.key?(n)){"Missing mandatory smtp parameter: #{n}"}
|
1159
1127
|
end
|
1160
1128
|
Log.log.debug{"smtp=#{smtp}"}
|
1161
1129
|
return smtp
|
@@ -1169,7 +1137,7 @@ module Aspera
|
|
1169
1137
|
values[:from_name] ||= mail_conf[:from_name]
|
1170
1138
|
values[:from_email] ||= mail_conf[:from_email]
|
1171
1139
|
%i[from_name from_email].each do |n|
|
1172
|
-
|
1140
|
+
Aspera.assert(values.key?(n)){"Missing email parameter: #{n}"}
|
1173
1141
|
end
|
1174
1142
|
start_options = [mail_conf[:domain]]
|
1175
1143
|
start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
|
@@ -1177,7 +1145,7 @@ module Aspera
|
|
1177
1145
|
template_binding = Environment.empty_binding
|
1178
1146
|
# add variables to binding
|
1179
1147
|
values.each do |k, v|
|
1180
|
-
|
1148
|
+
Aspera.assert_type(k, Symbol)
|
1181
1149
|
template_binding.local_variable_set(k, v)
|
1182
1150
|
end
|
1183
1151
|
# execute template
|
@@ -1211,19 +1179,19 @@ module Aspera
|
|
1211
1179
|
# returns [String] name if config_presets has default
|
1212
1180
|
# returns nil if there is no config or bypass default params
|
1213
1181
|
def get_plugin_default_config_name(plugin_name_sym)
|
1214
|
-
|
1182
|
+
Aspera.assert(!@config_presets.nil?){'config_presets shall be defined'}
|
1215
1183
|
if !@use_plugin_defaults
|
1216
1184
|
Log.log.debug('skip default config')
|
1217
1185
|
return nil
|
1218
1186
|
end
|
1219
|
-
if @config_presets.key?(
|
1220
|
-
@config_presets[
|
1221
|
-
default_config_name = @config_presets[
|
1187
|
+
if @config_presets.key?(CONF_PRESET_DEFAULTS) &&
|
1188
|
+
@config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name_sym.to_s)
|
1189
|
+
default_config_name = @config_presets[CONF_PRESET_DEFAULTS][plugin_name_sym.to_s]
|
1222
1190
|
if !@config_presets.key?(default_config_name)
|
1223
1191
|
Log.log.error do
|
1224
1192
|
"Default config name [#{default_config_name}] specified for plugin [#{plugin_name_sym}], but it does not exist in config file.\n" \
|
1225
1193
|
'Please fix the issue: either create preset with one parameter: ' \
|
1226
|
-
"(#{@
|
1194
|
+
"(#{@name} config id #{default_config_name} init @json:'{}') or remove default (#{@name} config id default remove #{plugin_name_sym})."
|
1227
1195
|
end
|
1228
1196
|
end
|
1229
1197
|
raise Cli::Error, "Config name [#{default_config_name}] must be a hash, check config file." if !@config_presets[default_config_name].is_a?(Hash)
|
@@ -1235,16 +1203,17 @@ module Aspera
|
|
1235
1203
|
# TODO: delete: ALLOWED_KEYS = %i[password username description].freeze
|
1236
1204
|
# @return [Hash] result of execution of vault command
|
1237
1205
|
def execute_vault
|
1238
|
-
command = options.get_next_command(%i[list show create delete password])
|
1206
|
+
command = options.get_next_command(%i[info list show create delete password])
|
1239
1207
|
case command
|
1208
|
+
when :info
|
1209
|
+
return {type: :single_object, data: vault_info}
|
1240
1210
|
when :list
|
1241
1211
|
return {type: :object_list, data: vault.list}
|
1242
1212
|
when :show
|
1243
1213
|
return {type: :single_object, data: vault.get(label: options.get_next_argument('label'))}
|
1244
1214
|
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)
|
1215
|
+
label = options.get_next_argument('label', type: String)
|
1216
|
+
info = options.get_next_argument('info', type: Hash)
|
1248
1217
|
info = info.symbolize_keys
|
1249
1218
|
info[:label] = label
|
1250
1219
|
vault.set(info)
|
@@ -1253,7 +1222,7 @@ module Aspera
|
|
1253
1222
|
vault.delete(label: options.get_next_argument('label'))
|
1254
1223
|
return Main.result_status('Password deleted')
|
1255
1224
|
when :password
|
1256
|
-
|
1225
|
+
Aspera.assert(vault.respond_to?(:password=)){'Vault does not support password change'}
|
1257
1226
|
new_password = options.get_next_argument('new_password')
|
1258
1227
|
vault.password = new_password
|
1259
1228
|
vault.save
|
@@ -1263,36 +1232,45 @@ module Aspera
|
|
1263
1232
|
|
1264
1233
|
# @return [String] value from vault matching <name>.<param>
|
1265
1234
|
def vault_value(name)
|
1266
|
-
m = name.
|
1267
|
-
raise 'vault name shall match <name>.<param>'
|
1235
|
+
m = name.split('.')
|
1236
|
+
raise 'vault name shall match <name>.<param>' unless m.length.eql?(2)
|
1268
1237
|
# this raise exception if label not found:
|
1269
|
-
info = vault.get(label: m[
|
1270
|
-
value = info[m[
|
1271
|
-
raise "no such entry value: #{m[
|
1238
|
+
info = vault.get(label: m[0])
|
1239
|
+
value = info[m[1].to_sym]
|
1240
|
+
raise "no such entry value: #{m[1]}" if value.nil?
|
1272
1241
|
return value
|
1273
1242
|
end
|
1274
1243
|
|
1244
|
+
def vault_info
|
1245
|
+
info = options.get_option(:vault) || {}
|
1246
|
+
info = info.symbolize_keys
|
1247
|
+
info[:type] ||= 'file'
|
1248
|
+
info[:name] ||= (info[:type].eql?('file') ? DEFAULT_VAULT_FILENAME : PROGRAM_NAME)
|
1249
|
+
Aspera.assert(info.keys.sort == %i[name type]) {"vault info shall have exactly keys 'type' and 'name'"}
|
1250
|
+
Aspera.assert(info.values.all?(String)){'vault info shall have only string values'}
|
1251
|
+
info[:password] = options.get_option(:vault_password, mandatory: true)
|
1252
|
+
return info
|
1253
|
+
end
|
1254
|
+
|
1275
1255
|
# @return [Object] vault, from options or cache
|
1276
1256
|
def vault
|
1277
1257
|
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
|
1258
|
+
info = vault_info
|
1259
|
+
case info[:type]
|
1283
1260
|
when 'file'
|
1284
1261
|
# absolute_path? introduced in ruby 2.7
|
1285
|
-
|
1286
|
-
|
1262
|
+
@vault = Keychain::EncryptedHash.new(
|
1263
|
+
info[:name].eql?(File.absolute_path(info[:name])) ? info[:name] : File.join(@main_folder, info[:name]),
|
1264
|
+
info[:password])
|
1287
1265
|
when 'system'
|
1288
1266
|
case Environment.os
|
1289
1267
|
when Environment::OS_X
|
1290
|
-
@vault = Keychain::MacosSystem.new(
|
1268
|
+
@vault = Keychain::MacosSystem.new(info[:name], info[:password])
|
1291
1269
|
else
|
1292
1270
|
raise 'not implemented for this OS'
|
1293
1271
|
end
|
1294
1272
|
else
|
1295
|
-
raise Cli::BadArgument, "Unknown vault type: #{
|
1273
|
+
raise Cli::BadArgument, "Unknown vault type: #{info[:type]}"
|
1296
1274
|
end
|
1297
1275
|
end
|
1298
1276
|
raise 'No vault defined' if @vault.nil?
|
@@ -1301,14 +1279,14 @@ module Aspera
|
|
1301
1279
|
|
1302
1280
|
# version of URL without trailing "/" and removing default port
|
1303
1281
|
def canonical_url(url)
|
1304
|
-
url.
|
1282
|
+
url.sub(%r{/+$}, '').sub(%r{^(https://[^/]+):443$}, '\1')
|
1305
1283
|
end
|
1306
1284
|
|
1307
1285
|
def lookup_preset(url:, username:)
|
1308
1286
|
# remove extra info to maximize match
|
1309
1287
|
url = canonical_url(url)
|
1310
1288
|
Log.log.debug{"Lookup preset for #{username}@#{url}"}
|
1311
|
-
@config_presets.
|
1289
|
+
@config_presets.each_value do |v|
|
1312
1290
|
next unless v.is_a?(Hash)
|
1313
1291
|
conf_url = v['url'].is_a?(String) ? canonical_url(v['url']) : nil
|
1314
1292
|
return self.class.protect_presets(v) if conf_url.eql?(url) && v['username'].eql?(username)
|