aspera-cli 4.21.2 → 4.23.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 (105) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +1 -1
  4. data/CHANGELOG.md +402 -374
  5. data/CONTRIBUTING.md +6 -10
  6. data/README.md +1018 -687
  7. data/lib/aspera/agent/base.rb +9 -5
  8. data/lib/aspera/agent/connect.rb +30 -28
  9. data/lib/aspera/agent/desktop.rb +29 -25
  10. data/lib/aspera/agent/direct.rb +137 -125
  11. data/lib/aspera/agent/httpgw.rb +22 -26
  12. data/lib/aspera/agent/node.rb +14 -11
  13. data/lib/aspera/agent/transferd.rb +6 -2
  14. data/lib/aspera/api/aoc.rb +15 -18
  15. data/lib/aspera/api/cos_node.rb +1 -1
  16. data/lib/aspera/api/httpgw.rb +15 -7
  17. data/lib/aspera/api/node.rb +6 -4
  18. data/lib/aspera/ascmd.rb +17 -9
  19. data/lib/aspera/ascp/installation.rb +21 -19
  20. data/lib/aspera/ascp/management.rb +1 -1
  21. data/lib/aspera/assert.rb +14 -5
  22. data/lib/aspera/cli/error.rb +2 -2
  23. data/lib/aspera/cli/extended_value.rb +38 -19
  24. data/lib/aspera/cli/formatter.rb +48 -48
  25. data/lib/aspera/cli/hints.rb +10 -2
  26. data/lib/aspera/cli/main.rb +190 -168
  27. data/lib/aspera/cli/manager.rb +16 -16
  28. data/lib/aspera/cli/plugin.rb +24 -21
  29. data/lib/aspera/cli/plugin_factory.rb +1 -1
  30. data/lib/aspera/cli/plugins/alee.rb +1 -1
  31. data/lib/aspera/cli/plugins/aoc.rb +173 -126
  32. data/lib/aspera/cli/plugins/ats.rb +19 -17
  33. data/lib/aspera/cli/plugins/config.rb +87 -98
  34. data/lib/aspera/cli/plugins/console.rb +5 -3
  35. data/lib/aspera/cli/plugins/faspex.rb +39 -35
  36. data/lib/aspera/cli/plugins/faspex5.rb +104 -80
  37. data/lib/aspera/cli/plugins/faspio.rb +13 -1
  38. data/lib/aspera/cli/plugins/httpgw.rb +13 -1
  39. data/lib/aspera/cli/plugins/node.rb +336 -205
  40. data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
  41. data/lib/aspera/cli/plugins/preview.rb +3 -3
  42. data/lib/aspera/cli/plugins/server.rb +7 -6
  43. data/lib/aspera/cli/plugins/shares.rb +5 -5
  44. data/lib/aspera/cli/sync_actions.rb +19 -18
  45. data/lib/aspera/cli/transfer_agent.rb +11 -15
  46. data/lib/aspera/cli/transfer_progress.rb +2 -2
  47. data/lib/aspera/cli/version.rb +1 -1
  48. data/lib/aspera/command_line_builder.rb +116 -95
  49. data/lib/aspera/coverage.rb +4 -3
  50. data/lib/aspera/data_repository.rb +1 -0
  51. data/lib/aspera/environment.rb +7 -6
  52. data/lib/aspera/faspex_gw.rb +14 -14
  53. data/lib/aspera/faspex_postproc.rb +7 -6
  54. data/lib/aspera/hash_ext.rb +2 -2
  55. data/lib/aspera/json_rpc.rb +1 -1
  56. data/lib/aspera/keychain/encrypted_hash.rb +47 -34
  57. data/lib/aspera/keychain/factory.rb +41 -0
  58. data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
  59. data/lib/aspera/keychain/macos_security.rb +19 -11
  60. data/lib/aspera/log.rb +29 -34
  61. data/lib/aspera/nagios.rb +6 -6
  62. data/lib/aspera/node_simulator.rb +8 -8
  63. data/lib/aspera/oauth/base.rb +10 -6
  64. data/lib/aspera/oauth/factory.rb +6 -6
  65. data/lib/aspera/oauth/url_json.rb +6 -6
  66. data/lib/aspera/persistency_action_once.rb +6 -4
  67. data/lib/aspera/persistency_folder.rb +2 -2
  68. data/lib/aspera/preview/file_types.rb +40 -33
  69. data/lib/aspera/preview/generator.rb +1 -1
  70. data/lib/aspera/preview/options.rb +16 -16
  71. data/lib/aspera/preview/terminal.rb +3 -3
  72. data/lib/aspera/preview/utils.rb +11 -13
  73. data/lib/aspera/products/connect.rb +2 -1
  74. data/lib/aspera/products/desktop.rb +1 -1
  75. data/lib/aspera/products/transferd.rb +1 -1
  76. data/lib/aspera/proxy_auto_config.rb +2 -2
  77. data/lib/aspera/rest.rb +70 -50
  78. data/lib/aspera/rest_error_analyzer.rb +1 -0
  79. data/lib/aspera/rest_errors_aspera.rb +1 -1
  80. data/lib/aspera/secret_hider.rb +5 -5
  81. data/lib/aspera/ssh.rb +5 -5
  82. data/lib/aspera/temp_file_manager.rb +1 -0
  83. data/lib/aspera/timer_limiter.rb +7 -5
  84. data/lib/aspera/transfer/async_conf.schema.yaml +716 -0
  85. data/lib/aspera/transfer/convert.rb +29 -0
  86. data/lib/aspera/transfer/error_info.rb +66 -66
  87. data/lib/aspera/transfer/parameters.rb +13 -68
  88. data/lib/aspera/transfer/spec.rb +5 -6
  89. data/lib/aspera/transfer/spec.schema.yaml +753 -0
  90. data/lib/aspera/transfer/spec_doc.rb +62 -0
  91. data/lib/aspera/transfer/sync.rb +37 -76
  92. data/lib/aspera/transfer/sync_instance.schema.yaml +20 -0
  93. data/lib/aspera/transfer/sync_session.schema.yaml +86 -0
  94. data/lib/aspera/transfer/uri.rb +6 -6
  95. data/lib/aspera/uri_reader.rb +1 -1
  96. data/lib/aspera/web_auth.rb +1 -1
  97. data/lib/aspera/web_server_simple.rb +53 -44
  98. data.tar.gz.sig +0 -0
  99. metadata +38 -7
  100. metadata.gz.sig +0 -0
  101. data/examples/build_package.sh +0 -28
  102. data/examples/dascli +0 -30
  103. data/examples/get_proto_file.rb +0 -8
  104. data/examples/proxy.pac +0 -60
  105. data/lib/aspera/transfer/spec.yaml +0 -718
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspera
4
+ module Keychain
5
+ # Manage secrets in a Hashicorp Vault
6
+ class Factory
7
+ LIST = %i[file system vault].freeze
8
+ class << self
9
+ def create(info, name, folder, password)
10
+ Aspera.assert_type(info, Hash)
11
+ Aspera.assert(info.values.all?(String)){'vault info shall have only string values'}
12
+ info = info.symbolize_keys
13
+ vault_type = info.delete(:type)
14
+ Aspera.assert_values(vault_type, LIST.map(&:to_s)){'vault.type'}
15
+ case vault_type
16
+ when 'file'
17
+ info[:file] ||= 'vault.bin'
18
+ info[:file] = File.join(folder, info[:file]) unless File.absolute_path?(info[:file])
19
+ Aspera.assert(!password.nil?){'please provide password'}
20
+ info[:password] = password
21
+ # this module requires compilation, so it is optional
22
+ require 'aspera/keychain/encrypted_hash'
23
+ @vault = Keychain::EncryptedHash.new(**info)
24
+ when 'system'
25
+ case Environment.os
26
+ when Environment::OS_MACOS
27
+ info[:name] ||= name
28
+ @vault = Keychain::MacosSystem.new(**info)
29
+ else
30
+ raise 'not implemented for this OS'
31
+ end
32
+ when 'vault'
33
+ require 'aspera/keychain/hashicorp_vault'
34
+ @vault = Keychain::HashicorpVault.new(**info)
35
+ else Aspera.error_unexpected_value(vault_type)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/environment'
4
+ require 'aspera/log'
5
+ require 'aspera/assert'
6
+ require 'vault'
7
+
8
+ module Aspera
9
+ module Keychain
10
+ # Manage secrets in a Hashicorp Vault
11
+ class HashicorpVault
12
+ SECRET_PATH = 'secret/data/'
13
+
14
+ private_constant :SECRET_PATH
15
+
16
+ def initialize(url:, token:)
17
+ Vault.configure do |config|
18
+ config.address = url
19
+ config.token = token
20
+ end
21
+ end
22
+
23
+ def info
24
+ {
25
+ url: Vault.address,
26
+ password: Vault.auth_token
27
+ }
28
+ end
29
+
30
+ def list
31
+ metadata_path = SECRET_PATH.sub('/data/', '/metadata/')
32
+ return Vault.logical.list(metadata_path).filter_map do |label|
33
+ get(label: label).merge(label: label)
34
+ end
35
+ end
36
+
37
+ # Set a secret
38
+ # @param options [Hash] with keys :label, :username, :password, :url, :description
39
+ def set(options)
40
+ label = options.fetch(:label)
41
+ data = {
42
+ username: options[:username],
43
+ password: options[:password],
44
+ url: options[:url],
45
+ description: options[:description]
46
+ }.compact
47
+ Vault.logical.write(path(label), data: data)
48
+ end
49
+
50
+ def get(label:, exception: true)
51
+ secret = Vault.logical.read(path(label))
52
+ if secret.nil?
53
+ raise "Secret '#{label}' not found" if exception
54
+ return nil
55
+ end
56
+ return secret.data[:data]
57
+ end
58
+
59
+ def delete(label:)
60
+ path = path(label)
61
+ Vault.logical.delete(path)
62
+ end
63
+
64
+ private
65
+
66
+ def path(label)
67
+ "#{SECRET_PATH}#{label}"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -60,7 +60,7 @@ module Aspera
60
60
  end
61
61
 
62
62
  def key_chains(output)
63
- output.split("\n").collect { |line| new(line.strip.gsub(/^"|"$/, '')) }
63
+ output.split("\n").collect{ |line| new(line.strip.gsub(/^"|"$/, ''))}
64
64
  end
65
65
 
66
66
  def default
@@ -77,7 +77,7 @@ module Aspera
77
77
  end
78
78
 
79
79
  def by_name(name)
80
- list.find{|kc|kc.path.end_with?("/#{name}.keychain-db")}
80
+ list.find{ |kc| kc.path.end_with?("/#{name}.keychain-db")}
81
81
  end
82
82
  end
83
83
  attr_reader :path
@@ -123,15 +123,28 @@ module Aspera
123
123
  end
124
124
 
125
125
  class MacosSystem
126
- def initialize(name=nil, _password=nil)
127
- @keychain = name.nil? ? MacosSecurity::Keychain.default_keychain : MacosSecurity::Keychain.by_name(name)
126
+ OPTIONS = %i[label username password url description].freeze
127
+ def initialize(name: nil)
128
+ @keychain_name = name.nil? ? 'default keychain' : name
129
+ @keychain = name.nil? ? MacosSecurity::Keychain.default : MacosSecurity::Keychain.by_name(name)
128
130
  raise "no such keychain #{name}" if @keychain.nil?
129
131
  end
130
132
 
133
+ def info
134
+ return {
135
+ keychain: @keychain_name
136
+ }
137
+ end
138
+
139
+ def list
140
+ # the only way to list is `dump-keychain` which triggers security alert
141
+ raise 'list not implemented, use macos keychain app'
142
+ end
143
+
131
144
  def set(options)
132
145
  Aspera.assert_type(options, Hash){'options'}
133
- unsupported = options.keys - %i[label username password url description]
134
- Aspera.assert(unsupported.empty?){"unsupported options: #{unsupported}"}
146
+ unsupported = options.keys - OPTIONS
147
+ Aspera.assert(unsupported.empty?){"unsupported options: #{unsupported}, use #{OPTIONS.join(', ')}"}
135
148
  @keychain.password(
136
149
  :add, :generic, service: options[:label],
137
150
  account: options[:username] || 'none', password: options[:password], comment: options[:description])
@@ -149,11 +162,6 @@ module Aspera
149
162
  return result
150
163
  end
151
164
 
152
- def list
153
- # the only way to list is `dump-keychain` which triggers security alert
154
- raise 'list not implemented, use macos keychain app'
155
- end
156
-
157
165
  def delete(options)
158
166
  Aspera.assert_type(options, Hash){'options'}
159
167
  unsupported = options.keys - %i[label]
data/lib/aspera/log.rb CHANGED
@@ -9,57 +9,53 @@ require 'json'
9
9
  require 'singleton'
10
10
  require 'stringio'
11
11
 
12
+ # Ignore warnings
12
13
  old_verbose = $VERBOSE
13
14
  $VERBOSE = nil
14
15
 
15
- # extend Ruby logger with trace levels
16
+ # Extend Ruby logger with trace levels
16
17
  class Logger
18
+ # Two additionnal trace levels
17
19
  TRACE_MAX = 2
18
- # add custom level to logger severity
20
+ # Add custom level to logger severity, below debug level
19
21
  module Severity
20
- 1.upto(TRACE_MAX).each { |level| const_set(:"TRACE#{level}", - level)}
22
+ 1.upto(TRACE_MAX).each{ |level| const_set(:"TRACE#{level}", - level)}
21
23
  end
22
- # quick access to label
23
- SEVERITY_LABEL = Severity.constants.each_with_object({}) { |name, hash| hash[Severity.const_get(name)] = name}
24
+ # Quick access to label
25
+ SEVERITY_LABEL = Severity.constants.each_with_object({}){ |name, hash| hash[Severity.const_get(name)] = name}
24
26
  def format_severity(severity)
25
27
  SEVERITY_LABEL[severity] || 'ANY'
26
28
  end
27
29
 
28
- # define methods for a given level
29
- def self.make_methods(str_level) # rubocop:disable Style/ClassMethodsDefinitions
30
- int_level = ::Logger.const_get(str_level.upcase)
31
- str_level = str_level.downcase
32
- Kernel.send('lave'.reverse, <<-EOM, nil, __FILE__, __LINE__ + 1)
33
- def #{str_level}(message = nil, &block)
34
- add(#{int_level}, message, &block)
35
- end
36
-
37
- def #{str_level}?
38
- level <= #{int_level}
39
- end
40
-
41
- def #{str_level}!
42
- self.level = #{int_level}
43
- end
44
- EOM
30
+ class << self
31
+ # Define methods for a given log level
32
+ def make_methods(str_level)
33
+ int_level = ::Logger.const_get(str_level.upcase)
34
+ method_base = str_level.downcase
35
+ define_method(method_base, ->(message = nil, &block){add(int_level, message, &block)})
36
+ define_method("#{method_base}?", ->{level <= int_level})
37
+ define_method("#{method_base}!", ->{self.level = int_level})
38
+ end
45
39
  end
46
- # declare methods for all levels
47
- Logger::Severity.constants.each { |severity| make_methods(severity) }
40
+ # Declare methods for all levels
41
+ Logger::Severity.constants.each{ |severity| make_methods(severity)}
48
42
  end
49
43
 
44
+ # Restore warnings
50
45
  $VERBOSE = old_verbose
51
46
 
52
47
  module Aspera
53
48
  # Singleton object for logging
54
49
  class Log
55
50
  include Singleton
56
- # where logs are sent to
51
+
52
+ # Where logs are sent to
57
53
  LOG_TYPES = %i[stderr stdout syslog].freeze
58
54
  @@format = :json # rubocop:disable Style/ClassVars
59
- # class methods
55
+ # Class methods
60
56
  class << self
61
57
  # levels are :debug,:info,:warn,:error,fatal,:unknown
62
- def levels; Logger::Severity.constants.sort{|a, b|Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym}; end
58
+ def levels; Logger::Severity.constants.sort{ |a, b| Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{ |c| c.downcase.to_sym}; end
63
59
 
64
60
  # get the logger object of singleton
65
61
  def log; instance.logger; end
@@ -83,7 +79,7 @@ module Aspera
83
79
  def capture_stderr
84
80
  real_stderr = $stderr
85
81
  $stderr = StringIO.new
86
- yield
82
+ yield if block_given?
87
83
  log.debug($stderr.string)
88
84
  ensure
89
85
  $stderr = real_stderr
@@ -93,12 +89,12 @@ module Aspera
93
89
  attr_reader :logger_type, :logger
94
90
  attr_writer :program_name
95
91
 
96
- # set log level of underlying logger given symbol level
92
+ # Set log level of underlying logger given symbol level
97
93
  def level=(new_level)
98
94
  @logger.level = Logger::Severity.const_get(new_level.to_sym.upcase)
99
95
  end
100
96
 
101
- # get symbol of debug level of underlying logger
97
+ # Get symbol of debug level of underlying logger
102
98
  def level
103
99
  Logger::Severity.constants.each do |name|
104
100
  return name.downcase.to_sym if @logger.level.eql?(Logger::Severity.const_get(name))
@@ -106,7 +102,7 @@ module Aspera
106
102
  Aspera.error_unexpected_value(@logger.level){'log level'}
107
103
  end
108
104
 
109
- # change underlying logger, but keep log level
105
+ # Change underlying logger, but keep log level
110
106
  def logger_type=(new_log_type)
111
107
  current_severity_integer = @logger.level unless @logger.nil?
112
108
  current_severity_integer = ENV.fetch('AS_LOG_LEVEL', nil) if current_severity_integer.nil? && ENV.key?('AS_LOG_LEVEL')
@@ -131,7 +127,7 @@ module Aspera
131
127
  end
132
128
  @logger.level = current_severity_integer
133
129
  @logger_type = new_log_type
134
- # update formatter with password hiding
130
+ # Update formatter with password hiding
135
131
  @logger.formatter = SecretHider.log_formatter(@logger.formatter)
136
132
  end
137
133
 
@@ -140,9 +136,8 @@ module Aspera
140
136
  def initialize
141
137
  @logger = nil
142
138
  @program_name = 'aspera'
143
- # this sets @logger and @logger_type (self needed to call method instead of local var)
139
+ # This sets @logger and @logger_type (self needed to call method instead of local var)
144
140
  self.logger_type = :stderr
145
- raise 'error logger shall be defined' if @logger.nil?
146
141
  end
147
142
  end
148
143
  end
data/lib/aspera/nagios.rb CHANGED
@@ -17,7 +17,7 @@ module Aspera
17
17
  # add methods to add nagios error levels, each take component name and message
18
18
  LEVELS.each_index do |code|
19
19
  name = "#{ADD_PREFIX}#{LEVELS[code]}".to_sym
20
- define_method(name){|comp, msg|@data.push({code: code, comp: comp, msg: msg})}
20
+ define_method(name){ |comp, msg| @data.push({code: code, comp: comp, msg: msg})}
21
21
  end
22
22
 
23
23
  class << self
@@ -28,17 +28,17 @@ module Aspera
28
28
  %w[status component message].each do |c|
29
29
  Aspera.assert(data.first.key?(c)){"result must have #{c}"}
30
30
  end
31
- res_errors = data.reject{|s|s['status'].eql?('ok')}
31
+ res_errors = data.reject{ |s| s['status'].eql?('ok')}
32
32
  # keep only errors in case of problem, other ok are assumed so
33
33
  data = res_errors unless res_errors.empty?
34
34
  # first is most critical
35
- data.sort!{|a, b|LEVELS.index(a['status'].to_sym) <=> LEVELS.index(b['status'].to_sym)}
35
+ data.sort!{ |a, b| LEVELS.index(a['status'].to_sym) <=> LEVELS.index(b['status'].to_sym)}
36
36
  # build message: if multiple components: concatenate
37
37
  # message = data.map{|i|"#{i['component']}:#{i['message']}"}.join(', ').gsub("\n",' ')
38
38
  message = data
39
- .map{|i|i['component']}
39
+ .map{ |i| i['component']}
40
40
  .uniq
41
- .map{|comp|comp + ':' + data.select{|d|d['component'].eql?(comp)}.map{|d|d['message']}.join(',')}
41
+ .map{ |comp| comp + ':' + data.select{ |d| d['component'].eql?(comp)}.map{ |d| d['message']}.join(',')}
42
42
  .join(', ')
43
43
  .tr("\n", ' ')
44
44
  status = data.first['status'].upcase
@@ -80,7 +80,7 @@ module Aspera
80
80
  # translate for display
81
81
  def result
82
82
  raise 'missing result' if @data.empty?
83
- {type: :object_list, data: @data.map{|i|{'status' => LEVELS[i[:code]].to_s, 'component' => i[:comp], 'message' => i[:msg]}}}
83
+ {type: :object_list, data: @data.map{ |i| {'status' => LEVELS[i[:code]].to_s, 'component' => i[:comp], 'message' => i[:msg]}}}
84
84
  end
85
85
  end
86
86
  end
@@ -18,7 +18,7 @@ module Aspera
18
18
  end
19
19
 
20
20
  def all_sessions
21
- @agent.sessions.map { |session| session[:job_id] }.uniq.each.map{|job_id|job_to_transfer(job_id)}
21
+ @agent.sessions.map{ |session| session[:job_id]}.uniq.each.map{ |job_id| job_to_transfer(job_id)}
22
22
  end
23
23
 
24
24
  # status: ('waiting', 'partially_completed', 'unknown', 'waiting(read error)',] 'running', 'completed', 'failed'
@@ -185,9 +185,9 @@ module Aspera
185
185
  'size' => folder_stat.size,
186
186
  'mtime' => folder_stat.mtime.utc.iso8601,
187
187
  'permissions' => [
188
- { 'name' => 'view' },
189
- { 'name' => 'edit' },
190
- { 'name' => 'delete' }
188
+ {'name' => 'view'},
189
+ {'name' => 'edit'},
190
+ {'name' => 'delete'}
191
191
  ]
192
192
  },
193
193
  'items' => []
@@ -208,9 +208,9 @@ module Aspera
208
208
  'size' => item_stat.size,
209
209
  'mtime' => item_stat.mtime.utc.iso8601,
210
210
  'permissions' => [
211
- { 'name' => 'view' },
212
- { 'name' => 'edit' },
213
- { 'name' => 'delete' }
211
+ {'name' => 'view'},
212
+ {'name' => 'edit'},
213
+ {'name' => 'delete'}
214
214
  ]
215
215
  }
216
216
 
@@ -323,7 +323,7 @@ module Aspera
323
323
 
324
324
  def set_json_response(request, response, json, code: 200)
325
325
  response.status = code
326
- response['Content-Type'] = 'application/json'
326
+ response['Content-Type'] = Rest::MIME_JSON
327
327
  response.body = json.to_json
328
328
  Log.log.trace1{Log.dump("response for #{request.request_method} #{request.path}", json)}
329
329
  end
@@ -10,14 +10,15 @@ module Aspera
10
10
  # OAuth 2 client for the REST client
11
11
  # Generate bearer token
12
12
  # Bearer tokens are cached in memory and in a file cache for later re-use
13
- # https://tools.ietf.org/html/rfc6749
13
+ # OAuth 2.0 Authorization Framework: https://tools.ietf.org/html/rfc6749
14
+ # Bearer Token Usage: https://tools.ietf.org/html/rfc6750
14
15
  class Base
15
16
  # @param ** Parameters for REST
16
17
  # @param client_id [String, nil]
17
18
  # @param client_secret [String, nil]
18
19
  # @param scope [String, nil]
19
20
  # @param use_query [bool] Provide parameters in query instead of body
20
- # @param path_token [String] API end point to create a token
21
+ # @param path_token [String] API end point to create a token from base URL
21
22
  # @param token_field [String] Field in result that contains the token
22
23
  # @param cache_ids [Array, nil] List of unique identifiers for cache id generation
23
24
  def initialize(
@@ -53,10 +54,12 @@ module Aspera
53
54
  @token_cache_id = Factory.cache_id(@api.base_url, self.class, @base_cache_ids, @scope)
54
55
  end
55
56
 
57
+ attr_reader :scope
58
+
56
59
  # helper method to create token as per RFC
57
60
  def create_token_call(creation_params)
58
61
  Log.log.debug{'Generating a new token'.bg_green}
59
- payload = { body_type: :www }
62
+ payload = {content_type: Rest::MIME_WWW}
60
63
  if @use_query
61
64
  payload[:query] = creation_params
62
65
  else
@@ -65,7 +68,7 @@ module Aspera
65
68
  return @api.call(
66
69
  operation: 'POST',
67
70
  subpath: @path_token,
68
- headers: {'Accept' => 'application/json'},
71
+ headers: {'Accept' => Rest::MIME_JSON},
69
72
  **payload
70
73
  )
71
74
  end
@@ -96,12 +99,13 @@ module Aspera
96
99
  unless token_info.nil?
97
100
  token_data = token_info[:data]
98
101
  # Optional optimization:
99
- # check if node token is expired based on decoded content then force refresh if close enough
102
+ # Check if token is expired based on decoded content then force refresh if close enough
100
103
  # might help in case the transfer agent cannot refresh himself
101
104
  # `direct` agent is equipped with refresh code
102
105
  # an API was already called, but failed, we need to regenerate or refresh
103
106
  if refresh || token_info[:expired]
104
- if token_data.key?('refresh_token') && token_data['refresh_token'].eql?('not_supported')
107
+ refresh_token = nil
108
+ if token_data.key?('refresh_token') && !token_data['refresh_token'].eql?('not_supported')
105
109
  # save possible refresh token, before deleting the cache
106
110
  refresh_token = token_data['refresh_token']
107
111
  end
@@ -9,6 +9,7 @@ module Aspera
9
9
  # Factory to create tokens and manage their cache
10
10
  class Factory
11
11
  include Singleton
12
+
12
13
  # a prefix for persistency of tokens (simplify garbage collect)
13
14
  PERSIST_CATEGORY_TOKEN = 'token'
14
15
  # prefix for bearer token when in header
@@ -36,9 +37,8 @@ module Aspera
36
37
  return IdGenerator.from_list([
37
38
  PERSIST_CATEGORY_TOKEN,
38
39
  url,
39
- Factory.class_to_id(creator_class),
40
- *params
41
- ].flatten)
40
+ Factory.class_to_id(creator_class)] +
41
+ params)
42
42
  end
43
43
 
44
44
  # @return snake version of class name
@@ -111,7 +111,7 @@ module Aspera
111
111
  token_data = JSON.parse(token_raw_string)
112
112
  Aspera.assert_type(token_data, Hash)
113
113
  decoded_token = decode_token(token_data[TOKEN_FIELD])
114
- info = { data: token_data }
114
+ info = {data: token_data}
115
115
  Log.log.debug{Log.dump('decoded_token', decoded_token)}
116
116
  if decoded_token.is_a?(Hash)
117
117
  info[:decoded] = decoded_token
@@ -159,11 +159,11 @@ module Aspera
159
159
  Aspera.assert_type(parameters, Hash)
160
160
  id = parameters[:grant_method]
161
161
  Aspera.assert(@token_type_classes.key?(id)){"token grant method unknown: '#{id}'"}
162
- create_parameters = parameters.reject { |k, _v| k.eql?(:grant_method) }
162
+ create_parameters = parameters.reject{ |k, _v| k.eql?(:grant_method)}
163
163
  @token_type_classes[id].new(**create_parameters)
164
164
  end
165
165
  end
166
166
  # JSON Web Signature (JWS) compact serialization: https://datatracker.ietf.org/doc/html/rfc7515
167
- Factory.instance.register_decoder(lambda { |token| parts = token.split('.'); Aspera.assert(parts.length.eql?(3)){'not JWS token'}; JSON.parse(Base64.decode64(parts[1]))}) # rubocop:disable Style/Semicolon, Layout/LineLength
167
+ Factory.instance.register_decoder(lambda{ |token| parts = token.split('.'); Aspera.assert(parts.length.eql?(3)){'not JWS token'}; JSON.parse(Base64.decode64(parts[1]))}) # rubocop:disable Style/Semicolon, Layout/LineLength
168
168
  end
169
169
  end
@@ -20,12 +20,12 @@ module Aspera
20
20
 
21
21
  def create_token
22
22
  @api.call(
23
- operation: 'POST',
24
- subpath: @path_token,
25
- headers: {'Accept' => 'application/json'},
26
- query: @query.merge(scope: @scope), # scope is here because it may change over time (node)
27
- body: @body,
28
- body_type: :json
23
+ operation: 'POST',
24
+ subpath: @path_token,
25
+ query: @query.merge(scope: @scope), # scope is here because it may change over time (node)
26
+ content_type: Rest::MIME_JSON,
27
+ body: @body,
28
+ headers: {'Accept' => Rest::MIME_JSON}
29
29
  )
30
30
  end
31
31
  end
@@ -8,9 +8,9 @@ module Aspera
8
8
  # Persist data on file system
9
9
  class PersistencyActionOnce
10
10
  DELETE_DEFAULT = lambda(&:empty?)
11
- PARSE_DEFAULT = lambda {|t| JSON.parse(t)}
12
- FORMAT_DEFAULT = lambda {|h| JSON.generate(h)}
13
- MERGE_DEFAULT = lambda {|current, file| current.concat(file).uniq rescue current}
11
+ PARSE_DEFAULT = lambda{ |t| JSON.parse(t)}
12
+ FORMAT_DEFAULT = lambda{ |h| JSON.generate(h)}
13
+ MERGE_DEFAULT = lambda{ |current, file| current.concat(file).uniq rescue current}
14
14
  MANAGER_METHODS = %i[get put delete]
15
15
  private_constant :DELETE_DEFAULT, :PARSE_DEFAULT, :FORMAT_DEFAULT, :MERGE_DEFAULT, :MANAGER_METHODS
16
16
 
@@ -22,7 +22,7 @@ module Aspera
22
22
  # @param :format Optional dump method (default to JSON)
23
23
  # @param :merge Optional merge data from file to current data
24
24
  def initialize(manager:, data:, id:, delete: DELETE_DEFAULT, parse: PARSE_DEFAULT, format: FORMAT_DEFAULT, merge: MERGE_DEFAULT)
25
- Aspera.assert(MANAGER_METHODS.all?{|i|manager.respond_to?(i)}){"Manager must answer to #{MANAGER_METHODS}"}
25
+ Aspera.assert(MANAGER_METHODS.all?{ |i| manager.respond_to?(i)}){"Manager must answer to #{MANAGER_METHODS}"}
26
26
  Aspera.assert(!data.nil?)
27
27
  Aspera.assert_type(id, String)
28
28
  Aspera.assert(!id.empty?)
@@ -39,6 +39,7 @@ module Aspera
39
39
  merge.call(@persisted_object, parse.call(value)) unless value.nil?
40
40
  end
41
41
 
42
+ # Save persisted object on storage
42
43
  def save
43
44
  if @delete_condition.call(@persisted_object)
44
45
  @manager.delete(@object_id)
@@ -47,6 +48,7 @@ module Aspera
47
48
  end
48
49
  end
49
50
 
51
+ # @return internal persisted object, in order to modify its content
50
52
  def data
51
53
  return @persisted_object
52
54
  end
@@ -64,7 +64,7 @@ module Aspera
64
64
  garbage_files = current_files(persist_category)
65
65
  if !max_age_seconds.nil?
66
66
  current_time = Time.now
67
- garbage_files.select! { |filepath| (current_time - File.stat(filepath).mtime).to_i > max_age_seconds}
67
+ garbage_files.select!{ |filepath| (current_time - File.stat(filepath).mtime).to_i > max_age_seconds}
68
68
  end
69
69
  garbage_files.each do |filepath|
70
70
  File.delete(filepath)
@@ -79,7 +79,7 @@ module Aspera
79
79
  end
80
80
 
81
81
  def current_items(persist_category)
82
- current_files(persist_category).each_with_object({}) {|i, h| h[File.basename(i, FILE_SUFFIX)] = File.read(i)}
82
+ current_files(persist_category).each_with_object({}){ |i, h| h[File.basename(i, FILE_SUFFIX)] = File.read(i)}
83
83
  end
84
84
 
85
85
  private