aspera-cli 4.17.0 → 4.18.0
Sign up to get free protection for your applications and to get access to all the features.
- 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')
|