aspera-cli 4.9.0 → 4.11.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 +20 -0
- data/CHANGELOG.md +509 -0
- data/CONTRIBUTING.md +118 -0
- data/README.md +1241 -916
- data/bin/ascli +4 -4
- data/bin/asession +11 -11
- data/docs/test_env.conf +32 -21
- data/examples/aoc.rb +4 -4
- data/examples/dascli +16 -9
- data/examples/faspex4.rb +8 -8
- data/examples/node.rb +12 -12
- data/examples/server.rb +10 -10
- data/lib/aspera/aoc.rb +273 -266
- data/lib/aspera/ascmd.rb +56 -54
- data/lib/aspera/ats_api.rb +4 -4
- data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
- data/lib/aspera/cli/extended_value.rb +5 -5
- data/lib/aspera/cli/formater.rb +64 -64
- data/lib/aspera/cli/info.rb +2 -2
- data/lib/aspera/cli/listener/line_dump.rb +1 -1
- data/lib/aspera/cli/listener/logger.rb +1 -1
- data/lib/aspera/cli/listener/progress.rb +5 -6
- data/lib/aspera/cli/listener/progress_multi.rb +14 -19
- data/lib/aspera/cli/main.rb +66 -67
- data/lib/aspera/cli/manager.rb +112 -110
- data/lib/aspera/cli/plugin.rb +57 -36
- data/lib/aspera/cli/plugins/alee.rb +4 -4
- data/lib/aspera/cli/plugins/aoc.rb +309 -670
- data/lib/aspera/cli/plugins/ats.rb +44 -46
- data/lib/aspera/cli/plugins/bss.rb +10 -10
- data/lib/aspera/cli/plugins/config.rb +497 -378
- data/lib/aspera/cli/plugins/console.rb +12 -12
- data/lib/aspera/cli/plugins/cos.rb +18 -20
- data/lib/aspera/cli/plugins/faspex.rb +112 -114
- data/lib/aspera/cli/plugins/faspex5.rb +71 -46
- data/lib/aspera/cli/plugins/node.rb +379 -283
- data/lib/aspera/cli/plugins/orchestrator.rb +46 -46
- data/lib/aspera/cli/plugins/preview.rb +122 -114
- data/lib/aspera/cli/plugins/server.rb +137 -83
- data/lib/aspera/cli/plugins/shares.rb +30 -29
- data/lib/aspera/cli/plugins/sync.rb +13 -33
- data/lib/aspera/cli/transfer_agent.rb +60 -59
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +27 -27
- data/lib/aspera/cos_node.rb +22 -20
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +35 -15
- data/lib/aspera/fasp/agent_base.rb +15 -15
- data/lib/aspera/fasp/agent_connect.rb +23 -21
- data/lib/aspera/fasp/agent_direct.rb +66 -64
- data/lib/aspera/fasp/agent_httpgw.rb +141 -78
- data/lib/aspera/fasp/agent_node.rb +23 -21
- data/lib/aspera/fasp/agent_trsdk.rb +20 -20
- data/lib/aspera/fasp/error.rb +3 -2
- data/lib/aspera/fasp/error_info.rb +11 -8
- data/lib/aspera/fasp/installation.rb +79 -79
- data/lib/aspera/fasp/listener.rb +1 -1
- data/lib/aspera/fasp/parameters.rb +86 -71
- data/lib/aspera/fasp/parameters.yaml +7 -4
- data/lib/aspera/fasp/resume_policy.rb +8 -8
- data/lib/aspera/fasp/transfer_spec.rb +35 -2
- data/lib/aspera/fasp/uri.rb +7 -7
- data/lib/aspera/faspex_gw.rb +7 -5
- data/lib/aspera/hash_ext.rb +3 -3
- data/lib/aspera/id_generator.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +38 -105
- data/lib/aspera/keychain/macos_security.rb +128 -57
- data/lib/aspera/log.rb +7 -7
- data/lib/aspera/nagios.rb +19 -18
- data/lib/aspera/node.rb +209 -35
- data/lib/aspera/oauth.rb +37 -36
- data/lib/aspera/open_application.rb +19 -11
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +16 -15
- data/lib/aspera/preview/file_types.rb +8 -8
- data/lib/aspera/preview/generator.rb +67 -67
- data/lib/aspera/preview/utils.rb +27 -27
- data/lib/aspera/proxy_auto_config.js +41 -41
- data/lib/aspera/proxy_auto_config.rb +21 -14
- data/lib/aspera/rest.rb +72 -67
- data/lib/aspera/rest_call_error.rb +2 -1
- data/lib/aspera/rest_error_analyzer.rb +18 -17
- data/lib/aspera/rest_errors_aspera.rb +16 -16
- data/lib/aspera/secret_hider.rb +15 -13
- data/lib/aspera/ssh.rb +11 -10
- data/lib/aspera/sync.rb +158 -44
- data/lib/aspera/temp_file_manager.rb +2 -2
- data/lib/aspera/uri_reader.rb +4 -4
- data/lib/aspera/web_auth.rb +14 -13
- data.tar.gz.sig +0 -0
- metadata +11 -36
- metadata.gz.sig +0 -0
@@ -4,6 +4,7 @@ require 'aspera/cli/basic_auth_plugin'
|
|
4
4
|
require 'aspera/cli/extended_value'
|
5
5
|
require 'aspera/cli/version'
|
6
6
|
require 'aspera/cli/formater'
|
7
|
+
require 'aspera/cli/info'
|
7
8
|
require 'aspera/fasp/installation'
|
8
9
|
require 'aspera/fasp/parameters'
|
9
10
|
require 'aspera/fasp/transfer_spec'
|
@@ -27,7 +28,7 @@ module Aspera
|
|
27
28
|
module Cli
|
28
29
|
module Plugins
|
29
30
|
# manage the CLI config file
|
30
|
-
class Config < Plugin
|
31
|
+
class Config < Aspera::Cli::Plugin
|
31
32
|
# folder in $HOME for application files (config, cache)
|
32
33
|
ASPERA_HOME_FOLDER_NAME = '.aspera'
|
33
34
|
# default config file
|
@@ -37,7 +38,6 @@ module Aspera
|
|
37
38
|
CONF_PRESET_VERSION = 'version'
|
38
39
|
CONF_PRESET_DEFAULT = 'default'
|
39
40
|
CONF_PRESET_GLOBAL = 'global_common_defaults'
|
40
|
-
CONF_PRESET_SECRETS = 'default_secrets' # pragma: allowlist secret
|
41
41
|
CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
|
42
42
|
CONF_GLOBAL_SYM = :config
|
43
43
|
# old tool name
|
@@ -62,24 +62,45 @@ module Aspera
|
|
62
62
|
EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
|
63
63
|
From: <%=from_name%> <<%=from_email%>>
|
64
64
|
To: <<%=to%>>
|
65
|
-
Subject:
|
65
|
+
Subject: #{GEM_NAME} email test
|
66
66
|
|
67
|
-
|
67
|
+
This email was sent to test #{PROGRAM_NAME}.
|
68
68
|
END_OF_TEMPLATE
|
69
69
|
# special extended values
|
70
70
|
EXTV_INCLUDE_PRESETS = :incps
|
71
71
|
EXTV_PRESET = :preset
|
72
|
+
EXTV_VAULT = :vault
|
72
73
|
PRESET_DIG_SEPARATOR = '.'
|
73
74
|
DEFAULT_CHECK_NEW_VERSION_DAYS = 7
|
74
75
|
DEFAULT_PRIV_KEY_FILENAME = 'aspera_aoc_key' # pragma: allowlist secret
|
75
76
|
DEFAULT_PRIVKEY_LENGTH = 4096
|
76
|
-
private_constant :DEFAULT_CONFIG_FILENAME
|
77
|
-
:
|
78
|
-
:
|
79
|
-
:
|
80
|
-
:
|
77
|
+
private_constant :DEFAULT_CONFIG_FILENAME,
|
78
|
+
:CONF_PRESET_CONFIG,
|
79
|
+
:CONF_PRESET_VERSION,
|
80
|
+
:CONF_PRESET_DEFAULT,
|
81
|
+
:CONF_PRESET_GLOBAL,
|
82
|
+
:PROGRAM_NAME_V1,
|
83
|
+
:PROGRAM_NAME_V2,
|
84
|
+
:DEFAULT_REDIRECT,
|
85
|
+
:ASPERA_PLUGINS_FOLDERNAME,
|
86
|
+
:RUBY_FILE_EXT,
|
87
|
+
:AOC_COMMAND_V1,
|
88
|
+
:AOC_COMMAND_V2,
|
89
|
+
:AOC_COMMAND_V3,
|
90
|
+
:AOC_COMMAND_CURRENT,
|
91
|
+
:DEMO,
|
92
|
+
:TRANSFER_SDK_ARCHIVE_URL,
|
93
|
+
:AOC_PATH_API_CLIENTS,
|
94
|
+
:DEMO_SERVER_PRESET,
|
95
|
+
:EMAIL_TEST_TEMPLATE,
|
96
|
+
:EXTV_INCLUDE_PRESETS,
|
97
|
+
:EXTV_PRESET,
|
98
|
+
:EXTV_VAULT,
|
99
|
+
:DEFAULT_CHECK_NEW_VERSION_DAYS,
|
100
|
+
:DEFAULT_PRIV_KEY_FILENAME,
|
101
|
+
:SERVER_COMMAND,
|
81
102
|
:PRESET_DIG_SEPARATOR
|
82
|
-
def initialize(env,params)
|
103
|
+
def initialize(env, params)
|
83
104
|
raise 'env and params must be Hash' unless env.is_a?(Hash) && params.is_a?(Hash)
|
84
105
|
raise 'missing param' unless %i[name help version gem].sort.eql?(params.keys.sort)
|
85
106
|
super(env)
|
@@ -91,60 +112,70 @@ module Aspera
|
|
91
112
|
@config_presets = nil
|
92
113
|
@connect_versions = nil
|
93
114
|
@vault = nil
|
94
|
-
@conf_file_default = File.join(@main_folder,DEFAULT_CONFIG_FILENAME)
|
115
|
+
@conf_file_default = File.join(@main_folder, DEFAULT_CONFIG_FILENAME)
|
95
116
|
@option_config_file = @conf_file_default
|
96
117
|
@pac_exec = nil
|
97
|
-
Log.log.debug
|
118
|
+
Log.log.debug{"#{@info[:name]} folder: #{@main_folder}"}
|
98
119
|
# set folder for FASP SDK
|
99
120
|
add_plugin_lookup_folder(self.class.gem_plugins_folder)
|
100
|
-
add_plugin_lookup_folder(File.join(@main_folder,ASPERA_PLUGINS_FOLDERNAME))
|
121
|
+
add_plugin_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
101
122
|
# do file parameter first
|
102
|
-
options.set_obj_attr(:config_file,self
|
103
|
-
options.add_opt_simple(:config_file,"read parameters from file in YAML format, current=#{@option_config_file}")
|
123
|
+
options.set_obj_attr(:config_file, self, :option_config_file)
|
124
|
+
options.add_opt_simple(:config_file, "read parameters from file in YAML format, current=#{@option_config_file}")
|
104
125
|
options.parse_options!
|
105
|
-
# read correct file
|
126
|
+
# read correct file (set @config_presets)
|
106
127
|
read_config_file
|
107
128
|
# add preset handler (needed for smtp)
|
108
|
-
ExtendedValue.instance.set_handler(EXTV_PRESET
|
109
|
-
ExtendedValue.instance.set_handler(EXTV_INCLUDE_PRESETS
|
129
|
+
ExtendedValue.instance.set_handler(EXTV_PRESET, :reader, lambda{|v|preset_by_name(v)})
|
130
|
+
ExtendedValue.instance.set_handler(EXTV_INCLUDE_PRESETS, :decoder, lambda{|v|expanded_with_preset_includes(v)})
|
131
|
+
ExtendedValue.instance.set_handler(EXTV_VAULT, :decoder, lambda{|v|vault_value(v)})
|
110
132
|
# load defaults before it can be overriden
|
111
133
|
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
112
134
|
options.parse_options!
|
113
|
-
options.set_obj_attr(:ascp_path,Fasp::Installation.instance
|
114
|
-
options.set_obj_attr(:sdk_folder,Fasp::Installation.instance
|
115
|
-
options.set_obj_attr(:use_product,self
|
116
|
-
options.set_obj_attr(:preset,self
|
117
|
-
options.set_obj_attr(:plugin_folder,self
|
118
|
-
options.add_opt_switch(:no_default,'-N','do not load default configuration for plugin') { @use_plugin_defaults = false }
|
119
|
-
options.add_opt_boolean(:override,'Wizard: override existing value')
|
120
|
-
options.add_opt_boolean(:use_generic_client,'Wizard: AoC: use global or org specific jwt client id')
|
121
|
-
options.add_opt_boolean(:default,'Wizard: set as default configuration for specified plugin (also: update)')
|
122
|
-
options.add_opt_boolean(:test_mode,'Wizard: skip private key check step')
|
123
|
-
options.add_opt_simple(:preset,'-PVALUE','load the named option preset from current config file')
|
124
|
-
options.add_opt_simple(:pkeypath,'Wizard: path to private key for JWT')
|
125
|
-
options.add_opt_simple(:ascp_path,'
|
126
|
-
options.add_opt_simple(:use_product,'
|
127
|
-
options.add_opt_simple(:smtp,'
|
128
|
-
options.add_opt_simple(:fpac,'
|
129
|
-
options.add_opt_simple(:
|
130
|
-
options.add_opt_simple(:
|
131
|
-
options.add_opt_simple(:
|
132
|
-
options.add_opt_simple(:
|
133
|
-
options.add_opt_simple(:
|
134
|
-
options.add_opt_simple(:
|
135
|
-
options.add_opt_simple(:
|
136
|
-
options.add_opt_simple(:
|
137
|
-
options.
|
138
|
-
options.
|
139
|
-
options.set_option(:
|
140
|
-
options.set_option(:
|
141
|
-
options.set_option(:
|
142
|
-
options.set_option(:
|
143
|
-
options.set_option(:
|
135
|
+
options.set_obj_attr(:ascp_path, Fasp::Installation.instance, :ascp_path)
|
136
|
+
options.set_obj_attr(:sdk_folder, Fasp::Installation.instance, :sdk_folder)
|
137
|
+
options.set_obj_attr(:use_product, self, :option_use_product)
|
138
|
+
options.set_obj_attr(:preset, self, :option_preset)
|
139
|
+
options.set_obj_attr(:plugin_folder, self, :option_plugin_folder)
|
140
|
+
options.add_opt_switch(:no_default, '-N', 'do not load default configuration for plugin') { @use_plugin_defaults = false }
|
141
|
+
options.add_opt_boolean(:override, 'Wizard: override existing value')
|
142
|
+
options.add_opt_boolean(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id')
|
143
|
+
options.add_opt_boolean(:default, 'Wizard: set as default configuration for specified plugin (also: update)')
|
144
|
+
options.add_opt_boolean(:test_mode, 'Wizard: skip private key check step')
|
145
|
+
options.add_opt_simple(:preset, '-PVALUE', 'load the named option preset from current config file')
|
146
|
+
options.add_opt_simple(:pkeypath, 'Wizard: path to private key for JWT')
|
147
|
+
options.add_opt_simple(:ascp_path, 'Path to ascp')
|
148
|
+
options.add_opt_simple(:use_product, 'Use ascp from specified product')
|
149
|
+
options.add_opt_simple(:smtp, 'SMTP configuration (extended value: hash)')
|
150
|
+
options.add_opt_simple(:fpac, 'Proxy auto configuration script')
|
151
|
+
options.add_opt_simple(:proxy_credentials, 'HTTP proxy credentials (Array with user and password)')
|
152
|
+
options.add_opt_simple(:secret, 'Secret for access keys')
|
153
|
+
options.add_opt_simple(:vault, 'Vault for secrets')
|
154
|
+
options.add_opt_simple(:vault_password, 'Vault password')
|
155
|
+
options.add_opt_simple(:sdk_url, 'URL to get SDK')
|
156
|
+
options.add_opt_simple(:sdk_folder, 'SDK folder path')
|
157
|
+
options.add_opt_simple(:notif_to, 'Email recipient for notification of transfers')
|
158
|
+
options.add_opt_simple(:notif_template, 'Email ERB template for notification of transfers')
|
159
|
+
options.add_opt_simple(:version_check_days, Integer, 'Period in days to check new version (zero to disable)')
|
160
|
+
options.add_opt_simple(:plugin_folder, 'Folder where to find additional plugins')
|
161
|
+
options.set_option(:use_generic_client, true)
|
162
|
+
options.set_option(:test_mode, false)
|
163
|
+
options.set_option(:default, true)
|
164
|
+
options.set_option(:version_check_days, DEFAULT_CHECK_NEW_VERSION_DAYS)
|
165
|
+
options.set_option(:sdk_url, TRANSFER_SDK_ARCHIVE_URL)
|
166
|
+
options.set_option(:sdk_folder, File.join(@main_folder, 'sdk'))
|
167
|
+
options.set_option(:override, :no)
|
144
168
|
options.parse_options!
|
145
169
|
pac_script = options.get_option(:fpac)
|
146
170
|
# create PAC executor
|
147
171
|
@pac_exec = Aspera::ProxyAutoConfig.new(pac_script).register_uri_generic unless pac_script.nil?
|
172
|
+
proxy_creds = options.get_option(:proxy_credentials)
|
173
|
+
if !proxy_creds.nil?
|
174
|
+
raise CliBadArgument, 'proxy credentials shall be an array (#{proxy_creds.class})' unless proxy_creds.is_a?(Array)
|
175
|
+
raise CliBadArgument, 'proxy credentials shall have two elements (#{proxy_creds.length})' unless proxy_creds.length.eql?(2)
|
176
|
+
@pac_exec.proxy_user = Rest.proxy_user = proxy_creds[0]
|
177
|
+
@pac_exec.proxy_pass = Rest.proxy_pass = proxy_creds[1]
|
178
|
+
end
|
148
179
|
end
|
149
180
|
|
150
181
|
# env var name to override the app's main folder
|
@@ -159,8 +190,8 @@ module Aspera
|
|
159
190
|
# if env var undefined or empty
|
160
191
|
if app_folder.nil? || app_folder.empty?
|
161
192
|
user_home_folder = Dir.home
|
162
|
-
raise CliError,"Home folder does not exist: #{user_home_folder}. Check your user environment or use #{conf_dir_env_var}." unless Dir.exist?(user_home_folder)
|
163
|
-
app_folder = File.join(user_home_folder,ASPERA_HOME_FOLDER_NAME
|
193
|
+
raise CliError, "Home folder does not exist: #{user_home_folder}. Check your user environment or use #{conf_dir_env_var}." unless Dir.exist?(user_home_folder)
|
194
|
+
app_folder = File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME, @info[:name])
|
164
195
|
end
|
165
196
|
return app_folder
|
166
197
|
end
|
@@ -174,8 +205,10 @@ module Aspera
|
|
174
205
|
'0'
|
175
206
|
end
|
176
207
|
if Gem::Version.new(Environment.ruby_version) < Gem::Version.new(RUBY_FUTURE_MINIMUM_VERSION)
|
177
|
-
Log.log.warn
|
178
|
-
"
|
208
|
+
Log.log.warn do
|
209
|
+
"Note that a future version will require Ruby version #{RUBY_FUTURE_MINIMUM_VERSION} at minimum, "\
|
210
|
+
"you are using #{Environment.ruby_version}"
|
211
|
+
end
|
179
212
|
end
|
180
213
|
return {
|
181
214
|
name: @info[:gem],
|
@@ -187,8 +220,8 @@ module Aspera
|
|
187
220
|
|
188
221
|
def periodic_check_newer_gem_version
|
189
222
|
# get verification period
|
190
|
-
delay_days = options.get_option(:version_check_days,is_type: :mandatory)
|
191
|
-
Log.log.info
|
223
|
+
delay_days = options.get_option(:version_check_days, is_type: :mandatory)
|
224
|
+
Log.log.info{"check days: #{delay_days}"}
|
192
225
|
# check only if not zero day
|
193
226
|
return if delay_days.eql?(0)
|
194
227
|
# get last date from persistency
|
@@ -206,28 +239,29 @@ module Aspera
|
|
206
239
|
# negative value will force check
|
207
240
|
-1
|
208
241
|
end
|
209
|
-
Log.log.debug
|
242
|
+
Log.log.debug{"days elapsed: #{last_check_days}"}
|
210
243
|
return if last_check_days < delay_days
|
211
244
|
# generate timestamp
|
212
245
|
last_check_array[0] = current_date.strftime('%Y/%m/%d')
|
213
246
|
check_date_persist.save
|
214
247
|
# compare this version and the one on internet
|
215
248
|
check_data = check_gem_version
|
216
|
-
Log.log.warn
|
217
|
-
"You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}"
|
249
|
+
Log.log.warn do
|
250
|
+
"A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}"
|
251
|
+
end if check_data[:need_update]
|
218
252
|
end
|
219
253
|
|
220
254
|
# retrieve structure from cloud (CDN) with all versions available
|
221
255
|
def connect_versions
|
222
256
|
if @connect_versions.nil?
|
223
257
|
api_connect_cdn = Rest.new({base_url: CONNECT_WEB_URL})
|
224
|
-
javascript = api_connect_cdn.call({operation: 'GET',subpath: CONNECT_VERSIONS})
|
258
|
+
javascript = api_connect_cdn.call({operation: 'GET', subpath: CONNECT_VERSIONS})
|
225
259
|
# get result on one line
|
226
|
-
connect_versions_javascript = javascript[:http].body.gsub(/\r?\n\s*/,'')
|
227
|
-
Log.log.debug
|
260
|
+
connect_versions_javascript = javascript[:http].body.gsub(/\r?\n\s*/, '')
|
261
|
+
Log.log.debug{"javascript=[\n#{connect_versions_javascript}\n]"}
|
228
262
|
# get javascript object only
|
229
263
|
found = connect_versions_javascript.match(/^.*? = (.*);/)
|
230
|
-
raise CliError,'Problen when getting connect versions from internet' if found.nil?
|
264
|
+
raise CliError, 'Problen when getting connect versions from internet' if found.nil?
|
231
265
|
alldata = JSON.parse(found[1])
|
232
266
|
@connect_versions = alldata['entries']
|
233
267
|
end
|
@@ -240,20 +274,20 @@ module Aspera
|
|
240
274
|
# @param plugin_name_sym : symbol for plugin name
|
241
275
|
def add_plugin_default_preset(plugin_name_sym)
|
242
276
|
default_config_name = get_plugin_default_config_name(plugin_name_sym)
|
243
|
-
Log.log.debug
|
244
|
-
options.add_option_preset(preset_by_name(default_config_name),op: :unshift) unless default_config_name.nil?
|
277
|
+
Log.log.debug{"add_plugin_default_preset:#{plugin_name_sym}:#{default_config_name}"}
|
278
|
+
options.add_option_preset(preset_by_name(default_config_name), op: :unshift) unless default_config_name.nil?
|
245
279
|
return nil
|
246
280
|
end
|
247
281
|
|
248
282
|
private
|
249
283
|
|
250
|
-
def generate_rsa_private_key(private_key_path,length)
|
284
|
+
def generate_rsa_private_key(private_key_path, length)
|
251
285
|
require 'openssl'
|
252
286
|
priv_key = OpenSSL::PKey::RSA.new(length)
|
253
|
-
File.write(private_key_path,priv_key.to_s)
|
254
|
-
File.
|
255
|
-
|
256
|
-
|
287
|
+
File.write(private_key_path, priv_key.to_s)
|
288
|
+
File.write(private_key_path + '.pub', priv_key.public_key.to_s)
|
289
|
+
Environment.restrict_file_access(private_key_path)
|
290
|
+
Environment.restrict_file_access(private_key_path + '.pub')
|
257
291
|
nil
|
258
292
|
end
|
259
293
|
|
@@ -272,7 +306,7 @@ module Aspera
|
|
272
306
|
# @return main folder where code is, i.e. .../lib
|
273
307
|
# go up as many times as englobing modules (not counting class, as it is a file)
|
274
308
|
def gem_src_root
|
275
|
-
File.expand_path(module_full_name.gsub('::','/').gsub(%r{[^/]+},'..'),gem_plugins_folder)
|
309
|
+
File.expand_path(module_full_name.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
|
276
310
|
end
|
277
311
|
|
278
312
|
# instanciate a plugin
|
@@ -286,7 +320,7 @@ module Aspera
|
|
286
320
|
# set parameter and value in global config
|
287
321
|
# creates one if none already created
|
288
322
|
# @return preset name that contains global default
|
289
|
-
def set_global_default(key,value)
|
323
|
+
def set_global_default(key, value)
|
290
324
|
# get default preset if it exists
|
291
325
|
global_default_preset_name = get_plugin_default_config_name(CONF_GLOBAL_SYM)
|
292
326
|
if global_default_preset_name.nil?
|
@@ -312,17 +346,17 @@ module Aspera
|
|
312
346
|
# @param config_name name of the preset in config file
|
313
347
|
# @param include_path used to detect and avoid include loops
|
314
348
|
def preset_by_name(config_name, include_path=[])
|
315
|
-
raise CliError,'loop in include' if include_path.include?(config_name)
|
349
|
+
raise CliError, 'loop in include' if include_path.include?(config_name)
|
316
350
|
include_path = include_path.clone # avoid messing up if there are multiple branches
|
317
351
|
current = @config_presets
|
318
352
|
config_name.split(PRESET_DIG_SEPARATOR).each do |name|
|
319
|
-
raise CliError,"
|
353
|
+
raise CliError, "Expecting Hash for subkey: #{include_path} (#{current.class})" unless current.is_a?(Hash)
|
320
354
|
include_path.push(name)
|
321
355
|
current = current[name]
|
322
|
-
raise CliError,"
|
356
|
+
raise CliError, "No such config preset: #{include_path}" if current.nil?
|
323
357
|
end
|
324
358
|
case current
|
325
|
-
when Hash then return expanded_with_preset_includes(current,include_path)
|
359
|
+
when Hash then return expanded_with_preset_includes(current, include_path)
|
326
360
|
when String then return ExtendedValue.instance.evaluate(current)
|
327
361
|
else return current
|
328
362
|
end
|
@@ -332,8 +366,8 @@ module Aspera
|
|
332
366
|
# @param hash_val
|
333
367
|
# @param include_path to avoid inclusion loop
|
334
368
|
def expanded_with_preset_includes(hash_val, include_path=[])
|
335
|
-
raise CliError,"#{EXTV_INCLUDE_PRESETS} requires a Hash, have #{hash_val.class}" unless hash_val.is_a?(Hash)
|
336
|
-
if hash_val.
|
369
|
+
raise CliError, "#{EXTV_INCLUDE_PRESETS} requires a Hash, have #{hash_val.class}" unless hash_val.is_a?(Hash)
|
370
|
+
if hash_val.key?(EXTV_INCLUDE_PRESETS)
|
337
371
|
memory = hash_val.clone
|
338
372
|
includes = memory[EXTV_INCLUDE_PRESETS]
|
339
373
|
memory.delete(EXTV_INCLUDE_PRESETS)
|
@@ -341,7 +375,7 @@ module Aspera
|
|
341
375
|
raise "#{EXTV_INCLUDE_PRESETS} must be an Array" unless includes.is_a?(Array)
|
342
376
|
raise "#{EXTV_INCLUDE_PRESETS} must contain names" unless includes.map(&:class).uniq.eql?([String])
|
343
377
|
includes.each do |preset_name|
|
344
|
-
hash_val.merge!(preset_by_name(preset_name,include_path))
|
378
|
+
hash_val.merge!(preset_by_name(preset_name, include_path))
|
345
379
|
end
|
346
380
|
hash_val.merge!(memory)
|
347
381
|
end
|
@@ -381,121 +415,122 @@ module Aspera
|
|
381
415
|
end
|
382
416
|
end
|
383
417
|
|
384
|
-
def convert_preset_path(old_name,new_name,files_to_copy)
|
385
|
-
old_subpath = File.join('',ASPERA_HOME_FOLDER_NAME,old_name,'')
|
386
|
-
new_subpath = File.join('',ASPERA_HOME_FOLDER_NAME,new_name,'')
|
418
|
+
def convert_preset_path(old_name, new_name, files_to_copy)
|
419
|
+
old_subpath = File.join('', ASPERA_HOME_FOLDER_NAME, old_name, '')
|
420
|
+
new_subpath = File.join('', ASPERA_HOME_FOLDER_NAME, new_name, '')
|
387
421
|
# convert possible keys located in config folder
|
388
422
|
@config_presets.values.select{|p|p.is_a?(Hash)}.each do |preset|
|
389
423
|
preset.values.select{|v|v.is_a?(String) && v.include?(old_subpath)}.each do |value|
|
390
424
|
old_val = value.clone
|
391
|
-
included_path = File.expand_path(old_val.gsub(/^@file:/,''))
|
425
|
+
included_path = File.expand_path(old_val.gsub(/^@file:/, ''))
|
392
426
|
files_to_copy.push(included_path) unless files_to_copy.include?(included_path) || !File.exist?(included_path)
|
393
|
-
value.gsub!(old_subpath,new_subpath)
|
394
|
-
Log.log.warn
|
427
|
+
value.gsub!(old_subpath, new_subpath)
|
428
|
+
Log.log.warn{"Converted config value: #{old_val} -> #{value}"}
|
395
429
|
end
|
396
430
|
end
|
397
431
|
end
|
398
432
|
|
399
|
-
def convert_preset_plugin_name(old_name,new_name)
|
433
|
+
def convert_preset_plugin_name(old_name, new_name)
|
400
434
|
default_preset = @config_presets[CONF_PRESET_DEFAULT]
|
401
|
-
return unless default_preset.is_a?(Hash) && default_preset.
|
435
|
+
return unless default_preset.is_a?(Hash) && default_preset.key?(old_name)
|
402
436
|
default_preset[new_name] = default_preset[old_name]
|
403
437
|
default_preset.delete(old_name)
|
404
|
-
Log.log.warn
|
438
|
+
Log.log.warn{"Converted plugin default: #{old_name} -> #{new_name}"}
|
405
439
|
end
|
406
440
|
|
407
441
|
# read config file and validate format
|
408
442
|
# tries to convert from older version if possible and required
|
409
443
|
def read_config_file
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
Log.log.warn("..#{file} -> #{@main_folder}")
|
476
|
-
end
|
444
|
+
Log.log.debug{"config file is: #{@option_config_file}".red}
|
445
|
+
conf_file_v1 = File.join(Dir.home, ASPERA_HOME_FOLDER_NAME, PROGRAM_NAME_V1, DEFAULT_CONFIG_FILENAME)
|
446
|
+
conf_file_v2 = File.join(Dir.home, ASPERA_HOME_FOLDER_NAME, PROGRAM_NAME_V2, DEFAULT_CONFIG_FILENAME)
|
447
|
+
# files search for configuration, by default the one given by user
|
448
|
+
search_files = [@option_config_file]
|
449
|
+
# if default file, then also look for older versions
|
450
|
+
search_files.push(conf_file_v2, conf_file_v1) if @option_config_file.eql?(@conf_file_default)
|
451
|
+
# find first existing file (or nil)
|
452
|
+
conf_file_to_load = search_files.find{|f| File.exist?(f)}
|
453
|
+
# require save if old version of file
|
454
|
+
save_required = !@option_config_file.eql?(conf_file_to_load)
|
455
|
+
# if no file found, create default config
|
456
|
+
if conf_file_to_load.nil?
|
457
|
+
Log.log.warn{"No config file found. Creating empty configuration file: #{@option_config_file}"}
|
458
|
+
@config_presets = {CONF_PRESET_CONFIG => {CONF_PRESET_VERSION => @info[:version]}}
|
459
|
+
else
|
460
|
+
Log.log.debug{"loading #{@option_config_file}"}
|
461
|
+
@config_presets = YAML.load_file(conf_file_to_load)
|
462
|
+
end
|
463
|
+
files_to_copy = []
|
464
|
+
Log.log.debug{"Available_presets: #{@config_presets}"}
|
465
|
+
raise 'Expecting YAML Hash' unless @config_presets.is_a?(Hash)
|
466
|
+
# check there is at least the config section
|
467
|
+
if !@config_presets.key?(CONF_PRESET_CONFIG)
|
468
|
+
raise "Cannot find key: #{CONF_PRESET_CONFIG}"
|
469
|
+
end
|
470
|
+
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]
|
471
|
+
if version.nil?
|
472
|
+
raise 'No version found in config section.'
|
473
|
+
end
|
474
|
+
# oldest compatible conf file format, update to latest version when an incompatible change is made
|
475
|
+
# check compatibility of version of conf file
|
476
|
+
config_tested_version = '0.4.5'
|
477
|
+
if Gem::Version.new(version) < Gem::Version.new(config_tested_version)
|
478
|
+
raise "Unsupported config file version #{version}. Expecting min version #{config_tested_version}"
|
479
|
+
end
|
480
|
+
config_tested_version = '0.6.15'
|
481
|
+
if Gem::Version.new(version) < Gem::Version.new(config_tested_version)
|
482
|
+
convert_preset_plugin_name(AOC_COMMAND_V1, AOC_COMMAND_V2)
|
483
|
+
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = config_tested_version
|
484
|
+
save_required = true
|
485
|
+
end
|
486
|
+
config_tested_version = '0.8.10'
|
487
|
+
if Gem::Version.new(version) <= Gem::Version.new(config_tested_version)
|
488
|
+
convert_preset_path(PROGRAM_NAME_V1, PROGRAM_NAME_V2, files_to_copy)
|
489
|
+
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = config_tested_version
|
490
|
+
save_required = true
|
491
|
+
end
|
492
|
+
config_tested_version = '1.0'
|
493
|
+
if Gem::Version.new(version) <= Gem::Version.new(config_tested_version)
|
494
|
+
convert_preset_plugin_name(AOC_COMMAND_V2, AOC_COMMAND_V3)
|
495
|
+
convert_preset_path(PROGRAM_NAME_V2, @info[:name], files_to_copy)
|
496
|
+
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = config_tested_version
|
497
|
+
save_required = true
|
498
|
+
end
|
499
|
+
Log.log.debug{"conf version: #{version}"}
|
500
|
+
# Place new compatibility code here
|
501
|
+
if save_required
|
502
|
+
Log.log.warn('Saving automatic conversion.')
|
503
|
+
@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = @info[:version]
|
504
|
+
save_presets_to_config_file
|
505
|
+
Log.log.warn('Copying referenced files')
|
506
|
+
files_to_copy.each do |file|
|
507
|
+
FileUtils.cp(file, @main_folder)
|
508
|
+
Log.log.warn{"..#{file} -> #{@main_folder}"}
|
477
509
|
end
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
510
|
+
end
|
511
|
+
rescue Psych::SyntaxError => e
|
512
|
+
Log.log.error('YAML error in config file')
|
513
|
+
raise e
|
514
|
+
rescue StandardError => e
|
515
|
+
Log.log.debug{"-> #{e.class.name} : #{e}"}
|
516
|
+
if File.exist?(@option_config_file)
|
517
|
+
# then there is a problem with that file.
|
483
518
|
new_name = "#{@option_config_file}.pre#{@info[:version]}.manual_conversion_needed"
|
484
|
-
File.rename(@option_config_file,new_name)
|
485
|
-
Log.log.warn
|
519
|
+
File.rename(@option_config_file, new_name)
|
520
|
+
Log.log.warn{"Renamed config file to #{new_name}."}
|
486
521
|
Log.log.warn('Manual Conversion is required. Next time, a new empty file will be created.')
|
487
|
-
raise CliError,e.to_s
|
488
522
|
end
|
523
|
+
raise CliError, e.to_s
|
489
524
|
end
|
490
525
|
|
491
526
|
# find plugins in defined paths
|
492
527
|
def add_plugins_from_lookup_folders
|
493
528
|
@plugin_lookup_folders.each do |folder|
|
494
529
|
next unless File.directory?(folder)
|
495
|
-
#TODO: add gem root to load path ? and require short folder ?
|
496
|
-
|
530
|
+
# TODO: add gem root to load path ? and require short folder ?
|
531
|
+
# $LOAD_PATH.push(folder) if i[:add_path]
|
497
532
|
Dir.entries(folder).select{|file|file.end_with?(RUBY_FILE_EXT)}.each do |source|
|
498
|
-
add_plugin_info(File.join(folder,source))
|
533
|
+
add_plugin_info(File.join(folder, source))
|
499
534
|
end
|
500
535
|
end
|
501
536
|
end
|
@@ -506,17 +541,17 @@ module Aspera
|
|
506
541
|
|
507
542
|
def add_plugin_info(path)
|
508
543
|
raise "ERROR: plugin path must end with #{RUBY_FILE_EXT}" if !path.end_with?(RUBY_FILE_EXT)
|
509
|
-
plugin_symbol = File.basename(path,RUBY_FILE_EXT).to_sym
|
510
|
-
req = path.gsub(/#{RUBY_FILE_EXT}$/o,'')
|
511
|
-
if @plugins.
|
512
|
-
Log.log.warn
|
544
|
+
plugin_symbol = File.basename(path, RUBY_FILE_EXT).to_sym
|
545
|
+
req = path.gsub(/#{RUBY_FILE_EXT}$/o, '')
|
546
|
+
if @plugins.key?(plugin_symbol)
|
547
|
+
Log.log.warn{"skipping plugin already registered: #{plugin_symbol}"}
|
513
548
|
return
|
514
549
|
end
|
515
|
-
@plugins[plugin_symbol] = {source: path,require_stanza: req}
|
550
|
+
@plugins[plugin_symbol] = {source: path, require_stanza: req}
|
516
551
|
end
|
517
552
|
|
518
553
|
def identify_plugin_for_url(url)
|
519
|
-
plugins.each do |plugin_name_sym,plugin_info|
|
554
|
+
plugins.each do |plugin_name_sym, plugin_info|
|
520
555
|
# no detection for internal plugin
|
521
556
|
next if plugin_name_sym.eql?(CONF_PLUGIN_SYM)
|
522
557
|
# load plugin class
|
@@ -532,19 +567,19 @@ module Aspera
|
|
532
567
|
Log.log.warn(e.message)
|
533
568
|
Log.log.warn('Use option --insecure=yes to ignore certificate') if e.message.include?('cert')
|
534
569
|
rescue StandardError => e
|
535
|
-
Log.log.debug
|
570
|
+
Log.log.debug{"Cannot detect #{plugin_name_sym} : #{e.class}/#{e.message}"}
|
536
571
|
end
|
537
572
|
# second try : is there a redirect ?
|
538
573
|
if detection_info.nil?
|
539
574
|
begin
|
540
575
|
# TODO: check if redirect ?
|
541
|
-
new_url = Rest.new(base_url: url).call(operation: 'GET',subpath: '',redirect_max: 1)[:http].uri.to_s
|
576
|
+
new_url = Rest.new(base_url: url).call(operation: 'GET', subpath: '', redirect_max: 1)[:http].uri.to_s
|
542
577
|
unless url.eql?(new_url)
|
543
578
|
detection_info = c.detect(new_url)
|
544
579
|
current_url = new_url
|
545
580
|
end
|
546
581
|
rescue StandardError => e
|
547
|
-
Log.log.debug
|
582
|
+
Log.log.debug{"Cannot detect #{plugin_name_sym} : #{e.message}"}
|
548
583
|
end
|
549
584
|
end
|
550
585
|
return detection_info.merge(product: plugin_name_sym, url: current_url) unless detection_info.nil?
|
@@ -557,7 +592,7 @@ module Aspera
|
|
557
592
|
if %i[info version].include?(command)
|
558
593
|
connect_id = options.get_next_argument('id or title')
|
559
594
|
one_res = connect_versions.find{|i|i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}
|
560
|
-
raise CliNoSuchId.new(:connect,connect_id) if one_res.nil?
|
595
|
+
raise CliNoSuchId.new(:connect, connect_id) if one_res.nil?
|
561
596
|
end
|
562
597
|
case command
|
563
598
|
when :list
|
@@ -578,11 +613,11 @@ module Aspera
|
|
578
613
|
return {type: :object_list, data: all_links}
|
579
614
|
when :download
|
580
615
|
folder_dest = transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE)
|
581
|
-
#folder_dest=self.options.get_next_argument('destination folder')
|
616
|
+
# folder_dest=self.options.get_next_argument('destination folder')
|
582
617
|
api_connect_cdn = Rest.new({base_url: CONNECT_WEB_URL})
|
583
618
|
fileurl = one_link['href']
|
584
|
-
filename = fileurl.gsub(%r{.*/},'')
|
585
|
-
api_connect_cdn.call({operation: 'GET',subpath: fileurl,save_to_file: File.join(folder_dest,filename)})
|
619
|
+
filename = fileurl.gsub(%r{.*/}, '')
|
620
|
+
api_connect_cdn.call({operation: 'GET', subpath: fileurl, save_to_file: File.join(folder_dest, filename)})
|
586
621
|
return Main.result_status("Downloaded: #{filename}")
|
587
622
|
when :open
|
588
623
|
OpenApplication.instance.uri(one_link['href'])
|
@@ -600,12 +635,12 @@ module Aspera
|
|
600
635
|
ascp_path = options.get_next_argument('path to ascp')
|
601
636
|
ascp_version = Fasp::Installation.instance.get_ascp_version(ascp_path)
|
602
637
|
self.format.display_status("ascp version: #{ascp_version}")
|
603
|
-
preset_name = set_global_default(:ascp_path,ascp_path)
|
638
|
+
preset_name = set_global_default(:ascp_path, ascp_path)
|
604
639
|
return Main.result_status("Saved to default global preset #{preset_name}")
|
605
640
|
when :show # shows files used
|
606
641
|
return {type: :status, data: Fasp::Installation.instance.path(:ascp)}
|
607
642
|
when :info # shows files used
|
608
|
-
data = Fasp::Installation::FILES.each_with_object({}) do |v,m|
|
643
|
+
data = Fasp::Installation::FILES.each_with_object({}) do |v, m|
|
609
644
|
m[v.to_s] =
|
610
645
|
begin
|
611
646
|
Fasp::Installation.instance.path(v)
|
@@ -614,20 +649,26 @@ module Aspera
|
|
614
649
|
end
|
615
650
|
end
|
616
651
|
# read PATHs from ascp directly, and pvcl modules as well
|
617
|
-
Open3.popen3(Fasp::Installation.instance.path(:ascp),'-DDL-') do |_stdin, _stdout, stderr, thread|
|
652
|
+
Open3.popen3(Fasp::Installation.instance.path(:ascp), '-DDL-') do |_stdin, _stdout, stderr, thread|
|
618
653
|
last_line = ''
|
619
654
|
while (line = stderr.gets)
|
620
655
|
line.chomp!
|
621
656
|
last_line = line
|
622
657
|
case line
|
623
|
-
when
|
624
|
-
|
625
|
-
when
|
626
|
-
|
627
|
-
when %r{^
|
658
|
+
when /^DBG Path ([^ ]+) (dir|file) +: (.*)$/
|
659
|
+
data[Regexp.last_match(1)] = Regexp.last_match(3)
|
660
|
+
when /^DBG Added module group:"([^"]+)" name:"([^"]+)", version:"([^"]+)" interface:"([^"]+)"$/
|
661
|
+
data[Regexp.last_match(2)] = "#{Regexp.last_match(4)} #{Regexp.last_match(1)} v#{Regexp.last_match(3)}"
|
662
|
+
when %r{^DBG License result \(/license/(\S+)\): (.+)$}
|
663
|
+
data[Regexp.last_match(1)] = Regexp.last_match(2)
|
664
|
+
when /^LOG (.+) version ([0-9.]+)$/
|
665
|
+
data['product_name'] = Regexp.last_match(1)
|
666
|
+
data['product_version'] = Regexp.last_match(2)
|
667
|
+
when /^LOG Initializing FASP version ([^,]+),/
|
668
|
+
data['ascp_version'] = Regexp.last_match(1)
|
628
669
|
end
|
629
670
|
end
|
630
|
-
if !thread.value.exitstatus.eql?(1) && !data.
|
671
|
+
if !thread.value.exitstatus.eql?(1) && !data.key?('root')
|
631
672
|
raise last_line
|
632
673
|
end
|
633
674
|
end
|
@@ -641,21 +682,21 @@ module Aspera
|
|
641
682
|
when :use
|
642
683
|
default_product = options.get_next_argument('product name')
|
643
684
|
Fasp::Installation.instance.use_ascp_from_product(default_product)
|
644
|
-
preset_name = set_global_default(:ascp_path,Fasp::Installation.instance.path(:ascp))
|
685
|
+
preset_name = set_global_default(:ascp_path, Fasp::Installation.instance.path(:ascp))
|
645
686
|
return Main.result_status("Saved to default global preset #{preset_name}")
|
646
687
|
end
|
647
688
|
when :install
|
648
|
-
v = Fasp::Installation.instance.install_sdk(options.get_option(:sdk_url,is_type: :mandatory))
|
689
|
+
v = Fasp::Installation.instance.install_sdk(options.get_option(:sdk_url, is_type: :mandatory))
|
649
690
|
return Main.result_status("Installed version #{v}")
|
650
691
|
when :spec
|
651
692
|
return {
|
652
693
|
type: :object_list,
|
653
694
|
data: Fasp::Parameters.man_table,
|
654
|
-
fields: [
|
695
|
+
fields: %w[name type].concat(Fasp::Parameters::SUPPORTED_AGENTS_SHORT.map(&:to_s), %w[description])
|
655
696
|
}
|
656
697
|
when :errors
|
657
698
|
error_data = []
|
658
|
-
Fasp::ERROR_INFO.each_pair do |code,prop|
|
699
|
+
Fasp::ERROR_INFO.each_pair do |code, prop|
|
659
700
|
error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
|
660
701
|
end
|
661
702
|
return {type: :object_list, data: error_data}
|
@@ -664,112 +705,168 @@ module Aspera
|
|
664
705
|
end
|
665
706
|
|
666
707
|
# legacy actions available globally
|
667
|
-
PRESET_GBL_ACTIONS = %i[list overview].freeze
|
708
|
+
PRESET_GBL_ACTIONS = %i[list overview lookup secure].freeze
|
668
709
|
# require existing preset
|
669
710
|
PRESET_EXST_ACTIONS = %i[show delete get unset].freeze
|
670
711
|
# require id
|
671
|
-
PRESET_INSTANCE_ACTIONS =
|
672
|
-
PRESET_ALL_ACTIONS = [PRESET_GBL_ACTIONS,PRESET_INSTANCE_ACTIONS
|
712
|
+
PRESET_INSTANCE_ACTIONS = %i[initialize update ask set].concat(PRESET_EXST_ACTIONS).freeze
|
713
|
+
PRESET_ALL_ACTIONS = [].concat(PRESET_GBL_ACTIONS, PRESET_INSTANCE_ACTIONS).freeze
|
673
714
|
|
674
|
-
def
|
715
|
+
def execute_preset(action: nil, name: nil)
|
675
716
|
action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
|
676
|
-
|
717
|
+
name = instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
|
677
718
|
# those operations require existing option
|
678
|
-
raise "no such preset: #{
|
679
|
-
selected_preset = @config_presets[
|
719
|
+
raise "no such preset: #{name}" if PRESET_EXST_ACTIONS.include?(action) && !@config_presets.key?(name)
|
720
|
+
selected_preset = @config_presets[name]
|
680
721
|
case action
|
681
722
|
when :list
|
682
723
|
return {type: :value_list, data: @config_presets.keys, name: 'name'}
|
683
724
|
when :overview
|
684
725
|
return {type: :object_list, data: Formater.flatten_config_overview(@config_presets)}
|
685
726
|
when :show
|
686
|
-
raise "no such config: #{
|
727
|
+
raise "no such config: #{name}" if selected_preset.nil?
|
687
728
|
return {type: :single_object, data: selected_preset}
|
688
729
|
when :delete
|
689
|
-
@config_presets.delete(
|
730
|
+
@config_presets.delete(name)
|
690
731
|
save_presets_to_config_file
|
691
|
-
return Main.result_status("Deleted: #{
|
732
|
+
return Main.result_status("Deleted: #{name}")
|
692
733
|
when :get
|
693
734
|
param_name = options.get_next_argument('parameter name')
|
694
735
|
value = selected_preset[param_name]
|
695
|
-
raise "no such option in preset #{
|
736
|
+
raise "no such option in preset #{name} : #{param_name}" if value.nil?
|
696
737
|
case value
|
697
|
-
when Numeric,String then return {type: :text, data: ExtendedValue.instance.evaluate(value.to_s)}
|
738
|
+
when Numeric, String then return {type: :text, data: ExtendedValue.instance.evaluate(value.to_s)}
|
698
739
|
end
|
699
740
|
return {type: :single_object, data: value}
|
700
741
|
when :unset
|
701
742
|
param_name = options.get_next_argument('parameter name')
|
702
743
|
selected_preset.delete(param_name)
|
703
744
|
save_presets_to_config_file
|
704
|
-
return Main.result_status("Removed: #{
|
745
|
+
return Main.result_status("Removed: #{name}: #{param_name}")
|
705
746
|
when :set
|
706
747
|
param_name = options.get_next_argument('parameter name')
|
707
748
|
param_value = options.get_next_argument('parameter value')
|
708
|
-
if !@config_presets.
|
709
|
-
Log.log.debug
|
710
|
-
selected_preset = @config_presets[
|
749
|
+
if !@config_presets.key?(name)
|
750
|
+
Log.log.debug{"no such config name: #{name}, initializing"}
|
751
|
+
selected_preset = @config_presets[name] = {}
|
711
752
|
end
|
712
|
-
if selected_preset.
|
713
|
-
Log.log.warn
|
753
|
+
if selected_preset.key?(param_name)
|
754
|
+
Log.log.warn{"overwriting value: #{selected_preset[param_name]}"}
|
714
755
|
end
|
715
756
|
selected_preset[param_name] = param_value
|
716
757
|
save_presets_to_config_file
|
717
|
-
return Main.result_status("Updated: #{
|
758
|
+
return Main.result_status("Updated: #{name}: #{param_name} <- #{param_value}")
|
718
759
|
when :initialize
|
719
|
-
config_value = options.get_next_argument('extended value
|
720
|
-
if @config_presets.
|
721
|
-
Log.log.warn
|
760
|
+
config_value = options.get_next_argument('extended value', type: Hash)
|
761
|
+
if @config_presets.key?(name)
|
762
|
+
Log.log.warn{"configuration already exists: #{name}, overwriting"}
|
722
763
|
end
|
723
|
-
@config_presets[
|
764
|
+
@config_presets[name] = config_value
|
724
765
|
save_presets_to_config_file
|
725
766
|
return Main.result_status("Modified: #{@option_config_file}")
|
726
767
|
when :update
|
727
768
|
# get unprocessed options
|
728
769
|
theopts = options.get_options_table
|
729
|
-
Log.log.debug
|
730
|
-
@config_presets[
|
731
|
-
@config_presets[
|
770
|
+
Log.log.debug{"opts=#{theopts}"}
|
771
|
+
@config_presets[name] ||= {}
|
772
|
+
@config_presets[name].merge!(theopts)
|
732
773
|
# fix bug in 4.4 (creating key "true" in "default" preset)
|
733
774
|
@config_presets[CONF_PRESET_DEFAULT].delete(true) if @config_presets[CONF_PRESET_DEFAULT].is_a?(Hash)
|
734
775
|
save_presets_to_config_file
|
735
|
-
return Main.result_status("Updated: #{
|
776
|
+
return Main.result_status("Updated: #{name}")
|
736
777
|
when :ask
|
737
778
|
options.ask_missing_mandatory = :yes
|
738
|
-
@config_presets[
|
739
|
-
options.get_next_argument('option names',expected: :multiple).each do |optionname|
|
740
|
-
option_value = options.get_interactive(:option,optionname)
|
741
|
-
@config_presets[
|
779
|
+
@config_presets[name] ||= {}
|
780
|
+
options.get_next_argument('option names', expected: :multiple).each do |optionname|
|
781
|
+
option_value = options.get_interactive(:option, optionname)
|
782
|
+
@config_presets[name][optionname] = option_value
|
742
783
|
end
|
743
784
|
save_presets_to_config_file
|
744
|
-
return Main.result_status("Updated: #{
|
785
|
+
return Main.result_status("Updated: #{name}")
|
786
|
+
when :lookup
|
787
|
+
BasicAuthPlugin.register_options(@agents)
|
788
|
+
url = options.get_option(:url, is_type: :mandatory)
|
789
|
+
user = options.get_option(:username, is_type: :mandatory)
|
790
|
+
result = lookup_preset(url: url, username: user)
|
791
|
+
raise 'no such config found' if result.nil?
|
792
|
+
return {type: :single_object, data: result}
|
793
|
+
when :secure
|
794
|
+
identifier = options.get_next_argument('config name', mandatory: false)
|
795
|
+
preset_names = identifier.nil? ? @config_presets.keys : [identifier]
|
796
|
+
secret_keywords = %w[password secret].freeze
|
797
|
+
preset_names.each do |pset_name|
|
798
|
+
preset = @config_presets[pset_name]
|
799
|
+
next unless preset.is_a?(Hash)
|
800
|
+
preset.each_key do |option_name|
|
801
|
+
secret_keywords.each do |keyword|
|
802
|
+
next unless option_name.end_with?(keyword)
|
803
|
+
vault_label = pset_name
|
804
|
+
incr = 0
|
805
|
+
until vault.get(label: vault_label, exception: false).nil?
|
806
|
+
vault_label = "#{pset_name}#{incr}"
|
807
|
+
incr += 1
|
808
|
+
end
|
809
|
+
to_set = {label: vault_label, password: preset[option_name]}
|
810
|
+
puts "need to encode #{pset_name}.#{option_name} -> #{vault_label} -> #{to_set}"
|
811
|
+
# to_copy=%i[]
|
812
|
+
vault.set(to_set)
|
813
|
+
preset[option_name] = "@vault:#{vault_label}.password"
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
817
|
+
return Main.result_status('Secrets secured in vault: Make sure to save the vault password securely.')
|
745
818
|
end
|
746
819
|
end
|
747
820
|
|
748
|
-
ACTIONS =
|
749
|
-
|
821
|
+
ACTIONS = %i[
|
822
|
+
id
|
823
|
+
preset
|
824
|
+
open
|
825
|
+
documentation
|
826
|
+
genkey
|
827
|
+
gem
|
828
|
+
plugin
|
829
|
+
flush_tokens
|
830
|
+
echo
|
831
|
+
wizard
|
832
|
+
export_to_cli
|
833
|
+
detect
|
834
|
+
coffee
|
835
|
+
ascp
|
836
|
+
email_test
|
837
|
+
smtp_settings
|
838
|
+
proxy_check
|
839
|
+
folder
|
840
|
+
file
|
841
|
+
check_update
|
842
|
+
initdemo
|
843
|
+
vault].concat(PRESET_GBL_ACTIONS).freeze
|
750
844
|
|
751
845
|
# "config" plugin
|
752
846
|
def execute_action
|
753
847
|
action = options.get_next_command(ACTIONS)
|
754
848
|
case action
|
755
849
|
when *PRESET_GBL_ACTIONS # older syntax
|
756
|
-
|
850
|
+
Log.log.warn{"This syntax is deprecated, use command: preset #{action}"}
|
851
|
+
return execute_preset(action: action)
|
757
852
|
when :id # older syntax
|
758
|
-
|
853
|
+
identifier = options.get_next_argument('config name')
|
854
|
+
Log.log.warn{"This syntax is deprecated, use command: preset <verb> #{identifier}"}
|
855
|
+
return execute_preset(name: identifier)
|
759
856
|
when :preset # newer syntax
|
760
|
-
return
|
857
|
+
return execute_preset
|
761
858
|
when :open
|
762
|
-
OpenApplication.
|
859
|
+
OpenApplication.editor(@option_config_file.to_s)
|
763
860
|
return Main.result_nothing
|
764
861
|
when :documentation
|
765
|
-
section = options.get_next_argument('private key file path',mandatory: false)
|
862
|
+
section = options.get_next_argument('private key file path', mandatory: false)
|
766
863
|
section = '#' + section unless section.nil?
|
767
864
|
OpenApplication.instance.uri("#{@info[:help]}#{section}")
|
768
865
|
return Main.result_nothing
|
769
866
|
when :genkey # generate new rsa key
|
770
867
|
private_key_path = options.get_next_argument('private key file path')
|
771
|
-
private_key_length = options.get_next_argument('size in bits',mandatory: false) || DEFAULT_PRIVKEY_LENGTH
|
772
|
-
generate_rsa_private_key(private_key_path,private_key_length)
|
868
|
+
private_key_length = options.get_next_argument('size in bits', mandatory: false) || DEFAULT_PRIVKEY_LENGTH
|
869
|
+
generate_rsa_private_key(private_key_path, private_key_length)
|
773
870
|
return Main.result_status('Generated key: ' + private_key_path)
|
774
871
|
when :echo # display the content of a value given on command line
|
775
872
|
result = {type: :other_struct, data: options.get_next_argument('value')}
|
@@ -785,9 +882,9 @@ module Aspera
|
|
785
882
|
when :list
|
786
883
|
return {type: :object_list, data: @plugins.keys.map { |i| { 'plugin' => i.to_s, 'path' => @plugins[i][:source] } }, fields: %w[plugin path]}
|
787
884
|
when :create
|
788
|
-
plugin_name = options.get_next_argument('name',expected: :single).downcase
|
789
|
-
plugin_folder = options.get_next_argument('folder',expected: :single,mandatory: false) || File.join(@main_folder,ASPERA_PLUGINS_FOLDERNAME)
|
790
|
-
plugin_file = File.join(plugin_folder,"#{plugin_name}.rb")
|
885
|
+
plugin_name = options.get_next_argument('name', expected: :single).downcase
|
886
|
+
plugin_folder = options.get_next_argument('folder', expected: :single, mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
|
887
|
+
plugin_file = File.join(plugin_folder, "#{plugin_name}.rb")
|
791
888
|
content = <<~END_OF_PLUGIN_CODE
|
792
889
|
require 'aspera/cli/plugin'
|
793
890
|
module Aspera
|
@@ -801,7 +898,7 @@ module Aspera
|
|
801
898
|
end # Cli
|
802
899
|
end # Aspera
|
803
900
|
END_OF_PLUGIN_CODE
|
804
|
-
File.write(plugin_file,content)
|
901
|
+
File.write(plugin_file, content)
|
805
902
|
return Main.result_status("Created #{plugin_file}")
|
806
903
|
end
|
807
904
|
when :wizard
|
@@ -809,15 +906,15 @@ module Aspera
|
|
809
906
|
options.ask_missing_mandatory = true
|
810
907
|
# register url option
|
811
908
|
BasicAuthPlugin.register_options(@agents)
|
812
|
-
params={}
|
909
|
+
params = {}
|
813
910
|
# get from option, or ask
|
814
|
-
params[:instance_url] = options.get_option(:url,is_type: :mandatory)
|
911
|
+
params[:instance_url] = options.get_option(:url, is_type: :mandatory)
|
815
912
|
# allow user to tell the preset name
|
816
913
|
params[:preset_name] = options.get_option(:id)
|
817
914
|
# allow user to specify type of application
|
818
915
|
params[:application] = options.get_option(:value)
|
819
916
|
params[:application] = params[:application].nil? ? identify_plugin_for_url(params[:instance_url])[:product] : params[:application].to_sym
|
820
|
-
params[:plugin_name]=params[:application]
|
917
|
+
params[:plugin_name] = params[:application]
|
821
918
|
params[:test_args] = '<replace per app>'
|
822
919
|
case params[:application]
|
823
920
|
when :faspex5
|
@@ -825,7 +922,7 @@ module Aspera
|
|
825
922
|
when :aoc
|
826
923
|
wizard_aoc(params)
|
827
924
|
else
|
828
|
-
raise CliBadArgument,"Supports only: aoc. Detected: #{params[:application]}"
|
925
|
+
raise CliBadArgument, "Supports only: aoc. Detected: #{params[:application]}"
|
829
926
|
end # product
|
830
927
|
if params[:option_default]
|
831
928
|
self.format.display_status("Setting config preset as default for #{params[:plugin_name]}")
|
@@ -843,23 +940,23 @@ module Aspera
|
|
843
940
|
add_plugin_default_preset(AOC_COMMAND_V3.to_sym)
|
844
941
|
# instanciate AoC plugin
|
845
942
|
self.class.plugin_class(AOC_COMMAND_CURRENT).new(@agents) # TODO: is this line needed ? get options ?
|
846
|
-
url = options.get_option(:url,is_type: :mandatory)
|
943
|
+
url = options.get_option(:url, is_type: :mandatory)
|
847
944
|
cli_conf_file = Fasp::Installation.instance.cli_conf_file
|
848
945
|
data = JSON.parse(File.read(cli_conf_file))
|
849
|
-
organization,instance_domain = AoC.parse_url(url)
|
946
|
+
organization, instance_domain = AoC.parse_url(url)
|
850
947
|
key_basename = 'org_' + organization + '.pem'
|
851
|
-
key_file = File.join(File.dirname(File.dirname(cli_conf_file)),'etc',key_basename)
|
852
|
-
File.write(key_file,options.get_option(:private_key,is_type: :mandatory))
|
948
|
+
key_file = File.join(File.dirname(File.dirname(cli_conf_file)), 'etc', key_basename)
|
949
|
+
File.write(key_file, options.get_option(:private_key, is_type: :mandatory))
|
853
950
|
new_conf = {
|
854
951
|
'organization' => organization,
|
855
|
-
'hostname' => [organization,instance_domain].join('.'),
|
952
|
+
'hostname' => [organization, instance_domain].join('.'),
|
856
953
|
'privateKeyFilename' => key_basename,
|
857
|
-
'username' => options.get_option(:username,is_type: :mandatory)
|
954
|
+
'username' => options.get_option(:username, is_type: :mandatory)
|
858
955
|
}
|
859
956
|
new_conf['clientId'] = options.get_option(:client_id)
|
860
957
|
new_conf['clientSecret'] = options.get_option(:client_secret)
|
861
958
|
if new_conf['clientId'].nil?
|
862
|
-
new_conf['clientId'],new_conf['clientSecret'] = AoC.get_client_info
|
959
|
+
new_conf['clientId'], new_conf['clientSecret'] = AoC.get_client_info
|
863
960
|
end
|
864
961
|
entry = data['AoCAccounts'].find{|i|i['organization'].eql?(organization)}
|
865
962
|
if entry.nil?
|
@@ -869,12 +966,12 @@ module Aspera
|
|
869
966
|
self.format.display_status("Updating existing aoc entry: #{organization}")
|
870
967
|
entry.merge!(new_conf)
|
871
968
|
end
|
872
|
-
File.write(cli_conf_file,JSON.pretty_generate(data))
|
969
|
+
File.write(cli_conf_file, JSON.pretty_generate(data))
|
873
970
|
return Main.result_status("Updated: #{cli_conf_file}")
|
874
971
|
when :detect
|
875
972
|
# need url / username
|
876
973
|
BasicAuthPlugin.register_options(@agents)
|
877
|
-
return {type: :single_object, data: identify_plugin_for_url(options.get_option(:url,is_type: :mandatory))}
|
974
|
+
return {type: :single_object, data: identify_plugin_for_url(options.get_option(:url, is_type: :mandatory))}
|
878
975
|
when :coffee
|
879
976
|
OpenApplication.instance.uri('https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg')
|
880
977
|
return Main.result_nothing
|
@@ -891,22 +988,22 @@ module Aspera
|
|
891
988
|
when :file
|
892
989
|
return Main.result_status(@option_config_file)
|
893
990
|
when :email_test
|
894
|
-
send_email_template(
|
991
|
+
send_email_template(email_template_default: EMAIL_TEST_TEMPLATE)
|
895
992
|
return Main.result_nothing
|
896
993
|
when :smtp_settings
|
897
994
|
return {type: :single_object, data: email_settings}
|
898
995
|
when :proxy_check
|
899
996
|
# ensure fpac was provided
|
900
|
-
options.get_option(:fpac,is_type: :mandatory)
|
997
|
+
options.get_option(:fpac, is_type: :mandatory)
|
901
998
|
server_url = options.get_next_argument('server url')
|
902
999
|
return Main.result_status(@pac_exec.find_proxy_for_url(server_url))
|
903
1000
|
when :check_update
|
904
1001
|
return {type: :single_object, data: check_gem_version}
|
905
1002
|
when :initdemo
|
906
|
-
if @config_presets.
|
907
|
-
Log.log.warn
|
1003
|
+
if @config_presets.key?(DEMO_SERVER_PRESET)
|
1004
|
+
Log.log.warn{"Demo server preset already present: #{DEMO_SERVER_PRESET}"}
|
908
1005
|
else
|
909
|
-
Log.log.info
|
1006
|
+
Log.log.info{"Creating Demo server preset: #{DEMO_SERVER_PRESET}"}
|
910
1007
|
@config_presets[DEMO_SERVER_PRESET] = {
|
911
1008
|
'url' => 'ssh://' + DEMO + '.asperasoft.com:33001',
|
912
1009
|
'username' => AOC_COMMAND_V2,
|
@@ -914,76 +1011,37 @@ module Aspera
|
|
914
1011
|
}
|
915
1012
|
end
|
916
1013
|
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
917
|
-
if @config_presets[CONF_PRESET_DEFAULT].
|
918
|
-
Log.log.warn
|
919
|
-
Log.log.warn
|
1014
|
+
if @config_presets[CONF_PRESET_DEFAULT].key?(SERVER_COMMAND)
|
1015
|
+
Log.log.warn{"Server default preset already set to: #{@config_presets[CONF_PRESET_DEFAULT][SERVER_COMMAND]}"}
|
1016
|
+
Log.log.warn{"Use #{DEMO_SERVER_PRESET} for demo: -P#{DEMO_SERVER_PRESET}"} unless
|
920
1017
|
DEMO_SERVER_PRESET.eql?(@config_presets[CONF_PRESET_DEFAULT][SERVER_COMMAND])
|
921
1018
|
else
|
922
1019
|
@config_presets[CONF_PRESET_DEFAULT][SERVER_COMMAND] = DEMO_SERVER_PRESET
|
923
|
-
Log.log.info
|
1020
|
+
Log.log.info{"Setting server default preset to : #{DEMO_SERVER_PRESET}"}
|
924
1021
|
end
|
925
1022
|
save_presets_to_config_file
|
926
1023
|
return Main.result_status('Done')
|
927
|
-
when :vault
|
928
|
-
command = options.get_next_command(%i[init list get set delete])
|
929
|
-
case command
|
930
|
-
when :init
|
931
|
-
type = options.get_option(:value)
|
932
|
-
case type
|
933
|
-
when 'config',NilClass
|
934
|
-
raise 'default secrets already exists' if @config_presets.has_key?(CONF_PRESET_SECRETS)
|
935
|
-
@config_presets[CONF_PRESET_SECRETS] = {}
|
936
|
-
set_global_default(:secrets,"@preset:#{CONF_PRESET_SECRETS}")
|
937
|
-
else raise 'no such vault type'
|
938
|
-
end
|
939
|
-
return Main.result_status('Done')
|
940
|
-
when :list
|
941
|
-
return {type: :object_list, data: vault.list}
|
942
|
-
when :set
|
943
|
-
# register url option
|
944
|
-
BasicAuthPlugin.register_options(@agents)
|
945
|
-
username = options.get_option(:username,is_type: :mandatory)
|
946
|
-
url = options.get_option(:url,is_type: :mandatory)
|
947
|
-
description = options.get_option(:value)
|
948
|
-
secret = options.get_next_argument('secret')
|
949
|
-
vault.set(username: username, url: url, description: description, secret: secret)
|
950
|
-
save_presets_to_config_file if vault.is_a?(Keychain::EncryptedHash)
|
951
|
-
return Main.result_status('Done')
|
952
|
-
when :get
|
953
|
-
# register url option
|
954
|
-
BasicAuthPlugin.register_options(@agents)
|
955
|
-
username = options.get_option(:username,is_type: :mandatory)
|
956
|
-
url = options.get_option(:url)
|
957
|
-
result = vault.get(username: username, url: url)
|
958
|
-
return {type: :single_object, data: result}
|
959
|
-
when :delete
|
960
|
-
# register url option
|
961
|
-
BasicAuthPlugin.register_options(@agents)
|
962
|
-
username = options.get_option(:username,is_type: :mandatory)
|
963
|
-
url = options.get_option(:url)
|
964
|
-
vault.delete(username: username, url: url)
|
965
|
-
return Main.result_status('Done')
|
966
|
-
end
|
1024
|
+
when :vault then execute_vault
|
967
1025
|
else raise 'INTERNAL ERROR: wrong case'
|
968
1026
|
end
|
969
1027
|
end
|
970
1028
|
|
971
1029
|
# @return email server setting with defaults if not defined
|
972
1030
|
def email_settings
|
973
|
-
smtp = options.get_option(:smtp,is_type: :mandatory)
|
1031
|
+
smtp = options.get_option(:smtp, is_type: :mandatory)
|
974
1032
|
# change string keys into symbol keys
|
975
|
-
smtp = smtp.keys.each_with_object({}){|v,m|m[v.to_sym] = smtp[v];}
|
1033
|
+
smtp = smtp.keys.each_with_object({}){|v, m|m[v.to_sym] = smtp[v]; }
|
976
1034
|
# defaults
|
977
1035
|
smtp[:tls] ||= true
|
978
1036
|
smtp[:port] ||= smtp[:tls] ? 587 : 25
|
979
|
-
smtp[:from_email] ||= smtp[:username] if smtp.
|
980
|
-
smtp[:from_name] ||= smtp[:from_email].gsub(/@.*$/,'').gsub(/[^a-zA-Z]/,' ').capitalize if smtp.
|
981
|
-
smtp[:domain] ||= smtp[:from_email].gsub(/^.*@/,'') if smtp.
|
1037
|
+
smtp[:from_email] ||= smtp[:username] if smtp.key?(:username)
|
1038
|
+
smtp[:from_name] ||= smtp[:from_email].gsub(/@.*$/, '').gsub(/[^a-zA-Z]/, ' ').capitalize if smtp.key?(:username)
|
1039
|
+
smtp[:domain] ||= smtp[:from_email].gsub(/^.*@/, '') if smtp.key?(:from_email)
|
982
1040
|
# check minimum required
|
983
1041
|
%i[server port domain].each do |n|
|
984
|
-
raise "Missing smtp parameter: #{n}" unless smtp.
|
1042
|
+
raise "Missing smtp parameter: #{n}" unless smtp.key?(n)
|
985
1043
|
end
|
986
|
-
Log.log.debug
|
1044
|
+
Log.log.debug{"smtp=#{smtp}"}
|
987
1045
|
return smtp
|
988
1046
|
end
|
989
1047
|
|
@@ -992,41 +1050,41 @@ module Aspera
|
|
992
1050
|
Kernel.binding
|
993
1051
|
end
|
994
1052
|
|
995
|
-
def send_email_template(
|
996
|
-
|
997
|
-
notif_template = options.get_option(:notif_template,is_type: email_template_default.nil? ? :mandatory : :optional) || email_template_default
|
1053
|
+
def send_email_template(email_template_default: nil, values: {})
|
1054
|
+
values[:to] ||= options.get_option(:notif_to, is_type: :mandatory)
|
1055
|
+
notif_template = options.get_option(:notif_template, is_type: email_template_default.nil? ? :mandatory : :optional) || email_template_default
|
998
1056
|
mail_conf = email_settings
|
999
|
-
|
1000
|
-
|
1057
|
+
values[:from_name] ||= mail_conf[:from_name]
|
1058
|
+
values[:from_email] ||= mail_conf[:from_email]
|
1001
1059
|
%i[from_name from_email].each do |n|
|
1002
|
-
raise "Missing email parameter: #{n}" unless
|
1060
|
+
raise "Missing email parameter: #{n}" unless values.key?(n)
|
1003
1061
|
end
|
1004
1062
|
start_options = [mail_conf[:domain]]
|
1005
|
-
start_options.push(mail_conf[:username],mail_conf[:password]
|
1006
|
-
# create a binding with only variables defined in
|
1063
|
+
start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
|
1064
|
+
# create a binding with only variables defined in values
|
1007
1065
|
template_binding = empty_binding
|
1008
1066
|
# add variables to binding
|
1009
|
-
|
1067
|
+
values.each do |k, v|
|
1010
1068
|
raise "key (#{k.class}) must be Symbol" unless k.is_a?(Symbol)
|
1011
|
-
template_binding.local_variable_set(k,v)
|
1069
|
+
template_binding.local_variable_set(k, v)
|
1012
1070
|
end
|
1013
1071
|
# execute template
|
1014
1072
|
msg_with_headers = ERB.new(notif_template).result(template_binding)
|
1015
|
-
Log.dump(:msg_with_headers,msg_with_headers)
|
1073
|
+
Log.dump(:msg_with_headers, msg_with_headers)
|
1016
1074
|
smtp = Net::SMTP.new(mail_conf[:server], mail_conf[:port])
|
1017
1075
|
smtp.enable_starttls if mail_conf[:tls]
|
1018
1076
|
smtp.start(*start_options) do |smtp_session|
|
1019
|
-
smtp_session.send_message(msg_with_headers,
|
1077
|
+
smtp_session.send_message(msg_with_headers, values[:from_email], values[:to])
|
1020
1078
|
end
|
1021
1079
|
end
|
1022
1080
|
|
1023
1081
|
def save_presets_to_config_file
|
1024
1082
|
raise 'no configuration loaded' if @config_presets.nil?
|
1025
1083
|
FileUtils.mkdir_p(@main_folder) unless Dir.exist?(@main_folder)
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1084
|
+
Log.log.debug{"Writing #{@option_config_file}"}
|
1085
|
+
File.write(@option_config_file, @config_presets.to_yaml)
|
1086
|
+
Environment.restrict_file_access(@main_folder)
|
1087
|
+
Environment.restrict_file_access(@option_config_file)
|
1030
1088
|
end
|
1031
1089
|
|
1032
1090
|
# returns [String] name if config_presets has default
|
@@ -1037,53 +1095,114 @@ module Aspera
|
|
1037
1095
|
Log.log.debug('skip default config')
|
1038
1096
|
return nil
|
1039
1097
|
end
|
1040
|
-
if @config_presets.
|
1041
|
-
|
1098
|
+
if @config_presets.key?(CONF_PRESET_DEFAULT) &&
|
1099
|
+
@config_presets[CONF_PRESET_DEFAULT].key?(plugin_sym.to_s)
|
1042
1100
|
default_config_name = @config_presets[CONF_PRESET_DEFAULT][plugin_sym.to_s]
|
1043
|
-
if !@config_presets.
|
1044
|
-
Log.log.error
|
1045
|
-
|
1046
|
-
|
1101
|
+
if !@config_presets.key?(default_config_name)
|
1102
|
+
Log.log.error do
|
1103
|
+
"Default config name [#{default_config_name}] specified for plugin [#{plugin_sym}], but it does not exist in config file.\n"\
|
1104
|
+
'Please fix the issue: either create preset with one parameter: '\
|
1105
|
+
"(#{@info[:name]} config id #{default_config_name} init @json:'{}') or remove default (#{@info[:name]} config id default remove #{plugin_sym})."
|
1106
|
+
end
|
1047
1107
|
end
|
1048
|
-
raise CliError,"Config name [#{default_config_name}] must be a hash, check config file." if !@config_presets[default_config_name].is_a?(Hash)
|
1108
|
+
raise CliError, "Config name [#{default_config_name}] must be a hash, check config file." if !@config_presets[default_config_name].is_a?(Hash)
|
1049
1109
|
return default_config_name
|
1050
1110
|
end
|
1051
1111
|
return nil
|
1052
1112
|
end # get_plugin_default_config_name
|
1053
1113
|
|
1114
|
+
ALLOWED_KEYS = %i[password username description].freeze
|
1115
|
+
def execute_vault
|
1116
|
+
command = options.get_next_command(%i[list show create delete password])
|
1117
|
+
case command
|
1118
|
+
when :list
|
1119
|
+
return {type: :object_list, data: vault.list}
|
1120
|
+
when :show
|
1121
|
+
return {type: :single_object, data: vault.get(label: options.get_next_argument('label'))}
|
1122
|
+
when :create
|
1123
|
+
label = options.get_next_argument('label')
|
1124
|
+
info = options.get_next_argument('info Hash')
|
1125
|
+
raise 'info must be Hash' unless info.is_a?(Hash)
|
1126
|
+
info = info.symbolize_keys
|
1127
|
+
info[:label] = label
|
1128
|
+
vault.set(info)
|
1129
|
+
return Main.result_status('Password added')
|
1130
|
+
when :delete
|
1131
|
+
vault.delete(label: options.get_next_argument('label'))
|
1132
|
+
return Main.result_status('Password deleted')
|
1133
|
+
when :password
|
1134
|
+
raise 'Vault does not support password change' unless vault.respond_to?(:password=)
|
1135
|
+
new_password = options.get_next_argument('new_password')
|
1136
|
+
vault.password = new_password
|
1137
|
+
vault.save
|
1138
|
+
return Main.result_status('Password updated')
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
def vault_value(name)
|
1143
|
+
m = name.match(/^(.+)\.(.+)$/)
|
1144
|
+
raise 'vault name shall match <name>.<param>' if m.nil?
|
1145
|
+
info = vault.get(label: m[1])
|
1146
|
+
# raise "no such vault entry: #{m[1]}" if info.nil?
|
1147
|
+
value = info[m[2].to_sym]
|
1148
|
+
raise "no such entry value: #{m[2]}" if value.nil?
|
1149
|
+
return value
|
1150
|
+
end
|
1151
|
+
|
1054
1152
|
def vault
|
1055
1153
|
if @vault.nil?
|
1056
|
-
vault_info = options.get_option(:
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1154
|
+
vault_info = options.get_option(:vault) || {'type' => 'file', 'name' => 'vault.bin'}
|
1155
|
+
vault_password = options.get_option(:vault_password, is_type: :mandatory)
|
1156
|
+
raise 'vault must be Hash' unless vault_info.is_a?(Hash)
|
1157
|
+
vault_type = vault_info['type'] || 'file'
|
1158
|
+
vault_name = vault_info['name'] || (vault_type.eql?('file') ? 'vault.bin' : PROGRAM_NAME)
|
1159
|
+
case vault_type
|
1160
|
+
when 'file'
|
1161
|
+
# absolute_path? introduced in ruby 2.7
|
1162
|
+
vault_path = vault_name.eql?(File.absolute_path(vault_name)) ? vault_name : File.join(@main_folder, vault_name)
|
1163
|
+
@vault = Keychain::EncryptedHash.new(vault_path, vault_password)
|
1164
|
+
when 'system'
|
1062
1165
|
case Environment.os
|
1063
1166
|
when Environment::OS_X
|
1064
|
-
@vault = Keychain::
|
1065
|
-
when Environment::OS_WINDOWS,Environment::OS_LINUX,Environment::OS_AIX
|
1167
|
+
@vault = Keychain::MacosSystem.new(vault_name, vault_password)
|
1168
|
+
when Environment::OS_WINDOWS, Environment::OS_LINUX, Environment::OS_AIX
|
1066
1169
|
raise 'not implemented'
|
1067
|
-
else raise 'Error'
|
1170
|
+
else raise 'Error, OS not supported'
|
1068
1171
|
end
|
1069
|
-
when NilClass
|
1070
|
-
# keep nil
|
1071
1172
|
else
|
1072
|
-
raise CliBadArgument,
|
1173
|
+
raise CliBadArgument, "Unknown vault type: #{vault_type}"
|
1073
1174
|
end
|
1074
1175
|
end
|
1075
1176
|
raise 'No vault defined' if @vault.nil?
|
1076
1177
|
@vault
|
1077
1178
|
end
|
1078
1179
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1180
|
+
# version of URL without trailing "/" and removing default port
|
1181
|
+
def canonical_url(url)
|
1182
|
+
url.gsub(%r{/+$}, '').gsub(%r{^(https://[^/]+):443$}, '\1')
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
def lookup_preset(url:, username:)
|
1186
|
+
# remove extra info to maximize match
|
1187
|
+
url = canonical_url(url)
|
1188
|
+
Log.log.debug{"Lookup preset for #{username}@#{url}"}
|
1189
|
+
@config_presets.each do |_k, v|
|
1190
|
+
next unless v.is_a?(Hash)
|
1191
|
+
conf_url = v['url'].is_a?(String) ? canonical_url(v['url']) : nil
|
1192
|
+
return v if conf_url.eql?(url) && v['username'].eql?(username)
|
1193
|
+
end
|
1194
|
+
nil
|
1195
|
+
end
|
1196
|
+
|
1197
|
+
def lookup_secret(url:, username:, mandatory: false)
|
1198
|
+
secret = options.get_option(:secret)
|
1083
1199
|
if secret.nil?
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1200
|
+
conf = lookup_preset(url: url, username: username)
|
1201
|
+
if conf.is_a?(Hash)
|
1202
|
+
Log.log.debug{"Found preset #{conf} with URL and username"}
|
1203
|
+
secret = conf['password']
|
1204
|
+
end
|
1205
|
+
raise "Please provide secret for #{username} using option: secret or by setting a preset for #{username}@#{url}." if secret.nil? && mandatory
|
1087
1206
|
end
|
1088
1207
|
return secret
|
1089
1208
|
end
|
@@ -1093,33 +1212,33 @@ module Aspera
|
|
1093
1212
|
params[:plugin_name] = AOC_COMMAND_CURRENT
|
1094
1213
|
organization = AoC.parse_url(params[:instance_url]).first
|
1095
1214
|
# if not defined by user, generate name
|
1096
|
-
params[:preset_name] = [params[:application],organization].join('_') if params[:preset_name].nil?
|
1215
|
+
params[:preset_name] = [params[:application], organization].join('_') if params[:preset_name].nil?
|
1097
1216
|
self.format.display_status("Preparing preset: #{params[:preset_name]}")
|
1098
1217
|
# init defaults if necessary
|
1099
1218
|
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
1100
|
-
option_override = options.get_option(:override,is_type: :mandatory)
|
1101
|
-
params[:option_default] = options.get_option(:default,is_type: :mandatory)
|
1102
|
-
Log.log.error
|
1103
|
-
raise CliError,"A default configuration already exists for plugin '#{params[:plugin_name]}' (use --override=yes or --default=no)" \
|
1104
|
-
if !option_override && params[:option_default] && @config_presets[CONF_PRESET_DEFAULT].
|
1105
|
-
raise CliError,"Preset already exists: #{params[:preset_name]} (use --override=yes or --id=<name>)" \
|
1106
|
-
if !option_override && @config_presets.
|
1219
|
+
option_override = options.get_option(:override, is_type: :mandatory)
|
1220
|
+
params[:option_default] = options.get_option(:default, is_type: :mandatory)
|
1221
|
+
Log.log.error{"override=#{option_override} -> #{option_override.class}"}
|
1222
|
+
raise CliError, "A default configuration already exists for plugin '#{params[:plugin_name]}' (use --override=yes or --default=no)" \
|
1223
|
+
if !option_override && params[:option_default] && @config_presets[CONF_PRESET_DEFAULT].key?(params[:plugin_name])
|
1224
|
+
raise CliError, "Preset already exists: #{params[:preset_name]} (use --override=yes or --id=<name>)" \
|
1225
|
+
if !option_override && @config_presets.key?(params[:preset_name])
|
1107
1226
|
# lets see if path to priv key is provided
|
1108
1227
|
private_key_path = options.get_option(:pkeypath)
|
1109
1228
|
# give a chance to provide
|
1110
1229
|
if private_key_path.nil?
|
1111
1230
|
self.format.display_status('Please provide path to your private RSA key, or empty to generate one:')
|
1112
|
-
private_key_path = options.get_option(:pkeypath,is_type: :mandatory).to_s
|
1231
|
+
private_key_path = options.get_option(:pkeypath, is_type: :mandatory).to_s
|
1113
1232
|
end
|
1114
1233
|
# else generate path
|
1115
1234
|
if private_key_path.empty?
|
1116
|
-
private_key_path = File.join(@main_folder,DEFAULT_PRIV_KEY_FILENAME)
|
1235
|
+
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME)
|
1117
1236
|
end
|
1118
1237
|
if File.exist?(private_key_path)
|
1119
1238
|
self.format.display_status('Using existing key:')
|
1120
1239
|
else
|
1121
1240
|
self.format.display_status("Generating #{DEFAULT_PRIVKEY_LENGTH} bit RSA key...")
|
1122
|
-
generate_rsa_private_key(private_key_path,DEFAULT_PRIVKEY_LENGTH)
|
1241
|
+
generate_rsa_private_key(private_key_path, DEFAULT_PRIVKEY_LENGTH)
|
1123
1242
|
self.format.display_status('Created:')
|
1124
1243
|
end
|
1125
1244
|
self.format.display_status(private_key_path)
|
@@ -1127,7 +1246,7 @@ module Aspera
|
|
1127
1246
|
# declare command line options for AoC
|
1128
1247
|
require 'aspera/cli/plugins/aoc'
|
1129
1248
|
# make username mandatory for jwt, this triggers interactive input
|
1130
|
-
options.get_option(:username,is_type: :mandatory)
|
1249
|
+
options.get_option(:username, is_type: :mandatory)
|
1131
1250
|
# instanciate AoC plugin, so that command line options are known
|
1132
1251
|
aoc_api = self.class.plugin_class(params[:plugin_name]).new(@agents.merge({skip_basic_auth_options: true, private_key_path: private_key_path})).aoc_api
|
1133
1252
|
auto_set_pub_key = false
|
@@ -1146,7 +1265,7 @@ module Aspera
|
|
1146
1265
|
end
|
1147
1266
|
else
|
1148
1267
|
self.format.display_status('Using organization specific client_id.')
|
1149
|
-
if options.get_option(:client_id).nil? || options.get_option(:client_secret,is_type: :optional).nil?
|
1268
|
+
if options.get_option(:client_id).nil? || options.get_option(:client_secret, is_type: :optional).nil?
|
1150
1269
|
self.format.display_status('Please login to your Aspera on Cloud instance.'.red)
|
1151
1270
|
self.format.display_status('Go to: Apps->Admin->Organization->Integrations')
|
1152
1271
|
self.format.display_status('Create or check if there is an existing integration named:')
|
@@ -1157,8 +1276,8 @@ module Aspera
|
|
1157
1276
|
self.format.display_status('Please enter:'.red)
|
1158
1277
|
end
|
1159
1278
|
OpenApplication.instance.uri("#{params[:instance_url]}/#{AOC_PATH_API_CLIENTS}")
|
1160
|
-
options.get_option(:client_id,is_type: :mandatory)
|
1161
|
-
options.get_option(:client_secret,is_type: :mandatory)
|
1279
|
+
options.get_option(:client_id, is_type: :mandatory)
|
1280
|
+
options.get_option(:client_secret, is_type: :mandatory)
|
1162
1281
|
use_browser_authentication = true
|
1163
1282
|
end
|
1164
1283
|
if use_browser_authentication
|
@@ -1171,13 +1290,13 @@ module Aspera
|
|
1171
1290
|
end
|
1172
1291
|
myself = aoc_api.read('self')[:data]
|
1173
1292
|
if auto_set_pub_key
|
1174
|
-
raise CliError,'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
|
1293
|
+
raise CliError, 'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
|
1175
1294
|
self.format.display_status('Updating profile with new key')
|
1176
|
-
aoc_api.update("users/#{myself['id']}",{'public_key' => pub_key_pem})
|
1295
|
+
aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
|
1177
1296
|
end
|
1178
1297
|
if auto_set_jwt
|
1179
1298
|
self.format.display_status('Enabling JWT for client')
|
1180
|
-
aoc_api.update("clients/#{options.get_option(:client_id)}",{'jwt_grant_enabled' => true,'explicit_authorization_required' => false})
|
1299
|
+
aoc_api.update("clients/#{options.get_option(:client_id)}", {'jwt_grant_enabled' => true, 'explicit_authorization_required' => false})
|
1181
1300
|
end
|
1182
1301
|
self.format.display_status("Creating new config preset: #{params[:preset_name]}")
|
1183
1302
|
@config_presets[params[:preset_name]] = {
|
@@ -1197,34 +1316,34 @@ module Aspera
|
|
1197
1316
|
def wizard_faspex5(params)
|
1198
1317
|
self.format.display_status('Detected: Faspex v5'.bold)
|
1199
1318
|
# if not defined by user, generate unique name
|
1200
|
-
params[:preset_name] = [params[:application]
|
1319
|
+
params[:preset_name] = [params[:application]].concat(URI.parse(params[:instance_url]).host.gsub(/[^a-z0-9.]/, '').split('.')).join('_') \
|
1201
1320
|
if params[:preset_name].nil?
|
1202
1321
|
self.format.display_status("Preparing preset: #{params[:preset_name]}")
|
1203
1322
|
# init defaults if necessary
|
1204
1323
|
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
1205
|
-
option_override = options.get_option(:override,is_type: :mandatory)
|
1206
|
-
params[:option_default] = options.get_option(:default,is_type: :mandatory)
|
1207
|
-
Log.log.error
|
1208
|
-
raise CliError,"A default configuration already exists for plugin '#{params[:plugin_name]}' (use --override=yes or --default=no)" \
|
1209
|
-
if !option_override && params[:option_default] && @config_presets[CONF_PRESET_DEFAULT].
|
1210
|
-
raise CliError,"Preset already exists: #{params[:preset_name]} (use --override=yes or --id=<name>)" \
|
1211
|
-
if !option_override && @config_presets.
|
1324
|
+
option_override = options.get_option(:override, is_type: :mandatory)
|
1325
|
+
params[:option_default] = options.get_option(:default, is_type: :mandatory)
|
1326
|
+
Log.log.error{"override=#{option_override} -> #{option_override.class}"}
|
1327
|
+
raise CliError, "A default configuration already exists for plugin '#{params[:plugin_name]}' (use --override=yes or --default=no)" \
|
1328
|
+
if !option_override && params[:option_default] && @config_presets[CONF_PRESET_DEFAULT].key?(params[:plugin_name])
|
1329
|
+
raise CliError, "Preset already exists: #{params[:preset_name]} (use --override=yes or --id=<name>)" \
|
1330
|
+
if !option_override && @config_presets.key?(params[:preset_name])
|
1212
1331
|
# lets see if path to priv key is provided
|
1213
1332
|
private_key_path = options.get_option(:pkeypath)
|
1214
1333
|
# give a chance to provide
|
1215
1334
|
if private_key_path.nil?
|
1216
1335
|
self.format.display_status('Please provide path to your private RSA key, or empty to generate one:')
|
1217
|
-
private_key_path = options.get_option(:pkeypath,is_type: :mandatory).to_s
|
1336
|
+
private_key_path = options.get_option(:pkeypath, is_type: :mandatory).to_s
|
1218
1337
|
end
|
1219
1338
|
# else generate path
|
1220
1339
|
if private_key_path.empty?
|
1221
|
-
private_key_path = File.join(@main_folder,DEFAULT_PRIV_KEY_FILENAME)
|
1340
|
+
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME)
|
1222
1341
|
end
|
1223
1342
|
if File.exist?(private_key_path)
|
1224
1343
|
self.format.display_status('Using existing key:')
|
1225
1344
|
else
|
1226
1345
|
self.format.display_status("Generating #{DEFAULT_PRIVKEY_LENGTH} bit RSA key...")
|
1227
|
-
generate_rsa_private_key(private_key_path,DEFAULT_PRIVKEY_LENGTH)
|
1346
|
+
generate_rsa_private_key(private_key_path, DEFAULT_PRIVKEY_LENGTH)
|
1228
1347
|
self.format.display_status('Created:')
|
1229
1348
|
end
|
1230
1349
|
self.format.display_status(private_key_path)
|
@@ -1245,8 +1364,8 @@ module Aspera
|
|
1245
1364
|
:username.to_s => options.get_option(:username),
|
1246
1365
|
:auth.to_s => :jwt.to_s,
|
1247
1366
|
:private_key.to_s => '@file:' + private_key_path,
|
1248
|
-
:client_id.to_s => options.get_option(:client_id,is_type: :mandatory),
|
1249
|
-
:client_secret.to_s => options.get_option(:client_secret,is_type: :mandatory)
|
1367
|
+
:client_id.to_s => options.get_option(:client_id, is_type: :mandatory),
|
1368
|
+
:client_secret.to_s => options.get_option(:client_secret, is_type: :mandatory)
|
1250
1369
|
}
|
1251
1370
|
params[:test_args] = "#{params[:plugin_name]} user profile show"
|
1252
1371
|
end
|