aspera-cli 4.25.1 → 4.25.3
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 +456 -405
- data/CONTRIBUTING.md +22 -18
- data/README.md +33 -9741
- data/bin/asession +111 -88
- data/lib/aspera/agent/connect.rb +1 -1
- data/lib/aspera/agent/desktop.rb +1 -1
- data/lib/aspera/agent/direct.rb +19 -18
- data/lib/aspera/agent/node.rb +1 -1
- data/lib/aspera/api/aoc.rb +44 -20
- data/lib/aspera/api/faspex.rb +25 -6
- data/lib/aspera/api/node.rb +20 -16
- data/lib/aspera/ascp/installation.rb +32 -51
- data/lib/aspera/assert.rb +2 -2
- data/lib/aspera/cli/extended_value.rb +1 -0
- data/lib/aspera/cli/formatter.rb +0 -4
- data/lib/aspera/cli/hints.rb +18 -4
- data/lib/aspera/cli/main.rb +3 -6
- data/lib/aspera/cli/manager.rb +46 -30
- data/lib/aspera/cli/plugins/aoc.rb +155 -131
- data/lib/aspera/cli/plugins/base.rb +15 -18
- data/lib/aspera/cli/plugins/config.rb +50 -87
- data/lib/aspera/cli/plugins/factory.rb +2 -2
- data/lib/aspera/cli/plugins/faspex.rb +4 -4
- data/lib/aspera/cli/plugins/faspex5.rb +74 -76
- data/lib/aspera/cli/plugins/node.rb +3 -5
- data/lib/aspera/cli/plugins/oauth.rb +26 -25
- data/lib/aspera/cli/plugins/preview.rb +9 -14
- data/lib/aspera/cli/plugins/shares.rb +15 -7
- data/lib/aspera/cli/transfer_agent.rb +2 -2
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +7 -0
- data/lib/aspera/environment.rb +30 -16
- data/lib/aspera/faspex_gw.rb +6 -6
- data/lib/aspera/faspex_postproc.rb +20 -14
- data/lib/aspera/hash_ext.rb +8 -0
- data/lib/aspera/log.rb +15 -15
- data/lib/aspera/markdown.rb +22 -0
- data/lib/aspera/node_simulator.rb +1 -1
- data/lib/aspera/oauth/base.rb +2 -2
- data/lib/aspera/oauth/url_json.rb +2 -2
- data/lib/aspera/oauth/web.rb +1 -1
- data/lib/aspera/preview/generator.rb +9 -9
- data/lib/aspera/rest.rb +44 -37
- data/lib/aspera/rest_call_error.rb +16 -8
- data/lib/aspera/rest_error_analyzer.rb +38 -36
- data/lib/aspera/rest_errors_aspera.rb +19 -18
- data/lib/aspera/transfer/resumer.rb +2 -2
- data/lib/aspera/yaml.rb +49 -0
- data.tar.gz.sig +0 -0
- metadata +17 -3
- metadata.gz.sig +0 -0
- data/release_notes.md +0 -8
|
@@ -36,6 +36,7 @@ require 'digest'
|
|
|
36
36
|
require 'open3'
|
|
37
37
|
require 'date'
|
|
38
38
|
require 'erb'
|
|
39
|
+
require 'net/http'
|
|
39
40
|
|
|
40
41
|
module Aspera
|
|
41
42
|
module Cli
|
|
@@ -107,11 +108,7 @@ module Aspera
|
|
|
107
108
|
)
|
|
108
109
|
options.parse_options!
|
|
109
110
|
Log.log.debug{"#{Info::CMD_NAME} folder: #{@main_folder}"}
|
|
110
|
-
|
|
111
|
-
context.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
|
112
|
-
# Set folders for plugin lookup
|
|
113
|
-
Plugins::Factory.instance.add_lookup_folder(self.class.gem_plugins_folder)
|
|
114
|
-
Plugins::Factory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
|
111
|
+
setup_persistency_and_plugin_folders
|
|
115
112
|
# Option to set config file
|
|
116
113
|
options.declare(
|
|
117
114
|
:config_file, 'Path to YAML file with preset configuration',
|
|
@@ -121,12 +118,7 @@ module Aspera
|
|
|
121
118
|
options.parse_options!
|
|
122
119
|
# Read config file (set @config_presets)
|
|
123
120
|
read_config_file
|
|
124
|
-
|
|
125
|
-
ExtendedValue.instance.on(EXTEND_PRESET){ |v| preset_by_name(v)}
|
|
126
|
-
ExtendedValue.instance.on(EXTEND_VAULT){ |v| vault_value(v)}
|
|
127
|
-
ExtendedValue.instance.on(EXTEND_ARGS){ |v| options.args_as_extended(v)}
|
|
128
|
-
# Load defaults before it can be overridden
|
|
129
|
-
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
|
121
|
+
setup_extended_value_handlers
|
|
130
122
|
# Vault options
|
|
131
123
|
options.declare(:secret, 'Secret for access keys')
|
|
132
124
|
options.declare(:vault, 'Vault for secrets', allowed: Hash)
|
|
@@ -145,9 +137,8 @@ module Aspera
|
|
|
145
137
|
options.declare(:sdk_url, 'Ascp: URL to get Aspera Transfer Executables', default: SpecialValues::DEF)
|
|
146
138
|
options.parse_options!
|
|
147
139
|
set_sdk_dir
|
|
148
|
-
options.declare(:ascp_path, 'Ascp: Path to ascp (or product with "product:")', handler: {o: Ascp::Installation.instance, m: :ascp_path}, default: "#{Ascp::Installation::USE_PRODUCT_PREFIX}#{Ascp::Installation::FIRST_FOUND}")
|
|
149
140
|
options.declare(:locations_url, 'Ascp: URL to get download locations of Aspera Transfer Daemon', handler: {o: Ascp::Installation.instance, m: :transferd_urls})
|
|
150
|
-
options.declare(:sdk_folder, 'Ascp:
|
|
141
|
+
options.declare(:sdk_folder, 'Ascp: Path to folder with ascp (or product with "product:")', handler: {o: Products::Transferd, m: :sdk_directory})
|
|
151
142
|
options.declare(:progress_bar, 'Display progress bar', allowed: Allowed::TYPES_BOOLEAN, default: Environment.terminal?)
|
|
152
143
|
# Email options
|
|
153
144
|
options.declare(:smtp, 'Email: SMTP configuration', allowed: Hash)
|
|
@@ -165,17 +156,39 @@ module Aspera
|
|
|
165
156
|
options.declare(:proxy_credentials, 'HTTP proxy credentials for fpac: user, password', allowed: [Array, NilClass])
|
|
166
157
|
options.parse_options!
|
|
167
158
|
@progress_bar = TransferProgress.new if options.get_option(:progress_bar)
|
|
159
|
+
setup_pac_executor
|
|
160
|
+
setup_rest_and_transfer_runtime
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def setup_persistency_and_plugin_folders
|
|
166
|
+
context.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
|
167
|
+
Plugins::Factory.instance.add_lookup_folder(self.class.gem_plugins_folder)
|
|
168
|
+
Plugins::Factory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def setup_extended_value_handlers
|
|
172
|
+
ExtendedValue.instance.on(EXTEND_PRESET){ |v| preset_by_name(v)}
|
|
173
|
+
ExtendedValue.instance.on(EXTEND_VAULT){ |v| vault_value(v)}
|
|
174
|
+
ExtendedValue.instance.on(EXTEND_ARGS){ |v| options.args_as_extended(v)}
|
|
175
|
+
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def setup_pac_executor
|
|
168
179
|
pac_script = options.get_option(:fpac)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
end
|
|
180
|
+
return unless pac_script
|
|
181
|
+
|
|
182
|
+
@pac_exec = ProxyAutoConfig.new(pac_script).register_uri_generic
|
|
183
|
+
proxy_user_pass = options.get_option(:proxy_credentials)
|
|
184
|
+
if proxy_user_pass
|
|
185
|
+
Aspera.assert(proxy_user_pass.length.eql?(2), type: Cli::BadArgument){"proxy_credentials shall have two elements (#{proxy_user_pass.length})"}
|
|
186
|
+
@pac_exec.proxy_user = proxy_user_pass[0]
|
|
187
|
+
@pac_exec.proxy_pass = proxy_user_pass[1]
|
|
178
188
|
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def setup_rest_and_transfer_runtime
|
|
179
192
|
RestParameters.instance.user_agent = Info::CMD_NAME
|
|
180
193
|
RestParameters.instance.progress_bar = @progress_bar
|
|
181
194
|
RestParameters.instance.session_cb = lambda{ |http_session| update_http_session(http_session)}
|
|
@@ -197,12 +210,14 @@ module Aspera
|
|
|
197
210
|
keys_to_delete.each{ |k| @option_http_options.delete(k)}
|
|
198
211
|
OAuth::Factory.instance.persist_mgr = persistency if @option_cache_tokens
|
|
199
212
|
OAuth::Web.additional_info = "#{Info::CMD_NAME} v#{Cli::VERSION}"
|
|
200
|
-
Transfer::Parameters.file_list_folder = File.join(@main_folder,
|
|
201
|
-
RestErrorAnalyzer.instance.log_file = File.join(@main_folder,
|
|
213
|
+
Transfer::Parameters.file_list_folder = File.join(@main_folder, FILE_LIST_FOLDER_NAME)
|
|
214
|
+
RestErrorAnalyzer.instance.log_file = File.join(@main_folder, REST_EXCEPTIONS_LOG_FILENAME)
|
|
202
215
|
# Register aspera REST call error handlers
|
|
203
216
|
RestErrorsAspera.register_handlers
|
|
204
217
|
end
|
|
205
218
|
|
|
219
|
+
public
|
|
220
|
+
|
|
206
221
|
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_warn_insecure_cert, :option_http_options
|
|
207
222
|
attr_reader :option_ignore_cert_host_port, :progress_bar
|
|
208
223
|
|
|
@@ -307,10 +322,7 @@ module Aspera
|
|
|
307
322
|
if @option_warn_insecure_cert
|
|
308
323
|
base_url = "https://#{address}:#{port}"
|
|
309
324
|
if !@ssl_warned_urls.include?(base_url)
|
|
310
|
-
|
|
311
|
-
:error,
|
|
312
|
-
"#{Formatter::WARNING_FLASH} Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production."
|
|
313
|
-
)
|
|
325
|
+
Log.log.warn{"Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production."}
|
|
314
326
|
@ssl_warned_urls.push(base_url)
|
|
315
327
|
end
|
|
316
328
|
end
|
|
@@ -416,7 +428,7 @@ module Aspera
|
|
|
416
428
|
raise Cli::Error, "Preset already exists: #{preset_name} (use --override=yes or provide alternate name on command line)" \
|
|
417
429
|
if !option_override && @config_presets.key?(preset_name)
|
|
418
430
|
if option_default
|
|
419
|
-
|
|
431
|
+
Log.log.info("Setting config preset as default for #{plugin_name}")
|
|
420
432
|
@config_presets[CONF_PRESET_DEFAULTS][plugin_name.to_s] = preset_name
|
|
421
433
|
end
|
|
422
434
|
@config_presets[preset_name] = preset_values
|
|
@@ -439,7 +451,7 @@ module Aspera
|
|
|
439
451
|
Log.log.warn{"overwriting value for #{param_name}: #{selected_preset[param_name]}"}
|
|
440
452
|
end
|
|
441
453
|
selected_preset[param_name] = param_value
|
|
442
|
-
|
|
454
|
+
Log.log.info("Updated: #{preset}: #{param_name} <- #{param_value}")
|
|
443
455
|
nil
|
|
444
456
|
end
|
|
445
457
|
|
|
@@ -553,62 +565,15 @@ module Aspera
|
|
|
553
565
|
raise Cli::Error, e.to_s
|
|
554
566
|
end
|
|
555
567
|
|
|
556
|
-
def execute_connect_action
|
|
557
|
-
command = options.get_next_command(%i[list info version])
|
|
558
|
-
if %i[info version].include?(command)
|
|
559
|
-
connect_id = options.get_next_argument('id or title')
|
|
560
|
-
one_res = Products::Connect.instance.versions.find{ |i| i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}
|
|
561
|
-
raise Cli::BadIdentifier.new(:connect, connect_id) if one_res.nil?
|
|
562
|
-
end
|
|
563
|
-
case command
|
|
564
|
-
when :list
|
|
565
|
-
return Main.result_object_list(Products::Connect.instance.versions, fields: %w[id title version])
|
|
566
|
-
when :info
|
|
567
|
-
one_res.delete('links')
|
|
568
|
-
return Main.result_single_object(one_res)
|
|
569
|
-
when :version
|
|
570
|
-
all_links = one_res['links']
|
|
571
|
-
command = options.get_next_command(%i[list download open])
|
|
572
|
-
if %i[download open].include?(command)
|
|
573
|
-
link_title = options.get_next_argument('title or rel')
|
|
574
|
-
one_link = all_links.find{ |i| i['title'].eql?(link_title) || i['rel'].eql?(link_title)}
|
|
575
|
-
raise "no such value: #{link_title}" if one_link.nil?
|
|
576
|
-
end
|
|
577
|
-
case command
|
|
578
|
-
when :list
|
|
579
|
-
return Main.result_object_list(all_links)
|
|
580
|
-
when :download
|
|
581
|
-
archive_path = one_link['href']
|
|
582
|
-
save_to_path = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), archive_path.gsub(%r{.*/}, ''))
|
|
583
|
-
Products::Connect.instance.cdn_api.call(operation: 'GET', subpath: archive_path, save_to_file: save_to_path)
|
|
584
|
-
return Main.result_status("Downloaded: #{save_to_path}")
|
|
585
|
-
when :open
|
|
586
|
-
Environment.instance.open_uri(one_link['href'])
|
|
587
|
-
return Main.result_status("Opened: #{one_link['href']}")
|
|
588
|
-
end
|
|
589
|
-
end
|
|
590
|
-
Aspera.error_unreachable_line
|
|
591
|
-
end
|
|
592
|
-
|
|
593
568
|
def install_transfer_sdk
|
|
594
|
-
# Reset to default location, if older default was used
|
|
595
|
-
Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
|
|
596
569
|
asked_version = options.get_next_argument('transferd version', mandatory: false)
|
|
597
570
|
name, version, folder = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: asked_version)
|
|
598
571
|
return Main.result_status("Installed #{name} version #{version} in #{folder}")
|
|
599
572
|
end
|
|
600
573
|
|
|
601
574
|
def execute_action_ascp
|
|
602
|
-
command = options.get_next_command(%i[
|
|
575
|
+
command = options.get_next_command(%i[show products info install spec schema errors])
|
|
603
576
|
case command
|
|
604
|
-
when :connect
|
|
605
|
-
return execute_connect_action
|
|
606
|
-
when :use
|
|
607
|
-
ascp_path = options.get_next_argument('path to ascp')
|
|
608
|
-
Ascp::Installation.instance.ascp_path = ascp_path
|
|
609
|
-
formatter.display_status("ascp version: #{Ascp::Installation.instance.get_ascp_version(ascp_path)}")
|
|
610
|
-
set_global_default(:ascp_path, ascp_path)
|
|
611
|
-
return Main.result_nothing
|
|
612
577
|
when :show
|
|
613
578
|
return Main.result_text(Ascp::Installation.instance.path(:ascp))
|
|
614
579
|
when :info
|
|
@@ -622,15 +587,10 @@ module Aspera
|
|
|
622
587
|
SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
|
|
623
588
|
return Main.result_single_object(data)
|
|
624
589
|
when :products
|
|
625
|
-
command = options.get_next_command(%i[list
|
|
590
|
+
command = options.get_next_command(%i[list])
|
|
626
591
|
case command
|
|
627
592
|
when :list
|
|
628
593
|
return Main.result_object_list(Ascp::Installation.instance.installed_products, fields: %w[name app_root])
|
|
629
|
-
when :use
|
|
630
|
-
default_product = options.get_next_argument('product name')
|
|
631
|
-
Ascp::Installation.instance.use_ascp_from_product(default_product)
|
|
632
|
-
set_global_default(:ascp_path, "#{Ascp::Installation::USE_PRODUCT_PREFIX}#{default_product}")
|
|
633
|
-
return Main.result_nothing
|
|
634
594
|
end
|
|
635
595
|
when :install
|
|
636
596
|
return install_transfer_sdk
|
|
@@ -848,7 +808,7 @@ module Aspera
|
|
|
848
808
|
file_url = options.get_next_argument('source URL').chomp
|
|
849
809
|
file_dest = options.get_next_argument('file path', mandatory: false)
|
|
850
810
|
file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
|
|
851
|
-
|
|
811
|
+
Log.log.info("Downloading: #{file_url}")
|
|
852
812
|
Rest.new(base_url: file_url).call(operation: 'GET', save_to_file: file_dest)
|
|
853
813
|
return Main.result_status("Saved to: #{file_dest}")
|
|
854
814
|
when :tokens
|
|
@@ -1047,8 +1007,7 @@ module Aspera
|
|
|
1047
1007
|
return false if @config_checksum_on_disk.eql?(current_checksum)
|
|
1048
1008
|
FileUtils.mkdir_p(@main_folder)
|
|
1049
1009
|
Environment.restrict_file_access(@main_folder)
|
|
1050
|
-
Log.log.info{"
|
|
1051
|
-
formatter.display_status('Saving config file.')
|
|
1010
|
+
Log.log.info{"Saving config file: #{@option_config_file}"}
|
|
1052
1011
|
Environment.write_file_restricted(@option_config_file, force: true){@config_presets.to_yaml}
|
|
1053
1012
|
@config_checksum_on_disk = current_checksum
|
|
1054
1013
|
return true
|
|
@@ -1200,6 +1159,8 @@ module Aspera
|
|
|
1200
1159
|
# Folder containing custom plugins in user's config folder
|
|
1201
1160
|
ASPERA_PLUGINS_FOLDERNAME = 'plugins'
|
|
1202
1161
|
PERSISTENCY_FOLDER = 'persist_store'
|
|
1162
|
+
FILE_LIST_FOLDER_NAME = 'filelists'
|
|
1163
|
+
REST_EXCEPTIONS_LOG_FILENAME = 'rest_exceptions.log'
|
|
1203
1164
|
ASPERA = 'aspera'
|
|
1204
1165
|
SERVER_COMMAND = 'server'
|
|
1205
1166
|
TRANSFERD_APP_NAME = 'sdk'
|
|
@@ -1232,6 +1193,8 @@ module Aspera
|
|
|
1232
1193
|
:CONF_PRESET_DEFAULTS,
|
|
1233
1194
|
:CONF_PRESET_GLOBAL,
|
|
1234
1195
|
:ASPERA_PLUGINS_FOLDERNAME,
|
|
1196
|
+
:FILE_LIST_FOLDER_NAME,
|
|
1197
|
+
:REST_EXCEPTIONS_LOG_FILENAME,
|
|
1235
1198
|
:ASPERA,
|
|
1236
1199
|
:DEMO_SERVER,
|
|
1237
1200
|
:DEMO_PRESET,
|
|
@@ -20,9 +20,9 @@ module Aspera
|
|
|
20
20
|
@plugins = {}
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# @return list of registered plugins
|
|
23
|
+
# @return [Array<Symbol>] Sorted list of registered plugins
|
|
24
24
|
def plugin_list
|
|
25
|
-
@plugins.keys
|
|
25
|
+
@plugins.keys.sort
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# add a folder to the list of folders to look for plugins
|
|
@@ -51,7 +51,7 @@ module Aspera
|
|
|
51
51
|
http = api.call(
|
|
52
52
|
operation: 'POST',
|
|
53
53
|
headers: {
|
|
54
|
-
'Content-type' =>
|
|
54
|
+
'Content-type' => Mime::TEXT,
|
|
55
55
|
'Accept' => 'application/xrds+xml'
|
|
56
56
|
},
|
|
57
57
|
ret: :resp
|
|
@@ -254,7 +254,7 @@ module Aspera
|
|
|
254
254
|
package_creation_data = api_public_link.call(
|
|
255
255
|
operation: 'POST',
|
|
256
256
|
subpath: create_path,
|
|
257
|
-
content_type:
|
|
257
|
+
content_type: Mime::JSON,
|
|
258
258
|
body: package_create_params,
|
|
259
259
|
headers: {'Accept' => 'text/javascript'},
|
|
260
260
|
ret: :resp
|
|
@@ -418,9 +418,9 @@ module Aspera
|
|
|
418
418
|
operation: 'POST',
|
|
419
419
|
subpath: 'issue-token',
|
|
420
420
|
query: {'direction' => 'down'},
|
|
421
|
-
content_type:
|
|
421
|
+
content_type: Mime::TEXT,
|
|
422
422
|
body: xml_payload,
|
|
423
|
-
headers: {'Accept' =>
|
|
423
|
+
headers: {'Accept' => Mime::TEXT, 'Content-Type' => 'application/vnd.aspera.url-list+xml'},
|
|
424
424
|
ret: :resp
|
|
425
425
|
).body
|
|
426
426
|
end
|
|
@@ -94,7 +94,7 @@ module Aspera
|
|
|
94
94
|
|
|
95
95
|
def initialize(**_)
|
|
96
96
|
super
|
|
97
|
-
options.declare(:box, "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: '
|
|
97
|
+
options.declare(:box, "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox_all')
|
|
98
98
|
options.declare(:shared_folder, 'Send package with files from shared folder')
|
|
99
99
|
options.declare(:group_type, 'Type of shared box', allowed: %i[shared_inboxes workgroups], default: :shared_inboxes)
|
|
100
100
|
options.parse_options!
|
|
@@ -102,7 +102,7 @@ module Aspera
|
|
|
102
102
|
|
|
103
103
|
def set_api
|
|
104
104
|
# create an API object with the same options, but with a different subpath
|
|
105
|
-
@api_v5 =
|
|
105
|
+
@api_v5 = Api::Faspex.new(**Oauth.args_from_options(options))
|
|
106
106
|
# in case user wants to use HTTPGW tell transfer agent how to get address
|
|
107
107
|
transfer.httpgw_url_cb = lambda{@api_v5.read('account')['gateway_url']}
|
|
108
108
|
end
|
|
@@ -118,7 +118,7 @@ module Aspera
|
|
|
118
118
|
recipient_types = [recipient_types] unless recipient_types.is_a?(Array)
|
|
119
119
|
end
|
|
120
120
|
parameters['recipients'].map! do |recipient_data|
|
|
121
|
-
#
|
|
121
|
+
# If just a string, make a general lookup and build expected name/type hash
|
|
122
122
|
if recipient_data.is_a?(String)
|
|
123
123
|
matched = @api_v5.lookup_by_name('contacts', recipient_data, query: Rest.php_style({context: 'packages', type: recipient_types}))
|
|
124
124
|
recipient_data = {
|
|
@@ -174,8 +174,8 @@ module Aspera
|
|
|
174
174
|
# list all packages with optional filter
|
|
175
175
|
def list_packages_with_filter(query: {})
|
|
176
176
|
filter = options.get_next_argument('filter', mandatory: false, validation: Proc, default: ->(_x){true})
|
|
177
|
-
# translate box name to API prefix (with ending slash)
|
|
178
177
|
box = options.get_option(:box)
|
|
178
|
+
# Translate box name to API prefix (with ending slash)
|
|
179
179
|
entity =
|
|
180
180
|
case box
|
|
181
181
|
when SpecialValues::ALL then 'packages' # only admin can list all packages globally
|
|
@@ -236,17 +236,12 @@ module Aspera
|
|
|
236
236
|
rescue Cli::BadArgument
|
|
237
237
|
# paths is optional
|
|
238
238
|
end
|
|
239
|
+
box = options.get_option(:box)
|
|
239
240
|
download_params = {
|
|
240
|
-
type:
|
|
241
|
+
type: Api::Faspex.box_type(box),
|
|
241
242
|
transfer_type: Api::Faspex::TRANSFER_CONNECT
|
|
242
243
|
}
|
|
243
|
-
|
|
244
|
-
case box
|
|
245
|
-
when /outbox/ then download_params[:type] = 'sent'
|
|
246
|
-
when *Api::Faspex::API_LIST_MAILBOX_TYPES then nil # nothing to do
|
|
247
|
-
else # shared inbox / workgroup
|
|
248
|
-
download_params[:recipient_workgroup_id] = lookup_entity_by_field(api: @api_v5, entity: options.get_option(:group_type), value: box)['id']
|
|
249
|
-
end
|
|
244
|
+
download_params[:recipient_workgroup_id] = lookup_entity_by_field(api: @api_v5, entity: options.get_option(:group_type), value: box)['id'] if !Api::Faspex::API_LIST_MAILBOX_TYPES.include?(box) && box != SpecialValues::ALL
|
|
250
245
|
packages.each do |package|
|
|
251
246
|
pkg_id = package['id']
|
|
252
247
|
formatter.display_status("Receiving package #{pkg_id}")
|
|
@@ -255,9 +250,9 @@ module Aspera
|
|
|
255
250
|
operation: 'POST',
|
|
256
251
|
subpath: "packages/#{pkg_id}/transfer_spec/download",
|
|
257
252
|
query: download_params,
|
|
258
|
-
content_type:
|
|
253
|
+
content_type: Mime::JSON,
|
|
259
254
|
body: param_file_list,
|
|
260
|
-
headers: {'Accept' =>
|
|
255
|
+
headers: {'Accept' => Mime::JSON}
|
|
261
256
|
)
|
|
262
257
|
# delete flag for Connect Client
|
|
263
258
|
transfer_spec.delete('authentication')
|
|
@@ -272,6 +267,56 @@ module Aspera
|
|
|
272
267
|
return Main.result_transfer_multiple(result_transfer)
|
|
273
268
|
end
|
|
274
269
|
|
|
270
|
+
def package_send
|
|
271
|
+
parameters = value_create_modify(command: :send)
|
|
272
|
+
# autofill recipient for public url
|
|
273
|
+
if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
|
|
274
|
+
parameters['recipients'] = [{
|
|
275
|
+
name: @api_v5.pub_link_context['name'],
|
|
276
|
+
recipient_type: @api_v5.pub_link_context['recipient_type']
|
|
277
|
+
}]
|
|
278
|
+
end
|
|
279
|
+
normalize_recipients(parameters)
|
|
280
|
+
# User specified content prot in tspec, but faspex requires in package creation
|
|
281
|
+
# `transfer_spec/upload` will set `content_protection`
|
|
282
|
+
if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
|
|
283
|
+
transfer.user_transfer_spec.delete('content_protection')
|
|
284
|
+
parameters['ear_enabled'] = true
|
|
285
|
+
end
|
|
286
|
+
package = @api_v5.create('packages', parameters)
|
|
287
|
+
shared_folder = options.get_option(:shared_folder)
|
|
288
|
+
if shared_folder.nil?
|
|
289
|
+
# send from local files
|
|
290
|
+
transfer_spec = @api_v5.create(
|
|
291
|
+
"packages/#{package['id']}/transfer_spec/upload",
|
|
292
|
+
{paths: transfer.source_list},
|
|
293
|
+
query: {transfer_type: Api::Faspex::TRANSFER_CONNECT}
|
|
294
|
+
)
|
|
295
|
+
# well, we asked a TS for connect, but we actually want a generic one
|
|
296
|
+
transfer_spec.delete('authentication')
|
|
297
|
+
return Main.result_transfer(transfer.start(transfer_spec))
|
|
298
|
+
else
|
|
299
|
+
# send from remote shared folder
|
|
300
|
+
if (m = Base.percent_selector(shared_folder))
|
|
301
|
+
shared_folder = lookup_entity_by_field(
|
|
302
|
+
api: @api_v5,
|
|
303
|
+
entity: 'shared_folders',
|
|
304
|
+
field: m[:field],
|
|
305
|
+
value: m[:value]
|
|
306
|
+
)['id']
|
|
307
|
+
end
|
|
308
|
+
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
|
309
|
+
# start remote transfer and get first status
|
|
310
|
+
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)
|
|
311
|
+
result['id'] = package['id']
|
|
312
|
+
unless result['status'].eql?('completed')
|
|
313
|
+
formatter.display_status("Package #{package['id']}")
|
|
314
|
+
result = wait_package_status(package['id'])
|
|
315
|
+
end
|
|
316
|
+
return Main.result_single_object(result)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
275
320
|
# Browse a folder
|
|
276
321
|
# @param browse_endpoint [String] the endpoint to browse
|
|
277
322
|
def browse_folder(browse_endpoint)
|
|
@@ -300,9 +345,9 @@ module Aspera
|
|
|
300
345
|
operation: 'POST',
|
|
301
346
|
subpath: browse_endpoint,
|
|
302
347
|
query: query,
|
|
303
|
-
content_type:
|
|
348
|
+
content_type: Mime::JSON,
|
|
304
349
|
body: {'path' => path, 'filters' => filters},
|
|
305
|
-
headers: {'Accept' =>
|
|
350
|
+
headers: {'Accept' => Mime::JSON},
|
|
306
351
|
ret: :both
|
|
307
352
|
)
|
|
308
353
|
all_items.concat(data['items'])
|
|
@@ -339,12 +384,7 @@ module Aspera
|
|
|
339
384
|
when :show
|
|
340
385
|
return Main.result_single_object(@api_v5.read("packages/#{package_id}"))
|
|
341
386
|
when :browse
|
|
342
|
-
|
|
343
|
-
when 'inbox' then 'received'
|
|
344
|
-
when 'outbox' then 'sent'
|
|
345
|
-
else raise BadArgument, 'Browse only available for inbox and outbox'
|
|
346
|
-
end
|
|
347
|
-
return browse_folder("packages/#{package_id}/files/#{location}")
|
|
387
|
+
return browse_folder("packages/#{package_id}/files/#{Api::Faspex.box_type(options.get_option(:box))}")
|
|
348
388
|
when :status
|
|
349
389
|
status_list = options.get_next_argument('list of states, or nothing', mandatory: false, validation: Array)
|
|
350
390
|
status = wait_package_status(package_id, status_list: status_list)
|
|
@@ -357,64 +397,21 @@ module Aspera
|
|
|
357
397
|
@api_v5.call(
|
|
358
398
|
operation: 'DELETE',
|
|
359
399
|
subpath: 'packages',
|
|
360
|
-
content_type:
|
|
400
|
+
content_type: Mime::JSON,
|
|
361
401
|
body: {ids: ids},
|
|
362
|
-
headers: {'Accept' =>
|
|
402
|
+
headers: {'Accept' => Mime::JSON}
|
|
363
403
|
)
|
|
364
404
|
return Main.result_status('Package(s) deleted')
|
|
365
405
|
when :receive
|
|
366
406
|
return package_receive(package_id)
|
|
367
407
|
when :send
|
|
368
|
-
|
|
369
|
-
# autofill recipient for public url
|
|
370
|
-
if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
|
|
371
|
-
parameters['recipients'] = [{
|
|
372
|
-
name: @api_v5.pub_link_context['name'],
|
|
373
|
-
recipient_type: @api_v5.pub_link_context['recipient_type']
|
|
374
|
-
}]
|
|
375
|
-
end
|
|
376
|
-
normalize_recipients(parameters)
|
|
377
|
-
# User specified content prot in tspec, but faspex requires in package creation
|
|
378
|
-
# `transfer_spec/upload` will set `content_protection`
|
|
379
|
-
if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
|
|
380
|
-
transfer.user_transfer_spec.delete('content_protection')
|
|
381
|
-
parameters['ear_enabled'] = true
|
|
382
|
-
end
|
|
383
|
-
package = @api_v5.create('packages', parameters)
|
|
384
|
-
shared_folder = options.get_option(:shared_folder)
|
|
385
|
-
if shared_folder.nil?
|
|
386
|
-
# send from local files
|
|
387
|
-
transfer_spec = @api_v5.create(
|
|
388
|
-
"packages/#{package['id']}/transfer_spec/upload",
|
|
389
|
-
{paths: transfer.source_list},
|
|
390
|
-
query: {transfer_type: Api::Faspex::TRANSFER_CONNECT}
|
|
391
|
-
)
|
|
392
|
-
# well, we asked a TS for connect, but we actually want a generic one
|
|
393
|
-
transfer_spec.delete('authentication')
|
|
394
|
-
return Main.result_transfer(transfer.start(transfer_spec))
|
|
395
|
-
else
|
|
396
|
-
# send from remote shared folder
|
|
397
|
-
if (m = Base.percent_selector(shared_folder))
|
|
398
|
-
shared_folder = lookup_entity_by_field(
|
|
399
|
-
api: @api_v5,
|
|
400
|
-
entity: 'shared_folders',
|
|
401
|
-
field: m[:field],
|
|
402
|
-
value: m[:value]
|
|
403
|
-
)['id']
|
|
404
|
-
end
|
|
405
|
-
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
|
406
|
-
# start remote transfer and get first status
|
|
407
|
-
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)
|
|
408
|
-
result['id'] = package['id']
|
|
409
|
-
unless result['status'].eql?('completed')
|
|
410
|
-
formatter.display_status("Package #{package['id']}")
|
|
411
|
-
result = wait_package_status(package['id'])
|
|
412
|
-
end
|
|
413
|
-
return Main.result_single_object(result)
|
|
414
|
-
end
|
|
408
|
+
return package_send
|
|
415
409
|
when :list
|
|
416
410
|
list, total = list_packages_with_filter
|
|
417
|
-
|
|
411
|
+
fields = %w[id title status sender.name recipients.0.name release_date total_bytes total_files]
|
|
412
|
+
fields.delete('recipients.0.name') if %w[inbox inbox_history].include?(options.get_option(:box))
|
|
413
|
+
fields.delete('sender.name') if %w[outbox outbox_history].include?(options.get_option(:box))
|
|
414
|
+
return Main.result_object_list(list, total: total, fields: fields)
|
|
418
415
|
end
|
|
419
416
|
end
|
|
420
417
|
|
|
@@ -506,7 +503,7 @@ module Aspera
|
|
|
506
503
|
)
|
|
507
504
|
return Main.result_single_object(result)
|
|
508
505
|
when :members, :saml_groups
|
|
509
|
-
# :shared_inboxes, :workgroups
|
|
506
|
+
# res_command := :shared_inboxes, :workgroups
|
|
510
507
|
res_id = instance_identifier{ |field, value| lookup_entity_by_field(api: @api_v5, entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
|
|
511
508
|
res_path = "#{res_sym}/#{res_id}/#{res_command}"
|
|
512
509
|
list_key = res_command.to_s
|
|
@@ -533,6 +530,7 @@ module Aspera
|
|
|
533
530
|
access = options.get_next_argument('level', mandatory: false, accept_list: SHARED_INBOX_MEMBER_LEVELS, default: :standard)
|
|
534
531
|
options.unshift_next_argument({user: users.map{ |u| {id: u, access: access}}})
|
|
535
532
|
end
|
|
533
|
+
# TODO: test SAML group
|
|
536
534
|
return entity_execute(
|
|
537
535
|
api: @api_v5,
|
|
538
536
|
entity: res_path,
|
|
@@ -541,11 +539,11 @@ module Aspera
|
|
|
541
539
|
) do |field, value|
|
|
542
540
|
lookup_entity_by_field(
|
|
543
541
|
api: @api_v5,
|
|
544
|
-
entity:
|
|
542
|
+
entity: res_path,
|
|
545
543
|
field: field,
|
|
546
544
|
value: value,
|
|
547
545
|
query: Rest.php_style({type: %w[user]})
|
|
548
|
-
)['
|
|
546
|
+
)['user_id']
|
|
549
547
|
end
|
|
550
548
|
when :reset_password
|
|
551
549
|
# :accounts
|
|
@@ -686,7 +684,7 @@ module Aspera
|
|
|
686
684
|
@api_v5.create(invitation_endpoint, params)
|
|
687
685
|
end
|
|
688
686
|
when :resend
|
|
689
|
-
@api_v5.create("#{invitation_endpoint}/#{instance_identifier}/resend")
|
|
687
|
+
@api_v5.create("#{invitation_endpoint}/#{instance_identifier}/resend", nil)
|
|
690
688
|
return Main.result_status('Invitation resent')
|
|
691
689
|
else
|
|
692
690
|
return entity_execute(
|
|
@@ -450,7 +450,7 @@ module Aspera
|
|
|
450
450
|
@api_node.call(
|
|
451
451
|
operation: 'POST',
|
|
452
452
|
subpath: 'services/soap/Transfer-201210',
|
|
453
|
-
content_type:
|
|
453
|
+
content_type: Mime::TEXT,
|
|
454
454
|
body: CENTRAL_SOAP_API_TEST,
|
|
455
455
|
headers: {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
|
|
456
456
|
ret: :resp
|
|
@@ -471,9 +471,7 @@ module Aspera
|
|
|
471
471
|
return Main.result_single_object(nd_info)
|
|
472
472
|
when :license
|
|
473
473
|
# requires: asnodeadmin -mu <node user> --acl-add=internal --internal
|
|
474
|
-
|
|
475
|
-
Log.log.error('server must have: asnodeadmin -mu <node user> --acl-add=internal --internal') if node_license['failure'].is_a?(String) && node_license['failure'].include?('ACL')
|
|
476
|
-
return Main.result_single_object(node_license)
|
|
474
|
+
return Main.result_single_object(@api_node.read('license'))
|
|
477
475
|
when :api_details
|
|
478
476
|
return Main.result_single_object({base_url: @api_node.base_url}.merge(@api_node.params))
|
|
479
477
|
end
|
|
@@ -837,7 +835,7 @@ module Aspera
|
|
|
837
835
|
@api_node.call(
|
|
838
836
|
operation: 'POST',
|
|
839
837
|
subpath: "asyncs/#{asyncs_id}/#{sync_command}",
|
|
840
|
-
content_type:
|
|
838
|
+
content_type: Mime::TEXT,
|
|
841
839
|
body: '',
|
|
842
840
|
ret: :resp
|
|
843
841
|
).body
|
|
@@ -7,9 +7,33 @@ module Aspera
|
|
|
7
7
|
module Plugins
|
|
8
8
|
# base class for applications supporting OAuth 2.0 authentication
|
|
9
9
|
class Oauth < BasicAuth
|
|
10
|
-
|
|
10
|
+
class << self
|
|
11
|
+
# Get command line options specified by `AUTH_OPTIONS` and `defaults.keys` (value is default).
|
|
12
|
+
# Adds those not nil to the `kwargs`.
|
|
13
|
+
# Instantiate the provided `klass` with those `kwargs`.
|
|
14
|
+
# `defaults` can specify a default value (not `nil`)
|
|
15
|
+
# @param options [Cli::Manager] Object to get command line options.
|
|
16
|
+
# @param kwargs [Hash] Object creation arguments
|
|
17
|
+
# @param defaults [Hash] Additional options, key=symbol, value=default value or nil
|
|
18
|
+
# @return [Object] instance of `klass`
|
|
19
|
+
# @raise [Cli::Error] if a required option is missing
|
|
20
|
+
def args_from_options(options, defaults: nil, **kwargs)
|
|
21
|
+
defaults ||= {}
|
|
22
|
+
(AUTH_OPTIONS + defaults.keys).each_with_object(kwargs) do |i, m|
|
|
23
|
+
v = options.get_option(i)
|
|
24
|
+
m[i] = v unless v.nil?
|
|
25
|
+
m[i] = defaults[i] if m[i].nil? && !defaults[i].nil?
|
|
26
|
+
end
|
|
27
|
+
rescue ::ArgumentError => e
|
|
28
|
+
if (m = e.message.match(/missing keyword: :(.*)$/))
|
|
29
|
+
raise Cli::Error, "Missing option: #{m[1]}"
|
|
30
|
+
end
|
|
31
|
+
raise
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
# OAuth methods supported (web, jwt)
|
|
11
35
|
AUTH_TYPES = %i[web jwt boot].freeze
|
|
12
|
-
# Options used for authentication
|
|
36
|
+
# Options used for authentication (url, auth, client_id, etc...)
|
|
13
37
|
AUTH_OPTIONS = %i[url auth client_id client_secret redirect_uri private_key passphrase username password].freeze
|
|
14
38
|
def initialize(**_)
|
|
15
39
|
super
|
|
@@ -20,29 +44,6 @@ module Aspera
|
|
|
20
44
|
options.declare(:private_key, 'OAuth (JWT) RSA private key PEM value (prefix file path with @file:)')
|
|
21
45
|
options.declare(:passphrase, 'OAuth (JWT) RSA private key passphrase')
|
|
22
46
|
end
|
|
23
|
-
|
|
24
|
-
# Get command line options specified by `AUTH_OPTIONS` and `option.keys` (value is default).
|
|
25
|
-
# Adds those not nil to the `kwargs`.
|
|
26
|
-
# Instantiate the provided `klass` with those kwargs.
|
|
27
|
-
# `option` can specify a default value (not `nil`)
|
|
28
|
-
# @param klass [Class] API object to create
|
|
29
|
-
# @param kwargs [Hash] The fixed keyword arguments for creation
|
|
30
|
-
# @param option [Hash] Additional options, key=symbol, value:default value or nil
|
|
31
|
-
# @return [Object] instance of `klass`
|
|
32
|
-
# @raise [Cli::Error] if a required option is missing
|
|
33
|
-
def new_with_options(klass, kwargs: {}, option: {})
|
|
34
|
-
klass.new(**
|
|
35
|
-
(AUTH_OPTIONS + option.keys).each_with_object(kwargs) do |i, m|
|
|
36
|
-
v = options.get_option(i)
|
|
37
|
-
m[i] = v unless v.nil?
|
|
38
|
-
m[i] = option[i] unless !m[i].nil? || option[i].nil?
|
|
39
|
-
end)
|
|
40
|
-
rescue ::ArgumentError => e
|
|
41
|
-
if (m = e.message.match(/missing keyword: :(.*)$/))
|
|
42
|
-
raise Cli::Error, "Missing option: #{m[1]}"
|
|
43
|
-
end
|
|
44
|
-
raise
|
|
45
|
-
end
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
end
|