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