aspera-cli 4.4.0 → 4.5.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
- data/README.md +1042 -787
- data/bin/ascli +1 -1
- data/bin/asession +3 -5
- data/docs/Makefile +4 -7
- data/docs/README.erb.md +988 -740
- data/examples/faspex4.rb +4 -6
- data/examples/transfer.rb +2 -2
- data/lib/aspera/aoc.rb +139 -118
- data/lib/aspera/cli/listener/progress_multi.rb +5 -5
- data/lib/aspera/cli/main.rb +64 -34
- data/lib/aspera/cli/manager.rb +19 -20
- data/lib/aspera/cli/plugin.rb +9 -1
- data/lib/aspera/cli/plugins/aoc.rb +156 -143
- data/lib/aspera/cli/plugins/ats.rb +11 -10
- data/lib/aspera/cli/plugins/bss.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +236 -112
- data/lib/aspera/cli/plugins/faspex.rb +29 -7
- data/lib/aspera/cli/plugins/faspex5.rb +21 -8
- data/lib/aspera/cli/plugins/node.rb +21 -9
- data/lib/aspera/cli/plugins/orchestrator.rb +5 -3
- data/lib/aspera/cli/plugins/preview.rb +2 -2
- data/lib/aspera/cli/plugins/server.rb +3 -3
- data/lib/aspera/cli/plugins/shares.rb +17 -0
- data/lib/aspera/cli/transfer_agent.rb +47 -85
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/environment.rb +4 -4
- data/lib/aspera/fasp/{manager.rb → agent_base.rb} +7 -6
- data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +46 -39
- data/lib/aspera/fasp/{local.rb → agent_direct.rb} +14 -17
- data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +4 -4
- data/lib/aspera/fasp/{node.rb → agent_node.rb} +25 -8
- data/lib/aspera/fasp/agent_trsdk.rb +106 -0
- data/lib/aspera/fasp/default.rb +17 -0
- data/lib/aspera/fasp/installation.rb +64 -48
- data/lib/aspera/fasp/parameters.rb +7 -3
- data/lib/aspera/faspex_gw.rb +6 -6
- data/lib/aspera/keychain/encrypted_hash.rb +120 -0
- data/lib/aspera/keychain/macos_security.rb +94 -0
- data/lib/aspera/log.rb +45 -32
- data/lib/aspera/node.rb +3 -6
- data/lib/aspera/rest.rb +65 -49
- metadata +68 -27
- data/lib/aspera/api_detector.rb +0 -60
- data/lib/aspera/secrets.rb +0 -20
@@ -44,9 +44,7 @@ module Aspera
|
|
44
44
|
commands=[:create,:list,:show,:modify,:delete,:node,:cluster,:entitlement]
|
45
45
|
command=self.options.get_next_command(commands)
|
46
46
|
# those dont require access key id
|
47
|
-
unless [:create,:list].include?(command)
|
48
|
-
access_key_id=self.options.get_option(:id,:mandatory)
|
49
|
-
end
|
47
|
+
access_key_id=self.instance_identifier() unless [:create,:list].include?(command)
|
50
48
|
case command
|
51
49
|
when :create
|
52
50
|
params=self.options.get_option(:params,:optional) || {}
|
@@ -102,21 +100,24 @@ module Aspera
|
|
102
100
|
ak_data=ats_api_pub_v1.read("access_keys/#{access_key_id}")[:data]
|
103
101
|
server_data=@ats_api_pub.all_servers.select {|i| i['id'].start_with?(ak_data['transfer_server_id'])}.first
|
104
102
|
raise CliError,"no such server found" if server_data.nil?
|
103
|
+
base_url=server_data['transfer_setup_url']
|
105
104
|
api_node=Rest.new({
|
106
|
-
:base_url =>
|
105
|
+
:base_url => base_url,
|
107
106
|
:auth => {
|
108
107
|
:type => :basic,
|
109
108
|
:username => access_key_id,
|
110
|
-
:password => @agents[:
|
109
|
+
:password => @agents[:config].get_secret(url: base_url,username: access_key_id)
|
110
|
+
}})
|
111
111
|
command=self.options.get_next_command(Node::COMMON_ACTIONS)
|
112
112
|
return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action(command)
|
113
113
|
when :cluster
|
114
|
+
base_url=ats_api_pub_v1.params[:base_url]
|
114
115
|
rest_params={
|
115
|
-
:base_url =>
|
116
|
+
:base_url => base_url,
|
116
117
|
:auth => {
|
117
118
|
:type => :basic,
|
118
119
|
:username => access_key_id,
|
119
|
-
:password => @agents[:
|
120
|
+
:password => @agents[:config].get_secret(url: base_url, username: access_key_id)
|
120
121
|
}}
|
121
122
|
api_ak_auth=Rest.new(rest_params)
|
122
123
|
return {:type=>:single_object, :data=>api_ak_auth.read("servers")[:data]}
|
@@ -132,10 +133,10 @@ module Aspera
|
|
132
133
|
when :list
|
133
134
|
return {:type=>:object_list, :data=>@ats_api_pub.all_servers, :fields=>['id','cloud','region']}
|
134
135
|
when :show
|
135
|
-
|
136
|
-
if server_id.nil?
|
136
|
+
if self.options.get_option(:cloud,:optional) or self.options.get_option(:region,:optional)
|
137
137
|
server_data=server_by_cloud_region
|
138
138
|
else
|
139
|
+
server_id=instance_identifier()
|
139
140
|
server_data=@ats_api_pub.all_servers.select {|i| i['id'].eql?(server_id)}.first
|
140
141
|
raise "no such server id" if server_data.nil?
|
141
142
|
end
|
@@ -160,7 +161,7 @@ module Aspera
|
|
160
161
|
def execute_action_api_key
|
161
162
|
command=self.options.get_next_command([:instances, :create, :list, :show, :delete])
|
162
163
|
if [:show,:delete].include?(command)
|
163
|
-
concerned_id=self.
|
164
|
+
concerned_id=self.instance_identifier()
|
164
165
|
end
|
165
166
|
rest_add_header={}
|
166
167
|
if !command.eql?(:instances)
|
@@ -46,7 +46,7 @@ module Aspera
|
|
46
46
|
# give fields to keep order
|
47
47
|
return {:type=>:object_list, :data=>result['data'][object],:fields=> FIELDS['bssSubscriptions']}
|
48
48
|
when :show
|
49
|
-
id = self.
|
49
|
+
id = self.instance_identifier()
|
50
50
|
request={
|
51
51
|
'variables'=>{'id'=>id},
|
52
52
|
'query'=>"query($id: ID!) {#{object}(id: $id) { #{all_fields('bssSubscriptions')} roleAssignments(uniqueSubjectId: true) { id subjectId } instances { id state planId serviceId ssmSubscriptionId entitlement { id } aocOrganization { id subdomainName name status tier urlId trialExpiresAt users(organizationAdmin: true) { id name email atsAdmin subscriptionAdmin } } } } }"
|
@@ -55,7 +55,7 @@ module Aspera
|
|
55
55
|
result.delete('instances')
|
56
56
|
return {:type=>:single_object, :data=>result}
|
57
57
|
when :instances
|
58
|
-
id = self.
|
58
|
+
id = self.instance_identifier()
|
59
59
|
request={
|
60
60
|
'variables'=>{'id'=>id},
|
61
61
|
'query'=>"query($id: ID!) {#{object}(id: $id) { aocOrganization { id subdomainName name status tier urlId trialExpiresAt } } } }"
|
@@ -2,7 +2,6 @@ require 'aspera/cli/basic_auth_plugin'
|
|
2
2
|
require 'aspera/cli/extended_value'
|
3
3
|
require 'aspera/fasp/installation'
|
4
4
|
require 'aspera/fasp/parameters'
|
5
|
-
require 'aspera/api_detector'
|
6
5
|
require 'aspera/open_application'
|
7
6
|
require 'aspera/aoc'
|
8
7
|
require 'aspera/proxy_auto_config'
|
@@ -10,6 +9,8 @@ require 'aspera/uri_reader'
|
|
10
9
|
require 'aspera/rest'
|
11
10
|
require 'aspera/persistency_action_once'
|
12
11
|
require 'aspera/id_generator'
|
12
|
+
require 'aspera/keychain/encrypted_hash'
|
13
|
+
require 'aspera/keychain/macos_security'
|
13
14
|
require 'xmlsimple'
|
14
15
|
require 'base64'
|
15
16
|
require 'net/smtp'
|
@@ -31,6 +32,7 @@ module Aspera
|
|
31
32
|
CONF_PRESET_VERSION='version'
|
32
33
|
CONF_PRESET_DEFAULT='default'
|
33
34
|
CONF_PRESET_GLOBAL='global_common_defaults'
|
35
|
+
CONF_PRESET_SECRETS='default_secrets'
|
34
36
|
CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
|
35
37
|
CONF_GLOBAL_SYM = :config
|
36
38
|
# old tool name
|
@@ -48,7 +50,7 @@ module Aspera
|
|
48
50
|
SERVER_COMMAND='server'
|
49
51
|
CONNECT_WEB_URL = 'https://d3gcli72yxqn2z.cloudfront.net/connect'
|
50
52
|
CONNECT_VERSIONS = 'connectversions.js'
|
51
|
-
TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/
|
53
|
+
TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/aspera_transfer_sdk'
|
52
54
|
DEMO='demo'
|
53
55
|
DEMO_SERVER_PRESET='demoserver'
|
54
56
|
AOC_PATH_API_CLIENTS='admin/api-clients'
|
@@ -69,7 +71,7 @@ END_OF_TEMPLATE
|
|
69
71
|
:CONF_PRESET_GLOBAL,:PROGRAM_NAME_V1,:PROGRAM_NAME_V2,:DEFAULT_REDIRECT,:ASPERA_PLUGINS_FOLDERNAME,
|
70
72
|
:RUBY_FILE_EXT,:AOC_COMMAND_V1,:AOC_COMMAND_V2,:AOC_COMMAND_V3,:AOC_COMMAND_CURRENT,:DEMO,
|
71
73
|
:TRANSFER_SDK_ARCHIVE_URL,:AOC_PATH_API_CLIENTS,:DEMO_SERVER_PRESET,:EMAIL_TEST_TEMPLATE,:EXTV_INCLUDE_PRESETS,
|
72
|
-
:EXTV_PRESET,:DEFAULT_CHECK_NEW_VERSION_DAYS,:DEFAULT_PRIV_KEY_FILENAME,:SERVER_COMMAND
|
74
|
+
:EXTV_PRESET,:DEFAULT_CHECK_NEW_VERSION_DAYS,:DEFAULT_PRIV_KEY_FILENAME,:SERVER_COMMAND,:CONF_PRESET_SECRETS
|
73
75
|
def option_preset; nil; end
|
74
76
|
|
75
77
|
def option_preset=(value)
|
@@ -86,12 +88,12 @@ END_OF_TEMPLATE
|
|
86
88
|
|
87
89
|
def initialize(env,tool_name,help_url,version,main_folder)
|
88
90
|
super(env)
|
89
|
-
raise 'missing secret manager' if @agents[:secret].nil?
|
90
91
|
@plugins={}
|
91
92
|
@plugin_lookup_folders=[]
|
92
93
|
@use_plugin_defaults=true
|
93
94
|
@config_presets=nil
|
94
95
|
@connect_versions=nil
|
96
|
+
@vault=nil
|
95
97
|
@program_version=version
|
96
98
|
@tool_name=tool_name
|
97
99
|
@help_url=help_url
|
@@ -111,24 +113,25 @@ END_OF_TEMPLATE
|
|
111
113
|
# add preset handler (needed for smtp)
|
112
114
|
ExtendedValue.instance.set_handler(EXTV_PRESET,:reader,lambda{|v|preset_by_name(v)})
|
113
115
|
ExtendedValue.instance.set_handler(EXTV_INCLUDE_PRESETS,:decoder,lambda{|v|expanded_with_preset_includes(v)})
|
116
|
+
# load defaults before it can be overriden
|
117
|
+
self.add_plugin_default_preset(CONF_GLOBAL_SYM)
|
118
|
+
self.options.parse_options!
|
114
119
|
self.options.set_obj_attr(:ascp_path,self,:option_ascp_path)
|
115
120
|
self.options.set_obj_attr(:use_product,self,:option_use_product)
|
116
121
|
self.options.set_obj_attr(:preset,self,:option_preset)
|
117
|
-
self.options.set_obj_attr(:secret,@agents[:secret],:default_secret)
|
118
|
-
self.options.set_obj_attr(:secrets,@agents[:secret],:all_secrets)
|
119
122
|
self.options.add_opt_switch(:no_default,'-N','do not load default configuration for plugin') { @use_plugin_defaults=false }
|
120
123
|
self.options.add_opt_boolean(:override,'Wizard: override existing value')
|
121
124
|
self.options.add_opt_boolean(:use_generic_client,'Wizard: AoC: use global or org specific jwt client id')
|
122
125
|
self.options.add_opt_boolean(:default,'Wizard: set as default configuration for specified plugin (also: update)')
|
123
126
|
self.options.add_opt_boolean(:test_mode,'Wizard: skip private key check step')
|
127
|
+
self.options.add_opt_simple(:preset,'-PVALUE','load the named option preset from current config file')
|
124
128
|
self.options.add_opt_simple(:pkeypath,'Wizard: path to private key for JWT')
|
125
129
|
self.options.add_opt_simple(:ascp_path,'path to ascp')
|
126
130
|
self.options.add_opt_simple(:use_product,'use ascp from specified product')
|
127
131
|
self.options.add_opt_simple(:smtp,'smtp configuration (extended value: hash)')
|
128
132
|
self.options.add_opt_simple(:fpac,'proxy auto configuration URL')
|
129
|
-
self.options.add_opt_simple(:preset,'-PVALUE','load the named option preset from current config file')
|
130
133
|
self.options.add_opt_simple(:secret,'default secret')
|
131
|
-
self.options.add_opt_simple(:secrets,'secret
|
134
|
+
self.options.add_opt_simple(:secrets,'secret vault')
|
132
135
|
self.options.add_opt_simple(:sdk_url,'URL to get SDK')
|
133
136
|
self.options.add_opt_simple(:sdk_folder,'SDK folder path')
|
134
137
|
self.options.add_opt_simple(:notif_to,'email recipient for notification of transfers')
|
@@ -142,7 +145,6 @@ END_OF_TEMPLATE
|
|
142
145
|
self.options.set_option(:sdk_folder,File.join(@main_folder,'sdk'))
|
143
146
|
self.options.set_option(:override,:no)
|
144
147
|
self.options.parse_options!
|
145
|
-
raise CliBadArgument,'secrets shall be Hash' unless @agents[:secret].all_secrets.is_a?(Hash)
|
146
148
|
Fasp::Installation.instance.folder=self.options.get_option(:sdk_folder,:mandatory)
|
147
149
|
end
|
148
150
|
|
@@ -191,8 +193,8 @@ END_OF_TEMPLATE
|
|
191
193
|
# retrieve structure from cloud (CDN) with all versions available
|
192
194
|
def connect_versions
|
193
195
|
if @connect_versions.nil?
|
194
|
-
api_connect_cdn=Rest.new({:
|
195
|
-
javascript=api_connect_cdn.call({:
|
196
|
+
api_connect_cdn=Rest.new({base_url: CONNECT_WEB_URL})
|
197
|
+
javascript=api_connect_cdn.call({operation: 'GET',subpath: CONNECT_VERSIONS})
|
196
198
|
# get result on one line
|
197
199
|
connect_versions_javascript=javascript[:http].body.gsub(/\r?\n\s*/,'')
|
198
200
|
Log.log.debug("javascript=[\n#{connect_versions_javascript}\n]")
|
@@ -238,9 +240,9 @@ END_OF_TEMPLATE
|
|
238
240
|
|
239
241
|
# instanciate a plugin
|
240
242
|
# plugins must be Capitalized
|
241
|
-
def self.
|
243
|
+
def self.plugin_class(plugin_name_sym)
|
242
244
|
# Module.nesting[2] is Aspera::Cli
|
243
|
-
return Object::const_get("#{Module.nesting[2].to_s}::Plugins::#{plugin_name_sym.to_s.capitalize}")
|
245
|
+
return Object::const_get("#{Module.nesting[2].to_s}::Plugins::#{plugin_name_sym.to_s.capitalize}")
|
244
246
|
end
|
245
247
|
|
246
248
|
def self.flatten_all_config(t)
|
@@ -255,10 +257,15 @@ END_OF_TEMPLATE
|
|
255
257
|
|
256
258
|
# set parameter and value in global config
|
257
259
|
# creates one if none already created
|
258
|
-
# @return preset that contains global default
|
260
|
+
# @return preset name that contains global default
|
259
261
|
def set_global_default(key,value)
|
260
262
|
# get default preset if it exists
|
261
|
-
global_default_preset_name=get_plugin_default_config_name(CONF_GLOBAL_SYM)
|
263
|
+
global_default_preset_name=get_plugin_default_config_name(CONF_GLOBAL_SYM)
|
264
|
+
if global_default_preset_name.nil?
|
265
|
+
global_default_preset_name=CONF_PRESET_GLOBAL
|
266
|
+
@config_presets[CONF_PRESET_DEFAULT]||={}
|
267
|
+
@config_presets[CONF_PRESET_DEFAULT][CONF_GLOBAL_SYM.to_s]=global_default_preset_name
|
268
|
+
end
|
262
269
|
@config_presets[global_default_preset_name]||={}
|
263
270
|
@config_presets[global_default_preset_name][key.to_s]=value
|
264
271
|
self.format.display_status("Updated: #{global_default_preset_name}: #{key} <- #{value}")
|
@@ -446,14 +453,42 @@ END_OF_TEMPLATE
|
|
446
453
|
Log.log.warn("skipping plugin already registered: #{plugin_symbol}")
|
447
454
|
return
|
448
455
|
end
|
449
|
-
@plugins[plugin_symbol]={:
|
456
|
+
@plugins[plugin_symbol]={source: path,require_stanza: req}
|
457
|
+
end
|
458
|
+
|
459
|
+
def identify_plugin_for_url(url)
|
460
|
+
plugins.each do |plugin_name_sym,plugin_info|
|
461
|
+
next if plugin_name_sym.eql?(CONF_PLUGIN_SYM)
|
462
|
+
require plugin_info[:require_stanza]
|
463
|
+
c=self.class.plugin_class(plugin_name_sym)
|
464
|
+
if c.respond_to?(:detect)
|
465
|
+
current_url=url
|
466
|
+
begin
|
467
|
+
res=c.send(:detect,current_url)
|
468
|
+
return res.merge(product: plugin_name_sym, url: current_url) unless res.nil?
|
469
|
+
rescue
|
470
|
+
end
|
471
|
+
begin
|
472
|
+
# is there a redirect ?
|
473
|
+
result=Rest.new({:base_url=>url}).call({operation: 'GET',subpath: '',redirect_max: 1})
|
474
|
+
current_url=result[:http].uri.to_s
|
475
|
+
# check if redirect
|
476
|
+
if ! url.eql?(current_url)
|
477
|
+
res=c.send(:detect,current_url)
|
478
|
+
return res.merge(product: plugin_name_sym, url: current_url) unless res.nil?
|
479
|
+
end
|
480
|
+
rescue
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
raise "No known application found at #{url}"
|
450
485
|
end
|
451
486
|
|
452
487
|
def execute_connect_action
|
453
488
|
command=self.options.get_next_command([:list,:id])
|
454
489
|
case command
|
455
490
|
when :list
|
456
|
-
return {:
|
491
|
+
return {type: :object_list, data: connect_versions, fields: ['id','title','version']}
|
457
492
|
when :id
|
458
493
|
connect_id=self.options.get_next_argument('id or title')
|
459
494
|
one_res=connect_versions.select{|i|i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}.first
|
@@ -462,13 +497,13 @@ END_OF_TEMPLATE
|
|
462
497
|
case command
|
463
498
|
when :info # shows files used
|
464
499
|
one_res.delete('links')
|
465
|
-
return {:
|
500
|
+
return {type: :single_object, data: one_res}
|
466
501
|
when :links # shows files used
|
467
502
|
command=self.options.get_next_command([:list,:id])
|
468
503
|
all_links=one_res['links']
|
469
504
|
case command
|
470
505
|
when :list # shows files used
|
471
|
-
return {:
|
506
|
+
return {type: :object_list, data: all_links}
|
472
507
|
when :id
|
473
508
|
link_title=self.options.get_next_argument('title')
|
474
509
|
one_link=all_links.select {|i| i['title'].eql?(link_title)}.first
|
@@ -477,10 +512,10 @@ END_OF_TEMPLATE
|
|
477
512
|
when :download #
|
478
513
|
folder_dest=self.transfer.destination_folder('receive')
|
479
514
|
#folder_dest=self.options.get_next_argument('destination folder')
|
480
|
-
api_connect_cdn=Rest.new({:
|
515
|
+
api_connect_cdn=Rest.new({base_url: CONNECT_WEB_URL})
|
481
516
|
fileurl = one_link['href']
|
482
517
|
filename=fileurl.gsub(%r{.*/},'')
|
483
|
-
api_connect_cdn.call({:
|
518
|
+
api_connect_cdn.call({operation: 'GET',subpath: fileurl,save_to_file: File.join(folder_dest,filename)})
|
484
519
|
return Main.result_status("Downloaded: #{filename}")
|
485
520
|
when :open #
|
486
521
|
OpenApplication.instance.uri(one_link['href'])
|
@@ -503,7 +538,7 @@ END_OF_TEMPLATE
|
|
503
538
|
preset_name=set_global_default(:ascp_path,ascp_path)
|
504
539
|
return Main.result_status("Saved to default global preset #{preset_name}")
|
505
540
|
when :show # shows files used
|
506
|
-
return {:
|
541
|
+
return {type: :status, data: Fasp::Installation.instance.path(:ascp)}
|
507
542
|
when :info # shows files used
|
508
543
|
data=Fasp::Installation::FILES.inject({}) do |m,v|
|
509
544
|
m[v.to_s]=Fasp::Installation.instance.path(v) rescue 'Not Found'
|
@@ -528,12 +563,12 @@ END_OF_TEMPLATE
|
|
528
563
|
end
|
529
564
|
end
|
530
565
|
data['keypass']=Fasp::Installation.instance.bypass_pass
|
531
|
-
return {:
|
566
|
+
return {type: :single_object, data: data}
|
532
567
|
when :products
|
533
568
|
command=self.options.get_next_command([:list,:use])
|
534
569
|
case command
|
535
570
|
when :list
|
536
|
-
return {:
|
571
|
+
return {type: :object_list, data: Fasp::Installation.instance.installed_products, fields: ['name','app_root']}
|
537
572
|
when :use
|
538
573
|
default_product=self.options.get_next_argument('product name')
|
539
574
|
Fasp::Installation.instance.use_ascp_from_product(default_product)
|
@@ -549,109 +584,122 @@ END_OF_TEMPLATE
|
|
549
584
|
raise "unexpected case: #{command}"
|
550
585
|
end
|
551
586
|
|
552
|
-
|
587
|
+
# legacy actions available globally
|
588
|
+
PRESET_GBL_ACTIONS=[:list,:overview].freeze
|
589
|
+
# require existing preset
|
590
|
+
PRESET_EXST_ACTIONS=[:show,:delete,:get,:unset].freeze
|
591
|
+
# require id
|
592
|
+
PRESET_INSTANCE_ACTIONS=[PRESET_EXST_ACTIONS,:initialize,:update,:ask,:set].flatten.freeze
|
593
|
+
PRESET_ALL_ACTIONS=[PRESET_GBL_ACTIONS,PRESET_INSTANCE_ACTIONS].flatten.freeze
|
594
|
+
|
595
|
+
def execute_file_action(action,config_name)
|
596
|
+
action=self.options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
|
597
|
+
config_name=instance_identifier() if config_name.nil? and PRESET_INSTANCE_ACTIONS.include?(action)
|
598
|
+
# those operations require existing option
|
599
|
+
raise "no such preset: #{config_name}" if PRESET_EXST_ACTIONS.include?(action) and !@config_presets.has_key?(config_name)
|
600
|
+
selected_preset=@config_presets[config_name]
|
601
|
+
case action
|
602
|
+
when :list
|
603
|
+
return {type: :value_list, data: @config_presets.keys, name: 'name'}
|
604
|
+
when :overview
|
605
|
+
return {type: :object_list, data: self.class.flatten_all_config(@config_presets)}
|
606
|
+
when :show
|
607
|
+
raise "no such config: #{config_name}" if selected_preset.nil?
|
608
|
+
return {type: :single_object, data: selected_preset}
|
609
|
+
when :delete
|
610
|
+
@config_presets.delete(config_name)
|
611
|
+
save_presets_to_config_file
|
612
|
+
return Main.result_status("Deleted: #{config_name}")
|
613
|
+
when :get
|
614
|
+
param_name=self.options.get_next_argument('parameter name')
|
615
|
+
value=selected_preset[param_name]
|
616
|
+
raise "no such option in preset #{config_name} : #{param_name}" if value.nil?
|
617
|
+
case value
|
618
|
+
when Numeric,String; return {type: :text, data: ExtendedValue.instance.evaluate(value.to_s)}
|
619
|
+
end
|
620
|
+
return {type: :single_object, data: value}
|
621
|
+
when :unset
|
622
|
+
param_name=self.options.get_next_argument('parameter name')
|
623
|
+
selected_preset.delete(param_name)
|
624
|
+
save_presets_to_config_file
|
625
|
+
return Main.result_status("Removed: #{config_name}: #{param_name}")
|
626
|
+
when :set
|
627
|
+
param_name=self.options.get_next_argument('parameter name')
|
628
|
+
param_value=self.options.get_next_argument('parameter value')
|
629
|
+
if !@config_presets.has_key?(config_name)
|
630
|
+
Log.log.debug("no such config name: #{config_name}, initializing")
|
631
|
+
selected_preset=@config_presets[config_name]={}
|
632
|
+
end
|
633
|
+
if selected_preset.has_key?(param_name)
|
634
|
+
Log.log.warn("overwriting value: #{selected_preset[param_name]}")
|
635
|
+
end
|
636
|
+
selected_preset[param_name]=param_value
|
637
|
+
save_presets_to_config_file
|
638
|
+
return Main.result_status("Updated: #{config_name}: #{param_name} <- #{param_value}")
|
639
|
+
when :initialize
|
640
|
+
config_value=self.options.get_next_argument('extended value (Hash)')
|
641
|
+
if @config_presets.has_key?(config_name)
|
642
|
+
Log.log.warn("configuration already exists: #{config_name}, overwriting")
|
643
|
+
end
|
644
|
+
@config_presets[config_name]=config_value
|
645
|
+
save_presets_to_config_file
|
646
|
+
return Main.result_status("Modified: #{@option_config_file}")
|
647
|
+
when :update
|
648
|
+
# get unprocessed options
|
649
|
+
theopts=self.options.get_options_table
|
650
|
+
Log.log.debug("opts=#{theopts}")
|
651
|
+
@config_presets[config_name]||={}
|
652
|
+
@config_presets[config_name].merge!(theopts)
|
653
|
+
# fix bug in 4.4 (creating key "true" in "default" preset)
|
654
|
+
@config_presets[CONF_PRESET_DEFAULT].delete(true) if @config_presets[CONF_PRESET_DEFAULT].is_a?(Hash)
|
655
|
+
save_presets_to_config_file
|
656
|
+
return Main.result_status("Updated: #{config_name}")
|
657
|
+
when :ask
|
658
|
+
self.options.ask_missing_mandatory=:yes
|
659
|
+
@config_presets[config_name]||={}
|
660
|
+
self.options.get_next_argument('option names',:multiple).each do |optionname|
|
661
|
+
option_value=self.options.get_interactive(:option,optionname)
|
662
|
+
@config_presets[config_name][optionname]=option_value
|
663
|
+
end
|
664
|
+
save_presets_to_config_file
|
665
|
+
return Main.result_status("Updated: #{config_name}")
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
ACTIONS=[PRESET_GBL_ACTIONS,:id,:preset,:open,:documentation,:genkey,:gem_path,:plugins,:flush_tokens,:echo,:wizard,:export_to_cli,:detect,:coffee,:ascp,:email_test,:smtp_settings,:proxy_check,:folder,:file,:check_update,:initdemo,:vault].flatten.freeze
|
553
670
|
|
554
671
|
# "config" plugin
|
555
672
|
def execute_action
|
556
673
|
action=self.options.get_next_command(ACTIONS)
|
557
674
|
case action
|
558
|
-
when
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
return {:type=>:single_object,:data=>selected_preset}
|
568
|
-
when :delete
|
569
|
-
@config_presets.delete(config_name)
|
570
|
-
save_presets_to_config_file
|
571
|
-
return Main.result_status("Deleted: #{config_name}")
|
572
|
-
when :get
|
573
|
-
param_name=self.options.get_next_argument('parameter name')
|
574
|
-
value=selected_preset[param_name]
|
575
|
-
raise "no such option in preset #{config_name} : #{param_name}" if value.nil?
|
576
|
-
case value
|
577
|
-
when Numeric,String; return {:type=>:text,:data=>ExtendedValue.instance.evaluate(value.to_s)}
|
578
|
-
end
|
579
|
-
return {:type=>:single_object,:data=>value}
|
580
|
-
when :unset
|
581
|
-
param_name=self.options.get_next_argument('parameter name')
|
582
|
-
selected_preset.delete(param_name)
|
583
|
-
save_presets_to_config_file
|
584
|
-
return Main.result_status("Removed: #{config_name}: #{param_name}")
|
585
|
-
when :set
|
586
|
-
param_name=self.options.get_next_argument('parameter name')
|
587
|
-
param_value=self.options.get_next_argument('parameter value')
|
588
|
-
if !@config_presets.has_key?(config_name)
|
589
|
-
Log.log.debug("no such config name: #{config_name}, initializing")
|
590
|
-
selected_preset=@config_presets[config_name]={}
|
591
|
-
end
|
592
|
-
if selected_preset.has_key?(param_name)
|
593
|
-
Log.log.warn("overwriting value: #{selected_preset[param_name]}")
|
594
|
-
end
|
595
|
-
selected_preset[param_name]=param_value
|
596
|
-
save_presets_to_config_file
|
597
|
-
return Main.result_status("Updated: #{config_name}: #{param_name} <- #{param_value}")
|
598
|
-
when :initialize
|
599
|
-
config_value=self.options.get_next_argument('extended value (Hash)')
|
600
|
-
if @config_presets.has_key?(config_name)
|
601
|
-
Log.log.warn("configuration already exists: #{config_name}, overwriting")
|
602
|
-
end
|
603
|
-
@config_presets[config_name]=config_value
|
604
|
-
save_presets_to_config_file
|
605
|
-
return Main.result_status("Modified: #{@option_config_file}")
|
606
|
-
when :update
|
607
|
-
default_for_plugin=self.options.get_option(:default,:mandatory)
|
608
|
-
# get unprocessed options
|
609
|
-
theopts=self.options.get_options_table
|
610
|
-
Log.log.debug("opts=#{theopts}")
|
611
|
-
@config_presets[config_name]||={}
|
612
|
-
@config_presets[config_name].merge!(theopts)
|
613
|
-
if ! default_for_plugin.nil?
|
614
|
-
@config_presets[CONF_PRESET_DEFAULT]||=Hash.new
|
615
|
-
@config_presets[CONF_PRESET_DEFAULT][default_for_plugin]=config_name
|
616
|
-
end
|
617
|
-
save_presets_to_config_file
|
618
|
-
return Main.result_status("Updated: #{config_name}")
|
619
|
-
when :ask
|
620
|
-
self.options.ask_missing_mandatory=:yes
|
621
|
-
@config_presets[config_name]||={}
|
622
|
-
self.options.get_next_argument('option names',:multiple).each do |optionname|
|
623
|
-
option_value=self.options.get_interactive(:option,optionname)
|
624
|
-
@config_presets[config_name][optionname]=option_value
|
625
|
-
end
|
626
|
-
save_presets_to_config_file
|
627
|
-
return Main.result_status("Updated: #{config_name}")
|
628
|
-
end
|
675
|
+
when *PRESET_GBL_ACTIONS # older syntax
|
676
|
+
return execute_file_action(action,nil)
|
677
|
+
when :id # older syntax
|
678
|
+
return execute_file_action(nil,self.options.get_next_argument('config name'))
|
679
|
+
when :preset # newer syntax
|
680
|
+
return execute_file_action(nil,nil)
|
681
|
+
when :open
|
682
|
+
OpenApplication.instance.uri("#{@option_config_file}") #file://
|
683
|
+
return Main.result_nothing
|
629
684
|
when :documentation
|
630
685
|
section=options.get_next_argument('private key file path',:single,:optional)
|
631
686
|
section='#'+section unless section.nil?
|
632
687
|
OpenApplication.instance.uri("#{@help_url}#{section}")
|
633
688
|
return Main.result_nothing
|
634
|
-
when :open
|
635
|
-
OpenApplication.instance.uri("#{@option_config_file}") #file://
|
636
|
-
return Main.result_nothing
|
637
689
|
when :genkey # generate new rsa key
|
638
690
|
private_key_path=self.options.get_next_argument('private key file path')
|
639
691
|
generate_rsa_private_key(private_key_path,DEFAULT_PRIVKEY_LENGTH)
|
640
692
|
return Main.result_status('Generated key: '+private_key_path)
|
641
693
|
when :echo # display the content of a value given on command line
|
642
|
-
result={:
|
694
|
+
result={type: :other_struct, data: self.options.get_next_argument('value')}
|
643
695
|
# special for csv
|
644
696
|
result[:type]=:object_list if result[:data].is_a?(Array) and result[:data].first.is_a?(Hash)
|
645
697
|
return result
|
646
698
|
when :flush_tokens
|
647
699
|
deleted_files=Oauth.flush_tokens
|
648
|
-
return {:
|
700
|
+
return {type: :value_list, data: deleted_files, name: 'file'}
|
649
701
|
when :plugins
|
650
|
-
return {:data
|
651
|
-
when :list
|
652
|
-
return {:data => @config_presets.keys, :type => :value_list, :name => 'name'}
|
653
|
-
when :overview
|
654
|
-
return {:type=>:object_list,:data=>self.class.flatten_all_config(@config_presets)}
|
702
|
+
return {type: :object_list, data: @plugins.keys.map { |i| { 'plugin' => i.to_s, 'path' => @plugins[i][:source] } } , fields: ['plugin','path']}
|
655
703
|
when :wizard
|
656
704
|
# interactive mode
|
657
705
|
self.options.ask_missing_mandatory=true
|
@@ -661,7 +709,7 @@ END_OF_TEMPLATE
|
|
661
709
|
instance_url=self.options.get_option(:url,:mandatory)
|
662
710
|
# allow user to tell the preset name
|
663
711
|
preset_name=self.options.get_option(:id,:optional)
|
664
|
-
appli=
|
712
|
+
appli=identify_plugin_for_url(instance_url)
|
665
713
|
plugin_name="<replace per app>"
|
666
714
|
test_args="<replace per app>"
|
667
715
|
case appli[:product]
|
@@ -704,7 +752,7 @@ END_OF_TEMPLATE
|
|
704
752
|
# make username mandatory for jwt, this triggers interactive input
|
705
753
|
self.options.get_option(:username,:mandatory)
|
706
754
|
# instanciate AoC plugin, so that command line options are known
|
707
|
-
files_plugin=self.class.
|
755
|
+
files_plugin=self.class.plugin_class(plugin_name).new(@agents.merge({skip_basic_auth_options: true, private_key_path: private_key_path}))
|
708
756
|
aoc_api=files_plugin.get_api
|
709
757
|
auto_set_pub_key=false
|
710
758
|
auto_set_jwt=false
|
@@ -786,7 +834,7 @@ END_OF_TEMPLATE
|
|
786
834
|
# need url / username
|
787
835
|
add_plugin_default_preset(AOC_COMMAND_V3.to_sym)
|
788
836
|
# instanciate AoC plugin
|
789
|
-
files_plugin=self.class.
|
837
|
+
files_plugin=self.class.plugin_class(AOC_COMMAND_CURRENT).new(@agents) # TODO: is this line needed ?
|
790
838
|
url=self.options.get_option(:url,:mandatory)
|
791
839
|
cli_conf_file=Fasp::Installation.instance.cli_conf_file
|
792
840
|
data=JSON.parse(File.read(cli_conf_file))
|
@@ -818,7 +866,7 @@ END_OF_TEMPLATE
|
|
818
866
|
when :detect
|
819
867
|
# need url / username
|
820
868
|
BasicAuthPlugin.new(@agents)
|
821
|
-
return Main.result_status("Found: #{
|
869
|
+
return Main.result_status("Found: #{identify_plugin_for_url(self.options.get_option(:url,:mandatory))}")
|
822
870
|
when :coffee
|
823
871
|
OpenApplication.instance.uri('https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg')
|
824
872
|
return Main.result_nothing
|
@@ -834,13 +882,13 @@ END_OF_TEMPLATE
|
|
834
882
|
send_email_template({},EMAIL_TEST_TEMPLATE)
|
835
883
|
return Main.result_nothing
|
836
884
|
when :smtp_settings
|
837
|
-
return {:
|
885
|
+
return {type: :single_object, data: email_settings}
|
838
886
|
when :proxy_check
|
839
887
|
pac_url=self.options.get_option(:fpac,:mandatory)
|
840
888
|
server_url=self.options.get_next_argument('server url')
|
841
889
|
return Main.result_status(Aspera::ProxyAutoConfig.new(UriReader.read(pac_url)).get_proxy(server_url))
|
842
890
|
when :check_update
|
843
|
-
return {:
|
891
|
+
return {type: :single_object, data: check_gem_version}
|
844
892
|
when :initdemo
|
845
893
|
if @config_presets.has_key?(DEMO_SERVER_PRESET)
|
846
894
|
Log.log.warn("Demo server preset already present: #{DEMO_SERVER_PRESET}")
|
@@ -858,6 +906,46 @@ END_OF_TEMPLATE
|
|
858
906
|
end
|
859
907
|
save_presets_to_config_file
|
860
908
|
return Main.result_status("Done")
|
909
|
+
when :vault
|
910
|
+
command=self.options.get_next_command([:init,:list,:get,:set,:delete])
|
911
|
+
case command
|
912
|
+
when :init
|
913
|
+
type=self.options.get_option(:value,:optional)
|
914
|
+
case type
|
915
|
+
when 'config',NilClass
|
916
|
+
raise "default secrets already exists" if @config_presets.has_key?(CONF_PRESET_SECRETS)
|
917
|
+
@config_presets[CONF_PRESET_SECRETS]={}
|
918
|
+
set_global_default(:secrets,"@preset:#{CONF_PRESET_SECRETS}")
|
919
|
+
else raise "no such vault type"
|
920
|
+
end
|
921
|
+
return Main.result_status("Done")
|
922
|
+
when :list
|
923
|
+
return {type: :object_list, data: vault.list}
|
924
|
+
when :set
|
925
|
+
# register url option
|
926
|
+
BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
|
927
|
+
username=self.options.get_option(:username,:mandatory)
|
928
|
+
url=self.options.get_option(:url,:mandatory)
|
929
|
+
description=self.options.get_option(:value,:optional)
|
930
|
+
secret=self.options.get_next_argument('secret')
|
931
|
+
vault.set(username: username, url: url, description: description, secret: secret)
|
932
|
+
save_presets_to_config_file if vault.is_a?(Keychain::EncryptedHash)
|
933
|
+
return Main.result_status("Done")
|
934
|
+
when :get
|
935
|
+
# register url option
|
936
|
+
BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
|
937
|
+
username=self.options.get_option(:username,:mandatory)
|
938
|
+
url=self.options.get_option(:url,:optional)
|
939
|
+
result=vault.get(username: username, url: url)
|
940
|
+
return {type: :single_object, data: result}
|
941
|
+
when :delete
|
942
|
+
# register url option
|
943
|
+
BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
|
944
|
+
username=self.options.get_option(:username,:mandatory)
|
945
|
+
url=self.options.get_option(:url,:optional)
|
946
|
+
result=vault.delete(username: username, url: url)
|
947
|
+
return Main.result_status("Done")
|
948
|
+
end
|
861
949
|
else raise 'INTERNAL ERROR: wrong case'
|
862
950
|
end
|
863
951
|
end
|
@@ -941,6 +1029,42 @@ END_OF_TEMPLATE
|
|
941
1029
|
return nil
|
942
1030
|
end # get_plugin_default_config_name
|
943
1031
|
|
1032
|
+
def vault
|
1033
|
+
if @vault.nil?
|
1034
|
+
vault_info=self.options.get_option(:secrets,:optional)
|
1035
|
+
case vault_info
|
1036
|
+
when Hash
|
1037
|
+
@vault=Keychain::EncryptedHash.new(vault_info)
|
1038
|
+
when /^system/
|
1039
|
+
name=vault_info.start_with?('system:') ? vault_info[7..-1] : nil
|
1040
|
+
case Environment.os
|
1041
|
+
when Environment::OS_X
|
1042
|
+
@vault=Keychain::MacosSecurity.new(name)
|
1043
|
+
when Environment::OS_WINDOWS,Environment::OS_LINUX,Environment::OS_AIX
|
1044
|
+
raise "not implemented"
|
1045
|
+
else raise "Error"
|
1046
|
+
end
|
1047
|
+
when NilClass
|
1048
|
+
# keep nil
|
1049
|
+
else
|
1050
|
+
raise CliBadArgument,'secrets shall be Hash'
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
raise "No vault defined" if @vault.nil?
|
1054
|
+
@vault
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def get_secret(options)
|
1058
|
+
raise "options shall be Hash" unless options.is_a?(Hash)
|
1059
|
+
raise "options shall have username" unless options.has_key?(:username)
|
1060
|
+
secret=self.options.get_option(:secret,:optional)
|
1061
|
+
if secret.nil?
|
1062
|
+
secret=vault.get(options) rescue nil
|
1063
|
+
# mandatory by default
|
1064
|
+
raise "please provide secret for #{options[:username]}" if secret.nil? and ( options[:mandatory].nil? or options[:mandatory] )
|
1065
|
+
end
|
1066
|
+
return secret
|
1067
|
+
end
|
944
1068
|
end
|
945
1069
|
end
|
946
1070
|
end
|