aspera-cli 4.5.0 → 4.8.0

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