aspera-cli 4.17.0 → 4.18.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 (81) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -4
  3. data/CHANGELOG.md +23 -0
  4. data/CONTRIBUTING.md +15 -1
  5. data/README.md +620 -378
  6. data/bin/ascli +5 -0
  7. data/bin/asession +2 -2
  8. data/lib/aspera/agent/alpha.rb +6 -4
  9. data/lib/aspera/agent/base.rb +9 -6
  10. data/lib/aspera/agent/connect.rb +4 -4
  11. data/lib/aspera/agent/direct.rb +56 -37
  12. data/lib/aspera/agent/httpgw.rb +23 -324
  13. data/lib/aspera/agent/node.rb +19 -20
  14. data/lib/aspera/agent/trsdk.rb +19 -20
  15. data/lib/aspera/api/aoc.rb +17 -14
  16. data/lib/aspera/api/cos_node.rb +4 -4
  17. data/lib/aspera/api/httpgw.rb +339 -0
  18. data/lib/aspera/api/node.rb +34 -21
  19. data/lib/aspera/ascmd.rb +4 -3
  20. data/lib/aspera/ascp/installation.rb +15 -7
  21. data/lib/aspera/ascp/management.rb +2 -2
  22. data/lib/aspera/cli/basic_auth_plugin.rb +5 -9
  23. data/lib/aspera/cli/extended_value.rb +12 -6
  24. data/lib/aspera/cli/formatter.rb +155 -65
  25. data/lib/aspera/cli/hints.rb +18 -0
  26. data/lib/aspera/cli/main.rb +22 -29
  27. data/lib/aspera/cli/manager.rb +53 -36
  28. data/lib/aspera/cli/plugin.rb +26 -17
  29. data/lib/aspera/cli/plugin_factory.rb +31 -20
  30. data/lib/aspera/cli/plugins/alee.rb +14 -2
  31. data/lib/aspera/cli/plugins/aoc.rb +141 -131
  32. data/lib/aspera/cli/plugins/ats.rb +1 -1
  33. data/lib/aspera/cli/plugins/config.rb +52 -46
  34. data/lib/aspera/cli/plugins/console.rb +8 -5
  35. data/lib/aspera/cli/plugins/faspex.rb +27 -19
  36. data/lib/aspera/cli/plugins/faspex5.rb +222 -149
  37. data/lib/aspera/cli/plugins/faspio.rb +85 -0
  38. data/lib/aspera/cli/plugins/httpgw.rb +55 -0
  39. data/lib/aspera/cli/plugins/node.rb +86 -29
  40. data/lib/aspera/cli/plugins/orchestrator.rb +31 -29
  41. data/lib/aspera/cli/plugins/preview.rb +6 -2
  42. data/lib/aspera/cli/plugins/server.rb +5 -5
  43. data/lib/aspera/cli/plugins/shares.rb +16 -14
  44. data/lib/aspera/cli/sync_actions.rb +6 -6
  45. data/lib/aspera/cli/transfer_agent.rb +5 -4
  46. data/lib/aspera/cli/version.rb +1 -1
  47. data/lib/aspera/environment.rb +7 -6
  48. data/lib/aspera/faspex_gw.rb +5 -4
  49. data/lib/aspera/faspex_postproc.rb +2 -2
  50. data/lib/aspera/log.rb +6 -3
  51. data/lib/aspera/node_simulator.rb +2 -2
  52. data/lib/aspera/oauth/base.rb +31 -19
  53. data/lib/aspera/oauth/factory.rb +12 -13
  54. data/lib/aspera/oauth/generic.rb +1 -0
  55. data/lib/aspera/oauth/jwt.rb +18 -15
  56. data/lib/aspera/oauth/url_json.rb +8 -6
  57. data/lib/aspera/open_application.rb +5 -7
  58. data/lib/aspera/persistency_folder.rb +2 -2
  59. data/lib/aspera/preview/generator.rb +3 -3
  60. data/lib/aspera/preview/options.rb +3 -3
  61. data/lib/aspera/preview/terminal.rb +4 -4
  62. data/lib/aspera/preview/utils.rb +3 -3
  63. data/lib/aspera/proxy_auto_config.rb +5 -1
  64. data/lib/aspera/rest.rb +60 -74
  65. data/lib/aspera/rest_call_error.rb +1 -1
  66. data/lib/aspera/rest_error_analyzer.rb +2 -2
  67. data/lib/aspera/rest_errors_aspera.rb +1 -1
  68. data/lib/aspera/resumer.rb +1 -1
  69. data/lib/aspera/secret_hider.rb +2 -4
  70. data/lib/aspera/ssh.rb +1 -1
  71. data/lib/aspera/transfer/parameters.rb +39 -36
  72. data/lib/aspera/transfer/spec.rb +2 -0
  73. data/lib/aspera/transfer/sync.rb +2 -1
  74. data/lib/aspera/transfer/uri.rb +1 -1
  75. data/lib/aspera/uri_reader.rb +5 -4
  76. data/lib/aspera/web_auth.rb +1 -1
  77. data/lib/aspera/web_server_simple.rb +4 -3
  78. data.tar.gz.sig +0 -0
  79. metadata +5 -3
  80. metadata.gz.sig +0 -0
  81. data/lib/aspera/cli/plugins/bss.rb +0 -71
data/lib/aspera/rest.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'aspera/rest_errors_aspera'
4
+ require 'aspera/rest_error_analyzer'
3
5
  require 'aspera/log'
4
6
  require 'aspera/assert'
5
7
  require 'aspera/oauth'
6
- require 'aspera/rest_error_analyzer'
7
8
  require 'aspera/hash_ext'
8
- require 'aspera/rest_errors_aspera'
9
9
  require 'net/http'
10
10
  require 'net/https'
11
11
  require 'json'
@@ -153,47 +153,6 @@ module Aspera
153
153
  return @http_session
154
154
  end
155
155
 
156
- def build_request(
157
- operation:,
158
- subpath:,
159
- url_params:,
160
- json_params:,
161
- www_body_params:,
162
- text_body_params:,
163
- headers:
164
- )
165
- # TODO: shall we percent encode subpath (spaces) test with access key delete with space in id
166
- # URI.escape()
167
- separator = !['', '/'].include?(subpath) || @base_url.end_with?('/') ? '/' : ''
168
- uri = self.class.build_uri("#{@base_url}#{separator}#{subpath}", url_params)
169
- Log.log.debug{"URI=#{uri}"}
170
- begin
171
- # instantiate request object based on string name
172
- req = Net::HTTP.const_get(operation.capitalize).new(uri)
173
- rescue NameError
174
- raise "unsupported operation : #{operation}"
175
- end
176
- if !json_params.nil?
177
- req.body = JSON.generate(json_params) # , ascii_only: true
178
- req['Content-Type'] = 'application/json'
179
- end
180
- if !www_body_params.nil?
181
- req.body = URI.encode_www_form(www_body_params)
182
- req['Content-Type'] = 'application/x-www-form-urlencoded'
183
- end
184
- if !text_body_params.nil?
185
- req.body = text_body_params
186
- end
187
- # set headers
188
- headers.each do |key, value|
189
- req[key] = value
190
- end
191
- # :type = :basic
192
- req.basic_auth(@auth_params[:username], @auth_params[:password]) if @auth_params[:type].eql?(:basic)
193
- Log.log.debug{Log.dump(:req_body, req.body)}
194
- return req
195
- end
196
-
197
156
  public
198
157
 
199
158
  attr_reader :auth_params
@@ -217,7 +176,7 @@ module Aspera
217
176
  # :url_query [:url] a hash
218
177
  # :* [:oauth2] see OAuth::Factory class
219
178
  # @param not_auth_codes [Array] codes that trigger a refresh/regeneration of bearer token
220
- # @param redirect_max [int] max redirections allowed
179
+ # @param redirect_max [int] max redirection allowed
221
180
  def initialize(
222
181
  base_url:,
223
182
  auth: nil,
@@ -255,21 +214,21 @@ module Aspera
255
214
  return @oauth
256
215
  end
257
216
 
258
- def oauth_token(force_refresh: false)
259
- Aspera.assert_values(force_refresh, [true, false])
260
- return oauth.get_authorization(use_refresh_token: force_refresh)
261
- end
262
-
263
217
  # HTTP/S REST call
218
+ # @param operation [String] HTTP operation (GET, POST, PUT, DELETE)
219
+ # @param subpath [String] subpath of REST API
220
+ # @param query [Hash] URL parameters
221
+ # @param body [Hash, String] body parameters
222
+ # @param body_type [Symbol] type of body parameters (:json, :www, :text, nil)
264
223
  # @param save_to_file (filepath)
265
224
  # @param return_error (bool)
225
+ # @param headers [Hash] additional headers
266
226
  def call(
267
227
  operation:,
268
228
  subpath: nil,
269
- json_params: nil,
270
- url_params: nil,
271
- www_body_params: nil,
272
- text_body_params: nil,
229
+ query: nil,
230
+ body: nil,
231
+ body_type: nil,
273
232
  save_to_file: nil,
274
233
  return_error: false,
275
234
  headers: nil
@@ -293,20 +252,49 @@ module Aspera
293
252
  Log.log.debug('using Basic auth')
294
253
  # done in build_req
295
254
  when :oauth2
296
- headers['Authorization'] = oauth_token unless headers.key?('Authorization')
255
+ headers['Authorization'] = oauth.token unless headers.key?('Authorization')
297
256
  when :url
298
- url_params ||= {}
257
+ query ||= {}
299
258
  @auth_params[:url_query].each do |key, value|
300
- url_params[key] = value
259
+ query[key] = value
301
260
  end
302
261
  else Aspera.error_unexpected_value(@auth_params[:type])
303
262
  end
304
263
  result = {http: nil}
305
- # start a block to be able to retry the actual HTTP request
264
+ # start a block to be able to retry the actual HTTP request in case of OAuth token expiration
306
265
  begin
307
- req = build_request(
308
- operation: operation, subpath: subpath, json_params: json_params, url_params: url_params, www_body_params: www_body_params,
309
- text_body_params: text_body_params, headers: headers)
266
+ # TODO: shall we percent encode subpath (spaces) test with access key delete with space in id
267
+ # URI.escape()
268
+ separator = !['', '/'].include?(subpath) || @base_url.end_with?('/') ? '/' : ''
269
+ uri = self.class.build_uri("#{@base_url}#{separator}#{subpath}", query)
270
+ Log.log.debug{"URI=#{uri}"}
271
+ begin
272
+ # instantiate request object based on string name
273
+ req = Net::HTTP.const_get(operation.capitalize).new(uri)
274
+ rescue NameError
275
+ raise "unsupported operation : #{operation}"
276
+ end
277
+ case body_type
278
+ when :json
279
+ req.body = JSON.generate(body) # , ascii_only: true
280
+ req['Content-Type'] = 'application/json'
281
+ when :www
282
+ req.body = URI.encode_www_form(body)
283
+ req['Content-Type'] = 'application/x-www-form-urlencoded'
284
+ when :text
285
+ req.body = body
286
+ req['Content-Type'] = 'text/plain'
287
+ when nil
288
+ else
289
+ raise "unsupported body type : #{body_type}"
290
+ end
291
+ # set headers
292
+ headers.each do |key, value|
293
+ req[key] = value
294
+ end
295
+ # :type = :basic
296
+ req.basic_auth(@auth_params[:username], @auth_params[:password]) if @auth_params[:type].eql?(:basic)
297
+ Log.log.debug{Log.dump(:req_body, req.body)}
310
298
  # we try the call, and will retry only if oauth, as we can, first with refresh, and then re-auth if refresh is bad
311
299
  oauth_tries ||= 2
312
300
  # initialize with number of initial retries allowed, nil gives zero
@@ -347,7 +335,7 @@ module Aspera
347
335
  # rename at the end
348
336
  File.rename(target_file_tmp, target_file)
349
337
  file_saved = true
350
- end # save_to_file
338
+ end
351
339
  end
352
340
  # sometimes there is a UTF8 char (e.g. (c) ), TODO : related to mime type encoding ?
353
341
  # result[:http].body.force_encoding('UTF-8') if result[:http].body.is_a?(String)
@@ -367,16 +355,16 @@ module Aspera
367
355
  if @not_auth_codes.include?(result[:http].code.to_s) && @auth_params[:type].eql?(:oauth2)
368
356
  begin
369
357
  # try to use refresh token
370
- req['Authorization'] = oauth_token(force_refresh: true)
358
+ req['Authorization'] = oauth.token(refresh: true)
371
359
  rescue RestCallError => e_tok
372
360
  e = e_tok
373
361
  Log.log.error('refresh failed'.bg_red)
374
362
  # regenerate a brand new token
375
- req['Authorization'] = oauth_token(force_refresh: true)
363
+ req['Authorization'] = oauth.token(refresh: true)
376
364
  end
377
365
  Log.log.debug{"using new token=#{headers['Authorization']}"}
378
366
  retry if (oauth_tries -= 1).nonzero?
379
- end # if oauth
367
+ end
380
368
  # redirect ? (any code beginning with 3)
381
369
  if e.response.is_a?(Net::HTTPRedirection) && tries_remain_redirect.positive?
382
370
  tries_remain_redirect -= 1
@@ -390,13 +378,12 @@ module Aspera
390
378
  end
391
379
  # forwards the request to the new location
392
380
  return self.class.new(base_url: new_url, redirect_max: tries_remain_redirect).call(
393
- operation: operation, json_params: json_params,
394
- url_params: url_params, www_body_params: www_body_params, text_body_params: text_body_params,
381
+ operation: operation, query: query, body: body, body_type: body_type,
395
382
  save_to_file: save_to_file, return_error: return_error, headers: headers)
396
383
  end
397
384
  # raise exception if could not retry and not return error in result
398
385
  raise e unless return_error
399
- end # begin request
386
+ end
400
387
  Log.log.debug{"result=#{result}"}
401
388
  return result
402
389
  end
@@ -405,21 +392,20 @@ module Aspera
405
392
  # CRUD methods here
406
393
  #
407
394
 
408
- # @param encoding : one of: :json_params, :url_params
409
- def create(subpath, params, encoding=:json_params)
410
- return call(operation: 'POST', subpath: subpath, headers: {'Accept' => 'application/json'}, encoding => params)
395
+ def create(subpath, params)
396
+ return call(operation: 'POST', subpath: subpath, headers: {'Accept' => 'application/json'}, body: params, body_type: :json)
411
397
  end
412
398
 
413
399
  def read(subpath, query=nil)
414
- return call(operation: 'GET', subpath: subpath, headers: {'Accept' => 'application/json'}, url_params: query)
400
+ return call(operation: 'GET', subpath: subpath, headers: {'Accept' => 'application/json'}, query: query)
415
401
  end
416
402
 
417
403
  def update(subpath, params)
418
- return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => 'application/json'}, json_params: params)
404
+ return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => 'application/json'}, body: params, body_type: :json)
419
405
  end
420
406
 
421
407
  def delete(subpath, params=nil)
422
- return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => 'application/json'}, url_params: params)
408
+ return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => 'application/json'}, query: params)
423
409
  end
424
410
 
425
411
  def cancel(subpath)
@@ -453,4 +439,4 @@ module Aspera
453
439
  end
454
440
  end
455
441
  end
456
- end # module Aspera
442
+ end
@@ -3,7 +3,7 @@
3
3
  module Aspera
4
4
  # raised on error after REST call
5
5
  class RestCallError < StandardError
6
- attr_accessor :request, :response
6
+ attr_reader :request, :response
7
7
 
8
8
  # @param req HTTP Request object
9
9
  # @param resp HTTP Response object
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aspera/log'
4
3
  require 'aspera/rest_call_error'
4
+ require 'aspera/log'
5
5
  require 'singleton'
6
6
 
7
7
  module Aspera
@@ -80,7 +80,7 @@ module Aspera
80
80
  end
81
81
  end
82
82
  end
83
- end # add_simple_handler
83
+ end
84
84
 
85
85
  class << self
86
86
  # used by handler to add an error description to list of errors
@@ -61,7 +61,7 @@ module Aspera
61
61
  RestErrorAnalyzer.add_error(call_context, type, "missing parameter: #{param}")
62
62
  end
63
63
  end
64
- end # register_handlers
64
+ end
65
65
  end
66
66
  end
67
67
  end
@@ -71,7 +71,7 @@ module Aspera
71
71
  sleep_seconds *= @parameters[:sleep_factor]
72
72
  # cap value
73
73
  sleep_seconds = @parameters[:sleep_max] if sleep_seconds > @parameters[:sleep_max]
74
- end # loop
74
+ end
75
75
  end
76
76
  end
77
77
  end
@@ -25,10 +25,8 @@ module Aspera
25
25
  /(?<begin> (?:#{ASCP_ENV_SECRETS.join('|')})=)(\\.|[^ ])*(?<end> )/,
26
26
  # rendered JSON or Ruby
27
27
  /(?<begin>(?:(?<quote>["'])|:)[^"':=]*(?:#{ALL_SECRETS.join('|')})[^"':=]*\k<quote>?(?:=>|:) *")[^"]+(?<end>")/,
28
- # option "secret"
29
- /(?<begin>"[^"]*(secret)[^"]*"=>{)[^}]+(?<end>})/,
30
- # option "secrets"
31
- /(?<begin>(secrets)={)[^}]+(?<end>})/,
28
+ # logged data
29
+ /(?<begin>(?:#{ALL_SECRETS.join('|')})[ =:]+).*(?<end>$)/,
32
30
  # private key values
33
31
  /(?<begin>--+BEGIN [^-]+ KEY--+)[[:ascii:]]+?(?<end>--+?END [^-]+ KEY--+)/,
34
32
  # cred in http dump
data/lib/aspera/ssh.rb CHANGED
@@ -61,7 +61,7 @@ module Aspera
61
61
  ssh_channel.wait
62
62
  # main ssh session loop
63
63
  session.loop
64
- end # start
64
+ end
65
65
  # response as single string
66
66
  return response.join
67
67
  end
@@ -22,12 +22,10 @@ module Aspera
22
22
  # Agents shown in manual for parameters (sub list)
23
23
  SUPPORTED_AGENTS = %i[direct node connect trsdk httpgw].freeze
24
24
  # Short names of columns in manual
25
- SUPPORTED_AGENTS_SHORT = SUPPORTED_AGENTS.map{|a|a.to_s[0].to_sym}
25
+ SUPPORTED_AGENTS_SHORT = SUPPORTED_AGENTS.map{|agent_sym|agent_sym.to_s[0].to_sym}
26
26
  FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
27
- # options that can be provided to the constructor, and then in @options
28
- SUPPORTED_OPTIONS = %i[ascp_args wss check_ignore_cb quiet trusted_certs].freeze
29
27
 
30
- private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS, :SUPPORTED_OPTIONS
28
+ private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS
31
29
 
32
30
  class << self
33
31
  # Temp folder for file lists, must contain only file lists
@@ -35,18 +33,18 @@ module Aspera
35
33
  # this could be refined, as , for instance, on macos, temp folder is already user specific
36
34
  @file_list_folder = TempFileManager.instance.new_file_path_global('asession_filelists') # cspell:disable-line
37
35
 
38
- # @param to_text [bool] replace HTML entities with text equivalent
36
+ # @param formatter [Cli::Formatter] formatter to use
39
37
  # @return a table suitable to display in manual
40
- def man_table
38
+ def man_table(formatter)
41
39
  result = []
42
40
  Spec::DESCRIPTION.each do |name, options|
43
41
  param = {name: name, type: [options[:accepted_types]].flatten.join(','), description: options[:desc]}
44
42
  # add flags for supported agents in doc
45
- SUPPORTED_AGENTS.each do |a|
46
- param[a.to_s[0].to_sym] = Cli::Formatter.tick(options[:agents].nil? || options[:agents].include?(a))
43
+ SUPPORTED_AGENTS.each do |agent_sym|
44
+ param[agent_sym.to_s[0].to_sym] = Cli::Formatter.tick(options[:agents].nil? || options[:agents].include?(agent_sym))
47
45
  end
48
46
  # only keep lines that are usable in supported agents
49
- next if SUPPORTED_AGENTS_SHORT.inject(true){|m, j|m && param[j].empty?}
47
+ next if SUPPORTED_AGENTS_SHORT.inject(true){|memory, agent_short_sym|memory && param[agent_short_sym].empty?}
50
48
  param[:cli] =
51
49
  case options[:cli][:type]
52
50
  when :envvar then 'env:' + options[:cli][:variable]
@@ -63,8 +61,8 @@ module Aspera
63
61
  end.map{|n|"{#{n}}"}.join('|')
64
62
  conversion_tag = options[:cli].key?(:convert) ? '(conversion)' : ''
65
63
  "#{options[:cli][:switch]} #{conversion_tag}#{values}"
66
- when :special then Cli::Formatter.special('special')
67
- when :ignore then Cli::Formatter.special('ignored')
64
+ when :special then formatter.special_format('special')
65
+ when :ignore then formatter.special_format('ignored')
68
66
  else
69
67
  param[:d].eql?(tick_yes) ? '' : 'n/a'
70
68
  end
@@ -75,17 +73,17 @@ module Aspera
75
73
  param[:description] = param[:description].gsub('&sol;', '\\')
76
74
  result.push(param)
77
75
  end
78
- return result.sort_by { |a| a[:name] }
76
+ return result.sort_by { |parameter_info| parameter_info[:name] }
79
77
  end
80
78
 
81
79
  # special encoding methods used in YAML (key: :convert)
82
- def convert_remove_hyphen(v); v.tr('-', ''); end
80
+ def convert_remove_hyphen(value); value.tr('-', ''); end
83
81
 
84
82
  # special encoding methods used in YAML (key: :convert)
85
- def convert_json64(v); Base64.strict_encode64(JSON.generate(v)); end
83
+ def convert_json64(value); Base64.strict_encode64(JSON.generate(value)); end
86
84
 
87
85
  # special encoding methods used in YAML (key: :convert)
88
- def convert_base64(v); Base64.strict_encode64(v); end
86
+ def convert_base64(value); Base64.strict_encode64(value); end
89
87
 
90
88
  # file list is provided directly with ascp arguments
91
89
  # @param ascp_args [Array,NilClass] ascp arguments
@@ -94,8 +92,8 @@ module Aspera
94
92
  end
95
93
 
96
94
  # temp file list files are created here
97
- def file_list_folder=(v)
98
- @file_list_folder = v
95
+ def file_list_folder=(value)
96
+ @file_list_folder = value
99
97
  return if @file_list_folder.nil?
100
98
  FileUtils.mkdir_p(@file_list_folder)
101
99
  TempFileManager.instance.cleanup_expired(@file_list_folder)
@@ -103,28 +101,33 @@ module Aspera
103
101
 
104
102
  # static methods
105
103
  attr_reader :file_list_folder
106
- end # self
104
+ end
107
105
 
108
106
  # @param options [Hash] key: :wss: bool, :ascp_args: array of strings
109
- def initialize(job_spec, options)
110
- Aspera.assert_type(job_spec, Hash)
111
- Aspera.assert_type(options, Hash)
107
+ def initialize(
108
+ job_spec,
109
+ ascp_args:,
110
+ wss:,
111
+ quiet:,
112
+ trusted_certs:,
113
+ check_ignore_cb:
114
+ )
112
115
  @job_spec = job_spec
113
- # check necessary options
114
- missing_options = SUPPORTED_OPTIONS - options.keys
115
- Aspera.assert(missing_options.empty?){"missing options: #{missing_options.join(', ')}"}
116
- @options = SUPPORTED_OPTIONS.each_with_object({}){|o, h| h[o] = options[o]}
117
- Log.log.debug{Log.dump(:parameters_options, @options)}
118
- Log.log.debug{Log.dump(:dismiss_options, options.keys - SUPPORTED_OPTIONS)}
119
- Aspera.assert_type(@options[:ascp_args], Array){'ascp_args'}
120
- Aspera.assert(@options[:ascp_args].all?(String)){'ascp arguments must Strings'}
116
+ @ascp_args = ascp_args
117
+ @wss = wss
118
+ @quiet = quiet
119
+ @trusted_certs = trusted_certs
120
+ @check_ignore_cb = check_ignore_cb
121
+ Aspera.assert_type(job_spec, Hash)
122
+ Aspera.assert_type(@ascp_args, Array){'ascp_args'}
123
+ Aspera.assert(@ascp_args.all?(String)){'ascp arguments must be Strings'}
121
124
  @builder = CommandLineBuilder.new(@job_spec, Spec::DESCRIPTION)
122
125
  end
123
126
 
124
127
  # either place source files on command line, or add file list file
125
128
  def process_file_list
126
129
  # is the file list provided through ascp parameters?
127
- ascp_file_list_provided = self.class.ascp_args_file_list?(@options[:ascp_args])
130
+ ascp_file_list_provided = self.class.ascp_args_file_list?(@ascp_args)
128
131
  # set if paths is mandatory in ts
129
132
  @builder.params_definition['paths'][:mandatory] = !@job_spec.key?('keepalive') && !ascp_file_list_provided # cspell:words keepalive
130
133
  # get paths in transfer spec (after setting if it is mandatory)
@@ -162,7 +165,7 @@ module Aspera
162
165
  def remote_certificates
163
166
  certificates_to_use = []
164
167
  # use web socket secure for session ?
165
- if @builder.read_param('wss_enabled') && (@options[:wss] || !@job_spec.key?('fasp_port'))
168
+ if @builder.read_param('wss_enabled') && (@wss || !@job_spec.key?('fasp_port'))
166
169
  # by default use web socket session if available, unless removed by user
167
170
  @builder.add_command_line_options(['--ws-connect'])
168
171
  # TODO: option to give order ssh,ws (legacy http is implied by ssh)
@@ -171,9 +174,9 @@ module Aspera
171
174
  @job_spec.delete('fasp_port')
172
175
  @job_spec.delete('sshfp')
173
176
  # set location for CA bundle to be the one of Ruby, see env var SSL_CERT_FILE / SSL_CERT_DIR
174
- certificates_to_use.concat(@options[:trusted_certs]) if @options[:trusted_certs].is_a?(Array)
177
+ certificates_to_use.concat(@trusted_certs) if @trusted_certs.is_a?(Array)
175
178
  # ignore cert for wss ?
176
- if @options[:check_ignore_cb]&.call(@job_spec['remote_host'], @job_spec['wss_port'])
179
+ if @check_ignore_cb&.call(@job_spec['remote_host'], @job_spec['wss_port'])
177
180
  wss_cert_file = TempFileManager.instance.new_file_path_global('wss_cert')
178
181
  wss_url = "https://#{@job_spec['remote_host']}:#{@job_spec['wss_port']}"
179
182
  File.write(wss_cert_file, Rest.remote_certificate_chain(wss_url))
@@ -228,7 +231,7 @@ module Aspera
228
231
  # destination will be base64 encoded, put this before source path arguments
229
232
  @builder.add_command_line_options(['--dest64']) if base64_destination
230
233
  # optional arguments, at the end to override previous ones (to allow override)
231
- @builder.add_command_line_options(@options[:ascp_args])
234
+ @builder.add_command_line_options(@ascp_args)
232
235
  # get list of source files to transfer and build arg for ascp
233
236
  process_file_list
234
237
  # process destination folder
@@ -238,7 +241,7 @@ module Aspera
238
241
  # destination MUST be last command line argument to ascp
239
242
  @builder.add_command_line_options([destination_folder])
240
243
  @builder.add_env_args(env_args)
241
- env_args[:args].unshift('-q') if @options[:quiet]
244
+ env_args[:args].unshift('-q') if @quiet
242
245
  # add fallback cert and key as arguments if needed
243
246
  if ['1', 1, true, 'force'].include?(@job_spec['http_fallback'])
244
247
  env_args[:args].unshift('-Y', Ascp::Installation.instance.path(:fallback_private_key))
@@ -247,6 +250,6 @@ module Aspera
247
250
  Log.log.debug{"ascp args: #{env_args}"}
248
251
  return env_args
249
252
  end
250
- end # Parameters
253
+ end
251
254
  end
252
255
  end
@@ -17,6 +17,8 @@ module Aspera
17
17
  'ssh_port' => SSH_PORT,
18
18
  'fasp_port' => UDP_PORT
19
19
  }.freeze
20
+ # fields for transport
21
+ TRANSPORT_FIELDS = %w[remote_host remote_user ssh_port fasp_port wss_enabled wss_port].freeze
20
22
  # reserved tag for Aspera
21
23
  TAG_RESERVED = 'aspera'
22
24
  class << self
@@ -48,7 +48,8 @@ module Aspera
48
48
  'cooloff' => { cli: { type: :opt_with_arg}, accepted_types: :int},
49
49
  'pending_max' => { cli: { type: :opt_with_arg}, accepted_types: :int},
50
50
  'scan_intensity' => { cli: { type: :opt_with_arg}, accepted_types: :string},
51
- 'cipher' => { cli: { type: :opt_with_arg, convert: 'Aspera::Transfer::Parameters.convert_remove_hyphen'}, accepted_types: :string, ts: true},
51
+ 'cipher' => { cli: { type: :opt_with_arg, convert: 'Aspera::Transfer::Parameters.convert_remove_hyphen'},
52
+ accepted_types: :string, ts: true},
52
53
  'transfer_threads' => { cli: { type: :opt_with_arg}, accepted_types: :int},
53
54
  'preserve_time' => { cli: { type: :opt_without_arg}, ts: :preserve_times},
54
55
  'preserve_access_time' => { cli: { type: :opt_without_arg}, ts: nil},
@@ -24,7 +24,7 @@ module Aspera
24
24
  result_ts['ssh_port'] = @fasp_uri.port
25
25
  result_ts['paths'] = [{'source' => URI.decode_www_form_component(@fasp_uri.path)}]
26
26
  # faspex 4 does not encode trailing base64 padding, fix that to be able to decode properly
27
- fixed_query = @fasp_uri.query.gsub(/(=+)$/){|x|'%3D' * x.length}
27
+ fixed_query = @fasp_uri.query.gsub(/(=+)$/){|trail_equals|'%3D' * trail_equals.length}
28
28
 
29
29
  Rest.decode_query(fixed_query).each do |name, value|
30
30
  case name
@@ -4,21 +4,22 @@ require 'uri'
4
4
  require 'aspera/rest'
5
5
 
6
6
  module Aspera
7
+ # read some content from some URI, support file: , http: and https: schemes
7
8
  module UriReader
8
9
  class << self
9
10
  # read some content from some URI, support file: , http: and https: schemes
10
11
  def read(uri_to_read)
11
- proxy_uri = URI.parse(uri_to_read)
12
- case proxy_uri.scheme
12
+ uri = URI.parse(uri_to_read)
13
+ case uri.scheme
13
14
  when 'http', 'https'
14
15
  return Rest.new(base_url: uri_to_read, redirect_max: 5).call(operation: 'GET', subpath: '', headers: {'Accept' => 'text/plain'})[:data]
15
16
  when 'file', NilClass
16
- local_file_path = proxy_uri.path
17
+ local_file_path = uri.path
17
18
  raise 'URL shall have a path, check syntax' if local_file_path.nil?
18
19
  local_file_path = File.expand_path(local_file_path.gsub(%r{^/}, '')) if %r{^/(~|.|..)/}.match?(local_file_path)
19
20
  return File.read(local_file_path)
20
21
  else
21
- raise "unknown scheme: [#{proxy_uri.scheme}] for [#{uri_to_read}]"
22
+ raise "unknown scheme: [#{uri.scheme}] for [#{uri_to_read}]"
22
23
  end
23
24
  end
24
25
  end
@@ -28,7 +28,7 @@ module Aspera
28
28
  response.body = '<html><head><title>Ok</title></head><body><h1>Thank you !</h1><p>You can close this window.</p></body></html>'
29
29
  return nil
30
30
  end
31
- end # WebAuthServlet
31
+ end
32
32
 
33
33
  # start a local web server, then start a browser that will callback the local server upon authentication
34
34
  class WebAuth < WebServerSimple
@@ -7,6 +7,7 @@ require 'aspera/assert'
7
7
  require 'openssl'
8
8
 
9
9
  module Aspera
10
+ # Simple WEBrick server with HTTPS support
10
11
  class WebServerSimple < WEBrick::HTTPServer
11
12
  CERT_PARAMETERS = %i[key cert chain].freeze
12
13
  GENERIC_ISSUER = '/C=FR/O=Test/OU=Test/CN=Test'
@@ -18,8 +19,8 @@ module Aspera
18
19
  # generates and adds self signed cert to provided webrick options
19
20
  def fill_self_signed_cert(cert, key, digest = 'SHA256')
20
21
  cert.subject = cert.issuer = OpenSSL::X509::Name.parse(GENERIC_ISSUER)
21
- cert.not_before = Time.now
22
- cert.not_after = Time.now + ONE_YEAR_SECONDS
22
+ cert.not_before = cert.not_after = Time.now
23
+ cert.not_after += ONE_YEAR_SECONDS
23
24
  cert.public_key = key.public_key
24
25
  cert.serial = 0x0
25
26
  cert.version = 2
@@ -56,7 +57,7 @@ module Aspera
56
57
  else
57
58
  Aspera.assert_type(certificate, Hash)
58
59
  certificate = certificate.symbolize_keys
59
- raise "unexpected key in certificate config: only: #{CERT_PARAMETERS.join(', ')}" if certificate.keys.any?{|k|!CERT_PARAMETERS.include?(k)}
60
+ raise "unexpected key in certificate config: only: #{CERT_PARAMETERS.join(', ')}" if certificate.keys.any?{|key|!CERT_PARAMETERS.include?(key)}
60
61
  webrick_options[:SSLPrivateKey] = if certificate.key?(:key)
61
62
  OpenSSL::PKey::RSA.new(File.read(certificate[:key]))
62
63
  else
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.17.0
4
+ version: 4.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -37,7 +37,7 @@ cert_chain:
37
37
  eTf9kxhVM40wGQOECVNA8UsEEZHD48eF+csUYZtAJOF5oxTI8UyV9T/o6CgO0c9/
38
38
  Gzz+Qm5ULOUcPiJLjSpaiTrkiIVYiDGnqNSr6R1Hb1c=
39
39
  -----END CERTIFICATE-----
40
- date: 2024-05-13 00:00:00.000000000 Z
40
+ date: 2024-07-10 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: blankslate
@@ -403,6 +403,7 @@ files:
403
403
  - lib/aspera/api/aoc.rb
404
404
  - lib/aspera/api/ats.rb
405
405
  - lib/aspera/api/cos_node.rb
406
+ - lib/aspera/api/httpgw.rb
406
407
  - lib/aspera/api/node.rb
407
408
  - lib/aspera/ascmd.rb
408
409
  - lib/aspera/ascp/installation.rb
@@ -422,12 +423,13 @@ files:
422
423
  - lib/aspera/cli/plugins/alee.rb
423
424
  - lib/aspera/cli/plugins/aoc.rb
424
425
  - lib/aspera/cli/plugins/ats.rb
425
- - lib/aspera/cli/plugins/bss.rb
426
426
  - lib/aspera/cli/plugins/config.rb
427
427
  - lib/aspera/cli/plugins/console.rb
428
428
  - lib/aspera/cli/plugins/cos.rb
429
429
  - lib/aspera/cli/plugins/faspex.rb
430
430
  - lib/aspera/cli/plugins/faspex5.rb
431
+ - lib/aspera/cli/plugins/faspio.rb
432
+ - lib/aspera/cli/plugins/httpgw.rb
431
433
  - lib/aspera/cli/plugins/node.rb
432
434
  - lib/aspera/cli/plugins/orchestrator.rb
433
435
  - lib/aspera/cli/plugins/preview.rb
metadata.gz.sig CHANGED
Binary file