aspera-cli 4.4.0 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2095 -1503
  3. data/bin/ascli +2 -1
  4. data/bin/asession +4 -5
  5. data/docs/test_env.conf +3 -0
  6. data/examples/aoc.rb +4 -3
  7. data/examples/faspex4.rb +25 -25
  8. data/examples/proxy.pac +1 -1
  9. data/examples/transfer.rb +17 -17
  10. data/lib/aspera/aoc.rb +238 -185
  11. data/lib/aspera/ascmd.rb +93 -83
  12. data/lib/aspera/ats_api.rb +11 -10
  13. data/lib/aspera/cli/basic_auth_plugin.rb +13 -14
  14. data/lib/aspera/cli/extended_value.rb +42 -33
  15. data/lib/aspera/cli/formater.rb +142 -108
  16. data/lib/aspera/cli/info.rb +17 -0
  17. data/lib/aspera/cli/listener/line_dump.rb +3 -2
  18. data/lib/aspera/cli/listener/logger.rb +2 -1
  19. data/lib/aspera/cli/listener/progress.rb +16 -18
  20. data/lib/aspera/cli/listener/progress_multi.rb +18 -21
  21. data/lib/aspera/cli/main.rb +173 -149
  22. data/lib/aspera/cli/manager.rb +163 -168
  23. data/lib/aspera/cli/plugin.rb +43 -31
  24. data/lib/aspera/cli/plugins/alee.rb +6 -6
  25. data/lib/aspera/cli/plugins/aoc.rb +405 -370
  26. data/lib/aspera/cli/plugins/ats.rb +86 -79
  27. data/lib/aspera/cli/plugins/bss.rb +14 -16
  28. data/lib/aspera/cli/plugins/config.rb +580 -362
  29. data/lib/aspera/cli/plugins/console.rb +23 -19
  30. data/lib/aspera/cli/plugins/cos.rb +18 -18
  31. data/lib/aspera/cli/plugins/faspex.rb +201 -158
  32. data/lib/aspera/cli/plugins/faspex5.rb +80 -57
  33. data/lib/aspera/cli/plugins/node.rb +183 -166
  34. data/lib/aspera/cli/plugins/orchestrator.rb +71 -67
  35. data/lib/aspera/cli/plugins/preview.rb +92 -96
  36. data/lib/aspera/cli/plugins/server.rb +79 -75
  37. data/lib/aspera/cli/plugins/shares.rb +35 -19
  38. data/lib/aspera/cli/plugins/sync.rb +20 -22
  39. data/lib/aspera/cli/transfer_agent.rb +76 -113
  40. data/lib/aspera/cli/version.rb +2 -1
  41. data/lib/aspera/colors.rb +35 -27
  42. data/lib/aspera/command_line_builder.rb +48 -34
  43. data/lib/aspera/cos_node.rb +29 -21
  44. data/lib/aspera/data_repository.rb +3 -2
  45. data/lib/aspera/environment.rb +50 -45
  46. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +28 -25
  47. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +52 -43
  48. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +58 -72
  49. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +37 -43
  50. data/lib/aspera/fasp/{node.rb → agent_node.rb} +35 -16
  51. data/lib/aspera/fasp/agent_trsdk.rb +104 -0
  52. data/lib/aspera/fasp/error.rb +2 -1
  53. data/lib/aspera/fasp/error_info.rb +68 -52
  54. data/lib/aspera/fasp/installation.rb +152 -124
  55. data/lib/aspera/fasp/listener.rb +1 -0
  56. data/lib/aspera/fasp/parameters.rb +87 -92
  57. data/lib/aspera/fasp/parameters.yaml +305 -249
  58. data/lib/aspera/fasp/resume_policy.rb +11 -14
  59. data/lib/aspera/fasp/transfer_spec.rb +26 -0
  60. data/lib/aspera/fasp/uri.rb +22 -21
  61. data/lib/aspera/faspex_gw.rb +55 -89
  62. data/lib/aspera/hash_ext.rb +4 -3
  63. data/lib/aspera/id_generator.rb +8 -7
  64. data/lib/aspera/keychain/encrypted_hash.rb +121 -0
  65. data/lib/aspera/keychain/macos_security.rb +90 -0
  66. data/lib/aspera/log.rb +55 -37
  67. data/lib/aspera/nagios.rb +13 -12
  68. data/lib/aspera/node.rb +30 -25
  69. data/lib/aspera/oauth.rb +175 -226
  70. data/lib/aspera/open_application.rb +4 -3
  71. data/lib/aspera/persistency_action_once.rb +6 -6
  72. data/lib/aspera/persistency_folder.rb +5 -9
  73. data/lib/aspera/preview/file_types.rb +6 -5
  74. data/lib/aspera/preview/generator.rb +25 -24
  75. data/lib/aspera/preview/options.rb +16 -14
  76. data/lib/aspera/preview/utils.rb +98 -98
  77. data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
  78. data/lib/aspera/proxy_auto_config.rb +111 -20
  79. data/lib/aspera/rest.rb +154 -135
  80. data/lib/aspera/rest_call_error.rb +2 -2
  81. data/lib/aspera/rest_error_analyzer.rb +23 -25
  82. data/lib/aspera/rest_errors_aspera.rb +15 -14
  83. data/lib/aspera/ssh.rb +12 -10
  84. data/lib/aspera/sync.rb +42 -41
  85. data/lib/aspera/temp_file_manager.rb +18 -14
  86. data/lib/aspera/timer_limiter.rb +2 -1
  87. data/lib/aspera/uri_reader.rb +7 -5
  88. data/lib/aspera/web_auth.rb +79 -76
  89. metadata +116 -29
  90. data/docs/Makefile +0 -66
  91. data/docs/README.erb.md +0 -3973
  92. data/docs/README.md +0 -13
  93. data/docs/diagrams.txt +0 -49
  94. data/docs/doc_tools.rb +0 -58
  95. data/lib/aspera/api_detector.rb +0 -60
  96. data/lib/aspera/cli/plugins/shares2.rb +0 -114
  97. data/lib/aspera/secrets.rb +0 -20
@@ -1,15 +1,19 @@
1
+ # frozen_string_literal: true
1
2
  require 'aspera/cli/basic_auth_plugin'
2
3
  require 'aspera/cli/extended_value'
4
+ require 'aspera/cli/version'
3
5
  require 'aspera/fasp/installation'
4
6
  require 'aspera/fasp/parameters'
5
- require 'aspera/api_detector'
6
- require 'aspera/open_application'
7
- require 'aspera/aoc'
7
+ require 'aspera/fasp/transfer_spec'
8
+ require 'aspera/fasp/error_info'
8
9
  require 'aspera/proxy_auto_config'
9
- require 'aspera/uri_reader'
10
- require 'aspera/rest'
10
+ require 'aspera/open_application'
11
11
  require 'aspera/persistency_action_once'
12
12
  require 'aspera/id_generator'
13
+ require 'aspera/keychain/encrypted_hash'
14
+ require 'aspera/keychain/macos_security'
15
+ require 'aspera/aoc'
16
+ require 'aspera/rest'
13
17
  require 'xmlsimple'
14
18
  require 'base64'
15
19
  require 'net/smtp'
@@ -31,6 +35,7 @@ module Aspera
31
35
  CONF_PRESET_VERSION='version'
32
36
  CONF_PRESET_DEFAULT='default'
33
37
  CONF_PRESET_GLOBAL='global_common_defaults'
38
+ CONF_PRESET_SECRETS='default_secrets' # pragma: allowlist secret
34
39
  CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
35
40
  CONF_GLOBAL_SYM = :config
36
41
  # old tool name
@@ -48,113 +53,127 @@ module Aspera
48
53
  SERVER_COMMAND='server'
49
54
  CONNECT_WEB_URL = 'https://d3gcli72yxqn2z.cloudfront.net/connect'
50
55
  CONNECT_VERSIONS = 'connectversions.js'
51
- TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/aspera_sdk'
56
+ TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/aspera_transfer_sdk'
52
57
  DEMO='demo'
53
58
  DEMO_SERVER_PRESET='demoserver'
54
59
  AOC_PATH_API_CLIENTS='admin/api-clients'
55
- EMAIL_TEST_TEMPLATE=<<END_OF_TEMPLATE
56
- From: <%=from_name%> <<%=from_email%>>
57
- To: <<%=to%>>
58
- Subject: Amelia email test
60
+ EMAIL_TEST_TEMPLATE=<<~END_OF_TEMPLATE
61
+ From: <%=from_name%> <<%=from_email%>>
62
+ To: <<%=to%>>
63
+ Subject: Amelia email test
59
64
 
60
- It worked !
61
- END_OF_TEMPLATE
65
+ It worked !
66
+ END_OF_TEMPLATE
62
67
  # special extended values
63
- EXTV_INCLUDE_PRESETS='incps'
64
- EXTV_PRESET='preset'
68
+ EXTV_INCLUDE_PRESETS=:incps
69
+ EXTV_PRESET=:preset
70
+ PRESET_DIG_SEPARATOR='.'
65
71
  DEFAULT_CHECK_NEW_VERSION_DAYS=7
66
- DEFAULT_PRIV_KEY_FILENAME='aspera_aoc_key'
72
+ DEFAULT_PRIV_KEY_FILENAME='aspera_aoc_key' # pragma: allowlist secret
67
73
  DEFAULT_PRIVKEY_LENGTH=4096
68
74
  private_constant :DEFAULT_CONFIG_FILENAME,:CONF_PRESET_CONFIG,:CONF_PRESET_VERSION,:CONF_PRESET_DEFAULT,
69
75
  :CONF_PRESET_GLOBAL,:PROGRAM_NAME_V1,:PROGRAM_NAME_V2,:DEFAULT_REDIRECT,:ASPERA_PLUGINS_FOLDERNAME,
70
76
  :RUBY_FILE_EXT,:AOC_COMMAND_V1,:AOC_COMMAND_V2,:AOC_COMMAND_V3,:AOC_COMMAND_CURRENT,:DEMO,
71
77
  :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
73
- def option_preset; nil; end
74
-
75
- def option_preset=(value)
76
- case value
77
- when String
78
- self.options.add_option_preset(preset_by_name(value))
79
- when Hash
80
- self.options.add_option_preset(value)
81
- else
82
- raise "Preset definition must be a String for name, or Hash for value"
83
- end
84
- nil
85
- end
86
-
87
- def initialize(env,tool_name,help_url,version,main_folder)
78
+ :EXTV_PRESET,:DEFAULT_CHECK_NEW_VERSION_DAYS,:DEFAULT_PRIV_KEY_FILENAME,:SERVER_COMMAND,:CONF_PRESET_SECRETS,
79
+ :PRESET_DIG_SEPARATOR
80
+ def initialize(env,params)
81
+ raise 'env and params must be Hash' unless env.is_a?(Hash) && params.is_a?(Hash)
82
+ raise 'missing param' unless [:name,:help,:version,:gem].sort.eql?(params.keys.sort)
88
83
  super(env)
89
- raise 'missing secret manager' if @agents[:secret].nil?
84
+ @info=params
85
+ @main_folder=default_app_main_folder
90
86
  @plugins={}
91
87
  @plugin_lookup_folders=[]
92
88
  @use_plugin_defaults=true
93
89
  @config_presets=nil
94
90
  @connect_versions=nil
95
- @program_version=version
96
- @tool_name=tool_name
97
- @help_url=help_url
98
- @main_folder=main_folder
91
+ @vault=nil
99
92
  @conf_file_default=File.join(@main_folder,DEFAULT_CONFIG_FILENAME)
100
93
  @option_config_file=@conf_file_default
101
- Log.log.debug("#{tool_name} folder: #{@main_folder}")
94
+ @pac_exec=nil
95
+ Log.log.debug("#{@info[:name]} folder: #{@main_folder}")
102
96
  # set folder for FASP SDK
103
- add_plugin_lookup_folder(File.join(@main_folder,ASPERA_PLUGINS_FOLDERNAME))
104
97
  add_plugin_lookup_folder(self.class.gem_plugins_folder)
98
+ add_plugin_lookup_folder(File.join(@main_folder,ASPERA_PLUGINS_FOLDERNAME))
105
99
  # do file parameter first
106
- self.options.set_obj_attr(:config_file,self,:option_config_file)
107
- self.options.add_opt_simple(:config_file,"read parameters from file in YAML format, current=#{@option_config_file}")
108
- self.options.parse_options!
100
+ options.set_obj_attr(:config_file,self,:option_config_file)
101
+ options.add_opt_simple(:config_file,"read parameters from file in YAML format, current=#{@option_config_file}")
102
+ options.parse_options!
109
103
  # read correct file
110
104
  read_config_file
111
105
  # add preset handler (needed for smtp)
112
106
  ExtendedValue.instance.set_handler(EXTV_PRESET,:reader,lambda{|v|preset_by_name(v)})
113
107
  ExtendedValue.instance.set_handler(EXTV_INCLUDE_PRESETS,:decoder,lambda{|v|expanded_with_preset_includes(v)})
114
- self.options.set_obj_attr(:ascp_path,self,:option_ascp_path)
115
- self.options.set_obj_attr(:use_product,self,:option_use_product)
116
- 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
- self.options.add_opt_switch(:no_default,'-N','do not load default configuration for plugin') { @use_plugin_defaults=false }
120
- self.options.add_opt_boolean(:override,'Wizard: override existing value')
121
- self.options.add_opt_boolean(:use_generic_client,'Wizard: AoC: use global or org specific jwt client id')
122
- self.options.add_opt_boolean(:default,'Wizard: set as default configuration for specified plugin (also: update)')
123
- self.options.add_opt_boolean(:test_mode,'Wizard: skip private key check step')
124
- self.options.add_opt_simple(:pkeypath,'Wizard: path to private key for JWT')
125
- self.options.add_opt_simple(:ascp_path,'path to ascp')
126
- self.options.add_opt_simple(:use_product,'use ascp from specified product')
127
- self.options.add_opt_simple(:smtp,'smtp configuration (extended value: hash)')
128
- 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
- self.options.add_opt_simple(:secret,'default secret')
131
- self.options.add_opt_simple(:secrets,'secret repository (Hash)')
132
- self.options.add_opt_simple(:sdk_url,'URL to get SDK')
133
- self.options.add_opt_simple(:sdk_folder,'SDK folder path')
134
- self.options.add_opt_simple(:notif_to,'email recipient for notification of transfers')
135
- self.options.add_opt_simple(:notif_template,'email ERB template for notification of transfers')
136
- self.options.add_opt_simple(:version_check_days,Integer,'period in days to check new version (zero to disable)')
137
- self.options.set_option(:use_generic_client,true)
138
- self.options.set_option(:test_mode,false)
139
- self.options.set_option(:default,true)
140
- self.options.set_option(:version_check_days,DEFAULT_CHECK_NEW_VERSION_DAYS)
141
- self.options.set_option(:sdk_url,TRANSFER_SDK_ARCHIVE_URL)
142
- self.options.set_option(:sdk_folder,File.join(@main_folder,'sdk'))
143
- self.options.set_option(:override,:no)
144
- self.options.parse_options!
145
- raise CliBadArgument,'secrets shall be Hash' unless @agents[:secret].all_secrets.is_a?(Hash)
146
- Fasp::Installation.instance.folder=self.options.get_option(:sdk_folder,:mandatory)
108
+ # load defaults before it can be overriden
109
+ add_plugin_default_preset(CONF_GLOBAL_SYM)
110
+ options.parse_options!
111
+ options.set_obj_attr(:ascp_path,Fasp::Installation.instance,:ascp_path)
112
+ options.set_obj_attr(:sdk_folder,Fasp::Installation.instance,:sdk_folder)
113
+ options.set_obj_attr(:use_product,self,:option_use_product)
114
+ options.set_obj_attr(:preset,self,:option_preset)
115
+ options.set_obj_attr(:plugin_folder,self,:option_plugin_folder)
116
+ options.add_opt_switch(:no_default,'-N','do not load default configuration for plugin') { @use_plugin_defaults=false }
117
+ options.add_opt_boolean(:override,'Wizard: override existing value')
118
+ options.add_opt_boolean(:use_generic_client,'Wizard: AoC: use global or org specific jwt client id')
119
+ options.add_opt_boolean(:default,'Wizard: set as default configuration for specified plugin (also: update)')
120
+ options.add_opt_boolean(:test_mode,'Wizard: skip private key check step')
121
+ options.add_opt_simple(:preset,'-PVALUE','load the named option preset from current config file')
122
+ options.add_opt_simple(:pkeypath,'Wizard: path to private key for JWT')
123
+ options.add_opt_simple(:ascp_path,'path to ascp')
124
+ options.add_opt_simple(:use_product,'use ascp from specified product')
125
+ options.add_opt_simple(:smtp,'smtp configuration (extended value: hash)')
126
+ options.add_opt_simple(:fpac,'proxy auto configuration script')
127
+ options.add_opt_simple(:secret,'default secret')
128
+ options.add_opt_simple(:secrets,'secret vault')
129
+ options.add_opt_simple(:sdk_url,'URL to get SDK')
130
+ options.add_opt_simple(:sdk_folder,'SDK folder path')
131
+ options.add_opt_simple(:notif_to,'email recipient for notification of transfers')
132
+ options.add_opt_simple(:notif_template,'email ERB template for notification of transfers')
133
+ options.add_opt_simple(:version_check_days,Integer,'period in days to check new version (zero to disable)')
134
+ options.add_opt_simple(:plugin_folder,'folder where to find additional plugins')
135
+ options.set_option(:use_generic_client,true)
136
+ options.set_option(:test_mode,false)
137
+ options.set_option(:default,true)
138
+ options.set_option(:version_check_days,DEFAULT_CHECK_NEW_VERSION_DAYS)
139
+ options.set_option(:sdk_url,TRANSFER_SDK_ARCHIVE_URL)
140
+ options.set_option(:sdk_folder,File.join(@main_folder,'sdk'))
141
+ options.set_option(:override,:no)
142
+ options.parse_options!
143
+ pac_script=options.get_option(:fpac,:optional)
144
+ # create PAC executor
145
+ @pac_exec=Aspera::ProxyAutoConfig.new(pac_script).register_uri_generic unless pac_script.nil?
146
+ end
147
+
148
+ # env var name to override the app's main folder
149
+ # default main folder is $HOME/<vendor main app folder>/<program name>
150
+ def conf_dir_env_var
151
+ return "#{@info[:name]}_home".upcase
152
+ end
153
+
154
+ def default_app_main_folder
155
+ # find out application main folder
156
+ app_folder=ENV[conf_dir_env_var]
157
+ # if env var undefined or empty
158
+ if app_folder.nil? || app_folder.empty?
159
+ user_home_folder=Dir.home
160
+ raise CliError,"Home folder does not exist: #{user_home_folder}. Check your user environment or use #{conf_dir_env_var}." unless Dir.exist?(user_home_folder)
161
+ app_folder=File.join(user_home_folder,ASPERA_HOME_FOLDER_NAME,@info[:name])
162
+ end
163
+ return app_folder
147
164
  end
148
165
 
149
166
  def check_gem_version
150
- this_gem_name=File.basename(File.dirname(self.class.gem_root)).gsub(/-[0-9].*$/,'')
151
167
  latest_version=begin
152
- Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{this_gem_name}/latest.json")[:data]['version']
153
- rescue
168
+ Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{@info[:gem]}/latest.json")[:data]['version']
169
+ rescue StandardError
154
170
  Log.log.warn('Could not retrieve latest gem version on rubygems.')
155
171
  '0'
156
172
  end
157
- return {name: this_gem_name, current: Aspera::Cli::VERSION, latest: latest_version, need_update: Gem::Version.new(Aspera::Cli::VERSION) < Gem::Version.new(latest_version)}
173
+ if Gem::Version.new(Environment.ruby_version) < Gem::Version.new(RUBY_FUTURE_MINIMUM_VERSION)
174
+ Log.log.warn("Note that a future version will require Ruby version #{RUBY_FUTURE_MINIMUM_VERSION} at minimum, you are using #{Environment.ruby_version}")
175
+ end
176
+ return {name: @info[:gem], current: Aspera::Cli::VERSION, latest: latest_version, need_update: Gem::Version.new(Aspera::Cli::VERSION) < Gem::Version.new(latest_version)}
158
177
  end
159
178
 
160
179
  def periodic_check_newer_gem_version
@@ -162,37 +181,36 @@ END_OF_TEMPLATE
162
181
  delay_days=options.get_option(:version_check_days,:mandatory)
163
182
  Log.log.info("check days: #{delay_days}")
164
183
  # check only if not zero day
165
- if !delay_days.eql?(0)
166
- # get last date from persistency
167
- last_check_array=[]
168
- check_date_persist=PersistencyActionOnce.new(
169
- manager: persistency,
170
- data: last_check_array,
171
- id: 'version_last_check')
172
- # get persisted date or nil
173
- last_check_date = begin
174
- Date.strptime(last_check_array.first, '%Y/%m/%d')
175
- rescue
176
- nil
177
- end
178
- current_date=Date.today
179
- Log.log.debug("days elapsed: #{last_check_date.is_a?(Date) ? current_date - last_check_date : last_check_date.class.name}")
180
- if last_check_date.nil? or (current_date - last_check_date) > delay_days
181
- last_check_array[0]=current_date.strftime('%Y/%m/%d')
182
- check_date_persist.save
183
- check_data=check_gem_version
184
- if check_data[:need_update]
185
- Log.log.warn("A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}")
186
- end
187
- end
184
+ return if delay_days.eql?(0)
185
+ # get last date from persistency
186
+ last_check_array=[]
187
+ check_date_persist=PersistencyActionOnce.new(
188
+ manager: persistency,
189
+ data: last_check_array,
190
+ id: 'version_last_check')
191
+ # get persisted date or nil
192
+ current_date=Date.today
193
+ last_check_days = begin
194
+ current_date-Date.strptime(last_check_array.first, '%Y/%m/%d')
195
+ rescue StandardError
196
+ # negative value will force check
197
+ -1
188
198
  end
199
+ Log.log.debug("days elapsed: #{last_check_days}")
200
+ return if last_check_days < delay_days
201
+ # generate timestamp
202
+ last_check_array[0]=current_date.strftime('%Y/%m/%d')
203
+ check_date_persist.save
204
+ # compare this version and the one on internet
205
+ check_data=check_gem_version
206
+ Log.log.warn("A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}") if check_data[:need_update]
189
207
  end
190
208
 
191
209
  # retrieve structure from cloud (CDN) with all versions available
192
210
  def connect_versions
193
211
  if @connect_versions.nil?
194
- api_connect_cdn=Rest.new({:base_url=>CONNECT_WEB_URL})
195
- javascript=api_connect_cdn.call({:operation=>'GET',:subpath=>CONNECT_VERSIONS})
212
+ api_connect_cdn=Rest.new({base_url: CONNECT_WEB_URL})
213
+ javascript=api_connect_cdn.call({operation: 'GET',subpath: CONNECT_VERSIONS})
196
214
  # get result on one line
197
215
  connect_versions_javascript=javascript[:http].body.gsub(/\r?\n\s*/,'')
198
216
  Log.log.debug("javascript=[\n#{connect_versions_javascript}\n]")
@@ -212,9 +230,10 @@ END_OF_TEMPLATE
212
230
  def add_plugin_default_preset(plugin_name_sym)
213
231
  default_config_name=get_plugin_default_config_name(plugin_name_sym)
214
232
  Log.log.debug("add_plugin_default_preset:#{plugin_name_sym}:#{default_config_name}")
215
- self.options.add_option_preset(preset_by_name(default_config_name),:unshift) unless default_config_name.nil?
233
+ options.add_option_preset(preset_by_name(default_config_name),:unshift) unless default_config_name.nil?
216
234
  return nil
217
235
  end
236
+
218
237
  private
219
238
 
220
239
  def generate_rsa_private_key(private_key_path,length)
@@ -225,40 +244,53 @@ END_OF_TEMPLATE
225
244
  nil
226
245
  end
227
246
 
228
- # folder containing plugins in the gem's main folder
229
- def self.gem_plugins_folder
230
- File.dirname(File.expand_path(__FILE__))
231
- end
247
+ class << self
248
+ # folder containing plugins in the gem's main folder
249
+ def gem_plugins_folder
250
+ File.dirname(File.expand_path(__FILE__))
251
+ end
232
252
 
233
- # find the root folder of gem where this class is
234
- # go up as many times as englobing modules (not counting class, as it is a file)
235
- def self.gem_root
236
- File.expand_path(Module.nesting[1].to_s.gsub('::','/').gsub(%r([^/]+),'..'),File.dirname(__FILE__))
237
- end
253
+ # name of englobin module
254
+ # @return "Aspera::Cli::Plugins"
255
+ def module_full_name
256
+ return Module.nesting[2].to_s
257
+ end
238
258
 
239
- # instanciate a plugin
240
- # plugins must be Capitalized
241
- def self.plugin_new(plugin_name_sym,env)
242
- # Module.nesting[2] is Aspera::Cli
243
- return Object::const_get("#{Module.nesting[2].to_s}::Plugins::#{plugin_name_sym.to_s.capitalize}").new(env)
244
- end
259
+ # @return main folder where code is, i.e. .../lib
260
+ # go up as many times as englobing modules (not counting class, as it is a file)
261
+ def gem_src_root
262
+ File.expand_path(module_full_name.gsub('::','/').gsub(%r{[^/]+},'..'),gem_plugins_folder)
263
+ end
245
264
 
246
- def self.flatten_all_config(t)
247
- r=[]
248
- t.each do |k,v|
249
- v.each do |kk,vv|
250
- r.push({'config'=>k,'parameter'=>kk,'value'=>vv})
265
+ # instanciate a plugin
266
+ # plugins must be Capitalized
267
+ def plugin_class(plugin_name_sym)
268
+ # Module.nesting[2] is Aspera::Cli::Plugins
269
+ return Object.const_get("#{module_full_name}::#{plugin_name_sym.to_s.capitalize}")
270
+ end
271
+
272
+ def flatten_all_config(t)
273
+ r=[]
274
+ t.each do |k,v|
275
+ v.each do |kk,vv|
276
+ r.push({'config'=>k,'parameter'=>kk,'value'=>vv})
277
+ end
251
278
  end
279
+ return r
252
280
  end
253
- return r
254
281
  end
255
282
 
256
283
  # set parameter and value in global config
257
284
  # creates one if none already created
258
- # @return preset that contains global default
285
+ # @return preset name that contains global default
259
286
  def set_global_default(key,value)
260
287
  # get default preset if it exists
261
- global_default_preset_name=get_plugin_default_config_name(CONF_GLOBAL_SYM) || CONF_PRESET_GLOBAL
288
+ global_default_preset_name=get_plugin_default_config_name(CONF_GLOBAL_SYM)
289
+ if global_default_preset_name.nil?
290
+ global_default_preset_name=CONF_PRESET_GLOBAL
291
+ @config_presets[CONF_PRESET_DEFAULT]||={}
292
+ @config_presets[CONF_PRESET_DEFAULT][CONF_GLOBAL_SYM.to_s]=global_default_preset_name
293
+ end
262
294
  @config_presets[global_default_preset_name]||={}
263
295
  @config_presets[global_default_preset_name][key.to_s]=value
264
296
  self.format.display_status("Updated: #{global_default_preset_name}: #{key} <- #{value}")
@@ -270,27 +302,41 @@ END_OF_TEMPLATE
270
302
 
271
303
  # $HOME/.aspera/`program_name`
272
304
  attr_reader :main_folder
273
- attr_reader :gem_url
274
- attr_reader :plugins
305
+ attr_reader :gem_url, :plugins
275
306
  attr_accessor :option_config_file
276
307
 
277
308
  # @return the hash from name (also expands possible includes)
309
+ # @param config_name name of the preset in config file
310
+ # @param include_path used to detect and avoid include loops
278
311
  def preset_by_name(config_name, include_path=[])
279
- raise CliError,"no such config preset: #{config_name}" unless @config_presets.has_key?(config_name)
280
312
  raise CliError,'loop in include' if include_path.include?(config_name)
281
- return expanded_with_preset_includes(@config_presets[config_name],include_path.clone.push(config_name))
313
+ include_path=include_path.clone # avoid messing up if there are multiple branches
314
+ current=@config_presets
315
+ config_name.split(PRESET_DIG_SEPARATOR).each do |name|
316
+ raise CliError,"not a Hash: #{include_path} (#{current.class})" unless current.is_a?(Hash)
317
+ include_path.push(name)
318
+ current=current[name]
319
+ raise CliError,"no such config preset: #{include_path}" if nil?
320
+ end
321
+ case current
322
+ when Hash then return expanded_with_preset_includes(current,include_path)
323
+ when String then return ExtendedValue.instance.evaluate(current)
324
+ else return current
325
+ end
282
326
  end
283
327
 
328
+ # @return the hash value with 'incps' keys expanced to include other presets
284
329
  # @param hash_val
330
+ # @param include_path to avoid inclusion loop
285
331
  def expanded_with_preset_includes(hash_val, include_path=[])
286
- raise CliError,"#{EXTV_INCLUDE_PRESETS} requires a Hash" unless hash_val.is_a?(Hash)
332
+ raise CliError,"#{EXTV_INCLUDE_PRESETS} requires a Hash, have #{hash_val.class}" unless hash_val.is_a?(Hash)
287
333
  if hash_val.has_key?(EXTV_INCLUDE_PRESETS)
288
334
  memory=hash_val.clone
289
335
  includes=memory[EXTV_INCLUDE_PRESETS]
290
336
  memory.delete(EXTV_INCLUDE_PRESETS)
291
337
  hash_val={}
292
338
  raise "#{EXTV_INCLUDE_PRESETS} must be an Array" unless includes.is_a?(Array)
293
- raise "#{EXTV_INCLUDE_PRESETS} must contain names" unless includes.map{|i|i.class}.uniq.eql?([String])
339
+ raise "#{EXTV_INCLUDE_PRESETS} must contain names" unless includes.map(&:class).uniq.eql?([String])
294
340
  includes.each do |preset_name|
295
341
  hash_val.merge!(preset_by_name(preset_name,include_path))
296
342
  end
@@ -299,20 +345,37 @@ END_OF_TEMPLATE
299
345
  return hash_val
300
346
  end
301
347
 
302
- def option_ascp_path=(new_value)
303
- Fasp::Installation.instance.ascp_path=new_value
348
+ def option_use_product=(value)
349
+ Fasp::Installation.instance.use_ascp_from_product(value)
304
350
  end
305
351
 
306
- def option_ascp_path
307
- Fasp::Installation.instance.path(:ascp)
352
+ def option_use_product
353
+ 'write-only option, see value of ascp_path'
308
354
  end
309
355
 
310
- def option_use_product=(value)
311
- Fasp::Installation.instance.use_ascp_from_product(value)
356
+ def option_plugin_folder=(value)
357
+ case value
358
+ when String then add_plugin_lookup_folder(value)
359
+ when Array then value.each{|f|add_plugin_lookup_folder(f)}
360
+ else raise "folder shall be Array or String, not #{value.class}"
361
+ end
312
362
  end
313
363
 
314
- def option_use_product
315
- 'write-only value'
364
+ def option_plugin_folder
365
+ return @plugin_lookup_folders
366
+ end
367
+
368
+ def option_preset; 'write-only option'; end
369
+
370
+ def option_preset=(value)
371
+ case value
372
+ when String
373
+ options.add_option_preset(preset_by_name(value))
374
+ when Hash
375
+ options.add_option_preset(value)
376
+ else
377
+ raise 'Preset definition must be a String for name, or Hash for value'
378
+ end
316
379
  end
317
380
 
318
381
  def convert_preset_path(old_name,new_name,files_to_copy)
@@ -323,7 +386,7 @@ END_OF_TEMPLATE
323
386
  preset.values.select{|v|v.is_a?(String) and v.include?(old_subpath)}.each do |value|
324
387
  old_val=value.clone
325
388
  included_path=File.expand_path(old_val.gsub(/^@file:/,''))
326
- files_to_copy.push(included_path) unless files_to_copy.include?(included_path) or !File.exist?(included_path)
389
+ files_to_copy.push(included_path) unless files_to_copy.include?(included_path) || !File.exist?(included_path)
327
390
  value.gsub!(old_subpath,new_subpath)
328
391
  Log.log.warn("Converted config value: #{old_val} -> #{value}")
329
392
  end
@@ -331,11 +394,11 @@ END_OF_TEMPLATE
331
394
  end
332
395
 
333
396
  def convert_preset_plugin_name(old_name,new_name)
334
- if @config_presets[CONF_PRESET_DEFAULT].is_a?(Hash) and @config_presets[CONF_PRESET_DEFAULT].has_key?(old_name)
335
- @config_presets[CONF_PRESET_DEFAULT][new_name]=@config_presets[CONF_PRESET_DEFAULT][old_name]
336
- @config_presets[CONF_PRESET_DEFAULT].delete(old_name)
337
- Log.log.warn("Converted plugin default: #{old_name} -> #{new_name}")
338
- end
397
+ default_preset=@config_presets[CONF_PRESET_DEFAULT]
398
+ return unless default_preset.is_a?(Hash) && default_preset.has_key?(old_name)
399
+ default_preset[new_name]=default_preset[old_name]
400
+ default_preset.delete(old_name)
401
+ Log.log.warn("Converted plugin default: #{old_name} -> #{new_name}")
339
402
  end
340
403
 
341
404
  # read config file and validate format
@@ -352,11 +415,11 @@ END_OF_TEMPLATE
352
415
  # find first existing file (or nil)
353
416
  conf_file_to_load=search_files.select{|f| File.exist?(f)}.first
354
417
  # require save if old version of file
355
- save_required=!@option_config_file.eql?(conf_file_to_load)
418
+ save_required= !@option_config_file.eql?(conf_file_to_load)
356
419
  # if no file found, create default config
357
420
  if conf_file_to_load.nil?
358
421
  Log.log.warn("No config file found. Creating empty configuration file: #{@option_config_file}")
359
- @config_presets={CONF_PRESET_CONFIG=>{CONF_PRESET_VERSION=>@program_version}}
422
+ @config_presets={CONF_PRESET_CONFIG=>{CONF_PRESET_VERSION=>@info[:version]}}
360
423
  else
361
424
  Log.log.debug("loading #{@option_config_file}")
362
425
  @config_presets=YAML.load_file(conf_file_to_load)
@@ -393,14 +456,15 @@ END_OF_TEMPLATE
393
456
  config_tested_version='1.0'
394
457
  if Gem::Version.new(version) <= Gem::Version.new(config_tested_version)
395
458
  convert_preset_plugin_name(AOC_COMMAND_V2,AOC_COMMAND_V3)
396
- convert_preset_path(PROGRAM_NAME_V2,@tool_name,files_to_copy)
459
+ convert_preset_path(PROGRAM_NAME_V2,@info[:name],files_to_copy)
397
460
  version=@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]=config_tested_version
398
461
  save_required=true
399
462
  end
463
+ Log.log.debug("conf version: #{version}")
400
464
  # Place new compatibility code here
401
465
  if save_required
402
466
  Log.log.warn('Saving automatic conversion.')
403
- @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]=@program_version
467
+ @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]=@info[:version]
404
468
  save_presets_to_config_file
405
469
  Log.log.warn('Copying referenced files')
406
470
  files_to_copy.each do |file|
@@ -411,9 +475,9 @@ END_OF_TEMPLATE
411
475
  rescue Psych::SyntaxError => e
412
476
  Log.log.error('YAML error in config file')
413
477
  raise e
414
- rescue => e
478
+ rescue StandardError => e
415
479
  Log.log.debug("-> #{e}")
416
- new_name="#{@option_config_file}.pre#{@program_version}.manual_conversion_needed"
480
+ new_name="#{@option_config_file}.pre#{@info[:version]}.manual_conversion_needed"
417
481
  File.rename(@option_config_file,new_name)
418
482
  Log.log.warn("Renamed config file to #{new_name}.")
419
483
  Log.log.warn('Manual Conversion is required. Next time, a new empty file will be created.')
@@ -424,18 +488,17 @@ END_OF_TEMPLATE
424
488
  # find plugins in defined paths
425
489
  def add_plugins_from_lookup_folders
426
490
  @plugin_lookup_folders.each do |folder|
427
- if File.directory?(folder)
428
- #TODO: add gem root to load path ? and require short folder ?
429
- #$LOAD_PATH.push(folder) if i[:add_path]
430
- Dir.entries(folder).select{|file|file.end_with?(RUBY_FILE_EXT)}.each do |source|
431
- add_plugin_info(File.join(folder,source))
432
- end
491
+ next unless File.directory?(folder)
492
+ #TODO: add gem root to load path ? and require short folder ?
493
+ #$LOAD_PATH.push(folder) if i[:add_path]
494
+ Dir.entries(folder).select{|file|file.end_with?(RUBY_FILE_EXT)}.each do |source|
495
+ add_plugin_info(File.join(folder,source))
433
496
  end
434
497
  end
435
498
  end
436
499
 
437
500
  def add_plugin_lookup_folder(folder)
438
- @plugin_lookup_folders.push(folder)
501
+ @plugin_lookup_folders.unshift(folder)
439
502
  end
440
503
 
441
504
  def add_plugin_info(path)
@@ -446,245 +509,319 @@ END_OF_TEMPLATE
446
509
  Log.log.warn("skipping plugin already registered: #{plugin_symbol}")
447
510
  return
448
511
  end
449
- @plugins[plugin_symbol]={:source=>path,:require_stanza=>req}
512
+ @plugins[plugin_symbol]={source: path,require_stanza: req}
513
+ end
514
+
515
+ def identify_plugin_for_url(url)
516
+ plugins.each do |plugin_name_sym,plugin_info|
517
+ # no detection for internal plugin
518
+ next if plugin_name_sym.eql?(CONF_PLUGIN_SYM)
519
+ # load plugin class
520
+ require plugin_info[:require_stanza]
521
+ c=self.class.plugin_class(plugin_name_sym)
522
+ next unless c.respond_to?(:detect)
523
+ current_url=url
524
+ detection_info=nil
525
+ # first try : direct
526
+ begin
527
+ detection_info=c.detect(current_url)
528
+ rescue StandardError => e
529
+ Log.log.debug("Cannot detect #{plugin_name_sym} : #{e.message}")
530
+ end
531
+ # second try : is there a redirect ?
532
+ if detection_info.nil?
533
+ begin
534
+ # TODO: check if redirect ?
535
+ new_url=Rest.new(base_url: url).call(operation: 'GET',subpath: '',redirect_max: 1)[:http].uri.to_s
536
+ unless url.eql?(new_url)
537
+ detection_info=c.detect(new_url)
538
+ current_url=new_url
539
+ end
540
+ rescue StandardError => e
541
+ Log.log.debug("Cannot detect #{plugin_name_sym} : #{e.message}")
542
+ end
543
+ end
544
+ return detection_info.merge(product: plugin_name_sym, url: current_url) unless detection_info.nil?
545
+ end # loop
546
+ raise "No known application found at #{url}"
450
547
  end
451
548
 
452
549
  def execute_connect_action
453
- command=self.options.get_next_command([:list,:id])
454
- case command
455
- when :list
456
- return {:type=>:object_list, :data=>connect_versions, :fields => ['id','title','version']}
457
- when :id
458
- connect_id=self.options.get_next_argument('id or title')
550
+ command=options.get_next_command([:list,:info,:version])
551
+ if [:info,:version].include?(command)
552
+ connect_id=options.get_next_argument('id or title')
459
553
  one_res=connect_versions.select{|i|i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}.first
460
554
  raise CliNoSuchId.new(:connect,connect_id) if one_res.nil?
461
- command=self.options.get_next_command([:info,:links])
555
+ end
556
+ case command
557
+ when :list
558
+ return {type: :object_list, data: connect_versions, fields: ['id','title','version']}
559
+ when :info # shows files used
560
+ one_res.delete('links')
561
+ return {type: :single_object, data: one_res}
562
+ when :version # shows files used
563
+ all_links=one_res['links']
564
+ command=options.get_next_command([:list,:download,:open])
565
+ if [:download,:open].include?(command)
566
+ link_title=options.get_next_argument('title or rel')
567
+ one_link=all_links.select {|i| i['title'].eql?(link_title) or i['rel'].eql?(link_title)}.first
568
+ raise 'no such value' if one_link.nil?
569
+ end
462
570
  case command
463
- when :info # shows files used
464
- one_res.delete('links')
465
- return {:type=>:single_object, :data=>one_res}
466
- when :links # shows files used
467
- command=self.options.get_next_command([:list,:id])
468
- all_links=one_res['links']
469
- case command
470
- when :list # shows files used
471
- return {:type=>:object_list, :data=>all_links}
472
- when :id
473
- link_title=self.options.get_next_argument('title')
474
- one_link=all_links.select {|i| i['title'].eql?(link_title)}.first
475
- command=self.options.get_next_command([:download,:open])
476
- case command
477
- when :download #
478
- folder_dest=self.transfer.destination_folder('receive')
479
- #folder_dest=self.options.get_next_argument('destination folder')
480
- api_connect_cdn=Rest.new({:base_url=>CONNECT_WEB_URL})
481
- fileurl = one_link['href']
482
- filename=fileurl.gsub(%r{.*/},'')
483
- api_connect_cdn.call({:operation=>'GET',:subpath=>fileurl,:save_to_file=>File.join(folder_dest,filename)})
484
- return Main.result_status("Downloaded: #{filename}")
485
- when :open #
486
- OpenApplication.instance.uri(one_link['href'])
487
- return Main.result_status("Opened: #{one_link['href']}")
488
- end
489
- end
571
+ when :list # shows files used
572
+ return {type: :object_list, data: all_links}
573
+ when :download
574
+ folder_dest=transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE)
575
+ #folder_dest=self.options.get_next_argument('destination folder')
576
+ api_connect_cdn=Rest.new({base_url: CONNECT_WEB_URL})
577
+ fileurl = one_link['href']
578
+ filename=fileurl.gsub(%r{.*/},'')
579
+ api_connect_cdn.call({operation: 'GET',subpath: fileurl,save_to_file: File.join(folder_dest,filename)})
580
+ return Main.result_status("Downloaded: #{filename}")
581
+ when :open
582
+ OpenApplication.instance.uri(one_link['href'])
583
+ return Main.result_status("Opened: #{one_link['href']}")
490
584
  end
491
585
  end
492
586
  end
493
587
 
494
588
  def execute_action_ascp
495
- command=self.options.get_next_command([:connect,:use,:show,:products,:info,:install,:spec])
589
+ command=options.get_next_command([:connect,:use,:show,:products,:info,:install,:spec,:errors])
496
590
  case command
497
591
  when :connect
498
592
  return execute_connect_action
499
593
  when :use
500
- ascp_path=self.options.get_next_argument('path to ascp')
594
+ ascp_path=options.get_next_argument('path to ascp')
501
595
  ascp_version=Fasp::Installation.instance.get_ascp_version(ascp_path)
502
596
  self.format.display_status("ascp version: #{ascp_version}")
503
597
  preset_name=set_global_default(:ascp_path,ascp_path)
504
598
  return Main.result_status("Saved to default global preset #{preset_name}")
505
599
  when :show # shows files used
506
- return {:type=>:status, :data=>Fasp::Installation.instance.path(:ascp)}
600
+ return {type: :status, data: Fasp::Installation.instance.path(:ascp)}
507
601
  when :info # shows files used
508
- data=Fasp::Installation::FILES.inject({}) do |m,v|
602
+ data=Fasp::Installation::FILES.each_with_object({}) do |v,m|
509
603
  m[v.to_s]=Fasp::Installation.instance.path(v) rescue 'Not Found'
510
- m
511
604
  end
512
605
  # read PATHs from ascp directly, and pvcl modules as well
513
- Open3.popen3(Fasp::Installation.instance.path(:ascp),'-DDL-') do |stdin, stdout, stderr, thread|
606
+ Open3.popen3(Fasp::Installation.instance.path(:ascp),'-DDL-') do |_stdin, _stdout, stderr, thread|
514
607
  last_line=''
515
- while line=stderr.gets do
608
+ while (line=stderr.gets)
516
609
  line.chomp!
517
610
  last_line=line
518
611
  case line
519
- when %r{^DBG Path ([^ ]+) (dir|file) +: (.*)$};data[$1]=$3
520
- when %r{^DBG Added module group:"([^"]+)" name:"([^"]+)", version:"([^"]+)" interface:"([^"]+)"$};data[$2]=$4
521
- when %r{^DBG License result \(/license/(\S+)\): (.+)$};data[$1]=$2
522
- when %r{^LOG (.+) version ([0-9.]+)$};data['product_name']=$1;data['product_version']=$2
523
- when %r{^LOG Initializing FASP version ([^,]+),};data['ascp_version']=$1
612
+ when %r{^DBG Path ([^ ]+) (dir|file) +: (.*)$} then data[Regexp.last_match(1)]=Regexp.last_match(3)
613
+ when %r{^DBG Added module group:"([^"]+)" name:"([^"]+)", version:"([^"]+)" interface:"([^"]+)"$} then data[Regexp.last_match(2)]=Regexp.last_match(4)
614
+ when %r{^DBG License result \(/license/(\S+)\): (.+)$} then data[Regexp.last_match(1)]=Regexp.last_match(2)
615
+ when %r{^LOG (.+) version ([0-9.]+)$} then data['product_name']=Regexp.last_match(1);data['product_version']=Regexp.last_match(2)
616
+ when %r{^LOG Initializing FASP version ([^,]+),} then data['ascp_version']=Regexp.last_match(1)
524
617
  end
525
618
  end
526
- if !thread.value.exitstatus.eql?(1) and !data.has_key?('root')
619
+ if !thread.value.exitstatus.eql?(1) && !data.has_key?('root')
527
620
  raise last_line
528
621
  end
529
622
  end
530
623
  data['keypass']=Fasp::Installation.instance.bypass_pass
531
- return {:type=>:single_object, :data=>data}
624
+ return {type: :single_object, data: data}
532
625
  when :products
533
- command=self.options.get_next_command([:list,:use])
626
+ command=options.get_next_command([:list,:use])
534
627
  case command
535
628
  when :list
536
- return {:type=>:object_list, :data=>Fasp::Installation.instance.installed_products, :fields=>['name','app_root']}
629
+ return {type: :object_list, data: Fasp::Installation.instance.installed_products, fields: ['name','app_root']}
537
630
  when :use
538
- default_product=self.options.get_next_argument('product name')
631
+ default_product=options.get_next_argument('product name')
539
632
  Fasp::Installation.instance.use_ascp_from_product(default_product)
540
633
  preset_name=set_global_default(:ascp_path,Fasp::Installation.instance.path(:ascp))
541
634
  return Main.result_status("Saved to default global preset #{preset_name}")
542
635
  end
543
636
  when :install
544
- v=Fasp::Installation.instance.install_sdk(self.options.get_option(:sdk_url,:mandatory))
637
+ v=Fasp::Installation.instance.install_sdk(options.get_option(:sdk_url,:mandatory))
545
638
  return Main.result_status("Installed version #{v}")
546
639
  when :spec
547
- return {type: :object_list, data: Fasp::Parameters.man_table, fields: ['name','type',Fasp::Parameters::SUPPORTED_AGENTS_SHORT.map{|i|i.to_s},'description'].flatten}
640
+ return {type: :object_list, data: Fasp::Parameters.man_table, fields: ['name','type',Fasp::Parameters::SUPPORTED_AGENTS_SHORT.map(&:to_s),'description'].flatten}
641
+ when :errors
642
+ error_data=[]
643
+ Fasp::ERROR_INFO.each_pair do |code,prop|
644
+ error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
645
+ end
646
+ return {type: :object_list, data: error_data}
548
647
  end
549
648
  raise "unexpected case: #{command}"
550
649
  end
551
650
 
552
- ACTIONS=[:gem_path, :genkey,:plugins,:flush_tokens,:list,:overview,:open,:echo,:id,:documentation,:wizard,:export_to_cli,:detect,:coffee,:ascp,:email_test,:smtp_settings,:proxy_check,:folder,:file,:check_update,:initdemo]
651
+ # legacy actions available globally
652
+ PRESET_GBL_ACTIONS=[:list,:overview].freeze
653
+ # require existing preset
654
+ PRESET_EXST_ACTIONS=[:show,:delete,:get,:unset].freeze
655
+ # require id
656
+ PRESET_INSTANCE_ACTIONS=[PRESET_EXST_ACTIONS,:initialize,:update,:ask,:set].flatten.freeze
657
+ PRESET_ALL_ACTIONS=[PRESET_GBL_ACTIONS,PRESET_INSTANCE_ACTIONS].flatten.freeze
658
+
659
+ def execute_file_action(action,config_name)
660
+ action=options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
661
+ config_name=instance_identifier() if config_name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
662
+ # those operations require existing option
663
+ raise "no such preset: #{config_name}" if PRESET_EXST_ACTIONS.include?(action) && !@config_presets.has_key?(config_name)
664
+ selected_preset=@config_presets[config_name]
665
+ case action
666
+ when :list
667
+ return {type: :value_list, data: @config_presets.keys, name: 'name'}
668
+ when :overview
669
+ return {type: :object_list, data: self.class.flatten_all_config(@config_presets)}
670
+ when :show
671
+ raise "no such config: #{config_name}" if selected_preset.nil?
672
+ return {type: :single_object, data: selected_preset}
673
+ when :delete
674
+ @config_presets.delete(config_name)
675
+ save_presets_to_config_file
676
+ return Main.result_status("Deleted: #{config_name}")
677
+ when :get
678
+ param_name=options.get_next_argument('parameter name')
679
+ value=selected_preset[param_name]
680
+ raise "no such option in preset #{config_name} : #{param_name}" if value.nil?
681
+ case value
682
+ when Numeric,String then return {type: :text, data: ExtendedValue.instance.evaluate(value.to_s)}
683
+ end
684
+ return {type: :single_object, data: value}
685
+ when :unset
686
+ param_name=options.get_next_argument('parameter name')
687
+ selected_preset.delete(param_name)
688
+ save_presets_to_config_file
689
+ return Main.result_status("Removed: #{config_name}: #{param_name}")
690
+ when :set
691
+ param_name=options.get_next_argument('parameter name')
692
+ param_value=options.get_next_argument('parameter value')
693
+ if !@config_presets.has_key?(config_name)
694
+ Log.log.debug("no such config name: #{config_name}, initializing")
695
+ selected_preset=@config_presets[config_name]={}
696
+ end
697
+ if selected_preset.has_key?(param_name)
698
+ Log.log.warn("overwriting value: #{selected_preset[param_name]}")
699
+ end
700
+ selected_preset[param_name]=param_value
701
+ save_presets_to_config_file
702
+ return Main.result_status("Updated: #{config_name}: #{param_name} <- #{param_value}")
703
+ when :initialize
704
+ config_value=options.get_next_argument('extended value (Hash)')
705
+ if @config_presets.has_key?(config_name)
706
+ Log.log.warn("configuration already exists: #{config_name}, overwriting")
707
+ end
708
+ @config_presets[config_name]=config_value
709
+ save_presets_to_config_file
710
+ return Main.result_status("Modified: #{@option_config_file}")
711
+ when :update
712
+ # get unprocessed options
713
+ theopts=options.get_options_table
714
+ Log.log.debug("opts=#{theopts}")
715
+ @config_presets[config_name]||={}
716
+ @config_presets[config_name].merge!(theopts)
717
+ # fix bug in 4.4 (creating key "true" in "default" preset)
718
+ @config_presets[CONF_PRESET_DEFAULT].delete(true) if @config_presets[CONF_PRESET_DEFAULT].is_a?(Hash)
719
+ save_presets_to_config_file
720
+ return Main.result_status("Updated: #{config_name}")
721
+ when :ask
722
+ options.ask_missing_mandatory=:yes
723
+ @config_presets[config_name]||={}
724
+ options.get_next_argument('option names',:multiple).each do |optionname|
725
+ option_value=options.get_interactive(:option,optionname)
726
+ @config_presets[config_name][optionname]=option_value
727
+ end
728
+ save_presets_to_config_file
729
+ return Main.result_status("Updated: #{config_name}")
730
+ end
731
+ end
732
+
733
+ ACTIONS=[PRESET_GBL_ACTIONS,:id,:preset,:open,:documentation,:genkey,:gem,:plugin,:flush_tokens,:echo,:wizard,:export_to_cli,:detect,:coffee,:ascp,:email_test,
734
+ :smtp_settings,:proxy_check,:folder,:file,:check_update,:initdemo,:vault].flatten.freeze
553
735
 
554
736
  # "config" plugin
555
737
  def execute_action
556
- action=self.options.get_next_command(ACTIONS)
738
+ action=options.get_next_command(ACTIONS)
557
739
  case action
558
- when :id
559
- config_name=self.options.get_next_argument('config name')
560
- action=self.options.get_next_command([:show,:delete,:set,:get,:unset,:initialize,:update,:ask])
561
- # those operations require existing option
562
- raise "no such preset: #{config_name}" if [:show,:delete,:get,:unset].include?(action) and !@config_presets.has_key?(config_name)
563
- selected_preset=@config_presets[config_name]
564
- case action
565
- when :show
566
- raise "no such config: #{config_name}" if selected_preset.nil?
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
740
+ when *PRESET_GBL_ACTIONS # older syntax
741
+ return execute_file_action(action,nil)
742
+ when :id # older syntax
743
+ return execute_file_action(nil,options.get_next_argument('config name'))
744
+ when :preset # newer syntax
745
+ return execute_file_action(nil,nil)
746
+ when :open
747
+ OpenApplication.instance.uri(@option_config_file.to_s) #file://
748
+ return Main.result_nothing
629
749
  when :documentation
630
750
  section=options.get_next_argument('private key file path',:single,:optional)
631
751
  section='#'+section unless section.nil?
632
- OpenApplication.instance.uri("#{@help_url}#{section}")
633
- return Main.result_nothing
634
- when :open
635
- OpenApplication.instance.uri("#{@option_config_file}") #file://
752
+ OpenApplication.instance.uri("#{@info[:help]}#{section}")
636
753
  return Main.result_nothing
637
754
  when :genkey # generate new rsa key
638
- private_key_path=self.options.get_next_argument('private key file path')
755
+ private_key_path=options.get_next_argument('private key file path')
639
756
  generate_rsa_private_key(private_key_path,DEFAULT_PRIVKEY_LENGTH)
640
757
  return Main.result_status('Generated key: '+private_key_path)
641
758
  when :echo # display the content of a value given on command line
642
- result={:type=>:other_struct, :data=>self.options.get_next_argument('value')}
759
+ result={type: :other_struct, data: options.get_next_argument('value')}
643
760
  # special for csv
644
- result[:type]=:object_list if result[:data].is_a?(Array) and result[:data].first.is_a?(Hash)
761
+ result[:type]=:object_list if result[:data].is_a?(Array) && result[:data].first.is_a?(Hash)
645
762
  return result
646
763
  when :flush_tokens
647
764
  deleted_files=Oauth.flush_tokens
648
- return {:type=>:value_list, :name=>'file',:data=>deleted_files}
649
- when :plugins
650
- return {:data => @plugins.keys.map { |i| { 'plugin' => i.to_s, 'path' => @plugins[i][:source] } } , :fields => ['plugin','path'], :type => :object_list }
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)}
765
+ return {type: :value_list, data: deleted_files, name: 'file'}
766
+ when :plugin
767
+ case options.get_next_command([:list,:create])
768
+ when :list
769
+ return {type: :object_list, data: @plugins.keys.map { |i| { 'plugin' => i.to_s, 'path' => @plugins[i][:source] } }, fields: ['plugin','path']}
770
+ when :create
771
+ plugin_name=options.get_next_argument('name',:single,:mandatory).downcase
772
+ plugin_folder=options.get_next_argument('folder',:single,:optional) || File.join(@main_folder,ASPERA_PLUGINS_FOLDERNAME)
773
+ plugin_file=File.join(plugin_folder,"#{plugin_name}.rb")
774
+ content=<<~END_OF_PLUGIN_CODE
775
+ require 'aspera/cli/plugin'
776
+ module Aspera
777
+ module Cli
778
+ module Plugins
779
+ class #{plugin_name.capitalize} < Plugin
780
+ ACTIONS=[]
781
+ def execute_action; return Main.result_status('You called plugin #{plugin_name}'); end
782
+ end # #{plugin_name.capitalize}
783
+ end # Plugins
784
+ end # Cli
785
+ end # Aspera
786
+ END_OF_PLUGIN_CODE
787
+ File.write(plugin_file,content)
788
+ return Main.result_status("Created #{plugin_file}")
789
+ end
655
790
  when :wizard
656
791
  # interactive mode
657
- self.options.ask_missing_mandatory=true
792
+ options.ask_missing_mandatory=true
658
793
  # register url option
659
794
  BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
660
795
  # get from option, or ask
661
- instance_url=self.options.get_option(:url,:mandatory)
796
+ instance_url=options.get_option(:url,:mandatory)
662
797
  # allow user to tell the preset name
663
- preset_name=self.options.get_option(:id,:optional)
664
- appli=ApiDetector.discover_product(instance_url)
665
- plugin_name="<replace per app>"
666
- test_args="<replace per app>"
667
- case appli[:product]
798
+ preset_name=options.get_option(:id,:optional)
799
+ # allow user to specify type of application
800
+ application=options.get_option(:value,:optional)
801
+ application=application.nil? ? identify_plugin_for_url(instance_url)[:product] : application.to_sym
802
+ plugin_name='<replace per app>'
803
+ test_args='<replace per app>'
804
+ case application
668
805
  when :aoc
669
806
  self.format.display_status('Detected: Aspera on Cloud'.bold)
670
807
  plugin_name=AOC_COMMAND_CURRENT
671
- organization,instance_domain=AoC.parse_url(instance_url)
808
+ organization=AoC.parse_url(instance_url).first
672
809
  # if not defined by user, generate name
673
- preset_name=[appli[:product],organization].join('_') if preset_name.nil?
810
+ preset_name=[application,organization].join('_') if preset_name.nil?
674
811
  self.format.display_status("Preparing preset: #{preset_name}")
675
812
  # init defaults if necessary
676
813
  @config_presets[CONF_PRESET_DEFAULT]||={}
677
- option_override=self.options.get_option(:override,:mandatory)
678
- option_default=self.options.get_option(:default,:mandatory)
814
+ option_override=options.get_option(:override,:mandatory)
815
+ option_default=options.get_option(:default,:mandatory)
679
816
  Log.log.error("override=#{option_override} -> #{option_override.class}")
680
- raise CliError,"A default configuration already exists for plugin '#{plugin_name}' (use --override=yes or --default=no)" if !option_override and option_default and @config_presets[CONF_PRESET_DEFAULT].has_key?(plugin_name)
681
- raise CliError,"Preset already exists: #{preset_name} (use --override=yes or --id=<name>)" if !option_override and @config_presets.has_key?(preset_name)
817
+ raise CliError,"A default configuration already exists for plugin '#{plugin_name}' (use --override=yes or --default=no)" if !option_override && option_default && @config_presets[CONF_PRESET_DEFAULT].has_key?(plugin_name)
818
+ raise CliError,"Preset already exists: #{preset_name} (use --override=yes or --id=<name>)" if !option_override && @config_presets.has_key?(preset_name)
682
819
  # lets see if path to priv key is provided
683
- private_key_path=self.options.get_option(:pkeypath,:optional)
820
+ private_key_path=options.get_option(:pkeypath,:optional)
684
821
  # give a chance to provide
685
822
  if private_key_path.nil?
686
823
  self.format.display_status('Please provide path to your private RSA key, or empty to generate one:')
687
- private_key_path=self.options.get_option(:pkeypath,:mandatory).to_s
824
+ private_key_path=options.get_option(:pkeypath,:mandatory).to_s
688
825
  end
689
826
  # else generate path
690
827
  if private_key_path.empty?
@@ -702,74 +839,73 @@ END_OF_TEMPLATE
702
839
  # declare command line options for AoC
703
840
  require 'aspera/cli/plugins/aoc'
704
841
  # make username mandatory for jwt, this triggers interactive input
705
- self.options.get_option(:username,:mandatory)
842
+ options.get_option(:username,:mandatory)
706
843
  # instanciate AoC plugin, so that command line options are known
707
- files_plugin=self.class.plugin_new(plugin_name,@agents.merge({skip_basic_auth_options: true, private_key_path: private_key_path}))
708
- aoc_api=files_plugin.get_api
844
+ aoc_api=self.class.plugin_class(plugin_name).new(@agents.merge({skip_basic_auth_options: true, private_key_path: private_key_path})).aoc_api
709
845
  auto_set_pub_key=false
710
846
  auto_set_jwt=false
711
847
  use_browser_authentication=false
712
- if self.options.get_option(:use_generic_client)
848
+ if options.get_option(:use_generic_client)
713
849
  self.format.display_status('Using global client_id.')
714
850
  self.format.display_status('Please Login to your Aspera on Cloud instance.'.red)
715
851
  self.format.display_status('Navigate to your "Account Settings"'.red)
716
852
  self.format.display_status('Check or update the value of "Public Key" to be:'.red.blink)
717
- self.format.display_status("#{pub_key_pem}")
718
- if ! self.options.get_option(:test_mode)
853
+ self.format.display_status(pub_key_pem.to_s)
854
+ if !options.get_option(:test_mode)
719
855
  self.format.display_status('Once updated or validated, press enter.')
720
856
  OpenApplication.instance.uri(instance_url)
721
- STDIN.gets
857
+ $stdin.gets
722
858
  end
723
859
  else
724
860
  self.format.display_status('Using organization specific client_id.')
725
- if self.options.get_option(:client_id,:optional).nil? or self.options.get_option(:client_secret,:optional).nil?
861
+ if options.get_option(:client_id,:optional).nil? || options.get_option(:client_secret,:optional).nil?
726
862
  self.format.display_status('Please login to your Aspera on Cloud instance.'.red)
727
863
  self.format.display_status('Go to: Apps->Admin->Organization->Integrations')
728
864
  self.format.display_status('Create or check if there is an existing integration named:')
729
- self.format.display_status("- name: #{@tool_name}")
865
+ self.format.display_status("- name: #{@info[:name]}")
730
866
  self.format.display_status("- redirect uri: #{DEFAULT_REDIRECT}")
731
867
  self.format.display_status('- origin: localhost')
732
868
  self.format.display_status('Once created or identified,')
733
869
  self.format.display_status('Please enter:'.red)
734
870
  end
735
871
  OpenApplication.instance.uri("#{instance_url}/#{AOC_PATH_API_CLIENTS}")
736
- self.options.get_option(:client_id,:mandatory)
737
- self.options.get_option(:client_secret,:mandatory)
872
+ options.get_option(:client_id,:mandatory)
873
+ options.get_option(:client_secret,:mandatory)
738
874
  use_browser_authentication=true
739
875
  end
740
876
  if use_browser_authentication
741
877
  self.format.display_status('We will use web authentication to bootstrap.')
742
878
  auto_set_pub_key=true
743
879
  auto_set_jwt=true
744
- @api_aoc.oauth.params[:auth]=:web
745
- @api_aoc.oauth.params[:redirect_uri]=DEFAULT_REDIRECT
746
- @api_aoc.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
880
+ aoc_api.oauth.params[:crtype]=:web
881
+ aoc_api.oauth.params[:web][:redirect_uri]=DEFAULT_REDIRECT
882
+ aoc_api.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
747
883
  end
748
884
  myself=aoc_api.read('self')[:data]
749
885
  if auto_set_pub_key
750
- raise CliError,'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? or option_override
886
+ raise CliError,'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
751
887
  self.format.display_status('Updating profile with new key')
752
888
  aoc_api.update("users/#{myself['id']}",{'public_key'=>pub_key_pem})
753
889
  end
754
890
  if auto_set_jwt
755
891
  self.format.display_status('Enabling JWT for client')
756
- aoc_api.update("clients/#{self.options.get_option(:client_id)}",{'jwt_grant_enabled'=>true,'explicit_authorization_required'=>false})
892
+ aoc_api.update("clients/#{options.get_option(:client_id)}",{'jwt_grant_enabled'=>true,'explicit_authorization_required'=>false})
757
893
  end
758
894
  self.format.display_status("Creating new config preset: #{preset_name}")
759
895
  @config_presets[preset_name]={
760
- :url.to_s =>self.options.get_option(:url),
896
+ :url.to_s =>options.get_option(:url),
761
897
  :username.to_s =>myself['email'],
762
898
  :auth.to_s =>:jwt.to_s,
763
- :private_key.to_s =>'@file:'+private_key_path,
899
+ :private_key.to_s =>'@file:'+private_key_path
764
900
  }
765
901
  # set only if non nil
766
902
  [:client_id,:client_secret].each do |s|
767
- o=self.options.get_option(s)
903
+ o=options.get_option(s)
768
904
  @config_presets[preset_name][s.to_s] = o unless o.nil?
769
905
  end
770
- test_args="#{plugin_name} user info show"
906
+ test_args="#{plugin_name} user profile show"
771
907
  else
772
- raise CliBadArgument,"Supports only: aoc. Detected: #{appli}"
908
+ raise CliBadArgument,"Supports only: aoc. Detected: #{application}"
773
909
  end # product
774
910
  if option_default
775
911
  self.format.display_status("Setting config preset as default for #{plugin_name}")
@@ -779,29 +915,29 @@ END_OF_TEMPLATE
779
915
  end
780
916
  self.format.display_status('Saving config file.')
781
917
  save_presets_to_config_file
782
- return Main.result_status("Done.\nYou can test with:\n#{@tool_name} #{test_args}")
918
+ return Main.result_status("Done.\nYou can test with:\n#{@info[:name]} #{test_args}")
783
919
  when :export_to_cli
784
920
  self.format.display_status('Exporting: Aspera on Cloud')
785
921
  require 'aspera/cli/plugins/aoc'
786
922
  # need url / username
787
923
  add_plugin_default_preset(AOC_COMMAND_V3.to_sym)
788
924
  # instanciate AoC plugin
789
- files_plugin=self.class.plugin_new(AOC_COMMAND_CURRENT,@agents) # TODO: is this line needed ?
790
- url=self.options.get_option(:url,:mandatory)
925
+ self.class.plugin_class(AOC_COMMAND_CURRENT).new(@agents) # TODO: is this line needed ? get options ?
926
+ url=options.get_option(:url,:mandatory)
791
927
  cli_conf_file=Fasp::Installation.instance.cli_conf_file
792
928
  data=JSON.parse(File.read(cli_conf_file))
793
929
  organization,instance_domain=AoC.parse_url(url)
794
930
  key_basename='org_'+organization+'.pem'
795
931
  key_file=File.join(File.dirname(File.dirname(cli_conf_file)),'etc',key_basename)
796
- File.write(key_file,self.options.get_option(:private_key,:mandatory))
932
+ File.write(key_file,options.get_option(:private_key,:mandatory))
797
933
  new_conf={
798
934
  'organization' => organization,
799
935
  'hostname' => [organization,instance_domain].join('.'),
800
936
  'privateKeyFilename' => key_basename,
801
- 'username' => self.options.get_option(:username,:mandatory)
937
+ 'username' => options.get_option(:username,:mandatory)
802
938
  }
803
- new_conf['clientId']=self.options.get_option(:client_id,:optional)
804
- new_conf['clientSecret']=self.options.get_option(:client_secret,:optional)
939
+ new_conf['clientId']=options.get_option(:client_id,:optional)
940
+ new_conf['clientSecret']=options.get_option(:client_secret,:optional)
805
941
  if new_conf['clientId'].nil?
806
942
  new_conf['clientId'],new_conf['clientSecret']=AoC.get_client_info()
807
943
  end
@@ -818,14 +954,18 @@ END_OF_TEMPLATE
818
954
  when :detect
819
955
  # need url / username
820
956
  BasicAuthPlugin.new(@agents)
821
- return Main.result_status("Found: #{ApiDetector.discover_product(self.options.get_option(:url,:mandatory))}")
957
+ return {type: :single_object, data: identify_plugin_for_url(options.get_option(:url,:mandatory))}
822
958
  when :coffee
823
959
  OpenApplication.instance.uri('https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg')
824
960
  return Main.result_nothing
825
961
  when :ascp
826
962
  execute_action_ascp
827
- when :gem_path
828
- return Main.result_status(self.class.gem_root)
963
+ when :gem
964
+ case options.get_next_command([:path,:version,:name])
965
+ when :path then return Main.result_status(self.class.gem_src_root)
966
+ when :version then return Main.result_status(Aspera::Cli::VERSION)
967
+ when :name then return Main.result_status(@info[:gem])
968
+ end
829
969
  when :folder
830
970
  return Main.result_status(@main_folder)
831
971
  when :file
@@ -834,19 +974,21 @@ END_OF_TEMPLATE
834
974
  send_email_template({},EMAIL_TEST_TEMPLATE)
835
975
  return Main.result_nothing
836
976
  when :smtp_settings
837
- return {:type=>:single_object,:data=>email_settings}
977
+ return {type: :single_object, data: email_settings}
838
978
  when :proxy_check
839
- pac_url=self.options.get_option(:fpac,:mandatory)
840
- server_url=self.options.get_next_argument('server url')
841
- return Main.result_status(Aspera::ProxyAutoConfig.new(UriReader.read(pac_url)).get_proxy(server_url))
979
+ # ensure fpac was provided
980
+ options.get_option(:fpac,:mandatory)
981
+ server_url=options.get_next_argument('server url')
982
+ return Main.result_status(@pac_exec.find_proxy_for_url(server_url))
842
983
  when :check_update
843
- return {:type=>:single_object, :data=>check_gem_version}
984
+ return {type: :single_object, data: check_gem_version}
844
985
  when :initdemo
845
986
  if @config_presets.has_key?(DEMO_SERVER_PRESET)
846
987
  Log.log.warn("Demo server preset already present: #{DEMO_SERVER_PRESET}")
847
988
  else
848
989
  Log.log.info("Creating Demo server preset: #{DEMO_SERVER_PRESET}")
849
- @config_presets[DEMO_SERVER_PRESET]={'url'=>'ssh://'+DEMO+'.asperasoft.com:33001','username'=>AOC_COMMAND_V2,'ssAP'.downcase.reverse+'drow'.reverse=>DEMO+AOC_COMMAND_V2}
990
+ @config_presets[DEMO_SERVER_PRESET]=
991
+ {'url'=>'ssh://'+DEMO+'.asperasoft.com:33001','username'=>AOC_COMMAND_V2,'ssAP'.downcase.reverse+'drow'.reverse=>DEMO+AOC_COMMAND_V2}
850
992
  end
851
993
  @config_presets[CONF_PRESET_DEFAULT]||={}
852
994
  if @config_presets[CONF_PRESET_DEFAULT].has_key?(SERVER_COMMAND)
@@ -857,16 +999,56 @@ END_OF_TEMPLATE
857
999
  Log.log.info("Setting server default preset to : #{DEMO_SERVER_PRESET}")
858
1000
  end
859
1001
  save_presets_to_config_file
860
- return Main.result_status("Done")
1002
+ return Main.result_status('Done')
1003
+ when :vault
1004
+ command=options.get_next_command([:init,:list,:get,:set,:delete])
1005
+ case command
1006
+ when :init
1007
+ type=options.get_option(:value,:optional)
1008
+ case type
1009
+ when 'config',NilClass
1010
+ raise 'default secrets already exists' if @config_presets.has_key?(CONF_PRESET_SECRETS)
1011
+ @config_presets[CONF_PRESET_SECRETS]={}
1012
+ set_global_default(:secrets,"@preset:#{CONF_PRESET_SECRETS}")
1013
+ else raise 'no such vault type'
1014
+ end
1015
+ return Main.result_status('Done')
1016
+ when :list
1017
+ return {type: :object_list, data: vault.list}
1018
+ when :set
1019
+ # register url option
1020
+ BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
1021
+ username=options.get_option(:username,:mandatory)
1022
+ url=options.get_option(:url,:mandatory)
1023
+ description=options.get_option(:value,:optional)
1024
+ secret=options.get_next_argument('secret')
1025
+ vault.set(username: username, url: url, description: description, secret: secret)
1026
+ save_presets_to_config_file if vault.is_a?(Keychain::EncryptedHash)
1027
+ return Main.result_status('Done')
1028
+ when :get
1029
+ # register url option
1030
+ BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
1031
+ username=options.get_option(:username,:mandatory)
1032
+ url=options.get_option(:url,:optional)
1033
+ result=vault.get(username: username, url: url)
1034
+ return {type: :single_object, data: result}
1035
+ when :delete
1036
+ # register url option
1037
+ BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
1038
+ username=options.get_option(:username,:mandatory)
1039
+ url=options.get_option(:url,:optional)
1040
+ vault.delete(username: username, url: url)
1041
+ return Main.result_status('Done')
1042
+ end
861
1043
  else raise 'INTERNAL ERROR: wrong case'
862
1044
  end
863
1045
  end
864
1046
 
865
1047
  # @return email server setting with defaults if not defined
866
1048
  def email_settings
867
- smtp=self.options.get_option(:smtp,:mandatory)
1049
+ smtp=options.get_option(:smtp,:mandatory)
868
1050
  # change string keys into symbol keys
869
- smtp=smtp.keys.inject({}){|m,v|m[v.to_sym]=smtp[v];m}
1051
+ smtp=smtp.keys.each_with_object({}){|v,m|m[v.to_sym]=smtp[v];}
870
1052
  # defaults
871
1053
  smtp[:tls]||=true
872
1054
  smtp[:port]||=smtp[:tls]?587:25
@@ -896,7 +1078,7 @@ END_OF_TEMPLATE
896
1078
  raise "Missing email parameter: #{n}" unless vars.has_key?(n)
897
1079
  end
898
1080
  start_options=[mail_conf[:domain]]
899
- start_options.push(mail_conf[:username],mail_conf[:password],:login) if mail_conf.has_key?(:username) and mail_conf.has_key?(:password)
1081
+ start_options.push(mail_conf[:username],mail_conf[:password],:login) if mail_conf.has_key?(:username) && mail_conf.has_key?(:password)
900
1082
  # create a binding with only variables defined in vars
901
1083
  template_binding=empty_binding
902
1084
  # add variables to binding
@@ -909,8 +1091,8 @@ END_OF_TEMPLATE
909
1091
  Log.dump(:msg_with_headers,msg_with_headers)
910
1092
  smtp = Net::SMTP.new(mail_conf[:server], mail_conf[:port])
911
1093
  smtp.enable_starttls if mail_conf[:tls]
912
- smtp.start(*start_options) do |smtp|
913
- smtp.send_message(msg_with_headers, vars[:from_email], vars[:to])
1094
+ smtp.start(*start_options) do |smtp_session|
1095
+ smtp_session.send_message(msg_with_headers, vars[:from_email], vars[:to])
914
1096
  end
915
1097
  end
916
1098
 
@@ -924,16 +1106,16 @@ END_OF_TEMPLATE
924
1106
  # returns [String] name if config_presets has default
925
1107
  # returns nil if there is no config or bypass default params
926
1108
  def get_plugin_default_config_name(plugin_sym)
927
- raise "internal error: config_presets shall be defined" if @config_presets.nil?
1109
+ raise 'internal error: config_presets shall be defined' if @config_presets.nil?
928
1110
  if !@use_plugin_defaults
929
1111
  Log.log.debug('skip default config')
930
1112
  return nil
931
1113
  end
932
- if @config_presets.has_key?(CONF_PRESET_DEFAULT) and
1114
+ if @config_presets.has_key?(CONF_PRESET_DEFAULT) &&
933
1115
  @config_presets[CONF_PRESET_DEFAULT].has_key?(plugin_sym.to_s)
934
1116
  default_config_name=@config_presets[CONF_PRESET_DEFAULT][plugin_sym.to_s]
935
1117
  if !@config_presets.has_key?(default_config_name)
936
- Log.log.error("Default config name [#{default_config_name}] specified for plugin [#{plugin_sym.to_s}], but it does not exist in config file.\nPlease fix the issue: either create preset with one parameter (#{@tool_name} config id #{default_config_name} init @json:'{}') or remove default (#{@tool_name} config id default remove #{plugin_sym.to_s}).")
1118
+ Log.log.error("Default config name [#{default_config_name}] specified for plugin [#{plugin_sym}], but it does not exist in config file.\nPlease fix the issue: either create preset with one parameter (#{@info[:name]} config id #{default_config_name} init @json:'{}') or remove default (#{@info[:name]} config id default remove #{plugin_sym}).")
937
1119
  end
938
1120
  raise CliError,"Config name [#{default_config_name}] must be a hash, check config file." if !@config_presets[default_config_name].is_a?(Hash)
939
1121
  return default_config_name
@@ -941,6 +1123,42 @@ END_OF_TEMPLATE
941
1123
  return nil
942
1124
  end # get_plugin_default_config_name
943
1125
 
1126
+ def vault
1127
+ if @vault.nil?
1128
+ vault_info=options.get_option(:secrets,:optional)
1129
+ case vault_info
1130
+ when Hash
1131
+ @vault=Keychain::EncryptedHash.new(vault_info)
1132
+ when /^system/
1133
+ name=vault_info.start_with?('system:') ? vault_info[7..-1] : nil
1134
+ case Environment.os
1135
+ when Environment::OS_X
1136
+ @vault=Keychain::MacosSecurity.new(name)
1137
+ when Environment::OS_WINDOWS,Environment::OS_LINUX,Environment::OS_AIX
1138
+ raise 'not implemented'
1139
+ else raise 'Error'
1140
+ end
1141
+ when NilClass
1142
+ # keep nil
1143
+ else
1144
+ raise CliBadArgument,'secrets shall be Hash'
1145
+ end
1146
+ end
1147
+ raise 'No vault defined' if @vault.nil?
1148
+ @vault
1149
+ end
1150
+
1151
+ def get_secret(options)
1152
+ raise 'options shall be Hash' unless options.is_a?(Hash)
1153
+ raise 'options shall have username' unless options.has_key?(:username)
1154
+ secret=self.options.get_option(:secret,:optional)
1155
+ if secret.nil?
1156
+ secret=vault.get(options) rescue nil
1157
+ # mandatory by default
1158
+ raise "please provide secret for #{options[:username]}" if secret.nil? && (options[:mandatory].nil? || options[:mandatory])
1159
+ end
1160
+ return secret
1161
+ end
944
1162
  end
945
1163
  end
946
1164
  end