aspera-cli 4.17.0 → 4.18.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 +2 -4
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +15 -1
- data/README.md +620 -378
- data/bin/ascli +5 -0
- data/bin/asession +2 -2
- data/lib/aspera/agent/alpha.rb +6 -4
- data/lib/aspera/agent/base.rb +9 -6
- data/lib/aspera/agent/connect.rb +4 -4
- data/lib/aspera/agent/direct.rb +56 -37
- data/lib/aspera/agent/httpgw.rb +23 -324
- data/lib/aspera/agent/node.rb +19 -20
- data/lib/aspera/agent/trsdk.rb +19 -20
- data/lib/aspera/api/aoc.rb +17 -14
- data/lib/aspera/api/cos_node.rb +4 -4
- data/lib/aspera/api/httpgw.rb +339 -0
- data/lib/aspera/api/node.rb +34 -21
- data/lib/aspera/ascmd.rb +4 -3
- data/lib/aspera/ascp/installation.rb +15 -7
- data/lib/aspera/ascp/management.rb +2 -2
- data/lib/aspera/cli/basic_auth_plugin.rb +5 -9
- data/lib/aspera/cli/extended_value.rb +12 -6
- data/lib/aspera/cli/formatter.rb +155 -65
- data/lib/aspera/cli/hints.rb +18 -0
- data/lib/aspera/cli/main.rb +22 -29
- data/lib/aspera/cli/manager.rb +53 -36
- data/lib/aspera/cli/plugin.rb +26 -17
- data/lib/aspera/cli/plugin_factory.rb +31 -20
- data/lib/aspera/cli/plugins/alee.rb +14 -2
- data/lib/aspera/cli/plugins/aoc.rb +141 -131
- data/lib/aspera/cli/plugins/ats.rb +1 -1
- data/lib/aspera/cli/plugins/config.rb +52 -46
- data/lib/aspera/cli/plugins/console.rb +8 -5
- data/lib/aspera/cli/plugins/faspex.rb +27 -19
- data/lib/aspera/cli/plugins/faspex5.rb +222 -149
- data/lib/aspera/cli/plugins/faspio.rb +85 -0
- data/lib/aspera/cli/plugins/httpgw.rb +55 -0
- data/lib/aspera/cli/plugins/node.rb +86 -29
- data/lib/aspera/cli/plugins/orchestrator.rb +31 -29
- data/lib/aspera/cli/plugins/preview.rb +6 -2
- data/lib/aspera/cli/plugins/server.rb +5 -5
- data/lib/aspera/cli/plugins/shares.rb +16 -14
- data/lib/aspera/cli/sync_actions.rb +6 -6
- data/lib/aspera/cli/transfer_agent.rb +5 -4
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/environment.rb +7 -6
- data/lib/aspera/faspex_gw.rb +5 -4
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/log.rb +6 -3
- data/lib/aspera/node_simulator.rb +2 -2
- data/lib/aspera/oauth/base.rb +31 -19
- data/lib/aspera/oauth/factory.rb +12 -13
- data/lib/aspera/oauth/generic.rb +1 -0
- data/lib/aspera/oauth/jwt.rb +18 -15
- data/lib/aspera/oauth/url_json.rb +8 -6
- data/lib/aspera/open_application.rb +5 -7
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/options.rb +3 -3
- data/lib/aspera/preview/terminal.rb +4 -4
- data/lib/aspera/preview/utils.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +5 -1
- data/lib/aspera/rest.rb +60 -74
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +2 -2
- data/lib/aspera/rest_errors_aspera.rb +1 -1
- data/lib/aspera/resumer.rb +1 -1
- data/lib/aspera/secret_hider.rb +2 -4
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/transfer/parameters.rb +39 -36
- data/lib/aspera/transfer/spec.rb +2 -0
- data/lib/aspera/transfer/sync.rb +2 -1
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +5 -4
- data/lib/aspera/web_auth.rb +1 -1
- data/lib/aspera/web_server_simple.rb +4 -3
- data.tar.gz.sig +0 -0
- metadata +5 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/plugins/bss.rb +0 -71
@@ -18,12 +18,41 @@ module Aspera
|
|
18
18
|
module Cli
|
19
19
|
module Plugins
|
20
20
|
class Aoc < Cli::BasicAuthPlugin
|
21
|
-
AOC_PATH_API_CLIENTS = 'admin/api-clients'
|
22
21
|
# default redirect for AoC web auth
|
23
22
|
REDIRECT_LOCALHOST = 'http://localhost:12345'
|
24
23
|
# OAuth methods supported
|
25
24
|
STD_AUTH_TYPES = %i[web jwt].freeze
|
26
|
-
|
25
|
+
# admin objects that can be manipulated
|
26
|
+
ADMIN_OBJECTS = %i[
|
27
|
+
self
|
28
|
+
organization
|
29
|
+
user
|
30
|
+
group
|
31
|
+
group_membership
|
32
|
+
client
|
33
|
+
contact
|
34
|
+
dropbox
|
35
|
+
node
|
36
|
+
operation
|
37
|
+
package
|
38
|
+
saml_configuration
|
39
|
+
workspace
|
40
|
+
workspace_membership
|
41
|
+
dropbox_membership
|
42
|
+
short_link
|
43
|
+
application
|
44
|
+
client_registration_token
|
45
|
+
client_access_key
|
46
|
+
kms_profile].freeze
|
47
|
+
# query to list fully received packages
|
48
|
+
PACKAGE_RECEIVED_BASE_QUERY = {
|
49
|
+
'archived' => false,
|
50
|
+
'has_content' => true,
|
51
|
+
'received' => true,
|
52
|
+
'completed' => true}.freeze
|
53
|
+
# options and parameters for Api::AoC.new
|
54
|
+
OPTIONS_NEW = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password workspace].freeze
|
55
|
+
private_constant :REDIRECT_LOCALHOST, :STD_AUTH_TYPES, :ADMIN_OBJECTS, :PACKAGE_RECEIVED_BASE_QUERY, :OPTIONS_NEW
|
27
56
|
class << self
|
28
57
|
def application_name
|
29
58
|
'Aspera on Cloud'
|
@@ -33,12 +62,12 @@ module Aspera
|
|
33
62
|
# no protocol ?
|
34
63
|
base_url = "https://#{base_url}" unless base_url.match?(%r{^[a-z]{1,6}://})
|
35
64
|
# only org provided ?
|
36
|
-
base_url = "#{base_url}.#{Api::
|
65
|
+
base_url = "#{base_url}.#{Api::SAAS_DOMAIN_PROD}" unless base_url.include?('.')
|
37
66
|
# AoC is only https
|
38
67
|
return nil unless base_url.start_with?('https://')
|
39
68
|
result = Rest.new(base_url: base_url, redirect_max: 10).read('')
|
40
69
|
# Any AoC is on this domain
|
41
|
-
return nil unless result[:http].uri.host.end_with?(Api::
|
70
|
+
return nil unless result[:http].uri.host.end_with?(Api::SAAS_DOMAIN_PROD)
|
42
71
|
Log.log.debug{"AoC Main page: #{result[:http].body.include?(Api::AoC::PRODUCT_NAME)}"}
|
43
72
|
base_url = result[:http].uri.to_s if result[:http].uri.path.include?('/public')
|
44
73
|
# either in standard domain, or product name in page
|
@@ -48,6 +77,8 @@ module Aspera
|
|
48
77
|
}
|
49
78
|
end
|
50
79
|
|
80
|
+
# @param [String] url : url to check
|
81
|
+
# @return [Bool] true if private key is required for the url (i.e. no passcode)
|
51
82
|
def private_key_required?(url)
|
52
83
|
# pub link do not need private key
|
53
84
|
return Api::AoC.link_info(url)[:token].nil?
|
@@ -106,7 +137,7 @@ module Aspera
|
|
106
137
|
formatter.display_status('- origin: localhost')
|
107
138
|
formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
|
108
139
|
end
|
109
|
-
OpenApplication.instance.uri("#{instance_url}
|
140
|
+
OpenApplication.instance.uri("#{instance_url}/admin/api-clients")
|
110
141
|
options.get_option(:client_id, mandatory: true)
|
111
142
|
options.get_option(:client_secret, mandatory: true)
|
112
143
|
use_browser_authentication = true
|
@@ -147,35 +178,8 @@ module Aspera
|
|
147
178
|
}
|
148
179
|
end
|
149
180
|
end
|
150
|
-
# special value for package id
|
151
|
-
KNOWN_AOC_RES = %i[
|
152
|
-
self
|
153
|
-
organization
|
154
|
-
user
|
155
|
-
group
|
156
|
-
group_membership
|
157
|
-
client
|
158
|
-
contact
|
159
|
-
dropbox
|
160
|
-
node
|
161
|
-
operation
|
162
|
-
package
|
163
|
-
saml_configuration
|
164
|
-
workspace
|
165
|
-
workspace_membership
|
166
|
-
dropbox_membership
|
167
|
-
short_link
|
168
|
-
application
|
169
|
-
client_registration_token
|
170
|
-
client_access_key
|
171
|
-
kms_profile].freeze
|
172
|
-
PACKAGE_RECEIVED_BASE_QUERY = {
|
173
|
-
'archived' => false,
|
174
|
-
'has_content' => true,
|
175
|
-
'received' => true,
|
176
|
-
'completed' => true}.freeze
|
177
181
|
|
178
|
-
def initialize(**
|
182
|
+
def initialize(**_)
|
179
183
|
super
|
180
184
|
@cache_workspace_info = nil
|
181
185
|
@cache_home_node_file = nil
|
@@ -186,17 +190,15 @@ module Aspera
|
|
186
190
|
options.declare(:scope, 'OAuth scope for AoC API calls', default: Api::AoC::SCOPE_FILES_USER)
|
187
191
|
options.declare(:redirect_uri, 'OAuth API client redirect URI')
|
188
192
|
options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
|
189
|
-
options.declare(:passphrase, 'RSA private key passphrase')
|
193
|
+
options.declare(:passphrase, 'RSA private key passphrase', types: String)
|
190
194
|
options.declare(:workspace, 'Name of workspace', types: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE)
|
191
|
-
options.declare(:new_user_option, 'New user creation option for unknown package recipients')
|
195
|
+
options.declare(:new_user_option, 'New user creation option for unknown package recipients', types: Hash)
|
192
196
|
options.declare(:validate_metadata, 'Validate shared inbox metadata', values: :bool, default: true)
|
193
197
|
options.parse_options!
|
194
198
|
# add node plugin options (for manual)
|
195
199
|
Node.declare_options(options)
|
196
200
|
end
|
197
201
|
|
198
|
-
OPTIONS_NEW = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password workspace].freeze
|
199
|
-
|
200
202
|
def api_from_options(new_base_path)
|
201
203
|
create_values = {subpath: new_base_path, secret_finder: config}
|
202
204
|
# create an API object with the same options, but with a different subpath
|
@@ -333,12 +335,12 @@ module Aspera
|
|
333
335
|
client_apfid = top_node_api.resolve_api_fid(file_id, client_folder)
|
334
336
|
server_apfid = top_node_api.resolve_api_fid(file_id, server_folder)
|
335
337
|
# force node as transfer agent
|
336
|
-
transfer.agent_instance = Agent::Node.new(
|
338
|
+
transfer.agent_instance = Agent::Node.new(
|
337
339
|
url: client_apfid[:api].base_url,
|
338
340
|
username: client_apfid[:api].app_info[:node_info]['access_key'],
|
339
|
-
password: client_apfid[:api].
|
341
|
+
password: client_apfid[:api].oauth.token,
|
340
342
|
root_id: client_apfid[:file_id]
|
341
|
-
|
343
|
+
)
|
342
344
|
# additional node to node TS info
|
343
345
|
add_ts = {
|
344
346
|
'remote_access_key' => server_apfid[:api].app_info[:node_info]['access_key'],
|
@@ -350,15 +352,106 @@ module Aspera
|
|
350
352
|
client_direction,
|
351
353
|
add_ts)))
|
352
354
|
else Aspera.error_unreachable_line
|
353
|
-
end
|
355
|
+
end
|
354
356
|
Aspera.error_unreachable_line
|
355
|
-
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def execute_resource_action(resource_type)
|
360
|
+
# get path on API, resource type is singular, but api is plural
|
361
|
+
resource_class_path =
|
362
|
+
case resource_type
|
363
|
+
# special cases: singleton, in admin, with x
|
364
|
+
when :self, :organization then resource_type
|
365
|
+
when :client_registration_token, :client_access_key then "admin/#{resource_type}s"
|
366
|
+
when :application then 'admin/apps_new'
|
367
|
+
when :dropbox then "#{resource_type}es"
|
368
|
+
when :kms_profile then "integrations/#{resource_type}s"
|
369
|
+
else "#{resource_type}s"
|
370
|
+
end
|
371
|
+
# build list of supported operations
|
372
|
+
singleton_object = %i[self organization].include?(resource_type)
|
373
|
+
global_operations = %i[create list]
|
374
|
+
supported_operations = %i[show modify]
|
375
|
+
supported_operations.push(:delete, *global_operations) unless singleton_object
|
376
|
+
supported_operations.push(:do) if resource_type.eql?(:node)
|
377
|
+
supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
|
378
|
+
command = options.get_next_command(supported_operations)
|
379
|
+
# require identifier for non global commands
|
380
|
+
if !singleton_object && !global_operations.include?(command)
|
381
|
+
res_id = get_resource_id_from_args(resource_class_path)
|
382
|
+
resource_instance_path = "#{resource_class_path}/#{res_id}"
|
383
|
+
end
|
384
|
+
resource_instance_path = resource_class_path if singleton_object
|
385
|
+
case command
|
386
|
+
when :create
|
387
|
+
id_result = 'id'
|
388
|
+
id_result = 'token' if resource_class_path.eql?('admin/client_registration_tokens')
|
389
|
+
# TODO: report inconsistency: creation url is !=, and does not return id.
|
390
|
+
resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
|
391
|
+
return do_bulk_operation(command: command, descr: 'creation data', id_result: id_result) do |params|
|
392
|
+
aoc_api.create(resource_class_path, params)[:data]
|
393
|
+
end
|
394
|
+
when :list
|
395
|
+
default_fields = ['id']
|
396
|
+
default_query = {}
|
397
|
+
case resource_type
|
398
|
+
when :application
|
399
|
+
default_query = {organization_apps: true}
|
400
|
+
default_fields.push('app_type', 'app_name', 'available', 'direct_authorizations_allowed', 'workspace_authorizations_allowed')
|
401
|
+
when :client, :client_access_key, :dropbox, :group, :package, :saml_configuration, :workspace then default_fields.push('name')
|
402
|
+
when :client_registration_token then default_fields.push('value', 'data.client_subject_scopes', 'created_at')
|
403
|
+
when :contact then default_fields = %w[email name source_id source_type]
|
404
|
+
when :node then default_fields.push('name', 'host', 'access_key')
|
405
|
+
when :operation then default_fields = nil
|
406
|
+
when :short_link then default_fields.push('short_url', 'data.url_token_data.purpose')
|
407
|
+
when :user then default_fields.push('name', 'email')
|
408
|
+
when :group_membership then default_fields.push(*%w[group_id member_type member_id])
|
409
|
+
when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
|
410
|
+
end
|
411
|
+
return result_list(resource_class_path, fields: default_fields, default_query: default_query)
|
412
|
+
when :show
|
413
|
+
object = aoc_api.read(resource_instance_path)[:data]
|
414
|
+
# default: show all, but certificate
|
415
|
+
fields = object.keys.reject{|k|k.eql?('certificate')}
|
416
|
+
return { type: :single_object, data: object, fields: fields }
|
417
|
+
when :modify
|
418
|
+
changes = options.get_next_argument('properties', type: Hash)
|
419
|
+
return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
|
420
|
+
aoc_api.update("#{resource_class_path}/#{one_id}", changes)
|
421
|
+
{'id' => one_id}
|
422
|
+
end
|
423
|
+
when :delete
|
424
|
+
return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
|
425
|
+
aoc_api.delete("#{resource_class_path}/#{one_id}")
|
426
|
+
{'id' => one_id}
|
427
|
+
end
|
428
|
+
when :set_pub_key
|
429
|
+
# special : reads private and generate public
|
430
|
+
the_private_key = options.get_next_argument('private_key PEM value', type: String)
|
431
|
+
the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
|
432
|
+
aoc_api.update(resource_instance_path, {jwt_grant_enabled: true, public_key: the_public_key})
|
433
|
+
return Main.result_success
|
434
|
+
when :do
|
435
|
+
command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
|
436
|
+
# init context
|
437
|
+
aoc_api.context(:files)
|
438
|
+
return execute_nodegen4_command(command_repo, res_id)
|
439
|
+
else Aspera.error_unexpected_value(command)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
ADMIN_ACTIONS = %i[ats resource usage_reports analytics subscription auth_providers].concat(ADMIN_OBJECTS).freeze
|
356
444
|
|
357
445
|
def execute_admin_action
|
358
446
|
# upgrade scope to admin
|
359
447
|
aoc_api.oauth.scope = Api::AoC::SCOPE_FILES_ADMIN
|
360
|
-
command_admin = options.get_next_command(
|
448
|
+
command_admin = options.get_next_command(ADMIN_ACTIONS)
|
361
449
|
case command_admin
|
450
|
+
when :resource
|
451
|
+
Log.log.warn('resource command is deprecated (4.18), directly use the specific command instead')
|
452
|
+
return execute_resource_action(options.get_next_argument('resource', expected: ADMIN_OBJECTS))
|
453
|
+
when *ADMIN_OBJECTS
|
454
|
+
return execute_resource_action(command_admin)
|
362
455
|
when :auth_providers
|
363
456
|
command_auth_prov = options.get_next_command(%i[list update])
|
364
457
|
case command_auth_prov
|
@@ -480,89 +573,6 @@ module Aspera
|
|
480
573
|
end
|
481
574
|
return {type: :object_list, data: events}
|
482
575
|
end
|
483
|
-
when :resource
|
484
|
-
resource_type = options.get_next_argument('resource', expected: KNOWN_AOC_RES)
|
485
|
-
# get path on API, resource type is singular, but api is plural
|
486
|
-
resource_class_path =
|
487
|
-
case resource_type
|
488
|
-
# special cases: singleton, in admin, with x
|
489
|
-
when :self, :organization then resource_type
|
490
|
-
when :client_registration_token, :client_access_key then "admin/#{resource_type}s"
|
491
|
-
when :application then 'admin/apps_new'
|
492
|
-
when :dropbox then "#{resource_type}es"
|
493
|
-
when :kms_profile then "integrations/#{resource_type}s"
|
494
|
-
else "#{resource_type}s"
|
495
|
-
end
|
496
|
-
# build list of supported operations
|
497
|
-
singleton_object = %i[self organization].include?(resource_type)
|
498
|
-
global_operations = %i[create list]
|
499
|
-
supported_operations = %i[show modify]
|
500
|
-
supported_operations.push(:delete, *global_operations) unless singleton_object
|
501
|
-
supported_operations.push(:do) if resource_type.eql?(:node)
|
502
|
-
supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
|
503
|
-
command = options.get_next_command(supported_operations)
|
504
|
-
# require identifier for non global commands
|
505
|
-
if !singleton_object && !global_operations.include?(command)
|
506
|
-
res_id = get_resource_id_from_args(resource_class_path)
|
507
|
-
resource_instance_path = "#{resource_class_path}/#{res_id}"
|
508
|
-
end
|
509
|
-
resource_instance_path = resource_class_path if singleton_object
|
510
|
-
case command
|
511
|
-
when :create
|
512
|
-
id_result = 'id'
|
513
|
-
id_result = 'token' if resource_class_path.eql?('admin/client_registration_tokens')
|
514
|
-
# TODO: report inconsistency: creation url is !=, and does not return id.
|
515
|
-
resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
|
516
|
-
return do_bulk_operation(command: command, descr: 'creation data', id_result: id_result) do |params|
|
517
|
-
aoc_api.create(resource_class_path, params)[:data]
|
518
|
-
end
|
519
|
-
when :list
|
520
|
-
default_fields = ['id']
|
521
|
-
default_query = {}
|
522
|
-
case resource_type
|
523
|
-
when :application
|
524
|
-
default_query = {organization_apps: true}
|
525
|
-
default_fields.push('app_type', 'app_name', 'available', 'direct_authorizations_allowed', 'workspace_authorizations_allowed')
|
526
|
-
when :client, :client_access_key, :dropbox, :group, :package, :saml_configuration, :workspace then default_fields.push('name')
|
527
|
-
when :client_registration_token then default_fields.push('value', 'data.client_subject_scopes', 'created_at')
|
528
|
-
when :contact then default_fields = %w[email name source_id source_type]
|
529
|
-
when :node then default_fields.push('name', 'host', 'access_key')
|
530
|
-
when :operation then default_fields = nil
|
531
|
-
when :short_link then default_fields.push('short_url', 'data.url_token_data.purpose')
|
532
|
-
when :user then default_fields.push('name', 'email')
|
533
|
-
when :group_membership then default_fields.push(*%w[group_id member_type member_id])
|
534
|
-
when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
|
535
|
-
end
|
536
|
-
return result_list(resource_class_path, fields: default_fields, default_query: default_query)
|
537
|
-
when :show
|
538
|
-
object = aoc_api.read(resource_instance_path)[:data]
|
539
|
-
# default: show all, but certificate
|
540
|
-
fields = object.keys.reject{|k|k.eql?('certificate')}
|
541
|
-
return { type: :single_object, data: object, fields: fields }
|
542
|
-
when :modify
|
543
|
-
changes = options.get_next_argument('properties', type: Hash)
|
544
|
-
return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
|
545
|
-
aoc_api.update("#{resource_class_path}/#{one_id}", changes)
|
546
|
-
{'id' => one_id}
|
547
|
-
end
|
548
|
-
when :delete
|
549
|
-
return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
|
550
|
-
aoc_api.delete("#{resource_class_path}/#{one_id}")
|
551
|
-
{'id' => one_id}
|
552
|
-
end
|
553
|
-
when :set_pub_key
|
554
|
-
# special : reads private and generate public
|
555
|
-
the_private_key = options.get_next_argument('private_key PEM value', type: String)
|
556
|
-
the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
|
557
|
-
aoc_api.update(resource_instance_path, {jwt_grant_enabled: true, public_key: the_public_key})
|
558
|
-
return Main.result_success
|
559
|
-
when :do
|
560
|
-
command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
|
561
|
-
# init context
|
562
|
-
aoc_api.context(:files)
|
563
|
-
return execute_nodegen4_command(command_repo, res_id)
|
564
|
-
else Aspera.error_unexpected_value(command)
|
565
|
-
end
|
566
576
|
when :usage_reports
|
567
577
|
return result_list('usage_reports', base_query: {workspace_id: aoc_api.context(:files)[:workspace_id]})
|
568
578
|
end
|
@@ -591,7 +601,7 @@ module Aspera
|
|
591
601
|
when :servers
|
592
602
|
return {type: :object_list, data: Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers')[:data]}
|
593
603
|
when :bearer_token
|
594
|
-
return {type: :text, data: aoc_api.
|
604
|
+
return {type: :text, data: aoc_api.oauth.token}
|
595
605
|
when :organization
|
596
606
|
return { type: :single_object, data: aoc_api.read('organization')[:data] }
|
597
607
|
when :tier_restrictions
|
@@ -696,7 +706,7 @@ module Aspera
|
|
696
706
|
ids_to_download = all_ids.reject{|id|skip_ids_data.include?(id)}
|
697
707
|
else
|
698
708
|
ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
|
699
|
-
end
|
709
|
+
end
|
700
710
|
# list here
|
701
711
|
result_transfer = []
|
702
712
|
formatter.display_status("found #{ids_to_download.length} package(s).")
|
@@ -847,8 +857,8 @@ module Aspera
|
|
847
857
|
# TODO: event ?
|
848
858
|
end
|
849
859
|
return {type: :single_object, data: result_create_short_link}
|
850
|
-
end
|
851
|
-
end
|
860
|
+
end
|
861
|
+
end
|
852
862
|
raise 'Error: shall not reach this line'
|
853
863
|
when :automation
|
854
864
|
Log.log.warn('BETA: work under progress')
|
@@ -891,7 +901,7 @@ module Aspera
|
|
891
901
|
server.start
|
892
902
|
return Main.result_status('Gateway terminated')
|
893
903
|
else Aspera.error_unreachable_line
|
894
|
-
end
|
904
|
+
end
|
895
905
|
Aspera.error_unreachable_line
|
896
906
|
end
|
897
907
|
|
@@ -100,7 +100,7 @@ module Aspera
|
|
100
100
|
return Main.result_status('modified')
|
101
101
|
when :entitlement
|
102
102
|
ak = ats_api_pub_v1.read("access_keys/#{access_key_id}")[:data]
|
103
|
-
api_bss = Api::
|
103
|
+
api_bss = Api::Alee.new(ak['license']['entitlement_id'], ak['license']['customer_id'])
|
104
104
|
return {type: :single_object, data: api_bss.read('entitlement')[:data]}
|
105
105
|
when :delete
|
106
106
|
ats_api_pub_v1.delete("access_keys/#{access_key_id}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# cspell:ignore initdemo genkey pubkey asperasoft
|
3
|
+
# cspell:ignore initdemo genkey pubkey asperasoft filelists
|
4
4
|
require 'aspera/cli/basic_auth_plugin'
|
5
5
|
require 'aspera/cli/extended_value'
|
6
6
|
require 'aspera/cli/version'
|
@@ -25,6 +25,7 @@ require 'aspera/rest'
|
|
25
25
|
require 'aspera/log'
|
26
26
|
require 'aspera/assert'
|
27
27
|
require 'aspera/oauth'
|
28
|
+
require 'openssl'
|
28
29
|
require 'open3'
|
29
30
|
require 'date'
|
30
31
|
require 'erb'
|
@@ -141,7 +142,7 @@ module Aspera
|
|
141
142
|
Aspera.assert(!app_name.empty?)
|
142
143
|
return File.join(module_family_folder, app_name)
|
143
144
|
end
|
144
|
-
end
|
145
|
+
end
|
145
146
|
|
146
147
|
def initialize(gem:, name:, help:, version:, **env)
|
147
148
|
# we need to defer parsing of options until we have the config file, so we can use @extend with @preset
|
@@ -163,7 +164,6 @@ module Aspera
|
|
163
164
|
@option_http_options = {}
|
164
165
|
@ssl_warned_urls = []
|
165
166
|
@option_cache_tokens = true
|
166
|
-
@proxy_credentials = nil
|
167
167
|
@main_folder = nil
|
168
168
|
@option_config_file = nil
|
169
169
|
# store is used for ruby https
|
@@ -230,9 +230,10 @@ module Aspera
|
|
230
230
|
options.declare(:silent_insecure, 'Issue a warning if certificate is ignored', values: :bool, handler: {o: self, m: :option_warn_insecure_cert}, default: :yes)
|
231
231
|
options.declare(:cert_stores, 'List of folder with trusted certificates', types: [Array, String], handler: {o: self, m: :trusted_cert_locations})
|
232
232
|
options.declare(:http_options, 'Options for HTTP/S socket', types: Hash, handler: {o: self, m: :option_http_options}, default: {})
|
233
|
+
options.declare(:http_proxy, 'URL for HTTP proxy with optional credentials', types: String, handler: {o: self, m: :option_http_proxy})
|
233
234
|
options.declare(:cache_tokens, 'Save and reuse OAuth tokens', values: :bool, handler: {o: self, m: :option_cache_tokens})
|
234
235
|
options.declare(:fpac, 'Proxy auto configuration script')
|
235
|
-
options.declare(:proxy_credentials, 'HTTP proxy credentials
|
236
|
+
options.declare(:proxy_credentials, 'HTTP proxy credentials for fpac. Array: user,password', types: Array)
|
236
237
|
options.parse_options!
|
237
238
|
@progress_bar = TransferProgress.new if options.get_option(:progress_bar)
|
238
239
|
# Check SDK folder is set or not, for compatibility, we check in two places
|
@@ -255,20 +256,21 @@ module Aspera
|
|
255
256
|
end
|
256
257
|
pac_script = options.get_option(:fpac)
|
257
258
|
# create PAC executor
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
259
|
+
if !pac_script.nil?
|
260
|
+
@pac_exec = ProxyAutoConfig.new(pac_script).register_uri_generic
|
261
|
+
proxy_user_pass = options.get_option(:proxy_credentials)
|
262
|
+
if !proxy_user_pass.nil?
|
263
|
+
Aspera.assert(proxy_user_pass.length.eql?(2), exception_class: Cli::BadArgument){"proxy_credentials shall have two elements (#{proxy_user_pass.length})"}
|
264
|
+
@pac_exec.proxy_user = proxy_user_pass[0]
|
265
|
+
@pac_exec.proxy_pass = proxy_user_pass[1]
|
266
|
+
end
|
265
267
|
end
|
266
268
|
Rest.set_parameters(
|
267
269
|
user_agent: PROGRAM_NAME,
|
268
270
|
session_cb: lambda{|http_session|update_http_session(http_session)},
|
269
271
|
progress_bar: @progress_bar)
|
270
272
|
OAuth::Factory.instance.persist_mgr = persistency if @option_cache_tokens
|
271
|
-
Transfer::Parameters.file_list_folder = File.join(@main_folder, 'filelists')
|
273
|
+
Transfer::Parameters.file_list_folder = File.join(@main_folder, 'filelists')
|
272
274
|
RestErrorAnalyzer.instance.log_file = File.join(@main_folder, 'rest_exceptions.log')
|
273
275
|
# register aspera REST call error handlers
|
274
276
|
RestErrorsAspera.register_handlers
|
@@ -330,6 +332,15 @@ module Aspera
|
|
330
332
|
return locations
|
331
333
|
end
|
332
334
|
|
335
|
+
def option_http_proxy
|
336
|
+
return ENV['http_proxy']
|
337
|
+
end
|
338
|
+
|
339
|
+
def option_http_proxy=(value)
|
340
|
+
URI.parse(value)
|
341
|
+
ENV['http_proxy'] = value
|
342
|
+
end
|
343
|
+
|
333
344
|
def option_ignore_cert_host_port=(url_list)
|
334
345
|
url_list.each do |url|
|
335
346
|
uri = URI.parse(url)
|
@@ -365,10 +376,6 @@ module Aspera
|
|
365
376
|
http_session.verify_mode = SELF_SIGNED_CERT if http_session.use_ssl? && ignore_cert?(http_session.address, http_session.port)
|
366
377
|
http_session.cert_store = @certificate_store if @certificate_store
|
367
378
|
Log.log.debug{"using cert store #{http_session.cert_store} (#{@certificate_store})"} unless http_session.cert_store.nil?
|
368
|
-
if @proxy_credentials
|
369
|
-
http_session.proxy_user = @proxy_credentials[:user]
|
370
|
-
http_session.proxy_pass = @proxy_credentials[:pass]
|
371
|
-
end
|
372
379
|
@option_http_options.each do |k, v|
|
373
380
|
method = "#{k}=".to_sym
|
374
381
|
# check if accessor is a method of Net::HTTP
|
@@ -453,7 +460,7 @@ module Aspera
|
|
453
460
|
def add_plugin_default_preset(plugin_name_sym)
|
454
461
|
default_config_name = get_plugin_default_config_name(plugin_name_sym)
|
455
462
|
Log.log.debug{"add_plugin_default_preset:#{plugin_name_sym}:#{default_config_name}"}
|
456
|
-
options.add_option_preset(preset_by_name(default_config_name),
|
463
|
+
options.add_option_preset(preset_by_name(default_config_name), 'default_plugin', override: false) unless default_config_name.nil?
|
457
464
|
return nil
|
458
465
|
end
|
459
466
|
|
@@ -539,12 +546,12 @@ module Aspera
|
|
539
546
|
|
540
547
|
def option_preset=(value)
|
541
548
|
case value
|
542
|
-
when String
|
543
|
-
options.add_option_preset(preset_by_name(value))
|
544
549
|
when Hash
|
545
|
-
options.add_option_preset(value)
|
550
|
+
options.add_option_preset(value, 'set')
|
551
|
+
when String
|
552
|
+
options.add_option_preset(preset_by_name(value), 'set_by_name')
|
546
553
|
else
|
547
|
-
raise 'Preset definition must be a String for name, or Hash for
|
554
|
+
raise 'Preset definition must be a String for preset name, or Hash for set of values'
|
548
555
|
end
|
549
556
|
end
|
550
557
|
|
@@ -613,18 +620,18 @@ module Aspera
|
|
613
620
|
check_only = check_only.to_sym unless check_only.nil?
|
614
621
|
found_apps = []
|
615
622
|
my_self_plugin_sym = self.class.name.split('::').last.downcase.to_sym
|
616
|
-
PluginFactory.instance.
|
623
|
+
PluginFactory.instance.plugin_list.each do |plugin_name_sym|
|
617
624
|
# no detection for internal plugin
|
618
625
|
next if plugin_name_sym.eql?(my_self_plugin_sym)
|
619
626
|
next if check_only && !check_only.eql?(plugin_name_sym)
|
620
627
|
# load plugin class
|
621
|
-
|
622
|
-
detect_plugin_class = PluginFactory.plugin_class(plugin_name_sym)
|
628
|
+
detect_plugin_class = PluginFactory.instance.plugin_class(plugin_name_sym)
|
623
629
|
# requires detection method
|
624
630
|
next unless detect_plugin_class.respond_to?(:detect)
|
625
631
|
detection_info = nil
|
626
632
|
begin
|
627
633
|
Log.log.debug{"detecting #{plugin_name_sym} at #{app_url}"}
|
634
|
+
formatter.long_operation_running("#{plugin_name_sym}\r")
|
628
635
|
detection_info = detect_plugin_class.detect(app_url)
|
629
636
|
rescue OpenSSL::SSL::SSLError => e
|
630
637
|
Log.log.warn(e.message)
|
@@ -639,7 +646,7 @@ module Aspera
|
|
639
646
|
app_name = detect_plugin_class.respond_to?(:application_name) ? detect_plugin_class.application_name : detect_plugin_class.name.split('::').last
|
640
647
|
# if there is a redirect, then the detector can override the url.
|
641
648
|
found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
|
642
|
-
end
|
649
|
+
end
|
643
650
|
raise "No known application found at #{app_url}" if found_apps.empty?
|
644
651
|
Aspera.assert(found_apps.all?{|a|a.keys.all?(Symbol)})
|
645
652
|
return found_apps
|
@@ -726,7 +733,7 @@ module Aspera
|
|
726
733
|
when :spec
|
727
734
|
return {
|
728
735
|
type: :object_list,
|
729
|
-
data: Transfer::Parameters.man_table,
|
736
|
+
data: Transfer::Parameters.man_table(formatter),
|
730
737
|
fields: [%w[name type], Transfer::Parameters::SUPPORTED_AGENTS_SHORT.map(&:to_s), %w[description]].flatten.freeze
|
731
738
|
}
|
732
739
|
when :errors
|
@@ -791,7 +798,7 @@ module Aspera
|
|
791
798
|
return Main.result_status("Modified: #{@option_config_file}")
|
792
799
|
when :update
|
793
800
|
# get unprocessed options
|
794
|
-
unprocessed_options = options.
|
801
|
+
unprocessed_options = options.unprocessed_options_with_value
|
795
802
|
Log.log.debug{"opts=#{unprocessed_options}"}
|
796
803
|
@config_presets[name] ||= {}
|
797
804
|
@config_presets[name].merge!(unprocessed_options)
|
@@ -853,6 +860,7 @@ module Aspera
|
|
853
860
|
wizard
|
854
861
|
detect
|
855
862
|
coffee
|
863
|
+
image
|
856
864
|
ascp
|
857
865
|
email_test
|
858
866
|
smtp_settings
|
@@ -908,14 +916,13 @@ module Aspera
|
|
908
916
|
case options.get_next_command(%i[list create])
|
909
917
|
when :list
|
910
918
|
result = []
|
911
|
-
PluginFactory.instance.
|
912
|
-
|
913
|
-
plugin_klass = PluginFactory.plugin_class(name)
|
919
|
+
PluginFactory.instance.plugin_list.each do |name|
|
920
|
+
plugin_class = PluginFactory.instance.plugin_class(name)
|
914
921
|
result.push({
|
915
922
|
plugin: name,
|
916
|
-
detect: Formatter.tick(
|
917
|
-
wizard: Formatter.tick(
|
918
|
-
path:
|
923
|
+
detect: Formatter.tick(plugin_class.respond_to?(:detect)),
|
924
|
+
wizard: Formatter.tick(plugin_class.respond_to?(:wizard)),
|
925
|
+
path: PluginFactory.instance.plugin_source(name)
|
919
926
|
})
|
920
927
|
end
|
921
928
|
return {type: :object_list, data: result, fields: %w[plugin detect wizard path]}
|
@@ -950,11 +957,9 @@ module Aspera
|
|
950
957
|
} if action.eql?(:detect)
|
951
958
|
return wizard_find(apps)
|
952
959
|
when :coffee
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
OpenApplication.instance.uri(COFFEE_IMAGE)
|
957
|
-
return Main.result_nothing
|
960
|
+
return Main.result_image(COFFEE_IMAGE, formatter: formatter)
|
961
|
+
when :image
|
962
|
+
return Main.result_image(options.get_next_argument('image uri or blob'), formatter: formatter)
|
958
963
|
when :ascp
|
959
964
|
execute_action_ascp
|
960
965
|
when :gem
|
@@ -1019,17 +1024,17 @@ module Aspera
|
|
1019
1024
|
apps.first
|
1020
1025
|
else
|
1021
1026
|
formatter.display_status('Multiple applications detected, please select from:')
|
1022
|
-
formatter.display_results(
|
1027
|
+
formatter.display_results(type: :object_list, data: apps, fields: %w[product url version])
|
1023
1028
|
answer = options.prompt_user_input_in_list('product', apps.map{|a|a[:product]})
|
1024
1029
|
apps.find{|a|a[:product].eql?(answer)}
|
1025
1030
|
end
|
1026
|
-
wiz_url = identification[:url]
|
1027
1031
|
Log.log.debug{Log.dump(:identification, identification)}
|
1032
|
+
wiz_url = identification[:url]
|
1028
1033
|
formatter.display_status("Using: #{identification[:name]} at #{wiz_url}".bold)
|
1029
1034
|
# set url for instantiation of plugin
|
1030
|
-
options.add_option_preset({url: wiz_url})
|
1035
|
+
options.add_option_preset({url: wiz_url}, 'wizard')
|
1031
1036
|
# instantiate plugin: command line options will be known and wizard can be called
|
1032
|
-
wiz_plugin_class = PluginFactory.plugin_class(identification[:product])
|
1037
|
+
wiz_plugin_class = PluginFactory.instance.plugin_class(identification[:product])
|
1033
1038
|
Aspera.assert(wiz_plugin_class.respond_to?(:wizard), exception_class: Cli::BadArgument) do
|
1034
1039
|
"Detected: #{identification[:product]}, but this application has no wizard"
|
1035
1040
|
end
|
@@ -1198,7 +1203,7 @@ module Aspera
|
|
1198
1203
|
return default_config_name
|
1199
1204
|
end
|
1200
1205
|
return nil
|
1201
|
-
end
|
1206
|
+
end
|
1202
1207
|
|
1203
1208
|
# TODO: delete: ALLOWED_KEYS = %i[password username description].freeze
|
1204
1209
|
# @return [Hash] result of execution of vault command
|
@@ -1208,7 +1213,7 @@ module Aspera
|
|
1208
1213
|
when :info
|
1209
1214
|
return {type: :single_object, data: vault_info}
|
1210
1215
|
when :list
|
1211
|
-
return {type: :object_list, data: vault.list}
|
1216
|
+
return {type: :object_list, data: vault.list, fields: %w(label url username password description)}
|
1212
1217
|
when :show
|
1213
1218
|
return {type: :single_object, data: vault.get(label: options.get_next_argument('label'))}
|
1214
1219
|
when :create
|
@@ -1219,8 +1224,9 @@ module Aspera
|
|
1219
1224
|
vault.set(info)
|
1220
1225
|
return Main.result_status('Password added')
|
1221
1226
|
when :delete
|
1222
|
-
|
1223
|
-
|
1227
|
+
label_to_delete = options.get_next_argument('label')
|
1228
|
+
vault.delete(label: label_to_delete)
|
1229
|
+
return Main.result_status("Entry deleted: #{label_to_delete}")
|
1224
1230
|
when :password
|
1225
1231
|
Aspera.assert(vault.respond_to?(:password=)){'Vault does not support password change'}
|
1226
1232
|
new_password = options.get_next_argument('new_password')
|