aspera-cli 4.13.0 → 4.15.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/CHANGELOG.md +81 -7
- data/CONTRIBUTING.md +22 -6
- data/README.md +2038 -1080
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/dascli +1 -1
- data/examples/proxy.pac +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +219 -159
- data/lib/aspera/ascmd.rb +25 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -179
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +104 -156
- data/lib/aspera/cli/manager.rb +259 -209
- data/lib/aspera/cli/plugin.rb +123 -63
- data/lib/aspera/cli/plugins/alee.rb +2 -3
- data/lib/aspera/cli/plugins/aoc.rb +341 -261
- data/lib/aspera/cli/plugins/ats.rb +22 -21
- data/lib/aspera/cli/plugins/bss.rb +5 -5
- data/lib/aspera/cli/plugins/config.rb +578 -627
- data/lib/aspera/cli/plugins/console.rb +44 -6
- data/lib/aspera/cli/plugins/cos.rb +15 -17
- data/lib/aspera/cli/plugins/faspex.rb +114 -100
- data/lib/aspera/cli/plugins/faspex5.rb +411 -264
- data/lib/aspera/cli/plugins/node.rb +354 -259
- data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
- data/lib/aspera/cli/plugins/preview.rb +82 -90
- data/lib/aspera/cli/plugins/server.rb +79 -32
- data/lib/aspera/cli/plugins/shares.rb +55 -42
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +66 -73
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +12 -8
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +24 -9
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +25 -21
- data/lib/aspera/fasp/agent_direct.rb +89 -103
- data/lib/aspera/fasp/agent_httpgw.rb +231 -149
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -32
- data/lib/aspera/fasp/error_info.rb +4 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +53 -195
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +71 -37
- data/lib/aspera/fasp/parameters.yaml +76 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +7 -6
- data/lib/aspera/fasp/uri.rb +26 -24
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +14 -4
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +58 -16
- data/lib/aspera/node.rb +157 -92
- data/lib/aspera/oauth.rb +37 -19
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +73 -16
- data/lib/aspera/preview/utils.rb +21 -28
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +136 -68
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +18 -15
- data/lib/aspera/ssh.rb +5 -2
- data/lib/aspera/sync.rb +127 -119
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +34 -17
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -186
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/data/7 +0 -0
- data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,25 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# cspell:ignore initdemo genkey asperasoft
|
3
4
|
require 'aspera/cli/basic_auth_plugin'
|
4
5
|
require 'aspera/cli/extended_value'
|
5
6
|
require 'aspera/cli/version'
|
6
7
|
require 'aspera/cli/formatter'
|
7
8
|
require 'aspera/cli/info'
|
9
|
+
require 'aspera/cli/transfer_progress'
|
8
10
|
require 'aspera/fasp/installation'
|
11
|
+
require 'aspera/fasp/products'
|
9
12
|
require 'aspera/fasp/parameters'
|
10
13
|
require 'aspera/fasp/transfer_spec'
|
11
14
|
require 'aspera/fasp/error_info'
|
15
|
+
require 'aspera/keychain/encrypted_hash'
|
16
|
+
require 'aspera/keychain/macos_security'
|
12
17
|
require 'aspera/proxy_auto_config'
|
13
18
|
require 'aspera/open_application'
|
14
19
|
require 'aspera/persistency_action_once'
|
15
20
|
require 'aspera/id_generator'
|
16
|
-
require 'aspera/
|
17
|
-
require 'aspera/
|
18
|
-
require 'aspera/aoc'
|
21
|
+
require 'aspera/persistency_folder'
|
22
|
+
require 'aspera/line_logger'
|
19
23
|
require 'aspera/rest'
|
20
|
-
require '
|
21
|
-
require 'base64'
|
22
|
-
require 'net/smtp'
|
24
|
+
require 'aspera/log'
|
23
25
|
require 'open3'
|
24
26
|
require 'date'
|
25
27
|
require 'erb'
|
@@ -38,27 +40,21 @@ module Aspera
|
|
38
40
|
CONF_PRESET_VERSION = 'version'
|
39
41
|
CONF_PRESET_DEFAULT = 'default'
|
40
42
|
CONF_PRESET_GLOBAL = 'global_common_defaults'
|
43
|
+
GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
|
41
44
|
CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
|
42
45
|
CONF_GLOBAL_SYM = :config
|
43
|
-
# old tool name
|
44
|
-
PROGRAM_NAME_V1 = 'aslmcli'
|
45
|
-
PROGRAM_NAME_V2 = 'mlia'
|
46
|
-
# default redirect for AoC web auth
|
47
|
-
DEFAULT_REDIRECT = 'http://localhost:12345'
|
48
46
|
# folder containing custom plugins in user's config folder
|
49
47
|
ASPERA_PLUGINS_FOLDERNAME = 'plugins'
|
48
|
+
PERSISTENCY_FOLDER = 'persist_store'
|
50
49
|
RUBY_FILE_EXT = '.rb'
|
51
|
-
|
52
|
-
AOC_COMMAND_V2 = 'aspera'
|
53
|
-
AOC_COMMAND_V3 = 'aoc'
|
54
|
-
AOC_COMMAND_CURRENT = AOC_COMMAND_V3
|
50
|
+
ASPERA = 'aspera'
|
55
51
|
SERVER_COMMAND = 'server'
|
52
|
+
APP_NAME_SDK = 'sdk'
|
56
53
|
CONNECT_WEB_URL = 'https://d3gcli72yxqn2z.cloudfront.net/connect'
|
57
|
-
CONNECT_VERSIONS = 'connectversions.js'
|
54
|
+
CONNECT_VERSIONS = 'connectversions.js' # cspell: disable-line
|
58
55
|
TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/aspera_transfer_sdk'
|
59
56
|
DEMO = 'demo'
|
60
|
-
DEMO_SERVER_PRESET = 'demoserver'
|
61
|
-
AOC_PATH_API_CLIENTS = 'admin/api-clients'
|
57
|
+
DEMO_SERVER_PRESET = 'demoserver' # cspell: disable-line
|
62
58
|
EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
|
63
59
|
From: <%=from_name%> <<%=from_email%>>
|
64
60
|
To: <<%=to%>>
|
@@ -67,135 +63,308 @@ module Aspera
|
|
67
63
|
This email was sent to test #{PROGRAM_NAME}.
|
68
64
|
END_OF_TEMPLATE
|
69
65
|
# special extended values
|
70
|
-
|
71
|
-
|
72
|
-
EXTV_VAULT = :vault
|
66
|
+
EXTEND_PRESET = :preset
|
67
|
+
EXTEND_VAULT = :vault
|
73
68
|
PRESET_DIG_SEPARATOR = '.'
|
74
69
|
DEFAULT_CHECK_NEW_VERSION_DAYS = 7
|
75
|
-
DEFAULT_PRIV_KEY_FILENAME = '
|
76
|
-
|
70
|
+
DEFAULT_PRIV_KEY_FILENAME = 'my_private_key.pem' # pragma: allowlist secret
|
71
|
+
DEFAULT_PRIV_KEY_LENGTH = 4096
|
77
72
|
COFFEE_IMAGE = 'https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg'
|
73
|
+
WIZARD_RESULT_KEYS = %i[preset_value test_args].freeze
|
74
|
+
GEM_CHECK_DATE_FMT = '%Y/%m/%d'
|
75
|
+
# for testing only
|
76
|
+
SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse) # cspell: disable-line
|
78
77
|
private_constant :DEFAULT_CONFIG_FILENAME,
|
79
78
|
:CONF_PRESET_CONFIG,
|
80
79
|
:CONF_PRESET_VERSION,
|
81
80
|
:CONF_PRESET_DEFAULT,
|
82
81
|
:CONF_PRESET_GLOBAL,
|
83
|
-
:PROGRAM_NAME_V1,
|
84
|
-
:PROGRAM_NAME_V2,
|
85
|
-
:DEFAULT_REDIRECT,
|
86
82
|
:ASPERA_PLUGINS_FOLDERNAME,
|
87
83
|
:RUBY_FILE_EXT,
|
88
|
-
:
|
89
|
-
:AOC_COMMAND_V2,
|
90
|
-
:AOC_COMMAND_V3,
|
91
|
-
:AOC_COMMAND_CURRENT,
|
84
|
+
:ASPERA,
|
92
85
|
:DEMO,
|
93
86
|
:TRANSFER_SDK_ARCHIVE_URL,
|
94
|
-
:AOC_PATH_API_CLIENTS,
|
95
87
|
:DEMO_SERVER_PRESET,
|
96
88
|
:EMAIL_TEST_TEMPLATE,
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:EXTV_VAULT,
|
89
|
+
:EXTEND_PRESET,
|
90
|
+
:EXTEND_VAULT,
|
100
91
|
:DEFAULT_CHECK_NEW_VERSION_DAYS,
|
101
92
|
:DEFAULT_PRIV_KEY_FILENAME,
|
102
93
|
:SERVER_COMMAND,
|
103
94
|
:PRESET_DIG_SEPARATOR,
|
104
|
-
:COFFEE_IMAGE
|
95
|
+
:COFFEE_IMAGE,
|
96
|
+
:WIZARD_RESULT_KEYS,
|
97
|
+
:SELF_SIGNED_CERT,
|
98
|
+
:PERSISTENCY_FOLDER,
|
99
|
+
:DEFAULT_PRIV_KEY_LENGTH
|
100
|
+
|
101
|
+
class << self
|
102
|
+
def generate_rsa_private_key(path:, length: DEFAULT_PRIV_KEY_LENGTH)
|
103
|
+
require 'openssl'
|
104
|
+
priv_key = OpenSSL::PKey::RSA.new(length)
|
105
|
+
File.write(path, priv_key.to_s)
|
106
|
+
File.write("#{path}.pub", priv_key.public_key.to_s)
|
107
|
+
Environment.restrict_file_access(path)
|
108
|
+
Environment.restrict_file_access("#{path}.pub")
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
# folder containing plugins in the gem's main folder
|
113
|
+
def gem_plugins_folder
|
114
|
+
File.dirname(File.expand_path(__FILE__))
|
115
|
+
end
|
116
|
+
|
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
|
+
# @return main folder where code is, i.e. .../lib
|
124
|
+
# go up as many times as englobing modules (not counting class, as it is a file)
|
125
|
+
def gem_src_root
|
126
|
+
File.expand_path(module_full_name.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
|
127
|
+
end
|
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}")
|
134
|
+
end
|
135
|
+
|
136
|
+
# deep clone hash so that it does not get modified in case of display and secret hide
|
137
|
+
def protect_presets(val)
|
138
|
+
return JSON.parse(JSON.generate(val))
|
139
|
+
end
|
140
|
+
|
141
|
+
# return product family folder (~/.aspera)
|
142
|
+
def module_family_folder
|
143
|
+
user_home_folder = Dir.home
|
144
|
+
raise Cli::Error, "Home folder does not exist: #{user_home_folder}. Check your user environment." unless Dir.exist?(user_home_folder)
|
145
|
+
return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
|
146
|
+
end
|
147
|
+
|
148
|
+
# return product config folder (~/.aspera/<name>)
|
149
|
+
def default_app_main_folder(app_name:)
|
150
|
+
raise 'app_name must be a non-empty String' unless app_name.is_a?(String) && !app_name.empty?
|
151
|
+
return File.join(module_family_folder, app_name)
|
152
|
+
end
|
153
|
+
end # self
|
154
|
+
|
105
155
|
def initialize(env, params)
|
106
|
-
raise 'env and params must be Hash' unless env.is_a?(Hash) && params.is_a?(Hash)
|
107
|
-
raise 'missing param' unless %i[
|
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)
|
158
|
+
# we need to defer parsing of options until we have the config file, so we can use @extend with @preset
|
108
159
|
super(env)
|
109
160
|
@info = params
|
110
|
-
@main_folder = default_app_main_folder
|
111
161
|
@plugins = {}
|
112
162
|
@plugin_lookup_folders = []
|
113
163
|
@use_plugin_defaults = true
|
114
164
|
@config_presets = nil
|
165
|
+
@config_checksum_on_disk = nil
|
115
166
|
@connect_versions = nil
|
116
167
|
@vault = nil
|
117
|
-
@conf_file_default = File.join(@main_folder, DEFAULT_CONFIG_FILENAME)
|
118
|
-
@option_config_file = @conf_file_default
|
119
168
|
@pac_exec = nil
|
169
|
+
@sdk_default_location = false
|
170
|
+
@option_insecure = false
|
171
|
+
@option_ignore_cert_host_port = []
|
172
|
+
@option_http_options = {}
|
173
|
+
@ssl_warned_urls = []
|
174
|
+
@option_cache_tokens = true
|
175
|
+
@proxy_credentials = nil
|
176
|
+
@main_folder = nil
|
177
|
+
@option_config_file = nil
|
178
|
+
@certificate_store = nil
|
179
|
+
@certificate_paths = nil
|
180
|
+
@progress_bar = nil
|
181
|
+
# option to set main folder
|
182
|
+
options.declare(
|
183
|
+
:home, 'Home folder for tool',
|
184
|
+
handler: {o: self, m: :main_folder},
|
185
|
+
types: String,
|
186
|
+
default: self.class.default_app_main_folder(app_name: @info[:name]))
|
187
|
+
options.parse_options!
|
120
188
|
Log.log.debug{"#{@info[:name]} folder: #{@main_folder}"}
|
121
|
-
#
|
189
|
+
# data persistency manager
|
190
|
+
env[:persistency] = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
191
|
+
# set folders for plugin lookup
|
122
192
|
add_plugin_lookup_folder(self.class.gem_plugins_folder)
|
123
193
|
add_plugin_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
124
|
-
#
|
125
|
-
options.
|
126
|
-
|
194
|
+
# option to set config file
|
195
|
+
options.declare(
|
196
|
+
:config_file, 'Path to YAML file with preset configuration',
|
197
|
+
handler: {o: self, m: :option_config_file},
|
198
|
+
default: File.join(@main_folder, DEFAULT_CONFIG_FILENAME))
|
127
199
|
options.parse_options!
|
128
|
-
# read
|
200
|
+
# read config file (set @config_presets)
|
129
201
|
read_config_file
|
130
202
|
# add preset handler (needed for smtp)
|
131
|
-
ExtendedValue.instance.set_handler(
|
132
|
-
ExtendedValue.instance.set_handler(
|
133
|
-
ExtendedValue.instance.set_handler(EXTV_VAULT, lambda{|v|vault_value(v)})
|
203
|
+
ExtendedValue.instance.set_handler(EXTEND_PRESET, lambda{|v|preset_by_name(v)})
|
204
|
+
ExtendedValue.instance.set_handler(EXTEND_VAULT, lambda{|v|vault_value(v)})
|
134
205
|
# load defaults before it can be overridden
|
135
206
|
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
207
|
+
# vault options
|
208
|
+
options.declare(:secret, 'Secret for access keys')
|
209
|
+
options.declare(:vault, 'Vault for secrets', types: Hash)
|
210
|
+
options.declare(:vault_password, 'Vault password')
|
136
211
|
options.parse_options!
|
137
|
-
options
|
138
|
-
|
139
|
-
|
140
|
-
options.
|
141
|
-
options.
|
142
|
-
options.
|
143
|
-
options.
|
144
|
-
|
145
|
-
options.
|
146
|
-
options.
|
147
|
-
options.
|
148
|
-
options.
|
149
|
-
|
150
|
-
options.
|
151
|
-
options.
|
152
|
-
options.
|
153
|
-
options.
|
154
|
-
options.
|
155
|
-
|
156
|
-
options.
|
157
|
-
options.
|
158
|
-
options.
|
159
|
-
|
160
|
-
options.
|
161
|
-
options.
|
162
|
-
options.
|
163
|
-
options.
|
164
|
-
options.
|
165
|
-
options.
|
166
|
-
options.
|
167
|
-
options.set_option(:sdk_url, TRANSFER_SDK_ARCHIVE_URL)
|
168
|
-
options.set_option(:sdk_folder, File.join(@main_folder, 'sdk'))
|
169
|
-
options.set_option(:override, :no)
|
212
|
+
# declare generic plugin options only after handlers are declared
|
213
|
+
Plugin.declare_generic_options(options)
|
214
|
+
# configuration options
|
215
|
+
options.declare(:no_default, 'Do not load default configuration for plugin', values: :none, short: 'N') { @use_plugin_defaults = false }
|
216
|
+
options.declare(:preset, 'Load the named option preset from current config file', short: 'P', handler: {o: self, m: :option_preset})
|
217
|
+
options.declare(:version_check_days, 'Period in days to check new version (zero to disable)', coerce: Integer, default: DEFAULT_CHECK_NEW_VERSION_DAYS)
|
218
|
+
options.declare(:plugin_folder, 'Folder where to find additional plugins', handler: {o: self, m: :option_plugin_folder})
|
219
|
+
# wizard options
|
220
|
+
options.declare(:override, 'Wizard: override existing value', values: :bool, default: :no)
|
221
|
+
options.declare(:default, 'Wizard: set as default configuration for specified plugin (also: update)', values: :bool, default: true)
|
222
|
+
options.declare(:test_mode, 'Wizard: skip private key check step', values: :bool, default: false)
|
223
|
+
options.declare(:key_path, 'Wizard: path to private key for JWT')
|
224
|
+
# Transfer SDK options
|
225
|
+
options.declare(:ascp_path, 'Path to ascp', handler: {o: Fasp::Installation.instance, m: :ascp_path})
|
226
|
+
options.declare(:use_product, 'Use ascp from specified product', handler: {o: self, m: :option_use_product})
|
227
|
+
options.declare(:sdk_url, 'URL to get SDK', default: TRANSFER_SDK_ARCHIVE_URL)
|
228
|
+
options.declare(:sdk_folder, 'SDK folder path', handler: {o: Fasp::Installation.instance, m: :sdk_folder})
|
229
|
+
options.declare(:progress_bar, 'Display progress bar', values: :bool, default: Environment.terminal?)
|
230
|
+
# email options
|
231
|
+
options.declare(:smtp, 'SMTP configuration', types: Hash)
|
232
|
+
options.declare(:notify_to, 'Email recipient for notification of transfers')
|
233
|
+
options.declare(:notify_template, 'Email ERB template for notification of transfers')
|
234
|
+
# HTTP options
|
235
|
+
options.declare(:insecure, 'Do not validate any HTTPS certificate', values: :bool, handler: {o: self, m: :option_insecure}, default: :no)
|
236
|
+
options.declare(:ignore_certificate, 'List of HTTPS url where to no validate certificate', types: Array, handler: {o: self, m: :option_ignore_cert_host_port})
|
237
|
+
options.declare(:cert_stores, 'List of folder with trusted certificates', types: [Array, String], handler: {o: self, m: :trusted_cert_locations})
|
238
|
+
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 Oauth tokens', values: :bool, handler: {o: self, m: :option_cache_tokens})
|
240
|
+
options.declare(:fpac, 'Proxy auto configuration script')
|
241
|
+
options.declare(:proxy_credentials, 'HTTP proxy credentials (user and password)', types: Array)
|
170
242
|
options.parse_options!
|
243
|
+
@progress_bar = TransferProgress.new if options.get_option(:progress_bar)
|
244
|
+
# Check SDK folder is set or not, for compatibility, we check in two places
|
245
|
+
sdk_folder = Fasp::Installation.instance.sdk_folder rescue nil
|
246
|
+
if sdk_folder.nil?
|
247
|
+
@sdk_default_location = true
|
248
|
+
Log.log.debug('SDK folder is not set, checking default')
|
249
|
+
# new location
|
250
|
+
sdk_folder = self.class.default_app_main_folder(app_name: APP_NAME_SDK)
|
251
|
+
Log.log.debug{"checking: #{sdk_folder}"}
|
252
|
+
if !Dir.exist?(sdk_folder)
|
253
|
+
Log.log.debug{"not exists: #{sdk_folder}"}
|
254
|
+
# former location
|
255
|
+
former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: @info[:name]), APP_NAME_SDK)
|
256
|
+
Log.log.debug{"checking: #{former_sdk_folder}"}
|
257
|
+
sdk_folder = former_sdk_folder if Dir.exist?(former_sdk_folder)
|
258
|
+
end
|
259
|
+
Log.log.debug{"using: #{sdk_folder}"}
|
260
|
+
Fasp::Installation.instance.sdk_folder = sdk_folder
|
261
|
+
end
|
171
262
|
pac_script = options.get_option(:fpac)
|
172
263
|
# create PAC executor
|
173
264
|
@pac_exec = Aspera::ProxyAutoConfig.new(pac_script).register_uri_generic unless pac_script.nil?
|
174
|
-
|
175
|
-
if !
|
176
|
-
raise
|
177
|
-
|
178
|
-
@pac_exec.proxy_user =
|
179
|
-
@pac_exec.proxy_pass =
|
265
|
+
proxy_user_pass = options.get_option(:proxy_credentials)
|
266
|
+
if !proxy_user_pass.nil?
|
267
|
+
raise Cli::BadArgument, "proxy_credentials shall have two elements (#{proxy_user_pass.length})" unless proxy_user_pass.length.eql?(2)
|
268
|
+
@proxy_credentials = {user: proxy_user_pass[0], pass: proxy_user_pass[1]}
|
269
|
+
@pac_exec.proxy_user = @proxy_credentials[:user]
|
270
|
+
@pac_exec.proxy_pass = @proxy_credentials[:pass]
|
271
|
+
end
|
272
|
+
Rest.set_parameters(
|
273
|
+
user_agent: PROGRAM_NAME,
|
274
|
+
session_cb: lambda{|http_session|update_http_session(http_session)},
|
275
|
+
progress_bar: @progress_bar)
|
276
|
+
Oauth.persist_mgr = persistency if @option_cache_tokens
|
277
|
+
Fasp::Parameters.file_list_folder = File.join(@main_folder, 'filelists') # cspell: disable-line
|
278
|
+
Aspera::RestErrorAnalyzer.instance.log_file = File.join(@main_folder, 'rest_exceptions.log')
|
279
|
+
# register aspera REST call error handlers
|
280
|
+
Aspera::RestErrorsAspera.register_handlers
|
281
|
+
end
|
282
|
+
|
283
|
+
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_http_options
|
284
|
+
attr_reader :option_ignore_cert_host_port, :progress_bar
|
285
|
+
|
286
|
+
def trusted_cert_locations=(path_list)
|
287
|
+
path_list = [path_list] unless path_list.is_a?(Array)
|
288
|
+
if @certificate_store.nil?
|
289
|
+
Log.log.debug('Creating SSL Cert store')
|
290
|
+
@certificate_store = OpenSSL::X509::Store.new
|
291
|
+
@certificate_store.set_default_paths
|
292
|
+
@certificate_paths = []
|
180
293
|
end
|
294
|
+
|
295
|
+
path_list.each do |path|
|
296
|
+
raise 'Expecting a String for cert location' unless path.is_a?(String)
|
297
|
+
Log.log.debug("Adding cert location: #{path}")
|
298
|
+
if path.eql?(ExtendedValue::DEF)
|
299
|
+
path = OpenSSL::X509::DEFAULT_CERT_DIR
|
300
|
+
@certificate_store.add_path(path)
|
301
|
+
@certificate_paths.push(path)
|
302
|
+
path = OpenSSL::X509::DEFAULT_CERT_FILE
|
303
|
+
@certificate_store.add_file(path)
|
304
|
+
elsif File.file?(path)
|
305
|
+
@certificate_store.add_file(path)
|
306
|
+
elsif File.directory?(path)
|
307
|
+
@certificate_store.add_path(path)
|
308
|
+
else
|
309
|
+
raise "No such file or folder: #{path}"
|
310
|
+
end
|
311
|
+
@certificate_paths.push(path)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def trusted_cert_locations(files_only: false)
|
316
|
+
locations = if @certificate_paths.nil?
|
317
|
+
[OpenSSL::X509::DEFAULT_CERT_DIR, OpenSSL::X509::DEFAULT_CERT_FILE]
|
318
|
+
else
|
319
|
+
@certificate_paths
|
320
|
+
end
|
321
|
+
locations = locations.select{|f|File.file?(f)} if files_only
|
322
|
+
return locations
|
181
323
|
end
|
182
324
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
325
|
+
def option_ignore_cert_host_port=(url_list)
|
326
|
+
url_list.each do |url|
|
327
|
+
uri = URI.parse(url)
|
328
|
+
@option_ignore_cert_host_port.push([uri.host, uri.port].freeze)
|
329
|
+
end
|
187
330
|
end
|
188
331
|
|
189
|
-
def
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
332
|
+
def ignore_cert?(address, port)
|
333
|
+
endpoint = [address, port].freeze
|
334
|
+
Log.log.debug{"ignore cert? #{endpoint}"}
|
335
|
+
return false unless @option_insecure || @option_ignore_cert_host_port.any?(endpoint)
|
336
|
+
base_url = "https://#{address}:#{port}"
|
337
|
+
if !@ssl_warned_urls.include?(base_url)
|
338
|
+
formatter.display_message(
|
339
|
+
:error,
|
340
|
+
"#{Formatter::WARNING_FLASH} Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production.")
|
341
|
+
@ssl_warned_urls.push(base_url)
|
342
|
+
end
|
343
|
+
return true
|
344
|
+
end
|
345
|
+
|
346
|
+
# called every time a new REST HTTP session is opened to set user-provided options
|
347
|
+
# @param http_session [Net::HTTP] the newly created HTTP/S session object
|
348
|
+
def update_http_session(http_session)
|
349
|
+
http_session.set_debug_output(LineLogger.new(:trace2)) if Log.instance.logger.trace2?
|
350
|
+
# Rest.io_http_session(http_session).debug_output = Log.log
|
351
|
+
http_session.verify_mode = SELF_SIGNED_CERT if http_session.use_ssl? && ignore_cert?(http_session.address, http_session.port)
|
352
|
+
http_session.cert_store = @certificate_store if @certificate_store
|
353
|
+
Log.log.debug{"using cert store #{http_session.cert_store} (#{@certificate_store})"} unless http_session.cert_store.nil?
|
354
|
+
if @proxy_credentials
|
355
|
+
http_session.proxy_user = @proxy_credentials[:user]
|
356
|
+
http_session.proxy_pass = @proxy_credentials[:pass]
|
357
|
+
end
|
358
|
+
@option_http_options.each do |k, v|
|
359
|
+
method = "#{k}=".to_sym
|
360
|
+
# check if accessor is a method of Net::HTTP
|
361
|
+
# continue_timeout= read_timeout= write_timeout=
|
362
|
+
if http_session.respond_to?(method)
|
363
|
+
http_session.send(method, v)
|
364
|
+
else
|
365
|
+
Log.log.error{"no such HTTP session attribute: #{k}"}
|
366
|
+
end
|
197
367
|
end
|
198
|
-
return app_folder
|
199
368
|
end
|
200
369
|
|
201
370
|
def check_gem_version
|
@@ -208,7 +377,7 @@ module Aspera
|
|
208
377
|
end
|
209
378
|
if Gem::Version.new(Environment.ruby_version) < Gem::Version.new(RUBY_FUTURE_MINIMUM_VERSION)
|
210
379
|
Log.log.warn do
|
211
|
-
"Note that a future version will require Ruby version #{RUBY_FUTURE_MINIMUM_VERSION} at minimum, "\
|
380
|
+
"Note that a future version will require Ruby version #{RUBY_FUTURE_MINIMUM_VERSION} at minimum, " \
|
212
381
|
"you are using #{Environment.ruby_version}"
|
213
382
|
end
|
214
383
|
end
|
@@ -222,8 +391,7 @@ module Aspera
|
|
222
391
|
|
223
392
|
def periodic_check_newer_gem_version
|
224
393
|
# get verification period
|
225
|
-
delay_days = options.get_option(:version_check_days,
|
226
|
-
Log.log.info{"check days: #{delay_days}"}
|
394
|
+
delay_days = options.get_option(:version_check_days, mandatory: true)
|
227
395
|
# check only if not zero day
|
228
396
|
return if delay_days.eql?(0)
|
229
397
|
# get last date from persistency
|
@@ -234,17 +402,11 @@ module Aspera
|
|
234
402
|
id: 'version_last_check')
|
235
403
|
# get persisted date or nil
|
236
404
|
current_date = Date.today
|
237
|
-
last_check_days =
|
238
|
-
|
239
|
-
|
240
|
-
rescue StandardError
|
241
|
-
# negative value will force check
|
242
|
-
-1
|
243
|
-
end
|
244
|
-
Log.log.debug{"days elapsed: #{last_check_days}"}
|
245
|
-
return if last_check_days < delay_days
|
405
|
+
last_check_days = (current_date - Date.strptime(last_check_array.first, GEM_CHECK_DATE_FMT)) rescue nil
|
406
|
+
Log.log.debug{"gem check new version: #{delay_days}, #{last_check_days}, #{current_date}, #{last_check_array}"}
|
407
|
+
return if !last_check_days.nil? && last_check_days < delay_days
|
246
408
|
# generate timestamp
|
247
|
-
last_check_array[0] = current_date.strftime(
|
409
|
+
last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
|
248
410
|
check_date_persist.save
|
249
411
|
# compare this version and the one on internet
|
250
412
|
check_data = check_gem_version
|
@@ -263,7 +425,7 @@ module Aspera
|
|
263
425
|
Log.log.debug{"javascript=[\n#{connect_versions_javascript}\n]"}
|
264
426
|
# get javascript object only
|
265
427
|
found = connect_versions_javascript.match(/^.*? = (.*);/)
|
266
|
-
raise
|
428
|
+
raise Cli::Error, 'Problem when getting connect versions from internet' if found.nil?
|
267
429
|
all_data = JSON.parse(found[1])
|
268
430
|
@connect_versions = all_data['entries']
|
269
431
|
end
|
@@ -281,66 +443,45 @@ module Aspera
|
|
281
443
|
return nil
|
282
444
|
end
|
283
445
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
File.write(private_key_path + '.pub', priv_key.public_key.to_s)
|
291
|
-
Environment.restrict_file_access(private_key_path)
|
292
|
-
Environment.restrict_file_access(private_key_path + '.pub')
|
293
|
-
nil
|
294
|
-
end
|
295
|
-
|
296
|
-
class << self
|
297
|
-
# folder containing plugins in the gem's main folder
|
298
|
-
def gem_plugins_folder
|
299
|
-
File.dirname(File.expand_path(__FILE__))
|
300
|
-
end
|
301
|
-
|
302
|
-
# name of englobing module
|
303
|
-
# @return "Aspera::Cli::Plugins"
|
304
|
-
def module_full_name
|
305
|
-
return Module.nesting[2].to_s
|
446
|
+
# get the default global preset, or init a new one
|
447
|
+
def global_default_preset
|
448
|
+
global_default_preset_name = get_plugin_default_config_name(CONF_GLOBAL_SYM)
|
449
|
+
if global_default_preset_name.nil?
|
450
|
+
global_default_preset_name = CONF_PRESET_GLOBAL.to_s
|
451
|
+
set_preset_key(CONF_PRESET_DEFAULT, CONF_GLOBAL_SYM, global_default_preset_name)
|
306
452
|
end
|
453
|
+
return global_default_preset_name
|
454
|
+
end
|
307
455
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
456
|
+
def set_preset_key(preset, param_name, param_value)
|
457
|
+
raise "Parameter name must be a String or Symbol, not #{param_name.class}" unless [String, Symbol].include?(param_name.class)
|
458
|
+
param_name = param_name.to_s
|
459
|
+
selected_preset = @config_presets[preset]
|
460
|
+
if selected_preset.nil?
|
461
|
+
Log.log.debug{"No such preset name: #{preset}, initializing"}
|
462
|
+
selected_preset = @config_presets[preset] = {}
|
312
463
|
end
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
464
|
+
raise "expecting Hash for #{preset}.#{param_name}" unless selected_preset.is_a?(Hash)
|
465
|
+
if selected_preset.key?(param_name)
|
466
|
+
if selected_preset[param_name].eql?(param_value)
|
467
|
+
Log.log.warn{"keeping same value for #{preset}: #{param_name}: #{param_value}"}
|
468
|
+
return
|
469
|
+
end
|
470
|
+
Log.log.warn{"overwriting value: #{selected_preset[param_name]}"}
|
319
471
|
end
|
472
|
+
selected_preset[param_name] = param_value
|
473
|
+
formatter.display_status("Updated: #{preset}: #{param_name} <- #{param_value}")
|
474
|
+
nil
|
320
475
|
end
|
321
476
|
|
322
477
|
# set parameter and value in global config
|
323
478
|
# creates one if none already created
|
324
479
|
# @return preset name that contains global default
|
325
480
|
def set_global_default(key, value)
|
326
|
-
|
327
|
-
global_default_preset_name = get_plugin_default_config_name(CONF_GLOBAL_SYM)
|
328
|
-
if global_default_preset_name.nil?
|
329
|
-
global_default_preset_name = CONF_PRESET_GLOBAL
|
330
|
-
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
331
|
-
@config_presets[CONF_PRESET_DEFAULT][CONF_GLOBAL_SYM.to_s] = global_default_preset_name
|
332
|
-
end
|
333
|
-
@config_presets[global_default_preset_name] ||= {}
|
334
|
-
@config_presets[global_default_preset_name][key.to_s] = value
|
335
|
-
formatter.display_status("Updated: #{global_default_preset_name}: #{key} <- #{value}")
|
336
|
-
save_presets_to_config_file
|
337
|
-
return global_default_preset_name
|
481
|
+
set_preset_key(global_default_preset, key, value)
|
338
482
|
end
|
339
483
|
|
340
|
-
public
|
341
|
-
|
342
484
|
# $HOME/.aspera/`program_name`
|
343
|
-
attr_reader :main_folder
|
344
485
|
attr_reader :gem_url, :plugins
|
345
486
|
attr_accessor :option_config_file
|
346
487
|
|
@@ -348,40 +489,17 @@ module Aspera
|
|
348
489
|
# @param config_name name of the preset in config file
|
349
490
|
# @param include_path used to detect and avoid include loops
|
350
491
|
def preset_by_name(config_name, include_path=[])
|
351
|
-
raise
|
492
|
+
raise Cli::Error, 'loop in include' if include_path.include?(config_name)
|
352
493
|
include_path = include_path.clone # avoid messing up if there are multiple branches
|
353
494
|
current = @config_presets
|
354
495
|
config_name.split(PRESET_DIG_SEPARATOR).each do |name|
|
355
|
-
raise
|
496
|
+
raise Cli::Error, "Expecting Hash for sub key: #{include_path} (#{current.class})" unless current.is_a?(Hash)
|
356
497
|
include_path.push(name)
|
357
498
|
current = current[name]
|
358
|
-
raise
|
359
|
-
end
|
360
|
-
case current
|
361
|
-
when Hash then return expanded_with_preset_includes(current, include_path)
|
362
|
-
when String then return ExtendedValue.instance.evaluate(current)
|
363
|
-
else return current
|
499
|
+
raise Cli::Error, "No such config preset: #{include_path}" if current.nil?
|
364
500
|
end
|
365
|
-
|
366
|
-
|
367
|
-
# @return the hash value with 'incps' keys expanded to include other presets
|
368
|
-
# @param hash_val
|
369
|
-
# @param include_path to avoid inclusion loop
|
370
|
-
def expanded_with_preset_includes(hash_val, include_path=[])
|
371
|
-
raise CliError, "#{EXTV_INCLUDE_PRESETS} requires a Hash, have #{hash_val.class}" unless hash_val.is_a?(Hash)
|
372
|
-
if hash_val.key?(EXTV_INCLUDE_PRESETS)
|
373
|
-
memory = hash_val.clone
|
374
|
-
includes = memory[EXTV_INCLUDE_PRESETS]
|
375
|
-
memory.delete(EXTV_INCLUDE_PRESETS)
|
376
|
-
hash_val = {}
|
377
|
-
raise "#{EXTV_INCLUDE_PRESETS} must be an Array" unless includes.is_a?(Array)
|
378
|
-
raise "#{EXTV_INCLUDE_PRESETS} must contain names" unless includes.map(&:class).uniq.eql?([String])
|
379
|
-
includes.each do |preset_name|
|
380
|
-
hash_val.merge!(preset_by_name(preset_name, include_path))
|
381
|
-
end
|
382
|
-
hash_val.merge!(memory)
|
383
|
-
end
|
384
|
-
return hash_val
|
501
|
+
current = self.class.protect_presets(current) unless current.is_a?(String)
|
502
|
+
return ExtendedValue.instance.evaluate(current)
|
385
503
|
end
|
386
504
|
|
387
505
|
def option_use_product=(value)
|
@@ -417,97 +535,46 @@ module Aspera
|
|
417
535
|
end
|
418
536
|
end
|
419
537
|
|
420
|
-
def
|
421
|
-
|
422
|
-
new_subpath = File.join('', ASPERA_HOME_FOLDER_NAME, new_name, '')
|
423
|
-
# convert possible keys located in config folder
|
424
|
-
@config_presets.values.select{|p|p.is_a?(Hash)}.each do |preset|
|
425
|
-
preset.values.select{|v|v.is_a?(String) && v.include?(old_subpath)}.each do |value|
|
426
|
-
old_val = value.clone
|
427
|
-
included_path = File.expand_path(old_val.gsub(/^@file:/, ''))
|
428
|
-
files_to_copy.push(included_path) unless files_to_copy.include?(included_path) || !File.exist?(included_path)
|
429
|
-
value.gsub!(old_subpath, new_subpath)
|
430
|
-
Log.log.warn{"Converted config value: #{old_val} -> #{value}"}
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
434
|
-
|
435
|
-
def convert_preset_plugin_name(old_name, new_name)
|
436
|
-
default_preset = @config_presets[CONF_PRESET_DEFAULT]
|
437
|
-
return unless default_preset.is_a?(Hash) && default_preset.key?(old_name)
|
438
|
-
default_preset[new_name] = default_preset[old_name]
|
439
|
-
default_preset.delete(old_name)
|
440
|
-
Log.log.warn{"Converted plugin default: #{old_name} -> #{new_name}"}
|
538
|
+
def config_checksum
|
539
|
+
JSON.generate(@config_presets).hash
|
441
540
|
end
|
442
541
|
|
443
542
|
# read config file and validate format
|
444
|
-
# tries to convert from older version if possible and required
|
445
543
|
def read_config_file
|
446
544
|
Log.log.debug{"config file is: #{@option_config_file}".red}
|
447
|
-
conf_file_v1 = File.join(Dir.home, ASPERA_HOME_FOLDER_NAME, PROGRAM_NAME_V1, DEFAULT_CONFIG_FILENAME)
|
448
|
-
conf_file_v2 = File.join(Dir.home, ASPERA_HOME_FOLDER_NAME, PROGRAM_NAME_V2, DEFAULT_CONFIG_FILENAME)
|
449
545
|
# files search for configuration, by default the one given by user
|
450
546
|
search_files = [@option_config_file]
|
451
|
-
# if default file, then also look for older versions
|
452
|
-
search_files.push(conf_file_v2, conf_file_v1) if @option_config_file.eql?(@conf_file_default)
|
453
547
|
# find first existing file (or nil)
|
454
548
|
conf_file_to_load = search_files.find{|f| File.exist?(f)}
|
455
|
-
# require save if old version of file
|
456
|
-
save_required = !@option_config_file.eql?(conf_file_to_load)
|
457
549
|
# if no file found, create default config
|
458
550
|
if conf_file_to_load.nil?
|
459
|
-
Log.log.warn{"No config file found.
|
460
|
-
@config_presets = {CONF_PRESET_CONFIG => {CONF_PRESET_VERSION =>
|
551
|
+
Log.log.warn{"No config file found. New configuration file: #{@option_config_file}"}
|
552
|
+
@config_presets = {CONF_PRESET_CONFIG => {CONF_PRESET_VERSION => 'new file'}}
|
553
|
+
# @config_checksum_on_disk is nil
|
461
554
|
else
|
462
555
|
Log.log.debug{"loading #{@option_config_file}"}
|
463
556
|
@config_presets = YAML.load_file(conf_file_to_load)
|
557
|
+
@config_checksum_on_disk = config_checksum
|
464
558
|
end
|
465
559
|
files_to_copy = []
|
466
|
-
Log.log.debug{
|
560
|
+
Log.log.debug{Log.dump('Available_presets', @config_presets)}
|
467
561
|
raise 'Expecting YAML Hash' unless @config_presets.is_a?(Hash)
|
468
562
|
# check there is at least the config section
|
469
|
-
|
470
|
-
raise "Cannot find key: #{CONF_PRESET_CONFIG}"
|
471
|
-
end
|
563
|
+
raise "Cannot find key: #{CONF_PRESET_CONFIG}" unless @config_presets.key?(CONF_PRESET_CONFIG)
|
472
564
|
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]
|
473
|
-
if version.nil?
|
474
|
-
raise 'No version found in config section.'
|
475
|
-
end
|
476
|
-
# oldest compatible conf file format, update to latest version when an incompatible change is made
|
477
|
-
# check compatibility of version of conf file
|
478
|
-
config_tested_version = '0.4.5'
|
479
|
-
if Gem::Version.new(version) < Gem::Version.new(config_tested_version)
|
480
|
-
raise "Unsupported config file version #{version}. Expecting min version #{config_tested_version}"
|
481
|
-
end
|
482
|
-
config_tested_version = '0.6.15'
|
483
|
-
if Gem::Version.new(version) < Gem::Version.new(config_tested_version)
|
484
|
-
convert_preset_plugin_name(AOC_COMMAND_V1, AOC_COMMAND_V2)
|
485
|
-
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = config_tested_version
|
486
|
-
save_required = true
|
487
|
-
end
|
488
|
-
config_tested_version = '0.8.10'
|
489
|
-
if Gem::Version.new(version) <= Gem::Version.new(config_tested_version)
|
490
|
-
convert_preset_path(PROGRAM_NAME_V1, PROGRAM_NAME_V2, files_to_copy)
|
491
|
-
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = config_tested_version
|
492
|
-
save_required = true
|
493
|
-
end
|
494
|
-
config_tested_version = '1.0'
|
495
|
-
if Gem::Version.new(version) <= Gem::Version.new(config_tested_version)
|
496
|
-
convert_preset_plugin_name(AOC_COMMAND_V2, AOC_COMMAND_V3)
|
497
|
-
convert_preset_path(PROGRAM_NAME_V2, @info[:name], files_to_copy)
|
498
|
-
version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = config_tested_version
|
499
|
-
save_required = true
|
500
|
-
end
|
565
|
+
raise 'No version found in config section.' if version.nil?
|
501
566
|
Log.log.debug{"conf version: #{version}"}
|
502
|
-
#
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
567
|
+
# VVV if there are any conversion needed, those happen here.
|
568
|
+
# fix bug in 4.4 (creating key "true" in "default" preset)
|
569
|
+
@config_presets[CONF_PRESET_DEFAULT].delete(true) if @config_presets[CONF_PRESET_DEFAULT].is_a?(Hash)
|
570
|
+
# ^^^ Place new compatibility code before this line
|
571
|
+
# set version to current
|
572
|
+
@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = @info[:version]
|
573
|
+
unless files_to_copy.empty?
|
507
574
|
Log.log.warn('Copying referenced files')
|
508
575
|
files_to_copy.each do |file|
|
509
576
|
FileUtils.cp(file, @main_folder)
|
510
|
-
Log.log.warn{"
|
577
|
+
Log.log.warn{"#{file} -> #{@main_folder}"}
|
511
578
|
end
|
512
579
|
end
|
513
580
|
rescue Psych::SyntaxError => e
|
@@ -522,7 +589,7 @@ module Aspera
|
|
522
589
|
Log.log.warn{"Renamed config file to #{new_name}."}
|
523
590
|
Log.log.warn('Manual Conversion is required. Next time, a new empty file will be created.')
|
524
591
|
end
|
525
|
-
raise
|
592
|
+
raise Cli::Error, e.to_s
|
526
593
|
end
|
527
594
|
|
528
595
|
# find plugins in defined paths
|
@@ -552,42 +619,41 @@ module Aspera
|
|
552
619
|
@plugins[plugin_symbol] = {source: path, require_stanza: req}
|
553
620
|
end
|
554
621
|
|
555
|
-
|
622
|
+
# Find a plugin, and issue the "require"
|
623
|
+
# @return [Hash] plugin info: { product: , url:, version: }
|
624
|
+
def identify_plugins_for_url
|
625
|
+
app_url = options.get_next_argument('url', mandatory: true)
|
626
|
+
check_only = options.get_next_argument('plugin name', mandatory: false)
|
627
|
+
check_only = check_only.to_sym unless check_only.nil?
|
628
|
+
found_apps = []
|
556
629
|
plugins.each do |plugin_name_sym, plugin_info|
|
557
630
|
# no detection for internal plugin
|
558
631
|
next if plugin_name_sym.eql?(CONF_PLUGIN_SYM)
|
632
|
+
next if check_only && !check_only.eql?(plugin_name_sym)
|
559
633
|
# load plugin class
|
560
634
|
require plugin_info[:require_stanza]
|
561
|
-
|
562
|
-
|
563
|
-
|
635
|
+
detect_plugin_class = self.class.plugin_class(plugin_name_sym)
|
636
|
+
# requires detection method
|
637
|
+
next unless detect_plugin_class.respond_to?(:detect)
|
564
638
|
detection_info = nil
|
565
|
-
# first try : direct
|
566
639
|
begin
|
567
|
-
detection_info =
|
640
|
+
detection_info = detect_plugin_class.detect(app_url)
|
568
641
|
rescue OpenSSL::SSL::SSLError => e
|
569
642
|
Log.log.warn(e.message)
|
570
|
-
Log.log.warn('Use option --insecure=yes to
|
643
|
+
Log.log.warn('Use option --insecure=yes to allow unchecked certificate') if e.message.include?('cert')
|
571
644
|
rescue StandardError => e
|
572
|
-
Log.log.debug{"
|
573
|
-
|
574
|
-
# second try : is there a redirect ?
|
575
|
-
if detection_info.nil?
|
576
|
-
begin
|
577
|
-
# TODO: check if redirect ?
|
578
|
-
new_url = Rest.new(base_url: url).call(operation: 'GET', subpath: '', redirect_max: 1)[:http].uri.to_s
|
579
|
-
unless url.eql?(new_url)
|
580
|
-
detection_info = c.detect(new_url)
|
581
|
-
current_url = new_url
|
582
|
-
end
|
583
|
-
rescue StandardError => e
|
584
|
-
Log.log.debug{"Cannot detect #{plugin_name_sym} : #{e.message}"}
|
585
|
-
end
|
645
|
+
Log.log.debug{"detect error: #{e}"}
|
646
|
+
next
|
586
647
|
end
|
648
|
+
next if detection_info.nil?
|
649
|
+
raise 'internal error' if detection_info.key?(:url) && !detection_info[:url].is_a?(String)
|
650
|
+
app_name = detect_plugin_class.respond_to?(:application_name) ? detect_plugin_class.application_name : detect_plugin_class.name.split('::').last
|
587
651
|
# if there is a redirect, then the detector can override the url.
|
588
|
-
|
652
|
+
found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
|
589
653
|
end # loop
|
590
|
-
raise "No known application found at #{
|
654
|
+
raise "No known application found at #{app_url}" if found_apps.empty?
|
655
|
+
raise 'Internal error' unless found_apps.all?{|a|a.keys.all?(Symbol)}
|
656
|
+
return found_apps
|
591
657
|
end
|
592
658
|
|
593
659
|
def execute_connect_action
|
@@ -595,7 +661,7 @@ module Aspera
|
|
595
661
|
if %i[info version].include?(command)
|
596
662
|
connect_id = options.get_next_argument('id or title')
|
597
663
|
one_res = connect_versions.find{|i|i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}
|
598
|
-
raise
|
664
|
+
raise Cli::NoSuchIdentifier.new(:connect, connect_id) if one_res.nil?
|
599
665
|
end
|
600
666
|
case command
|
601
667
|
when :list
|
@@ -638,21 +704,14 @@ module Aspera
|
|
638
704
|
ascp_path = options.get_next_argument('path to ascp')
|
639
705
|
ascp_version = Fasp::Installation.instance.get_ascp_version(ascp_path)
|
640
706
|
formatter.display_status("ascp version: #{ascp_version}")
|
641
|
-
|
642
|
-
return Main.
|
707
|
+
set_global_default(:ascp_path, ascp_path)
|
708
|
+
return Main.result_nothing
|
643
709
|
when :show # shows files used
|
644
710
|
return {type: :status, data: Fasp::Installation.instance.path(:ascp)}
|
645
711
|
when :info # shows files used
|
646
|
-
data = Fasp::Installation
|
647
|
-
m[v.to_s] =
|
648
|
-
begin
|
649
|
-
Fasp::Installation.instance.path(v)
|
650
|
-
rescue => e
|
651
|
-
e.message
|
652
|
-
end
|
653
|
-
end
|
712
|
+
data = Fasp::Installation.instance.file_paths
|
654
713
|
# read PATHs from ascp directly, and pvcl modules as well
|
655
|
-
Open3.popen3(
|
714
|
+
Open3.popen3(data['ascp'], '-DDL-') do |_stdin, _stdout, stderr, thread|
|
656
715
|
last_line = ''
|
657
716
|
while (line = stderr.gets)
|
658
717
|
line.chomp!
|
@@ -660,8 +719,11 @@ module Aspera
|
|
660
719
|
case line
|
661
720
|
when /^DBG Path ([^ ]+) (dir|file) +: (.*)$/
|
662
721
|
data[Regexp.last_match(1)] = Regexp.last_match(3)
|
663
|
-
when /^DBG Added module group:"([^"]+)" name:"([^"]+)", version:"([^"]+)" interface:"([^"]+)"$/
|
664
|
-
|
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]}")
|
665
727
|
when %r{^DBG License result \(/license/(\S+)\): (.+)$}
|
666
728
|
data[Regexp.last_match(1)] = Regexp.last_match(2)
|
667
729
|
when /^LOG (.+) version ([0-9.]+)$/
|
@@ -675,21 +737,34 @@ module Aspera
|
|
675
737
|
raise last_line
|
676
738
|
end
|
677
739
|
end
|
678
|
-
|
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
|
751
|
+
data['ts'] = transfer.updated_ts
|
679
752
|
return {type: :single_object, data: data}
|
680
753
|
when :products
|
681
754
|
command = options.get_next_command(%i[list use])
|
682
755
|
case command
|
683
756
|
when :list
|
684
|
-
return {type: :object_list, data: Fasp::
|
757
|
+
return {type: :object_list, data: Fasp::Products.installed_products, fields: %w[name app_root]}
|
685
758
|
when :use
|
686
759
|
default_product = options.get_next_argument('product name')
|
687
760
|
Fasp::Installation.instance.use_ascp_from_product(default_product)
|
688
|
-
|
689
|
-
return Main.
|
761
|
+
set_global_default(:ascp_path, Fasp::Installation.instance.path(:ascp))
|
762
|
+
return Main.result_nothing
|
690
763
|
end
|
691
764
|
when :install
|
692
|
-
|
765
|
+
# reset to default location, if older default was used
|
766
|
+
Fasp::Installation.instance.sdk_folder = self.class.default_app_main_folder(app_name: APP_NAME_SDK) if @sdk_default_location
|
767
|
+
v = Fasp::Installation.instance.install_sdk(options.get_option(:sdk_url, mandatory: true))
|
693
768
|
return Main.result_status("Installed version #{v}")
|
694
769
|
when :spec
|
695
770
|
return {
|
@@ -718,24 +793,23 @@ module Aspera
|
|
718
793
|
def execute_preset(action: nil, name: nil)
|
719
794
|
action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
|
720
795
|
name = instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
|
796
|
+
name = global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
|
721
797
|
# those operations require existing option
|
722
798
|
raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !@config_presets.key?(name)
|
723
|
-
selected_preset = @config_presets[name]
|
724
799
|
case action
|
725
800
|
when :list
|
726
801
|
return {type: :value_list, data: @config_presets.keys, name: 'name'}
|
727
802
|
when :overview
|
728
|
-
|
803
|
+
# display process modifies the value (hide secrets): we do not want to save removed secrets
|
804
|
+
return {type: :config_over, data: self.class.protect_presets(@config_presets)}
|
729
805
|
when :show
|
730
|
-
|
731
|
-
return {type: :single_object, data: selected_preset}
|
806
|
+
return {type: :single_object, data: self.class.protect_presets(@config_presets[name])}
|
732
807
|
when :delete
|
733
808
|
@config_presets.delete(name)
|
734
|
-
save_presets_to_config_file
|
735
809
|
return Main.result_status("Deleted: #{name}")
|
736
810
|
when :get
|
737
811
|
param_name = options.get_next_argument('parameter name')
|
738
|
-
value =
|
812
|
+
value = @config_presets[name][param_name]
|
739
813
|
raise "no such option in preset #{name} : #{param_name}" if value.nil?
|
740
814
|
case value
|
741
815
|
when Numeric, String then return {type: :text, data: ExtendedValue.instance.evaluate(value.to_s)}
|
@@ -743,30 +817,20 @@ module Aspera
|
|
743
817
|
return {type: :single_object, data: value}
|
744
818
|
when :unset
|
745
819
|
param_name = options.get_next_argument('parameter name')
|
746
|
-
|
747
|
-
save_presets_to_config_file
|
820
|
+
@config_presets[name].delete(param_name)
|
748
821
|
return Main.result_status("Removed: #{name}: #{param_name}")
|
749
822
|
when :set
|
750
823
|
param_name = options.get_next_argument('parameter name')
|
751
|
-
param_value = options.get_next_argument('parameter value')
|
752
824
|
param_name = Manager.option_line_to_name(param_name)
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
end
|
757
|
-
if selected_preset.key?(param_name)
|
758
|
-
Log.log.warn{"overwriting value: #{selected_preset[param_name]}"}
|
759
|
-
end
|
760
|
-
selected_preset[param_name] = param_value
|
761
|
-
save_presets_to_config_file
|
762
|
-
return Main.result_status("Updated: #{name}: #{param_name} <- #{param_value}")
|
825
|
+
param_value = options.get_next_argument('parameter value')
|
826
|
+
set_preset_key(name, param_name, param_value)
|
827
|
+
return Main.result_nothing
|
763
828
|
when :initialize
|
764
829
|
config_value = options.get_next_argument('extended value', type: Hash)
|
765
830
|
if @config_presets.key?(name)
|
766
831
|
Log.log.warn{"configuration already exists: #{name}, overwriting"}
|
767
832
|
end
|
768
833
|
@config_presets[name] = config_value
|
769
|
-
save_presets_to_config_file
|
770
834
|
return Main.result_status("Modified: #{@option_config_file}")
|
771
835
|
when :update
|
772
836
|
# get unprocessed options
|
@@ -774,9 +838,6 @@ module Aspera
|
|
774
838
|
Log.log.debug{"opts=#{unprocessed_options}"}
|
775
839
|
@config_presets[name] ||= {}
|
776
840
|
@config_presets[name].merge!(unprocessed_options)
|
777
|
-
# fix bug in 4.4 (creating key "true" in "default" preset)
|
778
|
-
@config_presets[CONF_PRESET_DEFAULT].delete(true) if @config_presets[CONF_PRESET_DEFAULT].is_a?(Hash)
|
779
|
-
save_presets_to_config_file
|
780
841
|
return Main.result_status("Updated: #{name}")
|
781
842
|
when :ask
|
782
843
|
options.ask_missing_mandatory = :yes
|
@@ -785,12 +846,11 @@ module Aspera
|
|
785
846
|
option_value = options.get_interactive(:option, option_name)
|
786
847
|
@config_presets[name][option_name] = option_value
|
787
848
|
end
|
788
|
-
save_presets_to_config_file
|
789
849
|
return Main.result_status("Updated: #{name}")
|
790
850
|
when :lookup
|
791
|
-
BasicAuthPlugin.
|
792
|
-
url = options.get_option(:url,
|
793
|
-
user = options.get_option(:username,
|
851
|
+
BasicAuthPlugin.declare_options(options)
|
852
|
+
url = options.get_option(:url, mandatory: true)
|
853
|
+
user = options.get_option(:username, mandatory: true)
|
794
854
|
result = lookup_preset(url: url, username: user)
|
795
855
|
raise 'no such config found' if result.nil?
|
796
856
|
return {type: :single_object, data: result}
|
@@ -823,17 +883,16 @@ module Aspera
|
|
823
883
|
end
|
824
884
|
|
825
885
|
ACTIONS = %i[
|
826
|
-
id
|
827
886
|
preset
|
828
887
|
open
|
829
888
|
documentation
|
830
889
|
genkey
|
890
|
+
remote_certificate
|
831
891
|
gem
|
832
|
-
|
892
|
+
plugins
|
833
893
|
flush_tokens
|
834
894
|
echo
|
835
895
|
wizard
|
836
|
-
export_to_cli
|
837
896
|
detect
|
838
897
|
coffee
|
839
898
|
ascp
|
@@ -844,19 +903,13 @@ module Aspera
|
|
844
903
|
file
|
845
904
|
check_update
|
846
905
|
initdemo
|
847
|
-
vault
|
906
|
+
vault
|
907
|
+
throw].freeze
|
848
908
|
|
849
|
-
#
|
909
|
+
# Main action procedure for plugin
|
850
910
|
def execute_action
|
851
911
|
action = options.get_next_command(ACTIONS)
|
852
912
|
case action
|
853
|
-
when *PRESET_GBL_ACTIONS # older syntax
|
854
|
-
Log.log.warn{"This syntax is deprecated, use command: preset #{action}"}
|
855
|
-
return execute_preset(action: action)
|
856
|
-
when :id # older syntax
|
857
|
-
identifier = options.get_next_argument('config name')
|
858
|
-
Log.log.warn{"This syntax is deprecated, use command: preset <verb> #{identifier}"}
|
859
|
-
return execute_preset(name: identifier)
|
860
913
|
when :preset # newer syntax
|
861
914
|
return execute_preset
|
862
915
|
when :open
|
@@ -864,31 +917,45 @@ module Aspera
|
|
864
917
|
return Main.result_nothing
|
865
918
|
when :documentation
|
866
919
|
section = options.get_next_argument('private key file path', mandatory: false)
|
867
|
-
section =
|
920
|
+
section = "##{section}" unless section.nil?
|
868
921
|
OpenApplication.instance.uri("#{@info[:help]}#{section}")
|
869
922
|
return Main.result_nothing
|
870
923
|
when :genkey # generate new rsa key
|
871
924
|
private_key_path = options.get_next_argument('private key file path')
|
872
|
-
private_key_length = options.get_next_argument('size in bits', mandatory: false
|
873
|
-
generate_rsa_private_key(private_key_path, private_key_length)
|
874
|
-
return Main.result_status(
|
925
|
+
private_key_length = options.get_next_argument('size in bits', mandatory: false, type: Integer, default: DEFAULT_PRIV_KEY_LENGTH)
|
926
|
+
self.class.generate_rsa_private_key(path: private_key_path, length: private_key_length)
|
927
|
+
return Main.result_status("Generated #{private_key_length} bit RSA key: #{private_key_path}")
|
928
|
+
when :remote_certificate
|
929
|
+
remote_url = options.get_next_argument('remote URL')
|
930
|
+
@option_insecure = true
|
931
|
+
remote_certificate = Rest.start_http_session(remote_url).peer_cert
|
932
|
+
remote_certificate.subject.to_a.find { |name, _, _| name == 'CN' }[1]
|
933
|
+
formatter.display_status("CN=#{remote_certificate.subject.to_a.find { |name, _, _| name == 'CN' }[1] rescue ''}")
|
934
|
+
return Main.result_status(remote_certificate.to_pem)
|
875
935
|
when :echo # display the content of a value given on command line
|
876
|
-
|
877
|
-
# special for csv
|
878
|
-
result[:type] = :object_list if result[:data].is_a?(Array) && result[:data].first.is_a?(Hash)
|
879
|
-
result[:type] = :single_object if result[:data].is_a?(Hash)
|
880
|
-
return result
|
936
|
+
return Formatter.auto_type(options.get_next_argument('value'))
|
881
937
|
when :flush_tokens
|
882
938
|
deleted_files = Oauth.flush_tokens
|
883
939
|
return {type: :value_list, data: deleted_files, name: 'file'}
|
884
|
-
when :
|
940
|
+
when :plugins
|
885
941
|
case options.get_next_command(%i[list create])
|
886
942
|
when :list
|
887
|
-
|
943
|
+
result = []
|
944
|
+
@plugins.each do |name, info|
|
945
|
+
require info[:require_stanza]
|
946
|
+
plugin_class = self.class.plugin_class(name)
|
947
|
+
result.push({
|
948
|
+
plugin: name,
|
949
|
+
detect: Formatter.tick(plugin_class.respond_to?(:detect)),
|
950
|
+
wizard: Formatter.tick(plugin_class.respond_to?(:wizard)),
|
951
|
+
path: info[:source]
|
952
|
+
})
|
953
|
+
end
|
954
|
+
return {type: :object_list, data: result, fields: %w[plugin detect wizard path]}
|
888
955
|
when :create
|
889
956
|
plugin_name = options.get_next_argument('name', expected: :single).downcase
|
890
|
-
|
891
|
-
plugin_file = File.join(
|
957
|
+
destination_folder = options.get_next_argument('folder', expected: :single, mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
|
958
|
+
plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
|
892
959
|
content = <<~END_OF_PLUGIN_CODE
|
893
960
|
require 'aspera/cli/plugin'
|
894
961
|
module Aspera
|
@@ -905,81 +972,20 @@ module Aspera
|
|
905
972
|
File.write(plugin_file, content)
|
906
973
|
return Main.result_status("Created #{plugin_file}")
|
907
974
|
end
|
908
|
-
when :wizard
|
975
|
+
when :detect, :wizard
|
909
976
|
# interactive mode
|
910
977
|
options.ask_missing_mandatory = true
|
911
|
-
#
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
# allow user to specify type of application
|
919
|
-
params[:application] = options.get_option(:value)
|
920
|
-
params[:application] = params[:application].nil? ? identify_plugin_for_url(params[:instance_url])[:product] : params[:application].to_sym
|
921
|
-
params[:plugin_name] = params[:application]
|
922
|
-
params[:test_args] = '<replace per app>'
|
923
|
-
case params[:application]
|
924
|
-
when :faspex5
|
925
|
-
wizard_faspex5(params)
|
926
|
-
when :aoc
|
927
|
-
wizard_aoc(params)
|
928
|
-
else
|
929
|
-
raise CliBadArgument, "Supports only: aoc. Detected: #{params[:application]}"
|
930
|
-
end # product
|
931
|
-
if params[:option_default]
|
932
|
-
formatter.display_status("Setting config preset as default for #{params[:plugin_name]}")
|
933
|
-
@config_presets[CONF_PRESET_DEFAULT][params[:plugin_name]] = params[:preset_name]
|
934
|
-
else
|
935
|
-
params[:test_args] = "-P#{params[:preset_name]} #{params[:test_args]}"
|
936
|
-
end
|
937
|
-
formatter.display_status('Saving config file.')
|
938
|
-
save_presets_to_config_file
|
939
|
-
return Main.result_status("Done.\nYou can test with:\n#{@info[:name]} #{params[:test_args]}")
|
940
|
-
when :export_to_cli # this method shall be deprecated in the future: it was used to export configuration to "aspera.exe" CLI
|
941
|
-
formatter.display_status('Exporting: Aspera on Cloud')
|
942
|
-
require 'aspera/cli/plugins/aoc'
|
943
|
-
# need url / username
|
944
|
-
add_plugin_default_preset(AOC_COMMAND_V3.to_sym)
|
945
|
-
# instantiate AoC plugin
|
946
|
-
self.class.plugin_class(AOC_COMMAND_CURRENT).new(@agents) # TODO: is this line needed ? get options ?
|
947
|
-
url = options.get_option(:url, is_type: :mandatory)
|
948
|
-
cli_conf_file = Fasp::Installation.instance.cli_conf_file
|
949
|
-
data = JSON.parse(File.read(cli_conf_file))
|
950
|
-
organization, instance_domain = AoC.parse_url(url)
|
951
|
-
key_basename = 'org_' + organization + '.pem'
|
952
|
-
key_file = File.join(File.dirname(File.dirname(cli_conf_file)), 'etc', key_basename)
|
953
|
-
File.write(key_file, options.get_option(:private_key, is_type: :mandatory))
|
954
|
-
new_conf = {
|
955
|
-
'organization' => organization,
|
956
|
-
'hostname' => [organization, instance_domain].join('.'),
|
957
|
-
'privateKeyFilename' => key_basename,
|
958
|
-
'username' => options.get_option(:username, is_type: :mandatory)
|
959
|
-
}
|
960
|
-
new_conf['clientId'] = options.get_option(:client_id)
|
961
|
-
new_conf['clientSecret'] = options.get_option(:client_secret)
|
962
|
-
if new_conf['clientId'].nil?
|
963
|
-
new_conf['clientId'], new_conf['clientSecret'] = AoC.get_client_info
|
964
|
-
end
|
965
|
-
entry = data['AoCAccounts'].find{|i|i['organization'].eql?(organization)}
|
966
|
-
if entry.nil?
|
967
|
-
data['AoCAccounts'].push(new_conf)
|
968
|
-
formatter.display_status("Creating new aoc entry: #{organization}")
|
969
|
-
else
|
970
|
-
formatter.display_status("Updating existing aoc entry: #{organization}")
|
971
|
-
entry.merge!(new_conf)
|
972
|
-
end
|
973
|
-
File.write(cli_conf_file, JSON.pretty_generate(data))
|
974
|
-
return Main.result_status("Updated: #{cli_conf_file}")
|
975
|
-
when :detect
|
976
|
-
# need url / username
|
977
|
-
BasicAuthPlugin.register_options(@agents)
|
978
|
-
return {type: :single_object, data: identify_plugin_for_url(options.get_option(:url, is_type: :mandatory))}
|
978
|
+
# detect plugins by url and optional query
|
979
|
+
apps = identify_plugins_for_url.freeze
|
980
|
+
return {
|
981
|
+
type: :object_list,
|
982
|
+
data: apps
|
983
|
+
} if action.eql?(:detect)
|
984
|
+
return wizard_find(apps)
|
979
985
|
when :coffee
|
980
986
|
if OpenApplication.instance.url_method.eql?(:text)
|
981
987
|
require 'aspera/preview/terminal'
|
982
|
-
return Main.result_status(Preview::Terminal.build(Rest.new(base_url: COFFEE_IMAGE).read('')[:http].body
|
988
|
+
return Main.result_status(Preview::Terminal.build(Rest.new(base_url: COFFEE_IMAGE).read('')[:http].body))
|
983
989
|
end
|
984
990
|
OpenApplication.instance.uri(COFFEE_IMAGE)
|
985
991
|
return Main.result_nothing
|
@@ -1002,7 +1008,7 @@ module Aspera
|
|
1002
1008
|
return {type: :single_object, data: email_settings}
|
1003
1009
|
when :proxy_check
|
1004
1010
|
# ensure fpac was provided
|
1005
|
-
options.get_option(:fpac,
|
1011
|
+
options.get_option(:fpac, mandatory: true)
|
1006
1012
|
server_url = options.get_next_argument('server url')
|
1007
1013
|
return Main.result_status(@pac_exec.find_proxy_for_url(server_url))
|
1008
1014
|
when :check_update
|
@@ -1013,9 +1019,9 @@ module Aspera
|
|
1013
1019
|
else
|
1014
1020
|
Log.log.info{"Creating Demo server preset: #{DEMO_SERVER_PRESET}"}
|
1015
1021
|
@config_presets[DEMO_SERVER_PRESET] = {
|
1016
|
-
'url' =>
|
1017
|
-
'username' =>
|
1018
|
-
'ssAP'.downcase.reverse + 'drow'.reverse => DEMO +
|
1022
|
+
'url' => "ssh://#{DEMO}.asperasoft.com:33001",
|
1023
|
+
'username' => ASPERA,
|
1024
|
+
'ssAP'.downcase.reverse + 'drow'.reverse => DEMO + ASPERA # cspell:disable-line
|
1019
1025
|
}
|
1020
1026
|
end
|
1021
1027
|
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
@@ -1027,40 +1033,138 @@ module Aspera
|
|
1027
1033
|
@config_presets[CONF_PRESET_DEFAULT][SERVER_COMMAND] = DEMO_SERVER_PRESET
|
1028
1034
|
Log.log.info{"Setting server default preset to : #{DEMO_SERVER_PRESET}"}
|
1029
1035
|
end
|
1030
|
-
save_presets_to_config_file
|
1031
1036
|
return Main.result_status('Done')
|
1032
1037
|
when :vault then execute_vault
|
1038
|
+
when :throw
|
1039
|
+
# :type [String]
|
1040
|
+
options
|
1041
|
+
exception_class_name = options.get_next_argument('exception class name', mandatory: true)
|
1042
|
+
exception_text = options.get_next_argument('exception text', mandatory: true)
|
1043
|
+
exception_class = Object.const_get(exception_class_name)
|
1044
|
+
raise "#{exception_class} is not an exception: #{exception_class.class}" unless exception_class <= Exception
|
1045
|
+
raise exception_class, exception_text
|
1033
1046
|
else raise 'INTERNAL ERROR: wrong case'
|
1034
1047
|
end
|
1035
1048
|
end
|
1036
1049
|
|
1037
|
-
|
1050
|
+
def wizard_find(apps)
|
1051
|
+
identification = if apps.length.eql?(1)
|
1052
|
+
Log.log.debug{"Detected: #{identification}"}
|
1053
|
+
apps.first
|
1054
|
+
else
|
1055
|
+
formatter.display_status('Multiple applications detected, please select from:')
|
1056
|
+
formatter.display_results({type: :object_list, data: apps, fields: %w[product url version]})
|
1057
|
+
answer = options.prompt_user_input_in_list('product', apps.map{|a|a[:product]})
|
1058
|
+
apps.find{|a|a[:product].eql?(answer)}
|
1059
|
+
end
|
1060
|
+
wiz_url = identification[:url]
|
1061
|
+
Log.log.debug{Log.dump(:identification, identification, :ruby)}
|
1062
|
+
formatter.display_status("Using: #{identification[:name]} at #{wiz_url}".bold)
|
1063
|
+
# set url for instantiation of plugin
|
1064
|
+
options.add_option_preset({url: wiz_url})
|
1065
|
+
# instantiate plugin: command line options will be known and wizard can be called
|
1066
|
+
wiz_plugin_class = self.class.plugin_class(identification[:product])
|
1067
|
+
raise Cli::BadArgument, "Detected: #{identification[:product]}, but this application has no wizard" unless wiz_plugin_class.respond_to?(:wizard)
|
1068
|
+
# instantiate plugin: command line options will be known, e.g. private_key
|
1069
|
+
plugin_instance = wiz_plugin_class.new(@agents)
|
1070
|
+
wiz_params = {
|
1071
|
+
object: plugin_instance
|
1072
|
+
}
|
1073
|
+
# is private key needed ?
|
1074
|
+
if options.known_options.key?(:private_key) &&
|
1075
|
+
(!wiz_plugin_class.respond_to?(:private_key_required?) || wiz_plugin_class.private_key_required?(wiz_url))
|
1076
|
+
# lets see if path to priv key is provided
|
1077
|
+
private_key_path = options.get_option(:key_path)
|
1078
|
+
# give a chance to provide
|
1079
|
+
if private_key_path.nil?
|
1080
|
+
formatter.display_status('Please provide the path to your private RSA key, or nothing to generate one:')
|
1081
|
+
private_key_path = options.get_option(:key_path, mandatory: true).to_s
|
1082
|
+
# private_key_path = File.expand_path(private_key_path)
|
1083
|
+
end
|
1084
|
+
# else generate path
|
1085
|
+
if private_key_path.empty?
|
1086
|
+
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME)
|
1087
|
+
end
|
1088
|
+
if File.exist?(private_key_path)
|
1089
|
+
formatter.display_status('Using existing key:')
|
1090
|
+
else
|
1091
|
+
formatter.display_status("Generating #{DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
|
1092
|
+
Config.generate_rsa_private_key(path: private_key_path)
|
1093
|
+
formatter.display_status('Created key:')
|
1094
|
+
end
|
1095
|
+
formatter.display_status(private_key_path)
|
1096
|
+
private_key_pem = File.read(private_key_path)
|
1097
|
+
options.set_option(:private_key, private_key_pem)
|
1098
|
+
wiz_params[:private_key_path] = private_key_path
|
1099
|
+
wiz_params[:pub_key_pem] = OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s
|
1100
|
+
end
|
1101
|
+
Log.log.debug{Log.dump(:wiz_params, wiz_params)}
|
1102
|
+
# finally, call the wizard
|
1103
|
+
wizard_result = wiz_plugin_class.wizard(**wiz_params)
|
1104
|
+
Log.log.debug{"wizard result: #{wizard_result}"}
|
1105
|
+
raise "Internal error: missing or extra keys in wizard result: #{wizard_result.keys}" unless WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)
|
1106
|
+
# get preset name from user or default
|
1107
|
+
wiz_preset_name = options.get_option(:id)
|
1108
|
+
if wiz_preset_name.nil?
|
1109
|
+
elements = [
|
1110
|
+
identification[:product],
|
1111
|
+
URI.parse(wiz_url).host
|
1112
|
+
]
|
1113
|
+
elements.push(options.get_option(:username, mandatory: true)) unless wizard_result[:preset_value].key?(:link)
|
1114
|
+
wiz_preset_name = elements.join('_').strip.downcase.gsub(/[^a-z0-9]/, '_').squeeze('_')
|
1115
|
+
end
|
1116
|
+
# test mode does not change conf file
|
1117
|
+
return {type: :single_object, data: wizard_result} if options.get_option(:test_mode)
|
1118
|
+
# Write configuration file
|
1119
|
+
formatter.display_status("Preparing preset: #{wiz_preset_name}")
|
1120
|
+
# init defaults if necessary
|
1121
|
+
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
1122
|
+
option_override = options.get_option(:override, mandatory: true)
|
1123
|
+
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[CONF_PRESET_DEFAULT].key?(identification[:product])
|
1125
|
+
raise Cli::Error, "Preset already exists: #{wiz_preset_name} (use --override=yes or --id=<name>)" \
|
1126
|
+
if !option_override && @config_presets.key?(wiz_preset_name)
|
1127
|
+
@config_presets[wiz_preset_name] = wizard_result[:preset_value].stringify_keys
|
1128
|
+
test_args = wizard_result[:test_args]
|
1129
|
+
if options.get_option(:default, mandatory: true)
|
1130
|
+
formatter.display_status("Setting config preset as default for #{identification[:product]}")
|
1131
|
+
@config_presets[CONF_PRESET_DEFAULT][identification[:product].to_s] = wiz_preset_name
|
1132
|
+
else
|
1133
|
+
test_args = "-P#{wiz_preset_name} #{test_args}"
|
1134
|
+
end
|
1135
|
+
# TODO: actually test the command
|
1136
|
+
return Main.result_status("You can test with:\n#{@info[:name]} #{identification[:product]} #{test_args}")
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
# @return [Hash] email server setting with defaults if not defined
|
1038
1140
|
def email_settings
|
1039
|
-
smtp = options.get_option(:smtp,
|
1141
|
+
smtp = options.get_option(:smtp, mandatory: true)
|
1040
1142
|
# change string keys into symbol keys
|
1041
|
-
smtp = smtp.
|
1143
|
+
smtp = smtp.symbolize_keys
|
1042
1144
|
# defaults
|
1043
|
-
smtp[:tls]
|
1044
|
-
smtp[:port] ||= smtp[:tls]
|
1145
|
+
smtp[:tls] = !smtp[:ssl] unless smtp.key?(:tls)
|
1146
|
+
smtp[:port] ||= if smtp[:tls]
|
1147
|
+
587
|
1148
|
+
elsif smtp[:ssl]
|
1149
|
+
465
|
1150
|
+
else
|
1151
|
+
25
|
1152
|
+
end
|
1045
1153
|
smtp[:from_email] ||= smtp[:username] if smtp.key?(:username)
|
1046
1154
|
smtp[:from_name] ||= smtp[:from_email].gsub(/@.*$/, '').gsub(/[^a-zA-Z]/, ' ').capitalize if smtp.key?(:username)
|
1047
1155
|
smtp[:domain] ||= smtp[:from_email].gsub(/^.*@/, '') if smtp.key?(:from_email)
|
1048
1156
|
# check minimum required
|
1049
1157
|
%i[server port domain].each do |n|
|
1050
|
-
raise "Missing smtp parameter: #{n}" unless smtp.key?(n)
|
1158
|
+
raise "Missing mandatory smtp parameter: #{n}" unless smtp.key?(n)
|
1051
1159
|
end
|
1052
1160
|
Log.log.debug{"smtp=#{smtp}"}
|
1053
1161
|
return smtp
|
1054
1162
|
end
|
1055
1163
|
|
1056
|
-
#
|
1057
|
-
def empty_binding
|
1058
|
-
Kernel.binding
|
1059
|
-
end
|
1060
|
-
|
1164
|
+
# send email using ERB template
|
1061
1165
|
def send_email_template(email_template_default: nil, values: {})
|
1062
|
-
values[:to] ||= options.get_option(:
|
1063
|
-
|
1166
|
+
values[:to] ||= options.get_option(:notify_to, mandatory: true)
|
1167
|
+
notify_template = options.get_option(:notify_template, mandatory: email_template_default.nil?) || email_template_default
|
1064
1168
|
mail_conf = email_settings
|
1065
1169
|
values[:from_name] ||= mail_conf[:from_name]
|
1066
1170
|
values[:from_email] ||= mail_conf[:from_email]
|
@@ -1070,56 +1174,66 @@ module Aspera
|
|
1070
1174
|
start_options = [mail_conf[:domain]]
|
1071
1175
|
start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
|
1072
1176
|
# create a binding with only variables defined in values
|
1073
|
-
template_binding = empty_binding
|
1177
|
+
template_binding = Environment.empty_binding
|
1074
1178
|
# add variables to binding
|
1075
1179
|
values.each do |k, v|
|
1076
1180
|
raise "key (#{k.class}) must be Symbol" unless k.is_a?(Symbol)
|
1077
1181
|
template_binding.local_variable_set(k, v)
|
1078
1182
|
end
|
1079
1183
|
# execute template
|
1080
|
-
msg_with_headers = ERB.new(
|
1081
|
-
Log.dump(:msg_with_headers, msg_with_headers)
|
1184
|
+
msg_with_headers = ERB.new(notify_template).result(template_binding)
|
1185
|
+
Log.log.debug{Log.dump(:msg_with_headers, msg_with_headers)}
|
1186
|
+
require 'net/smtp'
|
1082
1187
|
smtp = Net::SMTP.new(mail_conf[:server], mail_conf[:port])
|
1083
1188
|
smtp.enable_starttls if mail_conf[:tls]
|
1189
|
+
smtp.enable_tls if mail_conf[:ssl]
|
1084
1190
|
smtp.start(*start_options) do |smtp_session|
|
1085
1191
|
smtp_session.send_message(msg_with_headers, values[:from_email], values[:to])
|
1086
1192
|
end
|
1193
|
+
nil
|
1087
1194
|
end
|
1088
1195
|
|
1089
|
-
|
1196
|
+
# Save current configuration to config file
|
1197
|
+
# return true if file was saved
|
1198
|
+
def save_config_file_if_needed
|
1090
1199
|
raise 'no configuration loaded' if @config_presets.nil?
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1200
|
+
current_checksum = config_checksum
|
1201
|
+
return false if @config_checksum_on_disk.eql?(current_checksum)
|
1202
|
+
FileUtils.mkdir_p(@main_folder)
|
1094
1203
|
Environment.restrict_file_access(@main_folder)
|
1095
|
-
|
1204
|
+
Log.log.info{"Writing #{@option_config_file}"}
|
1205
|
+
formatter.display_status('Saving config file.')
|
1206
|
+
Environment.write_file_restricted(@option_config_file, force: true) {@config_presets.to_yaml}
|
1207
|
+
@config_checksum_on_disk = current_checksum
|
1208
|
+
return true
|
1096
1209
|
end
|
1097
1210
|
|
1098
1211
|
# returns [String] name if config_presets has default
|
1099
1212
|
# returns nil if there is no config or bypass default params
|
1100
|
-
def get_plugin_default_config_name(
|
1213
|
+
def get_plugin_default_config_name(plugin_name_sym)
|
1101
1214
|
raise 'internal error: config_presets shall be defined' if @config_presets.nil?
|
1102
1215
|
if !@use_plugin_defaults
|
1103
1216
|
Log.log.debug('skip default config')
|
1104
1217
|
return nil
|
1105
1218
|
end
|
1106
1219
|
if @config_presets.key?(CONF_PRESET_DEFAULT) &&
|
1107
|
-
@config_presets[CONF_PRESET_DEFAULT].key?(
|
1108
|
-
default_config_name = @config_presets[CONF_PRESET_DEFAULT][
|
1220
|
+
@config_presets[CONF_PRESET_DEFAULT].key?(plugin_name_sym.to_s)
|
1221
|
+
default_config_name = @config_presets[CONF_PRESET_DEFAULT][plugin_name_sym.to_s]
|
1109
1222
|
if !@config_presets.key?(default_config_name)
|
1110
1223
|
Log.log.error do
|
1111
|
-
"Default config name [#{default_config_name}] specified for plugin [#{
|
1112
|
-
'Please fix the issue: either create preset with one parameter: '\
|
1113
|
-
"(#{@info[:name]} config id #{default_config_name} init @json:'{}') or remove default (#{@info[:name]} config id default remove #{
|
1224
|
+
"Default config name [#{default_config_name}] specified for plugin [#{plugin_name_sym}], but it does not exist in config file.\n" \
|
1225
|
+
'Please fix the issue: either create preset with one parameter: ' \
|
1226
|
+
"(#{@info[:name]} config id #{default_config_name} init @json:'{}') or remove default (#{@info[:name]} config id default remove #{plugin_name_sym})."
|
1114
1227
|
end
|
1115
1228
|
end
|
1116
|
-
raise
|
1229
|
+
raise Cli::Error, "Config name [#{default_config_name}] must be a hash, check config file." if !@config_presets[default_config_name].is_a?(Hash)
|
1117
1230
|
return default_config_name
|
1118
1231
|
end
|
1119
1232
|
return nil
|
1120
1233
|
end # get_plugin_default_config_name
|
1121
1234
|
|
1122
|
-
ALLOWED_KEYS = %i[password username description].freeze
|
1235
|
+
# TODO: delete: ALLOWED_KEYS = %i[password username description].freeze
|
1236
|
+
# @return [Hash] result of execution of vault command
|
1123
1237
|
def execute_vault
|
1124
1238
|
command = options.get_next_command(%i[list show create delete password])
|
1125
1239
|
case command
|
@@ -1147,21 +1261,22 @@ module Aspera
|
|
1147
1261
|
end
|
1148
1262
|
end
|
1149
1263
|
|
1264
|
+
# @return [String] value from vault matching <name>.<param>
|
1150
1265
|
def vault_value(name)
|
1151
1266
|
m = name.match(/^(.+)\.(.+)$/)
|
1152
1267
|
raise 'vault name shall match <name>.<param>' if m.nil?
|
1268
|
+
# this raise exception if label not found:
|
1153
1269
|
info = vault.get(label: m[1])
|
1154
|
-
# raise "no such vault entry: #{m[1]}" if info.nil?
|
1155
1270
|
value = info[m[2].to_sym]
|
1156
1271
|
raise "no such entry value: #{m[2]}" if value.nil?
|
1157
1272
|
return value
|
1158
1273
|
end
|
1159
1274
|
|
1275
|
+
# @return [Object] vault, from options or cache
|
1160
1276
|
def vault
|
1161
1277
|
if @vault.nil?
|
1162
1278
|
vault_info = options.get_option(:vault) || {'type' => 'file', 'name' => 'vault.bin'}
|
1163
|
-
vault_password = options.get_option(:vault_password,
|
1164
|
-
raise 'vault must be Hash' unless vault_info.is_a?(Hash)
|
1279
|
+
vault_password = options.get_option(:vault_password, mandatory: true)
|
1165
1280
|
vault_type = vault_info['type'] || 'file'
|
1166
1281
|
vault_name = vault_info['name'] || (vault_type.eql?('file') ? 'vault.bin' : PROGRAM_NAME)
|
1167
1282
|
case vault_type
|
@@ -1173,12 +1288,11 @@ module Aspera
|
|
1173
1288
|
case Environment.os
|
1174
1289
|
when Environment::OS_X
|
1175
1290
|
@vault = Keychain::MacosSystem.new(vault_name, vault_password)
|
1176
|
-
|
1177
|
-
raise 'not implemented'
|
1178
|
-
else raise 'Error, OS not supported'
|
1291
|
+
else
|
1292
|
+
raise 'not implemented for this OS'
|
1179
1293
|
end
|
1180
1294
|
else
|
1181
|
-
raise
|
1295
|
+
raise Cli::BadArgument, "Unknown vault type: #{vault_type}"
|
1182
1296
|
end
|
1183
1297
|
end
|
1184
1298
|
raise 'No vault defined' if @vault.nil?
|
@@ -1197,7 +1311,7 @@ module Aspera
|
|
1197
1311
|
@config_presets.each do |_k, v|
|
1198
1312
|
next unless v.is_a?(Hash)
|
1199
1313
|
conf_url = v['url'].is_a?(String) ? canonical_url(v['url']) : nil
|
1200
|
-
return v if conf_url.eql?(url) && v['username'].eql?(username)
|
1314
|
+
return self.class.protect_presets(v) if conf_url.eql?(url) && v['username'].eql?(username)
|
1201
1315
|
end
|
1202
1316
|
nil
|
1203
1317
|
end
|
@@ -1214,169 +1328,6 @@ module Aspera
|
|
1214
1328
|
end
|
1215
1329
|
return secret
|
1216
1330
|
end
|
1217
|
-
|
1218
|
-
def wizard_aoc(params)
|
1219
|
-
formatter.display_status('Detected: Aspera on Cloud'.bold)
|
1220
|
-
params[:plugin_name] = AOC_COMMAND_CURRENT
|
1221
|
-
organization = AoC.parse_url(params[:instance_url]).first
|
1222
|
-
# if not defined by user, generate name
|
1223
|
-
params[:preset_name] = [params[:application], organization].join('_') if params[:preset_name].nil?
|
1224
|
-
formatter.display_status("Preparing preset: #{params[:preset_name]}")
|
1225
|
-
# init defaults if necessary
|
1226
|
-
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
1227
|
-
option_override = options.get_option(:override, is_type: :mandatory)
|
1228
|
-
params[:option_default] = options.get_option(:default, is_type: :mandatory)
|
1229
|
-
Log.log.error{"override=#{option_override} -> #{option_override.class}"}
|
1230
|
-
raise CliError, "A default configuration already exists for plugin '#{params[:plugin_name]}' (use --override=yes or --default=no)" \
|
1231
|
-
if !option_override && params[:option_default] && @config_presets[CONF_PRESET_DEFAULT].key?(params[:plugin_name])
|
1232
|
-
raise CliError, "Preset already exists: #{params[:preset_name]} (use --override=yes or --id=<name>)" \
|
1233
|
-
if !option_override && @config_presets.key?(params[:preset_name])
|
1234
|
-
# lets see if path to priv key is provided
|
1235
|
-
private_key_path = options.get_option(:pkeypath)
|
1236
|
-
# give a chance to provide
|
1237
|
-
if private_key_path.nil?
|
1238
|
-
formatter.display_status('Please provide path to your private RSA key, or empty to generate one:')
|
1239
|
-
private_key_path = options.get_option(:pkeypath, is_type: :mandatory).to_s
|
1240
|
-
end
|
1241
|
-
# else generate path
|
1242
|
-
if private_key_path.empty?
|
1243
|
-
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME)
|
1244
|
-
end
|
1245
|
-
if File.exist?(private_key_path)
|
1246
|
-
formatter.display_status('Using existing key:')
|
1247
|
-
else
|
1248
|
-
formatter.display_status("Generating #{DEFAULT_PRIVKEY_LENGTH} bit RSA key...")
|
1249
|
-
generate_rsa_private_key(private_key_path, DEFAULT_PRIVKEY_LENGTH)
|
1250
|
-
formatter.display_status('Created:')
|
1251
|
-
end
|
1252
|
-
formatter.display_status(private_key_path)
|
1253
|
-
pub_key_pem = OpenSSL::PKey::RSA.new(File.read(private_key_path)).public_key.to_s
|
1254
|
-
# declare command line options for AoC
|
1255
|
-
require 'aspera/cli/plugins/aoc'
|
1256
|
-
# make username mandatory for jwt, this triggers interactive input
|
1257
|
-
options.get_option(:username, is_type: :mandatory)
|
1258
|
-
# instantiate AoC plugin, so that command line options are known
|
1259
|
-
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
|
1260
|
-
auto_set_pub_key = false
|
1261
|
-
auto_set_jwt = false
|
1262
|
-
use_browser_authentication = false
|
1263
|
-
if options.get_option(:use_generic_client)
|
1264
|
-
formatter.display_status('Using global client_id.')
|
1265
|
-
formatter.display_status('Please Login to your Aspera on Cloud instance.'.red)
|
1266
|
-
formatter.display_status('Navigate to your "Account Settings"'.red)
|
1267
|
-
formatter.display_status('Check or update the value of "Public Key" to be:'.red.blink)
|
1268
|
-
formatter.display_status(pub_key_pem.to_s)
|
1269
|
-
if !options.get_option(:test_mode)
|
1270
|
-
formatter.display_status('Once updated or validated, press enter.')
|
1271
|
-
OpenApplication.instance.uri(params[:instance_url])
|
1272
|
-
$stdin.gets
|
1273
|
-
end
|
1274
|
-
else
|
1275
|
-
formatter.display_status('Using organization specific client_id.')
|
1276
|
-
if options.get_option(:client_id).nil? || options.get_option(:client_secret, is_type: :optional).nil?
|
1277
|
-
formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
|
1278
|
-
formatter.display_status('Go to: Apps->Admin->Organization->Integrations')
|
1279
|
-
formatter.display_status('Create or check if there is an existing integration named:')
|
1280
|
-
formatter.display_status("- name: #{@info[:name]}")
|
1281
|
-
formatter.display_status("- redirect uri: #{DEFAULT_REDIRECT}")
|
1282
|
-
formatter.display_status('- origin: localhost')
|
1283
|
-
formatter.display_status('Once created or identified,')
|
1284
|
-
formatter.display_status('Please enter:'.red)
|
1285
|
-
end
|
1286
|
-
OpenApplication.instance.uri("#{params[:instance_url]}/#{AOC_PATH_API_CLIENTS}")
|
1287
|
-
options.get_option(:client_id, is_type: :mandatory)
|
1288
|
-
options.get_option(:client_secret, is_type: :mandatory)
|
1289
|
-
use_browser_authentication = true
|
1290
|
-
end
|
1291
|
-
if use_browser_authentication
|
1292
|
-
formatter.display_status('We will use web authentication to bootstrap.')
|
1293
|
-
auto_set_pub_key = true
|
1294
|
-
auto_set_jwt = true
|
1295
|
-
aoc_api.oauth.generic_parameters[:grant_method] = :web
|
1296
|
-
aoc_api.oauth.generic_parameters[:scope] = AoC::SCOPE_FILES_ADMIN
|
1297
|
-
aoc_api.oauth.specific_parameters[:redirect_uri] = DEFAULT_REDIRECT
|
1298
|
-
end
|
1299
|
-
myself = aoc_api.read('self')[:data]
|
1300
|
-
if auto_set_pub_key
|
1301
|
-
raise CliError, 'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
|
1302
|
-
formatter.display_status('Updating profile with new key')
|
1303
|
-
aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
|
1304
|
-
end
|
1305
|
-
if auto_set_jwt
|
1306
|
-
formatter.display_status('Enabling JWT for client')
|
1307
|
-
aoc_api.update("clients/#{options.get_option(:client_id)}", {'jwt_grant_enabled' => true, 'explicit_authorization_required' => false})
|
1308
|
-
end
|
1309
|
-
formatter.display_status("Creating new config preset: #{params[:preset_name]}")
|
1310
|
-
@config_presets[params[:preset_name]] = {
|
1311
|
-
:url.to_s => options.get_option(:url),
|
1312
|
-
:username.to_s => myself['email'],
|
1313
|
-
:auth.to_s => :jwt.to_s,
|
1314
|
-
:private_key.to_s => '@file:' + private_key_path
|
1315
|
-
}
|
1316
|
-
# set only if non nil
|
1317
|
-
%i[client_id client_secret].each do |s|
|
1318
|
-
o = options.get_option(s)
|
1319
|
-
@config_presets[params[:preset_name]][s.to_s] = o unless o.nil?
|
1320
|
-
end
|
1321
|
-
params[:test_args] = "#{params[:plugin_name]} user profile show"
|
1322
|
-
end
|
1323
|
-
|
1324
|
-
def wizard_faspex5(params)
|
1325
|
-
formatter.display_status('Detected: Faspex v5'.bold)
|
1326
|
-
# if not defined by user, generate unique name
|
1327
|
-
params[:preset_name] = [params[:application]].concat(URI.parse(params[:instance_url]).host.gsub(/[^a-z0-9.]/, '').split('.')).join('_') \
|
1328
|
-
if params[:preset_name].nil?
|
1329
|
-
formatter.display_status("Preparing preset: #{params[:preset_name]}")
|
1330
|
-
# init defaults if necessary
|
1331
|
-
@config_presets[CONF_PRESET_DEFAULT] ||= {}
|
1332
|
-
option_override = options.get_option(:override, is_type: :mandatory)
|
1333
|
-
params[:option_default] = options.get_option(:default, is_type: :mandatory)
|
1334
|
-
Log.log.error{"override=#{option_override} -> #{option_override.class}"}
|
1335
|
-
raise CliError, "A default configuration already exists for plugin '#{params[:plugin_name]}' (use --override=yes or --default=no)" \
|
1336
|
-
if !option_override && params[:option_default] && @config_presets[CONF_PRESET_DEFAULT].key?(params[:plugin_name])
|
1337
|
-
raise CliError, "Preset already exists: #{params[:preset_name]} (use --override=yes or --id=<name>)" \
|
1338
|
-
if !option_override && @config_presets.key?(params[:preset_name])
|
1339
|
-
# lets see if path to priv key is provided
|
1340
|
-
private_key_path = options.get_option(:pkeypath)
|
1341
|
-
# give a chance to provide
|
1342
|
-
if private_key_path.nil?
|
1343
|
-
formatter.display_status('Please provide path to your private RSA key, or empty to generate one:')
|
1344
|
-
private_key_path = options.get_option(:pkeypath, is_type: :mandatory).to_s
|
1345
|
-
end
|
1346
|
-
# else generate path
|
1347
|
-
if private_key_path.empty?
|
1348
|
-
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME)
|
1349
|
-
end
|
1350
|
-
if File.exist?(private_key_path)
|
1351
|
-
formatter.display_status('Using existing key:')
|
1352
|
-
else
|
1353
|
-
formatter.display_status("Generating #{DEFAULT_PRIVKEY_LENGTH} bit RSA key...")
|
1354
|
-
generate_rsa_private_key(private_key_path, DEFAULT_PRIVKEY_LENGTH)
|
1355
|
-
formatter.display_status('Created:')
|
1356
|
-
end
|
1357
|
-
formatter.display_status(private_key_path)
|
1358
|
-
pub_key_pem = OpenSSL::PKey::RSA.new(File.read(private_key_path)).public_key.to_s
|
1359
|
-
# declare command line options for AoC
|
1360
|
-
require 'aspera/cli/plugins/faspex5'
|
1361
|
-
self.class.plugin_class(params[:plugin_name]).new(@agents.merge({skip_basic_auth_options: true}))
|
1362
|
-
formatter.display_status('Please login to Faspex 5.'.red)
|
1363
|
-
OpenApplication.instance.uri(params[:instance_url])
|
1364
|
-
formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
|
1365
|
-
formatter.display_status('Create a client with:')
|
1366
|
-
formatter.display_status('- JWT enabled')
|
1367
|
-
formatter.display_status('- The following public key:')
|
1368
|
-
formatter.display_status(pub_key_pem.to_s)
|
1369
|
-
formatter.display_status('Once created, copy the following parameters:')
|
1370
|
-
@config_presets[params[:preset_name]] = {
|
1371
|
-
:url.to_s => options.get_option(:url),
|
1372
|
-
:username.to_s => options.get_option(:username),
|
1373
|
-
:auth.to_s => :jwt.to_s,
|
1374
|
-
:private_key.to_s => '@file:' + private_key_path,
|
1375
|
-
:client_id.to_s => options.get_option(:client_id, is_type: :mandatory),
|
1376
|
-
:client_secret.to_s => options.get_option(:client_secret, is_type: :mandatory)
|
1377
|
-
}
|
1378
|
-
params[:test_args] = "#{params[:plugin_name]} user profile show"
|
1379
|
-
end
|
1380
1331
|
end
|
1381
1332
|
end
|
1382
1333
|
end
|