aspera-cli 4.24.2 → 4.25.0.pre

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 (65) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +1064 -758
  4. data/CONTRIBUTING.md +43 -100
  5. data/README.md +671 -419
  6. data/lib/aspera/api/aoc.rb +71 -43
  7. data/lib/aspera/api/cos_node.rb +3 -2
  8. data/lib/aspera/api/faspex.rb +6 -5
  9. data/lib/aspera/api/node.rb +10 -12
  10. data/lib/aspera/ascmd.rb +1 -2
  11. data/lib/aspera/ascp/installation.rb +53 -39
  12. data/lib/aspera/assert.rb +25 -3
  13. data/lib/aspera/cli/error.rb +4 -2
  14. data/lib/aspera/cli/extended_value.rb +84 -60
  15. data/lib/aspera/cli/formatter.rb +55 -22
  16. data/lib/aspera/cli/main.rb +21 -14
  17. data/lib/aspera/cli/manager.rb +348 -247
  18. data/lib/aspera/cli/plugins/alee.rb +3 -3
  19. data/lib/aspera/cli/plugins/aoc.rb +70 -14
  20. data/lib/aspera/cli/plugins/base.rb +57 -49
  21. data/lib/aspera/cli/plugins/config.rb +69 -84
  22. data/lib/aspera/cli/plugins/console.rb +13 -8
  23. data/lib/aspera/cli/plugins/cos.rb +1 -1
  24. data/lib/aspera/cli/plugins/faspex.rb +32 -26
  25. data/lib/aspera/cli/plugins/faspex5.rb +45 -43
  26. data/lib/aspera/cli/plugins/faspio.rb +5 -5
  27. data/lib/aspera/cli/plugins/httpgw.rb +1 -1
  28. data/lib/aspera/cli/plugins/node.rb +131 -120
  29. data/lib/aspera/cli/plugins/oauth.rb +1 -1
  30. data/lib/aspera/cli/plugins/orchestrator.rb +114 -32
  31. data/lib/aspera/cli/plugins/preview.rb +26 -46
  32. data/lib/aspera/cli/plugins/server.rb +6 -8
  33. data/lib/aspera/cli/plugins/shares.rb +27 -32
  34. data/lib/aspera/cli/sync_actions.rb +49 -38
  35. data/lib/aspera/cli/transfer_agent.rb +16 -34
  36. data/lib/aspera/cli/version.rb +1 -1
  37. data/lib/aspera/cli/wizard.rb +8 -5
  38. data/lib/aspera/command_line_builder.rb +20 -17
  39. data/lib/aspera/coverage.rb +1 -1
  40. data/lib/aspera/environment.rb +41 -34
  41. data/lib/aspera/faspex_gw.rb +1 -1
  42. data/lib/aspera/keychain/factory.rb +1 -2
  43. data/lib/aspera/markdown.rb +31 -0
  44. data/lib/aspera/nagios.rb +6 -5
  45. data/lib/aspera/oauth/base.rb +17 -27
  46. data/lib/aspera/oauth/factory.rb +1 -1
  47. data/lib/aspera/oauth/url_json.rb +2 -1
  48. data/lib/aspera/preview/file_types.rb +23 -37
  49. data/lib/aspera/products/connect.rb +3 -3
  50. data/lib/aspera/rest.rb +51 -39
  51. data/lib/aspera/rest_error_analyzer.rb +4 -4
  52. data/lib/aspera/ssh.rb +5 -2
  53. data/lib/aspera/ssl.rb +41 -0
  54. data/lib/aspera/sync/conf.schema.yaml +182 -34
  55. data/lib/aspera/sync/database.rb +2 -1
  56. data/lib/aspera/sync/operations.rb +125 -69
  57. data/lib/aspera/transfer/parameters.rb +3 -4
  58. data/lib/aspera/transfer/spec.rb +2 -3
  59. data/lib/aspera/transfer/spec.schema.yaml +48 -18
  60. data/lib/aspera/transfer/spec_doc.rb +14 -14
  61. data/lib/aspera/uri_reader.rb +1 -1
  62. data/lib/transferd_pb.rb +2 -2
  63. data.tar.gz.sig +0 -0
  64. metadata +19 -6
  65. metadata.gz.sig +3 -2
@@ -59,25 +59,13 @@ module Aspera
59
59
 
60
60
  attr_reader :scope, :api, :path_token, :client_id
61
61
 
62
- # helper method to create token as per RFC
62
+ # Helper method to create token as per RFC
63
+ # @return [HTTPResponse]
64
+ # @raise RestError if not 2XX code
63
65
  def create_token_call(creation_params)
64
66
  Log.log.debug{'Generating a new token'.bg_green}
65
- payload = if @use_query
66
- {
67
- query: creation_params
68
- }
69
- else
70
- {
71
- content_type: Rest::MIME_WWW,
72
- body: creation_params
73
- }
74
- end
75
- return @api.call(
76
- operation: 'POST',
77
- subpath: @path_token,
78
- headers: {'Accept' => Rest::MIME_JSON},
79
- **payload
80
- )
67
+ return @api.create(@path_token, nil, query: creation_params, ret: :resp) if @use_query
68
+ return @api.create(@path_token, creation_params, content_type: Rest::MIME_WWW, ret: :resp)
81
69
  end
82
70
 
83
71
  # @param add_secret [Boolean] Add secret in default call parameters
@@ -122,17 +110,18 @@ module Aspera
122
110
  Factory.instance.persist_mgr.delete(@token_cache_id)
123
111
  token_data = nil
124
112
  # lets try the existing refresh token
113
+ # NOTE: AoC admin token has no refresh, and lives by default 1800secs
125
114
  if !refresh_token.nil?
126
- Log.log.info{"refresh=[#{refresh_token}]".bg_green}
127
- # NOTE: AoC admin token has no refresh, and lives by default 1800secs
128
- resp = create_token_call(optional_scope_client_id(add_secret: true).merge(grant_type: 'refresh_token', refresh_token: refresh_token))
129
- if resp[:http].code.start_with?('2')
130
- # save only if success
131
- json_data = resp[:http].body
115
+ Log.log.info{"refresh token=[#{refresh_token}]".bg_green}
116
+ begin
117
+ http = create_token_call(optional_scope_client_id(add_secret: true).merge(grant_type: 'refresh_token', refresh_token: refresh_token))
118
+ # Save only if success
119
+ json_data = http.body
132
120
  token_data = JSON.parse(json_data)
133
121
  Factory.instance.persist_mgr.put(@token_cache_id, json_data)
134
- else
135
- Log.log.debug{"refresh failed: #{resp[:http].body}".bg_red}
122
+ rescue => e
123
+ # Refresh token can fail.
124
+ Log.log.warn{"Refresh failed: #{e}"}
136
125
  end
137
126
  end
138
127
  end
@@ -140,8 +129,9 @@ module Aspera
140
129
 
141
130
  # no cache, nor refresh: generate a token
142
131
  if token_data.nil?
143
- resp = create_token
144
- json_data = resp[:http].body
132
+ # Call the method-specific token creation
133
+ # which returns the result of create_token_call
134
+ json_data = create_token.body
145
135
  token_data = JSON.parse(json_data)
146
136
  Factory.instance.persist_mgr.put(@token_cache_id, json_data)
147
137
  end
@@ -156,7 +156,7 @@ module Aspera
156
156
  def register_token_creator(creator_class)
157
157
  Aspera.assert_type(creator_class, Class)
158
158
  id = Factory.class_to_id(creator_class)
159
- Log.log.debug{"registering token creator #{id}"}
159
+ Log.log.debug{"registering creator for #{id}"}
160
160
  @token_type_classes[id] = creator_class
161
161
  end
162
162
 
@@ -25,7 +25,8 @@ module Aspera
25
25
  query: @query.merge(scope: scope), # scope is here because it may change over time (node)
26
26
  content_type: Rest::MIME_JSON,
27
27
  body: @body,
28
- headers: {'Accept' => Rest::MIME_JSON}
28
+ headers: {'Accept' => Rest::MIME_JSON},
29
+ ret: :resp
29
30
  )
30
31
  end
31
32
  end
@@ -3,7 +3,7 @@
3
3
  require 'aspera/log'
4
4
  require 'aspera/assert'
5
5
  require 'singleton'
6
- require 'mime/types'
6
+ require 'marcel'
7
7
 
8
8
  module Aspera
9
9
  module Preview
@@ -61,7 +61,7 @@ module Aspera
61
61
  end
62
62
 
63
63
  # @param mimetype [String] mime type
64
- # @return file type, one of enum CONVERSION_TYPES, or nil if not found
64
+ # @return [NilClass,Symbol] file type, one of enum CONVERSION_TYPES, or nil if not found
65
65
  def mime_to_type(mimetype)
66
66
  Aspera.assert_type(mimetype, String)
67
67
  return SUPPORTED_MIME_TYPES[mimetype] if SUPPORTED_MIME_TYPES.key?(mimetype)
@@ -72,19 +72,17 @@ module Aspera
72
72
  return
73
73
  end
74
74
 
75
- # @param filepath [String] full path to file
76
- # @param mimetype [String] provided by node API
75
+ # @param filepath [String] Full path to file
76
+ # @param mimetype [String] MIME typre provided by node API
77
77
  # @return file type, one of enum CONVERSION_TYPES
78
78
  # @raise [RuntimeError] if no conversion type found
79
79
  def conversion_type(filepath, mimetype)
80
80
  Log.log.debug{"conversion_type(#{filepath},mime=#{mimetype},magic=#{@use_mimemagic})"}
81
- mimetype = nil if mimetype.is_a?(String) && (mimetype == 'application/octet-stream' || mimetype.empty?)
82
- # Use mimemagic if available
83
- mimetype ||= mime_using_mimemagic(filepath)
84
- mimetype ||= mime_using_file(filepath)
85
- # from extensions, using local mapping
86
- mimetype ||= MIME::Types.of(File.basename(filepath)).first
87
- raise "no MIME type found for #{File.basename(filepath)}" if mimetype.nil?
81
+ # Default type or empty means no type
82
+ mimetype = TYPE_NOT_FOUND if mimetype.nil? || (mimetype.is_a?(String) && mimetype.empty?)
83
+ mimetype = Marcel::MimeType.for(Pathname.new(filepath), name: File.basename(filepath), declared_type: mimetype)
84
+ mimetype = 'text/plain' if mimetype.eql?(TYPE_NOT_FOUND) && ascii_text_file?(filepath)
85
+ raise "no MIME type found for #{File.basename(filepath)}" if mimetype.eql?(TYPE_NOT_FOUND)
88
86
  conversion_type = mime_to_type(mimetype)
89
87
  raise "no conversion type found for #{File.basename(filepath)}" if conversion_type.nil?
90
88
  Log.log.trace1{"conversion_type(#{File.basename(filepath)}): #{conversion_type.class.name} [#{conversion_type}]"}
@@ -93,33 +91,21 @@ module Aspera
93
91
 
94
92
  private
95
93
 
96
- # Use mime magic to find mime type based on file content (magic numbers)
97
- # @param filepath [String] full path to file
98
- # @return [String] mime type, or nil if not found
99
- def mime_using_mimemagic(filepath)
100
- return unless @use_mimemagic
101
- # moved here, as `mimemagic` can cause installation issues
102
- require 'mimemagic'
103
- require 'mimemagic/version'
104
- require 'mimemagic/overlay' if MimeMagic::VERSION.start_with?('0.3.')
105
- # check magic number inside file (empty string if not found)
106
- detected_mime = MimeMagic.by_magic(File.open(filepath)).to_s
107
- # check extension only
108
- if mime_to_type(detected_mime).nil?
109
- Log.log.debug{"no conversion for #{detected_mime}, trying extension"}
110
- detected_mime = MimeMagic.by_extension(File.extname(filepath)).to_s
111
- end
112
- detected_mime = nil if detected_mime.empty?
113
- Log.log.debug{"mimemagic: #{detected_mime.class.name} [#{detected_mime}]"}
114
- return detected_mime
115
- end
94
+ TYPE_NOT_FOUND = 'application/octet-stream'
95
+ ACCEPT_CTRL_CHARS = [9, 10, 13]
116
96
 
117
- # Use 'file' command to find mime type based on file content (Unix)
118
- def mime_using_file(filepath)
119
- return Environment.secure_capture(exec: 'file', args: ['--mime-type', '--brief', filepath]).strip
120
- rescue => e
121
- Log.log.error{"error using 'file' command: #{e.message}"}
122
- return
97
+ # Returns true if the file looks like ASCII text (printable ASCII + \t, \r, \n, space).
98
+ # It reads only a small prefix (default: 64KB) and fails fast on the first bad byte.
99
+ def ascii_text_file?(path, sample_size: 64 * 1024)
100
+ File.open(path, 'rb') do |f|
101
+ sample = f.read(sample_size) || ''.b
102
+ sample.each_byte do |b|
103
+ next if b.between?(32, 126) || ACCEPT_CTRL_CHARS.include?(b)
104
+ # Any other control character => not ASCII text
105
+ return false
106
+ end
107
+ true
108
+ end
123
109
  end
124
110
  end
125
111
  end
@@ -56,10 +56,10 @@ module Aspera
56
56
  # Retrieve structure from cloud (CDN) with all versions available
57
57
  def versions
58
58
  if @connect_versions.nil?
59
- javascript = cdn_api.call(operation: 'GET', subpath: VERSION_INFO_FILE)
59
+ http = cdn_api.read(VERSION_INFO_FILE, ret: :resp)
60
60
  # get result on one line
61
- connect_versions_javascript = javascript[:http].body.gsub(/\r?\n\s*/, '')
62
- Log.log.debug{"javascript=[\n#{connect_versions_javascript}\n]"}
61
+ connect_versions_javascript = http.body.gsub(/\r?\n\s*/, '')
62
+ Log.dump(:javascript, connect_versions_javascript)
63
63
  # get javascript object only
64
64
  found = connect_versions_javascript.match(/^.*? = (.*);/)
65
65
  raise Cli::Error, 'Problem when getting connect versions from internet' if found.nil?
data/lib/aspera/rest.rb CHANGED
@@ -50,13 +50,13 @@ module Aspera
50
50
  end
51
51
  end
52
52
 
53
+ class EntityNotFound < Error
54
+ end
55
+
53
56
  # a simple class to make HTTP calls, equivalent to rest-client
54
57
  # rest call errors are raised as exception RestCallError
55
58
  # and error are analyzed in RestErrorAnalyzer
56
59
  class Rest
57
- # Error message when entity not found (TODO: use specific exception)
58
- ENTITY_NOT_FOUND = 'No such'
59
-
60
60
  MIME_JSON = 'application/json'
61
61
  MIME_WWW = 'application/x-www-form-urlencoded'
62
62
  MIME_TEXT = 'text/plain'
@@ -71,7 +71,7 @@ module Aspera
71
71
  def basic_authorization(user, pass); return "Basic #{Base64.strict_encode64("#{user}:#{pass}")}"; end
72
72
 
73
73
  # Indicate that the given Hash query uses php style for array parameters
74
- # a[]=1&a[]=2
74
+ # @param query [Hash] A key can have Array value and result will use PHP format: a[]=1&a[]=2
75
75
  def php_style(query)
76
76
  Aspera.assert_type(query, Hash){'query'}
77
77
  query[:x_array_php_style] = true
@@ -300,14 +300,16 @@ module Aspera
300
300
  end
301
301
 
302
302
  # HTTP/S REST call
303
- # @param operation [String] HTTP operation (GET, POST, PUT, DELETE)
304
- # @param subpath [String] subpath of REST API
305
- # @param query [Hash] URL parameters
306
- # @param content_type [String,nil] Type of body parameters (one of MIME_*) and serialization, else use headers
307
- # @param body [Hash, String] body parameters
308
- # @param headers [Hash] additional headers (override Content-Type)
309
- # @param save_to_file (filepath)
310
- # @param exception (bool) true, error raise exception
303
+ # @param operation [String] HTTP operation (GET, POST, PUT, DELETE)
304
+ # @param subpath [String] subpath of REST API
305
+ # @param query [Hash] URL parameters
306
+ # @param content_type [String, nil] Type of body parameters (one of MIME_*) and serialization, else use headers
307
+ # @param body [Hash, String] body parameters
308
+ # @param headers [Hash] additional headers (override Content-Type)
309
+ # @param save_to_file [String, nil](filepath)
310
+ # @param exception [Bool] `true`, error raise exception
311
+ # @param ret [:data, :resp, :both] Tell to return only data, only http response, or both
312
+ # @return [Object, Array] only data, only http response, or both
311
313
  def call(
312
314
  operation:,
313
315
  subpath: nil,
@@ -316,7 +318,8 @@ module Aspera
316
318
  body: nil,
317
319
  headers: nil,
318
320
  save_to_file: nil,
319
- exception: true
321
+ exception: true,
322
+ ret: :data
320
323
  )
321
324
  subpath = subpath.to_s if subpath.is_a?(Symbol)
322
325
  subpath = '' if subpath.nil?
@@ -325,6 +328,8 @@ module Aspera
325
328
  Log.dump(:query, query, level: :trace1)
326
329
  Log.dump(:headers, headers, level: :trace1)
327
330
  Aspera.assert_type(subpath, String)
331
+ # We must have a way to check return code
332
+ Aspera.assert(exception || !ret.eql?(:data))
328
333
  if headers.nil?
329
334
  headers = @headers.clone
330
335
  else
@@ -348,7 +353,8 @@ module Aspera
348
353
  end
349
354
  else Aspera.error_unexpected_value(@auth_params[:type])
350
355
  end
351
- result = {http: nil}
356
+ result_http = nil
357
+ result_data = nil
352
358
  # start a block to be able to retry the actual HTTP request in case of OAuth token expiration
353
359
  begin
354
360
  # TODO: shall we percent encode subpath (spaces) test with access key delete with space in id
@@ -391,14 +397,14 @@ module Aspera
391
397
  file_saved = false
392
398
  # make http request (pipelined)
393
399
  http_session.request(req) do |response|
394
- result[:http] = response
395
- result_mime = self.class.parse_header(result[:http]['Content-Type'] || MIME_TEXT)[:type]
396
- Log.log.debug{"response: code=#{result[:http].code}, mime=#{result_mime}, mime2= #{response['Content-Type']}"}
400
+ result_http = response
401
+ result_mime = self.class.parse_header(result_http['Content-Type'] || MIME_TEXT)[:type]
402
+ Log.log.debug{"response: code=#{result_http.code}, mime=#{result_mime}, mime2= #{response['Content-Type']}"}
397
403
  # JSON data needs to be parsed, in case it contains an error code
398
404
  if !save_to_file.nil? &&
399
- result[:http].code.to_s.start_with?('2') &&
405
+ result_http.code.to_s.start_with?('2') &&
400
406
  !JSON_DECODE.include?(result_mime)
401
- total_size = result[:http]['Content-Length']&.to_i
407
+ total_size = result_http['Content-Length']&.to_i
402
408
  Log.log.debug('before write file')
403
409
  target_file = save_to_file
404
410
  # override user's path to path in header
@@ -416,7 +422,7 @@ module Aspera
416
422
  FileUtils.mkdir_p(File.dirname(target_file_tmp))
417
423
  limiter = TimerLimiter.new(0.5)
418
424
  File.open(target_file_tmp, 'wb') do |file|
419
- result[:http].read_body do |fragment|
425
+ result_http.read_body do |fragment|
420
426
  file.write(fragment)
421
427
  written_size += fragment.length
422
428
  RestParameters.instance.progress_bar&.event(:transfer, session_id: session_id, info: written_size) if limiter.trigger?
@@ -429,32 +435,32 @@ module Aspera
429
435
  file_saved = true
430
436
  end
431
437
  end
432
- Log.log.debug{"result: code=#{result[:http].code} mime=#{result_mime}"}
438
+ Log.log.debug{"result: code=#{result_http.code} mime=#{result_mime}"}
433
439
  # sometimes there is a UTF8 char (e.g. (c) ), TODO : related to mime type encoding ?
434
- # result[:http].body.force_encoding('UTF-8') if result[:http].body.is_a?(String)
435
- # Log.log.debug{"result: body=#{result[:http].body}"}
440
+ # result_http.body.force_encoding('UTF-8') if result_http.body.is_a?(String)
441
+ # Log.log.debug{"result: body=#{result_http.body}"}
436
442
  case result_mime
437
443
  when *JSON_DECODE
438
- result[:data] = JSON.parse(result[:http].body) rescue result[:http].body
439
- Log.dump(:result_data, result[:data])
444
+ result_data = JSON.parse(result_http.body) rescue result_http.body
445
+ Log.dump(:result_data, result_data)
440
446
  else # when MIME_TEXT
441
- result[:data] = result[:http].body
447
+ result_data = result_http.body
442
448
  end
443
- RestErrorAnalyzer.instance.raise_on_error(req, result)
449
+ RestErrorAnalyzer.instance.raise_on_error(req, result_data, result_http)
444
450
  unless file_saved || save_to_file.nil?
445
451
  FileUtils.mkdir_p(File.dirname(save_to_file))
446
- File.write(save_to_file, result[:http].body, binmode: true)
452
+ File.write(save_to_file, result_http.body, binmode: true)
447
453
  end
448
454
  rescue RestCallError => e
449
455
  do_retry = false
450
456
  # AoC have some timeout , like Connect to platform.bss.asperasoft.com:443 ...
451
457
  do_retry ||= true if e.response.body.include?('failed: connect timed out') && RestParameters.instance.retry_on_timeout
452
458
  # AoC sometimes not available
453
- do_retry ||= true if RestParameters.instance.retry_on_unavailable && UNAVAILABLE_CODES.include?(result[:http].code.to_s)
459
+ do_retry ||= true if RestParameters.instance.retry_on_unavailable && UNAVAILABLE_CODES.include?(result_http.code.to_s)
454
460
  # possibility to retry anything if it fails
455
461
  do_retry ||= true if RestParameters.instance.retry_on_error
456
462
  # not authorized: oauth token expired
457
- if @not_auth_codes.include?(result[:http].code.to_s) && @auth_params[:type].eql?(:oauth2)
463
+ if @not_auth_codes.include?(result_http.code.to_s) && @auth_params[:type].eql?(:oauth2)
458
464
  begin
459
465
  # try to use refresh token
460
466
  req['Authorization'] = oauth.authorization(refresh: true)
@@ -494,14 +500,20 @@ module Aspera
494
500
  content_type: content_type,
495
501
  save_to_file: save_to_file,
496
502
  exception: exception,
497
- headers: headers
503
+ headers: headers,
504
+ ret: ret
498
505
  )
499
506
  end
500
507
  # raise exception if could not retry and not return error in result
501
508
  raise e if exception
502
509
  end
503
- Log.log.debug{"result=http:#{result[:http]}, data:#{result[:data].class}"}
504
- return result
510
+ Log.log.debug{"result=http:#{result_http}, data:#{result_data.class}"}
511
+ return case ret
512
+ when :data then result_data
513
+ when :resp then result_http
514
+ when :both then [result_data, result_http]
515
+ else Aspera.error_unexpected_value(ret){'Type of result for REST'}
516
+ end
505
517
  end
506
518
 
507
519
  #
@@ -511,27 +523,27 @@ module Aspera
511
523
 
512
524
  # Create: `POST`
513
525
  def create(subpath, params, **kwargs)
514
- return call(operation: 'POST', subpath: subpath, headers: {'Accept' => MIME_JSON}, body: params, content_type: MIME_JSON, **kwargs)[:data]
526
+ return call(operation: 'POST', subpath: subpath, headers: {'Accept' => MIME_JSON}, body: params, content_type: MIME_JSON, **kwargs)
515
527
  end
516
528
 
517
529
  # Read: `GET`
518
530
  def read(subpath, query = nil, **kwargs)
519
- return call(operation: 'GET', subpath: subpath, headers: {'Accept' => MIME_JSON}, query: query, **kwargs)[:data]
531
+ return call(operation: 'GET', subpath: subpath, headers: {'Accept' => MIME_JSON}, query: query, **kwargs)
520
532
  end
521
533
 
522
534
  # Update: `PUT`
523
535
  def update(subpath, params, **kwargs)
524
- return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => MIME_JSON}, body: params, content_type: MIME_JSON, **kwargs)[:data]
536
+ return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => MIME_JSON}, body: params, content_type: MIME_JSON, **kwargs)
525
537
  end
526
538
 
527
539
  # Delete: `DELETE`
528
540
  def delete(subpath, params = nil, **kwargs)
529
- return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => MIME_JSON}, query: params, **kwargs)[:data]
541
+ return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => MIME_JSON}, query: params, **kwargs)
530
542
  end
531
543
 
532
544
  # Cancel: `CANCEL`
533
545
  def cancel(subpath, **kwargs)
534
- return call(operation: 'CANCEL', subpath: subpath, headers: {'Accept' => MIME_JSON}, **kwargs)[:data]
546
+ return call(operation: 'CANCEL', subpath: subpath, headers: {'Accept' => MIME_JSON}, **kwargs)
535
547
  end
536
548
 
537
549
  # Query entity by general search (read with parameter `q`)
@@ -549,7 +561,7 @@ module Aspera
549
561
  Aspera.assert_type(matching_items, Array)
550
562
  case matching_items.length
551
563
  when 1 then return matching_items.first
552
- when 0 then raise %Q{#{ENTITY_NOT_FOUND} #{subpath}: "#{search_name}"}
564
+ when 0 then raise EntityNotFound, %Q{No such #{subpath}: "#{search_name}"}
553
565
  else
554
566
  # multiple case insensitive partial matches, try case insensitive full match
555
567
  # (anyway AoC does not allow creation of 2 entities with same case insensitive name)
@@ -27,13 +27,13 @@ module Aspera
27
27
  # Use this method to analyze a EST result and raise an exception
28
28
  # Analyzes REST call response and raises a RestCallError exception
29
29
  # if HTTP result code is not 2XX
30
- def raise_on_error(req, res)
31
- Log.log.debug{"raise_on_error #{req.method} #{req.path} #{res[:http].code}"}
30
+ def raise_on_error(req, data, http)
31
+ Log.log.debug{"raise_on_error #{req.method} #{req.path} #{http.code}"}
32
32
  call_context = {
33
33
  messages: [],
34
34
  request: req,
35
- response: res[:http],
36
- data: res[:data]
35
+ response: http,
36
+ data: data
37
37
  }
38
38
  # multiple error messages can be found
39
39
  # analyze errors from provided handlers
data/lib/aspera/ssh.rb CHANGED
@@ -11,6 +11,7 @@ module Aspera
11
11
  class Error < Aspera::Error
12
12
  end
13
13
  class << self
14
+ # HACK: disable some key type
14
15
  def disable_ed25519_keys
15
16
  Log.log.debug('Disabling SSH ed25519 user keys')
16
17
  old_verbose = $VERBOSE
@@ -24,6 +25,7 @@ module Aspera
24
25
  $VERBOSE = old_verbose
25
26
  end
26
27
 
28
+ # HACK: disable some algorithms
27
29
  def disable_ecd_sha2_algorithms
28
30
  Log.log.debug('Disabling SSH ecdsa')
29
31
  Net::SSH::Transport::Algorithms::ALGORITHMS.each_value{ |a| a.reject!{ |a| a =~ /^ecd(sa|h)-sha2/}}
@@ -38,6 +40,7 @@ module Aspera
38
40
  Aspera.assert_type(host, String)
39
41
  Aspera.assert_type(username, String)
40
42
  Aspera.assert_type(ssh_options, Hash)
43
+ ssh_options[:use_agent] = false unless ssh_options.key?(:use_agent)
41
44
  @host = host
42
45
  @username = username
43
46
  @ssh_options = ssh_options
@@ -62,7 +65,7 @@ module Aspera
62
65
  exit_code = data.read_long
63
66
  next if exit_code.zero?
64
67
  error_message = "#{cmd}: exit #{exit_code}, #{error.join.chomp}"
65
- raise Error, error_message if exception
68
+ raise Error, error_message if exception
66
69
  # Happens when windows user hasn't logged in and created home account.
67
70
  error_message += "\nHint: home not created in Windows?" if data.include?('Could not chdir to home directory')
68
71
  Log.log.debug(error_message)
@@ -82,5 +85,5 @@ module Aspera
82
85
  end
83
86
 
84
87
  # Deactivate ed25519 and ecdsa private keys from SSH identities, as it usually causes problems
85
- Aspera::Ssh.disable_ed25519_keys if Gem::Specification.find_all_by_name('ed25519').none?
88
+ Aspera::Ssh.disable_ed25519_keys if Gem::Specification.find_all_by_name('ed25519').none? || ENV.fetch('ASCLI_ENABLE_ED25519', 'true').eql?('false')
86
89
  Aspera::Ssh.disable_ecd_sha2_algorithms if defined?(JRUBY_VERSION) && ENV.fetch('ASCLI_ENABLE_ECDSHA2', 'false').eql?('false')
data/lib/aspera/ssl.rb ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'aspera/assert'
5
+ require 'aspera/log'
6
+
7
+ module Aspera
8
+ # Give possibility to globally override SSL options
9
+ module SSL
10
+ @extra_options = 0
11
+ class << self
12
+ @extra_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
13
+ attr_reader :extra_options
14
+
15
+ def option_list=(v)
16
+ Aspera.assert_type(v, Array){'ssl_options'}
17
+ v.each do |opt|
18
+ Aspera.assert_type(opt, String, Integer){'Expected String or Integer in ssl_options'}
19
+ case opt
20
+ when Integer
21
+ @extra_options = opt
22
+ when String
23
+ name = "OP_#{opt.start_with?('-') ? opt[1..] : opt}".upcase
24
+ raise Cli::BadArgument, "Unknown ssl_option: #{name}, use one of: #{OpenSSL::SSL.constants.grep(/^OP_/).map{ |c| c.to_s.sub(/^OP_/, '')}.join(', ')}" if !OpenSSL::SSL.const_defined?(name)
25
+ if opt.start_with?('-')
26
+ @extra_options &= ~OpenSSL::SSL.const_get(name)
27
+ else
28
+ @extra_options |= OpenSSL::SSL.const_get(name)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ def set_params(params = {})
35
+ super(params)
36
+ self.options = Aspera::SSL.extra_options unless Aspera::SSL.extra_options.nil?
37
+ self
38
+ end
39
+ end
40
+ end
41
+ OpenSSL::SSL::SSLContext.prepend(Aspera::SSL)