aspera-cli 4.12.0 → 4.13.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 +17 -0
- data/CONTRIBUTING.md +97 -22
- data/README.md +548 -394
- data/bin/ascli +3 -3
- data/docs/test_env.conf +12 -5
- data/lib/aspera/aoc.rb +42 -42
- data/lib/aspera/ascmd.rb +4 -3
- data/lib/aspera/cli/extended_value.rb +24 -37
- data/lib/aspera/cli/formatter.rb +6 -0
- data/lib/aspera/cli/info.rb +2 -4
- data/lib/aspera/cli/main.rb +6 -0
- data/lib/aspera/cli/manager.rb +15 -6
- data/lib/aspera/cli/plugin.rb +1 -5
- data/lib/aspera/cli/plugins/aoc.rb +23 -6
- data/lib/aspera/cli/plugins/config.rb +13 -6
- data/lib/aspera/cli/plugins/faspex.rb +4 -3
- data/lib/aspera/cli/plugins/faspex5.rb +175 -42
- data/lib/aspera/cli/plugins/node.rb +107 -50
- data/lib/aspera/cli/plugins/preview.rb +3 -3
- data/lib/aspera/cli/plugins/server.rb +11 -1
- data/lib/aspera/cli/plugins/sync.rb +3 -3
- data/lib/aspera/cli/transfer_agent.rb +24 -10
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/command_line_builder.rb +2 -1
- data/lib/aspera/cos_node.rb +1 -1
- data/lib/aspera/fasp/agent_connect.rb +1 -1
- data/lib/aspera/fasp/agent_direct.rb +12 -12
- data/lib/aspera/fasp/agent_node.rb +14 -4
- data/lib/aspera/fasp/installation.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +11 -3
- data/lib/aspera/fasp/parameters.yaml +3 -1
- data/lib/aspera/fasp/transfer_spec.rb +3 -1
- data/lib/aspera/faspex_gw.rb +1 -0
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/node.rb +11 -4
- data/lib/aspera/oauth.rb +3 -2
- data/lib/aspera/preview/file_types.rb +8 -6
- data/lib/aspera/preview/generator.rb +23 -11
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +34 -0
- data/lib/aspera/preview/utils.rb +8 -8
- data/lib/aspera/rest.rb +5 -4
- data/lib/aspera/rest_call_error.rb +3 -1
- data/lib/aspera/secret_hider.rb +4 -4
- data/lib/aspera/sync.rb +39 -33
- data/lib/aspera/web_server_simple.rb +22 -18
- data.tar.gz.sig +0 -0
- metadata +39 -46
- metadata.gz.sig +0 -0
- data/examples/aoc.rb +0 -30
- data/examples/faspex4.rb +0 -94
- data/examples/node.rb +0 -96
- data/examples/server.rb +0 -93
data/bin/ascli
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env ruby -EUTF-8:UTF-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'securerandom'
|
6
|
+
# compute gem root based on this script location
|
6
7
|
GEM_ROOT = File.realpath(File.join(File.dirname(File.realpath(__FILE__)), '..'))
|
7
8
|
# coverage for tests
|
8
9
|
if ENV.key?('ENABLE_COVERAGE')
|
@@ -20,10 +21,9 @@ if ENV.key?('ENABLE_COVERAGE')
|
|
20
21
|
end
|
21
22
|
SimpleCov.start
|
22
23
|
end
|
24
|
+
# if in development, add path to gem
|
23
25
|
$LOAD_PATH.unshift(File.join(GEM_ROOT, 'lib'))
|
24
26
|
require 'aspera/cli/main'
|
25
27
|
require 'aspera/environment'
|
26
|
-
Encoding.default_internal = Encoding::UTF_8
|
27
|
-
Encoding.default_external = Encoding::UTF_8
|
28
28
|
Aspera::Environment.fix_home
|
29
29
|
Aspera::Cli::Main.new(ARGV).process_command_line
|
data/docs/test_env.conf
CHANGED
@@ -4,7 +4,7 @@ config:
|
|
4
4
|
default:
|
5
5
|
config: cli_default
|
6
6
|
aoc: tst_aoc_jwt
|
7
|
-
faspex:
|
7
|
+
faspex: tst_faspex4
|
8
8
|
faspex5: tst_faspex5_jwt
|
9
9
|
shares: tst_shares
|
10
10
|
node: tst_node_simple
|
@@ -40,10 +40,18 @@ tst_aoc_web:
|
|
40
40
|
redirect_uri: your value here
|
41
41
|
client_id: your value here
|
42
42
|
client_secret: your value here
|
43
|
-
|
43
|
+
tst_faspex4:
|
44
44
|
url: your value here
|
45
45
|
username: your value here
|
46
46
|
password: your value here
|
47
|
+
tst_faspex4_admin:
|
48
|
+
username: your value here
|
49
|
+
password: your value here
|
50
|
+
faspex4_publink:
|
51
|
+
link_recv_from_user: your value here
|
52
|
+
link_send_to_user: your value here
|
53
|
+
link_send_to_dropbox: your value here
|
54
|
+
tst_faspex4_storage:
|
47
55
|
storage: your value here
|
48
56
|
tst_hstsfaspex_ssh:
|
49
57
|
url: your value here
|
@@ -65,6 +73,8 @@ tst_faspex5_jwt:
|
|
65
73
|
client_secret: your value here
|
66
74
|
private_key: your value here
|
67
75
|
username: your value here
|
76
|
+
f5_admin:
|
77
|
+
username: your value here
|
68
78
|
tst_shares:
|
69
79
|
url: your value here
|
70
80
|
username: your value here
|
@@ -133,9 +143,6 @@ nowss:
|
|
133
143
|
misc:
|
134
144
|
upload_folder: your value here
|
135
145
|
syncuser: your value here
|
136
|
-
faspex_publink_recv_from_fxuser: your value here
|
137
|
-
faspex_publink_send_to_fxuser: your value here
|
138
|
-
faspex_publink_send_to_dropbox: your value here
|
139
146
|
faspex_dbx: your value here
|
140
147
|
faspex_wkg: your value here
|
141
148
|
faspex_src: your value here
|
data/lib/aspera/aoc.rb
CHANGED
@@ -252,25 +252,30 @@ module Aspera
|
|
252
252
|
return @cache_user_info
|
253
253
|
end
|
254
254
|
|
255
|
-
# @returns [Aspera::Node] a node API for access key
|
256
255
|
# @param node_id [String] identifier of node in AoC
|
256
|
+
# @param workspace_id [String] workspace identifier
|
257
|
+
# @param workspace_name [String] workspace name
|
257
258
|
# @param scope e.g. SCOPE_NODE_USER, or nil (requires secret)
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
259
|
+
# @param package_info [Hash] created package information
|
260
|
+
# @returns [Aspera::Node] a node API for access key
|
261
|
+
def node_api_from(node_id:, workspace_id: nil, workspace_name: nil, scope: SCOPE_NODE_USER, package_info: nil)
|
262
|
+
raise 'invalid type for node_id' unless node_id.is_a?(String)
|
263
|
+
node_info = read("nodes/#{node_id}")[:data]
|
264
|
+
if workspace_name.nil? && !workspace_id.nil?
|
265
|
+
workspace_name = read("workspaces/#{workspace_id}")[:data]['name']
|
265
266
|
end
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
267
|
+
app_info = {
|
268
|
+
api: self, # for callback
|
269
|
+
app: package_info.nil? ? FILES_APP : PACKAGES_APP,
|
270
|
+
node_info: node_info,
|
271
|
+
workspace_id: workspace_id,
|
272
|
+
workspace_name: workspace_name
|
273
|
+
}
|
274
|
+
if PACKAGES_APP.eql?(app_info[:app])
|
275
|
+
raise 'package info required' if package_info.nil?
|
276
|
+
app_info[:package_id] = package_info['id']
|
277
|
+
app_info[:package_name] = package_info['name']
|
272
278
|
end
|
273
|
-
node_info = read("nodes/#{node_id}")[:data]
|
274
279
|
node_rest_params = {base_url: node_info['url']}
|
275
280
|
# if secret is available
|
276
281
|
if scope.nil?
|
@@ -286,13 +291,6 @@ module Aspera
|
|
286
291
|
# special header required for bearer token only
|
287
292
|
node_rest_params[:headers] = {Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => node_info['access_key']}
|
288
293
|
end
|
289
|
-
app_info = {
|
290
|
-
node_info: node_info,
|
291
|
-
workspace_info: workspace_info,
|
292
|
-
app: package_info.nil? ? FILES_APP : PACKAGES_APP,
|
293
|
-
api: self # for callback
|
294
|
-
}
|
295
|
-
app_info[:package_info] = package_info unless package_info.nil?
|
296
294
|
return Node.new(params: node_rest_params, app_info: app_info)
|
297
295
|
end
|
298
296
|
|
@@ -433,7 +431,10 @@ module Aspera
|
|
433
431
|
# create a new package container
|
434
432
|
created_package = create('packages', package_data)[:data]
|
435
433
|
|
436
|
-
package_node_api = node_api_from(
|
434
|
+
package_node_api = node_api_from(
|
435
|
+
node_id: created_package['node_id'],
|
436
|
+
workspace_id: created_package['workspace_id'],
|
437
|
+
package_info: created_package)
|
437
438
|
|
438
439
|
# tell AoC what to expect in package: 1 transfer (can also be done after transfer)
|
439
440
|
# TODO: if multi session was used we should probably tell
|
@@ -454,15 +455,14 @@ module Aspera
|
|
454
455
|
transfer_type = Fasp::TransferSpec.action(transfer_spec)
|
455
456
|
# Analytics tags
|
456
457
|
################
|
457
|
-
ws_info = app_info[:workspace_info]
|
458
458
|
transfer_spec.deep_merge!({
|
459
459
|
'tags' => {
|
460
|
-
|
461
|
-
'usage_id' => "aspera.files.workspace.#{
|
460
|
+
Fasp::TransferSpec::TAG_RESERVED => {
|
461
|
+
'usage_id' => "aspera.files.workspace.#{app_info[:workspace_id]}", # activity tracking
|
462
462
|
'files' => {
|
463
463
|
'files_transfer_action' => "#{transfer_type}_#{app_info[:app].gsub(/s$/, '')}",
|
464
|
-
'workspace_name' =>
|
465
|
-
'workspace_id' =>
|
464
|
+
'workspace_name' => app_info[:workspace_name], # activity tracking
|
465
|
+
'workspace_id' => app_info[:workspace_id]
|
466
466
|
}
|
467
467
|
}
|
468
468
|
}
|
@@ -477,37 +477,37 @@ module Aspera
|
|
477
477
|
##################
|
478
478
|
case app_info[:app]
|
479
479
|
when FILES_APP
|
480
|
-
file_id = transfer_spec['tags'][
|
481
|
-
transfer_spec.deep_merge!({'tags' => {
|
480
|
+
file_id = transfer_spec['tags'][Fasp::TransferSpec::TAG_RESERVED]['node']['file_id']
|
481
|
+
transfer_spec.deep_merge!({'tags' => {Fasp::TransferSpec::TAG_RESERVED => {'files' => {'parentCwd' => "#{app_info[:node_info]['id']}:#{file_id}"}}}}) \
|
482
482
|
unless transfer_spec.key?('remote_access_key')
|
483
483
|
when PACKAGES_APP
|
484
484
|
transfer_spec.deep_merge!({
|
485
485
|
'tags' => {
|
486
|
-
|
486
|
+
Fasp::TransferSpec::TAG_RESERVED => {
|
487
487
|
'files' => {
|
488
|
-
'package_id' => app_info[:
|
489
|
-
'package_name' => app_info[:
|
488
|
+
'package_id' => app_info[:package_id],
|
489
|
+
'package_name' => app_info[:package_name],
|
490
490
|
'package_operation' => transfer_type
|
491
491
|
}}}})
|
492
492
|
end
|
493
|
-
transfer_spec['tags'][
|
494
|
-
transfer_spec['tags'][
|
493
|
+
transfer_spec['tags'][Fasp::TransferSpec::TAG_RESERVED]['files']['node_id'] = app_info[:node_info]['id']
|
494
|
+
transfer_spec['tags'][Fasp::TransferSpec::TAG_RESERVED]['app'] = app_info[:app]
|
495
495
|
end
|
496
496
|
|
497
497
|
ID_AK_ADMIN = 'ASPERA_ACCESS_KEY_ADMIN'
|
498
498
|
# Callback from Plugins::Node
|
499
499
|
def permissions_create_params(create_param:, app_info:)
|
500
500
|
# workspace shared folder:
|
501
|
-
# access_id = "#{ID_AK_ADMIN}_WS_#{
|
501
|
+
# access_id = "#{ID_AK_ADMIN}_WS_#{app_info[:workspace_id]}"
|
502
502
|
default_params = {
|
503
503
|
# 'access_type' => 'user', # mandatory: user or group
|
504
504
|
# 'access_id' => access_id, # id of user or group
|
505
505
|
'tags' => {
|
506
|
-
|
506
|
+
Fasp::TransferSpec::TAG_RESERVED => {
|
507
507
|
'files' => {
|
508
508
|
'workspace' => {
|
509
|
-
'id' => app_info[:
|
510
|
-
'workspace_name' => app_info[:
|
509
|
+
'id' => app_info[:workspace_id],
|
510
|
+
'workspace_name' => app_info[:workspace_name],
|
511
511
|
'user_name' => current_user_info['name'],
|
512
512
|
'shared_by_user_id' => current_user_info['id'],
|
513
513
|
'shared_by_name' => current_user_info['name'],
|
@@ -520,11 +520,11 @@ module Aspera
|
|
520
520
|
contact_info = lookup_entity_by_name(
|
521
521
|
'contacts',
|
522
522
|
create_param['with'],
|
523
|
-
{'current_workspace_id' => app_info[:
|
523
|
+
{'current_workspace_id' => app_info[:workspace_id], 'context' => 'share_folder'})
|
524
524
|
create_param.delete('with')
|
525
525
|
create_param['access_type'] = contact_info['source_type']
|
526
526
|
create_param['access_id'] = contact_info['source_id']
|
527
|
-
create_param['tags'][
|
527
|
+
create_param['tags'][Fasp::TransferSpec::TAG_RESERVED]['files']['workspace']['shared_with_name'] = contact_info['email']
|
528
528
|
end
|
529
529
|
# optional
|
530
530
|
app_info[:opt_link_name] = create_param.delete('link_name')
|
@@ -535,7 +535,7 @@ module Aspera
|
|
535
535
|
event_creation = {
|
536
536
|
'types' => ['permission.created'],
|
537
537
|
'node_id' => app_info[:node_info]['id'],
|
538
|
-
'workspace_id' => app_info[:
|
538
|
+
'workspace_id' => app_info[:workspace_id],
|
539
539
|
'data' => created_data # Response from previous step
|
540
540
|
}
|
541
541
|
# (optional). The name of the folder to be displayed to the destination user. Use it if its value is different from the "share_as" field.
|
data/lib/aspera/ascmd.rb
CHANGED
@@ -24,10 +24,11 @@ module Aspera
|
|
24
24
|
# concatenate arguments, enclose in double quotes, protect backslash and double quotes, add "as_" command and as_exit
|
25
25
|
stdin_input = (args || []).map{|v| '"' + v.gsub(/["\\]/n) {|s| '\\' + s } + '"'}.unshift('as_' + action_sym.to_s).join(' ') + "\nas_exit\n"
|
26
26
|
# execute, get binary output
|
27
|
-
|
27
|
+
byte_buffer = @command_executor.execute('ascmd', stdin_input).unpack('C*')
|
28
|
+
raise 'ERROR: empty answer from server' if byte_buffer.empty?
|
28
29
|
# get hash or table result
|
29
|
-
result = self.class.parse(
|
30
|
-
raise 'ERROR: unparsed bytes remaining' unless
|
30
|
+
result = self.class.parse(byte_buffer, :result)
|
31
|
+
raise 'ERROR: unparsed bytes remaining' unless byte_buffer.empty?
|
31
32
|
# get and delete info,always present in results
|
32
33
|
system_info = result[:info]
|
33
34
|
result.delete(:info)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'aspera/cli/plugins/config'
|
4
3
|
require 'aspera/uri_reader'
|
5
4
|
require 'aspera/environment'
|
6
5
|
require 'json'
|
@@ -39,38 +38,33 @@ module Aspera
|
|
39
38
|
|
40
39
|
def initialize
|
41
40
|
@handlers = {
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
},
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
uri: lambda{|v|UriReader.read(v)},
|
57
|
-
stdin: lambda{|v|raise 'no value allowed for stdin' unless v.empty?; $stdin.read} # rubocop:disable Style/Semicolon
|
58
|
-
}
|
41
|
+
base64: lambda{|v|Base64.decode64(v)},
|
42
|
+
csvt: lambda{|v|ExtendedValue.decode_csvt(v)},
|
43
|
+
env: lambda{|v|ENV[v]},
|
44
|
+
file: lambda{|v|File.read(File.expand_path(v))},
|
45
|
+
json: lambda{|v|JSON.parse(v)},
|
46
|
+
lines: lambda{|v|v.split("\n")},
|
47
|
+
list: lambda{|v|v[1..-1].split(v[0])},
|
48
|
+
path: lambda{|v|File.expand_path(v)},
|
49
|
+
ruby: lambda{|v|Environment.secure_eval(v)},
|
50
|
+
secret: lambda{|v|raise 'no value allowed for secret' unless v.empty?; $stdin.getpass('secret> ')}, # rubocop:disable Style/Semicolon
|
51
|
+
stdin: lambda{|v|raise 'no value allowed for stdin' unless v.empty?; $stdin.read}, # rubocop:disable Style/Semicolon
|
52
|
+
uri: lambda{|v|UriReader.read(v)},
|
53
|
+
val: lambda{|v|v},
|
54
|
+
zlib: lambda{|v|Zlib::Inflate.inflate(v)}
|
59
55
|
# other handlers can be set using set_handler, e.g. preset is reader in config plugin
|
60
56
|
}
|
61
57
|
end
|
62
58
|
|
63
59
|
public
|
64
60
|
|
65
|
-
def modifiers; @handlers.keys
|
61
|
+
def modifiers; @handlers.keys; end
|
66
62
|
|
67
|
-
# add a new
|
68
|
-
|
69
|
-
|
70
|
-
Log.log.debug{"setting #{type} handler for #{name}"}
|
63
|
+
# add a new handler
|
64
|
+
def set_handler(name, method)
|
65
|
+
Log.log.debug{"setting handler for #{name}"}
|
71
66
|
raise 'name must be Symbol' unless name.is_a?(Symbol)
|
72
|
-
|
73
|
-
@handlers[type][name] = method
|
67
|
+
@handlers[name] = method
|
74
68
|
end
|
75
69
|
|
76
70
|
# parse an option value if it is a String using supported extended value modifiers
|
@@ -78,20 +72,13 @@ module Aspera
|
|
78
72
|
def evaluate(value)
|
79
73
|
return value if !value.is_a?(String)
|
80
74
|
# first determine decoders, in reversed order
|
81
|
-
|
82
|
-
while (m = value.match(/^@([^:]+):(.*)/)) && @handlers
|
83
|
-
|
75
|
+
handlers_reversed = []
|
76
|
+
while (m = value.match(/^@([^:]+):(.*)/)) && @handlers.include?(m[1].to_sym)
|
77
|
+
handlers_reversed.unshift(m[1].to_sym)
|
84
78
|
value = m[2]
|
85
79
|
end
|
86
|
-
|
87
|
-
|
88
|
-
if (m = value.match(/^@#{reader}:(.*)/))
|
89
|
-
value = method.call(m[1])
|
90
|
-
break
|
91
|
-
end
|
92
|
-
end
|
93
|
-
decoders_reversed.each do |decoder|
|
94
|
-
value = @handlers[:decoder][decoder].call(value)
|
80
|
+
handlers_reversed.each do |handler|
|
81
|
+
value = @handlers[handler].call(value)
|
95
82
|
end
|
96
83
|
return value
|
97
84
|
end # parse
|
data/lib/aspera/cli/formatter.rb
CHANGED
@@ -116,6 +116,12 @@ module Aspera
|
|
116
116
|
display_message(:info, status)
|
117
117
|
end
|
118
118
|
|
119
|
+
def display_item_count(number, total)
|
120
|
+
count_msg = "Items: #{number}/#{total}"
|
121
|
+
count_msg = count_msg.bg_red unless number.to_i.eql?(total.to_i)
|
122
|
+
display_status(count_msg)
|
123
|
+
end
|
124
|
+
|
119
125
|
def result_default_fields(results, table_rows_hash_val)
|
120
126
|
unless results[:fields].nil?
|
121
127
|
raise "internal error: [fields] must be Array, not #{results[:fields].class}" unless results[:fields].is_a?(Array)
|
data/lib/aspera/cli/info.rb
CHANGED
@@ -10,9 +10,7 @@ module Aspera
|
|
10
10
|
GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
|
11
11
|
SRC_URL = 'https://github.com/IBM/aspera-cli'
|
12
12
|
# set this to warn in advance when minimum required ruby version will increase
|
13
|
-
#
|
14
|
-
|
15
|
-
# the actual current minimum required version is in gemspec at required_ruby_version
|
16
|
-
RUBY_FUTURE_MINIMUM_VERSION = '2.7'
|
13
|
+
# see also required_ruby_version in gemspec file
|
14
|
+
RUBY_FUTURE_MINIMUM_VERSION = '3.0'
|
17
15
|
end
|
18
16
|
end
|
data/lib/aspera/cli/main.rb
CHANGED
@@ -335,6 +335,7 @@ module Aspera
|
|
335
335
|
# finish
|
336
336
|
@plugin_env[:transfer].shutdown
|
337
337
|
rescue Net::SSH::AuthenticationFailed => e; exception_info = {e: e, t: 'SSH', security: true}
|
338
|
+
rescue OpenSSL::SSL::SSLError => e; exception_info = {e: e, t: 'SSL'}
|
338
339
|
rescue CliBadArgument => e; exception_info = {e: e, t: 'Argument', usage: true}
|
339
340
|
rescue CliNoSuchId => e; exception_info = {e: e, t: 'Identifier'}
|
340
341
|
rescue CliError => e; exception_info = {e: e, t: 'Tool', usage: true}
|
@@ -351,10 +352,15 @@ module Aspera
|
|
351
352
|
Log.log.warn(exception_info[:e].message) if Aspera::Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
|
352
353
|
@formatter.display_message(:error, "#{ERROR_FLASH} #{exception_info[:t]}: #{exception_info[:e].message}")
|
353
354
|
@formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
|
355
|
+
# Provide hint on FASP errors
|
354
356
|
if exception_info[:e].is_a?(Fasp::Error) && exception_info[:e].message.eql?('Remote host is not who we expected')
|
355
357
|
@formatter.display_message(:error, "For this specific error, refer to:\n"\
|
356
358
|
"#{SRC_URL}#error-remote-host-is-not-who-we-expected\nAdd this to arguments:\n--ts=@json:'{\"sshfp\":null}'")
|
357
359
|
end
|
360
|
+
# Provide hint on SSL errors
|
361
|
+
if exception_info[:e].is_a?(OpenSSL::SSL::SSLError) && ['does not match the server certificate'].any?{|m|exception_info[:e].message.include?(m)}
|
362
|
+
@formatter.display_message(:error, "You can ignore SSL errors with option:\n--insecure=yes")
|
363
|
+
end
|
358
364
|
end
|
359
365
|
# 2- processing of command not processed (due to exception or bad command line)
|
360
366
|
if execute_command || @option_show_config
|
data/lib/aspera/cli/manager.rb
CHANGED
@@ -52,9 +52,9 @@ module Aspera
|
|
52
52
|
# option name separator on command line
|
53
53
|
OPTION_SEP_LINE = '-'
|
54
54
|
# option name separator in code (symbol)
|
55
|
-
|
55
|
+
OPTION_SEP_SYMB = '_'
|
56
56
|
|
57
|
-
private_constant :FALSE_VALUES, :TRUE_VALUES, :BOOLEAN_VALUES, :OPTION_SEP_LINE, :
|
57
|
+
private_constant :FALSE_VALUES, :TRUE_VALUES, :BOOLEAN_VALUES, :OPTION_SEP_LINE, :OPTION_SEP_SYMB
|
58
58
|
|
59
59
|
class << self
|
60
60
|
def enum_to_bool(enum)
|
@@ -81,6 +81,15 @@ module Aspera
|
|
81
81
|
def bad_arg_message_multi(error_msg, choices)
|
82
82
|
return [error_msg, 'Use:'].concat(choices.map{|c|"- #{c}"}.sort).join("\n")
|
83
83
|
end
|
84
|
+
|
85
|
+
# change option name with dash to name with underscore
|
86
|
+
def option_line_to_name(name)
|
87
|
+
return name.gsub(OPTION_SEP_LINE, OPTION_SEP_SYMB)
|
88
|
+
end
|
89
|
+
|
90
|
+
def option_name_to_line(name)
|
91
|
+
return "--#{name.to_s.gsub(OPTION_SEP_SYMB, OPTION_SEP_LINE)}"
|
92
|
+
end
|
84
93
|
end
|
85
94
|
|
86
95
|
attr_reader :parser
|
@@ -108,7 +117,7 @@ module Aspera
|
|
108
117
|
@parser = OptionParser.new
|
109
118
|
@parser.program_name = program_name
|
110
119
|
# options can also be provided by env vars : --param-name -> ASCLI_PARAM_NAME
|
111
|
-
env_prefix = program_name.upcase +
|
120
|
+
env_prefix = program_name.upcase + OPTION_SEP_SYMB
|
112
121
|
ENV.each do |k, v|
|
113
122
|
if k.start_with?(env_prefix)
|
114
123
|
@unprocessed_env.push([k[env_prefix.length..-1].downcase.to_sym, v])
|
@@ -223,7 +232,7 @@ module Aspera
|
|
223
232
|
end
|
224
233
|
end
|
225
234
|
|
226
|
-
#
|
235
|
+
# Get an option value by name
|
227
236
|
# either return value or call handler, can return nil
|
228
237
|
# ask interactively if requested/required
|
229
238
|
def get_option(option_symbol, is_type: :optional)
|
@@ -347,7 +356,7 @@ module Aspera
|
|
347
356
|
when /^--([^=]+)=(.*)$/
|
348
357
|
name = Regexp.last_match(1)
|
349
358
|
value = Regexp.last_match(2)
|
350
|
-
name.gsub!(OPTION_SEP_LINE,
|
359
|
+
name.gsub!(OPTION_SEP_LINE, OPTION_SEP_SYMB)
|
351
360
|
value = ExtendedValue.instance.evaluate(value)
|
352
361
|
Log.log.debug{"option #{name}=#{value}"}
|
353
362
|
result[name] = value
|
@@ -429,7 +438,7 @@ module Aspera
|
|
429
438
|
|
430
439
|
# generate command line option from option symbol
|
431
440
|
def symbol_to_option(symbol, opt_val)
|
432
|
-
result = '--' + symbol.to_s.gsub(
|
441
|
+
result = '--' + symbol.to_s.gsub(OPTION_SEP_SYMB, OPTION_SEP_LINE)
|
433
442
|
result = result + '=' + opt_val unless opt_val.nil?
|
434
443
|
return result
|
435
444
|
end
|
data/lib/aspera/cli/plugin.rb
CHANGED
@@ -140,11 +140,7 @@ module Aspera
|
|
140
140
|
if item_list_key
|
141
141
|
item_list = data[item_list_key]
|
142
142
|
total_count = data['total_count']
|
143
|
-
|
144
|
-
count_msg = "Items: #{item_list.length}/#{total_count}"
|
145
|
-
count_msg = count_msg.bg_red unless item_list.length.eql?(total_count.to_i)
|
146
|
-
formatter.display_status(count_msg)
|
147
|
-
end
|
143
|
+
formatter.display_item_count(item_list.length, total_count) unless total_count.nil?
|
148
144
|
data = item_list
|
149
145
|
end
|
150
146
|
case data
|
@@ -142,6 +142,13 @@ module Aspera
|
|
142
142
|
end
|
143
143
|
home_node_id ||= current_workspace_info['home_node_id'] || current_workspace_info['node_id']
|
144
144
|
home_file_id ||= current_workspace_info['home_file_id']
|
145
|
+
if home_node_id.to_s.empty?
|
146
|
+
# not part of any workspace, but has some folder shared
|
147
|
+
user_info = aoc_api.current_user_info(exception: true)
|
148
|
+
home_node_id = user_info['read_only_home_node_id']
|
149
|
+
home_file_id = user_info['read_only_home_file_id']
|
150
|
+
end
|
151
|
+
|
145
152
|
raise "Cannot get user's home node id, check your default workspace or specify one" if home_node_id.to_s.empty?
|
146
153
|
@cache_home_node_file = {
|
147
154
|
node_id: home_node_id,
|
@@ -214,7 +221,12 @@ module Aspera
|
|
214
221
|
# @param file_id [String] root file id for the operation (can be AK root, or other, e.g. package, or link)
|
215
222
|
# @param scope [String] node scope, or nil (admin)
|
216
223
|
def execute_nodegen4_command(command_repo, node_id, file_id: nil, scope: nil)
|
217
|
-
top_node_api = aoc_api.node_api_from(
|
224
|
+
top_node_api = aoc_api.node_api_from(
|
225
|
+
node_id: node_id,
|
226
|
+
workspace_id: current_workspace_info['id'],
|
227
|
+
workspace_name: current_workspace_info['name'],
|
228
|
+
scope: scope
|
229
|
+
)
|
218
230
|
file_id = top_node_api.read("access_keys/#{top_node_api.app_info[:node_info]['access_key']}")[:data]['root_file_id'] if file_id.nil?
|
219
231
|
node_plugin = Node.new(@agents.merge(
|
220
232
|
skip_basic_auth_options: true,
|
@@ -440,9 +452,7 @@ module Aspera
|
|
440
452
|
when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
|
441
453
|
end
|
442
454
|
items = read_with_paging(resource_class_path, option_url_query(default_query))
|
443
|
-
|
444
|
-
count_msg = count_msg.bg_red unless items[:list].length.eql?(items[:total].to_i)
|
445
|
-
formatter.display_status(count_msg)
|
455
|
+
formatter.display_item_count(items[:list].length, items[:total])
|
446
456
|
return {type: :object_list, data: items[:list], fields: default_fields}
|
447
457
|
when :show
|
448
458
|
object = aoc_api.read(resource_instance_path)[:data]
|
@@ -588,7 +598,11 @@ module Aspera
|
|
588
598
|
ids_to_download.each do |package_id|
|
589
599
|
package_info = aoc_api.read("packages/#{package_id}")[:data]
|
590
600
|
formatter.display_status("downloading package: #{package_info['name']}")
|
591
|
-
package_node_api = aoc_api.node_api_from(
|
601
|
+
package_node_api = aoc_api.node_api_from(
|
602
|
+
node_id: package_info['node_id'],
|
603
|
+
workspace_id: current_workspace_info['id'],
|
604
|
+
workspace_name: current_workspace_info['name'],
|
605
|
+
package_info: package_info)
|
592
606
|
statuses = transfer.start(
|
593
607
|
package_node_api.transfer_spec_gen4(
|
594
608
|
package_info['contents_file_id'],
|
@@ -653,7 +667,10 @@ module Aspera
|
|
653
667
|
create_params = nil
|
654
668
|
shared_apfid = nil
|
655
669
|
if !folder_dest.nil?
|
656
|
-
home_node_api = aoc_api.node_api_from(
|
670
|
+
home_node_api = aoc_api.node_api_from(
|
671
|
+
node_id: home_info[:node_id],
|
672
|
+
workspace_id: current_workspace_info['id'],
|
673
|
+
workspace_name: current_workspace_info['name'])
|
657
674
|
shared_apfid = home_node_api.resolve_api_fid(home_info[:file_id], folder_dest)
|
658
675
|
create_params = {
|
659
676
|
file_id: shared_apfid[:file_id],
|
@@ -74,6 +74,7 @@ module Aspera
|
|
74
74
|
DEFAULT_CHECK_NEW_VERSION_DAYS = 7
|
75
75
|
DEFAULT_PRIV_KEY_FILENAME = 'aspera_aoc_key' # pragma: allowlist secret
|
76
76
|
DEFAULT_PRIVKEY_LENGTH = 4096
|
77
|
+
COFFEE_IMAGE = 'https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg'
|
77
78
|
private_constant :DEFAULT_CONFIG_FILENAME,
|
78
79
|
:CONF_PRESET_CONFIG,
|
79
80
|
:CONF_PRESET_VERSION,
|
@@ -99,7 +100,8 @@ module Aspera
|
|
99
100
|
:DEFAULT_CHECK_NEW_VERSION_DAYS,
|
100
101
|
:DEFAULT_PRIV_KEY_FILENAME,
|
101
102
|
:SERVER_COMMAND,
|
102
|
-
:PRESET_DIG_SEPARATOR
|
103
|
+
:PRESET_DIG_SEPARATOR,
|
104
|
+
:COFFEE_IMAGE
|
103
105
|
def initialize(env, params)
|
104
106
|
raise 'env and params must be Hash' unless env.is_a?(Hash) && params.is_a?(Hash)
|
105
107
|
raise 'missing param' unless %i[name help version gem].sort.eql?(params.keys.sort)
|
@@ -126,9 +128,9 @@ module Aspera
|
|
126
128
|
# read correct file (set @config_presets)
|
127
129
|
read_config_file
|
128
130
|
# add preset handler (needed for smtp)
|
129
|
-
ExtendedValue.instance.set_handler(EXTV_PRESET,
|
130
|
-
ExtendedValue.instance.set_handler(EXTV_INCLUDE_PRESETS,
|
131
|
-
ExtendedValue.instance.set_handler(EXTV_VAULT,
|
131
|
+
ExtendedValue.instance.set_handler(EXTV_PRESET, lambda{|v|preset_by_name(v)})
|
132
|
+
ExtendedValue.instance.set_handler(EXTV_INCLUDE_PRESETS, lambda{|v|expanded_with_preset_includes(v)})
|
133
|
+
ExtendedValue.instance.set_handler(EXTV_VAULT, lambda{|v|vault_value(v)})
|
132
134
|
# load defaults before it can be overridden
|
133
135
|
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
134
136
|
options.parse_options!
|
@@ -350,7 +352,7 @@ module Aspera
|
|
350
352
|
include_path = include_path.clone # avoid messing up if there are multiple branches
|
351
353
|
current = @config_presets
|
352
354
|
config_name.split(PRESET_DIG_SEPARATOR).each do |name|
|
353
|
-
raise CliError, "Expecting Hash for
|
355
|
+
raise CliError, "Expecting Hash for sub key: #{include_path} (#{current.class})" unless current.is_a?(Hash)
|
354
356
|
include_path.push(name)
|
355
357
|
current = current[name]
|
356
358
|
raise CliError, "No such config preset: #{include_path}" if current.nil?
|
@@ -747,6 +749,7 @@ module Aspera
|
|
747
749
|
when :set
|
748
750
|
param_name = options.get_next_argument('parameter name')
|
749
751
|
param_value = options.get_next_argument('parameter value')
|
752
|
+
param_name = Manager.option_line_to_name(param_name)
|
750
753
|
if !@config_presets.key?(name)
|
751
754
|
Log.log.debug{"no such config name: #{name}, initializing"}
|
752
755
|
selected_preset = @config_presets[name] = {}
|
@@ -974,7 +977,11 @@ module Aspera
|
|
974
977
|
BasicAuthPlugin.register_options(@agents)
|
975
978
|
return {type: :single_object, data: identify_plugin_for_url(options.get_option(:url, is_type: :mandatory))}
|
976
979
|
when :coffee
|
977
|
-
OpenApplication.instance.
|
980
|
+
if OpenApplication.instance.url_method.eql?(:text)
|
981
|
+
require 'aspera/preview/terminal'
|
982
|
+
return Main.result_status(Preview::Terminal.build(Rest.new(base_url: COFFEE_IMAGE).read('')[:http].body, reserved_lines: 3))
|
983
|
+
end
|
984
|
+
OpenApplication.instance.uri(COFFEE_IMAGE)
|
978
985
|
return Main.result_nothing
|
979
986
|
when :ascp
|
980
987
|
execute_action_ascp
|
@@ -170,6 +170,7 @@ module Aspera
|
|
170
170
|
stop_condition = false
|
171
171
|
# results will be sorted in reverse id
|
172
172
|
items.reverse_each do |package|
|
173
|
+
# create the package id, based on recipient's box
|
173
174
|
package[PACKAGE_MATCH_FIELD] =
|
174
175
|
case mailbox
|
175
176
|
when :inbox, :archive
|
@@ -180,7 +181,7 @@ module Aspera
|
|
180
181
|
end
|
181
182
|
# if we look for a specific package
|
182
183
|
stop_condition = true if !stop_at_id.nil? && stop_at_id.eql?(package[PACKAGE_MATCH_FIELD])
|
183
|
-
# keep only those for the specified recipient
|
184
|
+
# keep only those for the specified recipient
|
184
185
|
result.push(package) unless package[PACKAGE_MATCH_FIELD].nil?
|
185
186
|
end
|
186
187
|
break if stop_condition
|
@@ -378,13 +379,13 @@ module Aspera
|
|
378
379
|
# NOTE: only external users have token in faspe: link !
|
379
380
|
if !transfer_spec.key?('token')
|
380
381
|
sanitized = id_uri[:uri].gsub('&', '&')
|
381
|
-
|
382
|
+
xml_payload =
|
382
383
|
%Q(<?xml version="1.0" encoding="UTF-8"?><url-list xmlns="http://schemas.asperasoft.com/xml/url-list"><url href="#{sanitized}"/></url-list>)
|
383
384
|
transfer_spec['token'] = api_v3.call({
|
384
385
|
operation: 'POST',
|
385
386
|
subpath: 'issue-token?direction=down',
|
386
387
|
headers: {'Accept' => 'text/plain', 'Content-Type' => 'application/vnd.aspera.url-list+xml'},
|
387
|
-
text_body_params:
|
388
|
+
text_body_params: xml_payload})[:http].body
|
388
389
|
end
|
389
390
|
transfer_spec['direction'] = Fasp::TransferSpec::DIRECTION_RECEIVE
|
390
391
|
statuses = transfer.start(transfer_spec)
|