aspera-cli 4.18.1 → 4.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +13 -0
  4. data/CONTRIBUTING.md +5 -12
  5. data/README.md +60 -29
  6. data/examples/build_exec +85 -0
  7. data/lib/aspera/agent/base.rb +2 -0
  8. data/lib/aspera/agent/direct.rb +108 -104
  9. data/lib/aspera/api/aoc.rb +2 -2
  10. data/lib/aspera/api/httpgw.rb +91 -56
  11. data/lib/aspera/ascp/installation.rb +47 -32
  12. data/lib/aspera/ascp/management.rb +4 -1
  13. data/lib/aspera/ascp/products.rb +1 -7
  14. data/lib/aspera/cli/formatter.rb +24 -18
  15. data/lib/aspera/cli/manager.rb +10 -10
  16. data/lib/aspera/cli/plugin.rb +2 -2
  17. data/lib/aspera/cli/plugin_factory.rb +10 -1
  18. data/lib/aspera/cli/plugins/config.rb +15 -10
  19. data/lib/aspera/cli/plugins/node.rb +4 -3
  20. data/lib/aspera/cli/plugins/server.rb +1 -1
  21. data/lib/aspera/cli/plugins/shares.rb +11 -7
  22. data/lib/aspera/cli/sync_actions.rb +72 -31
  23. data/lib/aspera/cli/transfer_agent.rb +1 -0
  24. data/lib/aspera/cli/transfer_progress.rb +1 -1
  25. data/lib/aspera/cli/version.rb +1 -1
  26. data/lib/aspera/environment.rb +43 -10
  27. data/lib/aspera/faspex_gw.rb +1 -1
  28. data/lib/aspera/keychain/encrypted_hash.rb +2 -0
  29. data/lib/aspera/log.rb +1 -0
  30. data/lib/aspera/node_simulator.rb +1 -1
  31. data/lib/aspera/oauth/jwt.rb +1 -1
  32. data/lib/aspera/oauth/url_json.rb +2 -0
  33. data/lib/aspera/oauth/web.rb +5 -4
  34. data/lib/aspera/secret_hider.rb +3 -2
  35. data/lib/aspera/ssh.rb +1 -1
  36. data/lib/aspera/transfer/faux_file.rb +7 -5
  37. data/lib/aspera/transfer/parameters.rb +27 -19
  38. data/lib/aspera/transfer/spec.rb +8 -10
  39. data/lib/aspera/transfer/sync.rb +52 -47
  40. data/lib/aspera/web_auth.rb +0 -1
  41. data/lib/aspera/web_server_simple.rb +24 -13
  42. data.tar.gz.sig +0 -0
  43. metadata +3 -3
  44. metadata.gz.sig +0 -0
  45. data/examples/rubyc +0 -24
@@ -7,11 +7,30 @@ module Aspera
7
7
  module Cli
8
8
  # Module for sync actions
9
9
  module SyncActions
10
- SIMPLE_ARGUMENTS_SYNC = {
11
- direction: Transfer::Sync::DIRECTIONS,
12
- local_dir: String,
13
- remote_dir: String
14
- }.stringify_keys.freeze
10
+ # optional simple command line arguments for sync
11
+ # in Array to keep option order
12
+ # conf: key in option --conf
13
+ # args: key for command line args
14
+ # values: possible values for argument
15
+ # type: type for validation
16
+ SYNC_ARGUMENTS_INFO = [
17
+ {
18
+ conf: 'direction',
19
+ args: 'direction',
20
+ values: Transfer::Sync::DIRECTIONS
21
+ }, {
22
+ conf: 'remote.path',
23
+ args: 'remote_dir',
24
+ type: String
25
+ }, {
26
+ conf: 'local.path',
27
+ args: 'local_dir',
28
+ type: String
29
+ }
30
+ ].freeze
31
+ # name of minimal arguments required, also used to generate a session name
32
+ SYNC_SIMPLE_ARGS = SYNC_ARGUMENTS_INFO.map{|i|i[:conf]}.freeze
33
+ private_constant :SYNC_ARGUMENTS_INFO, :SYNC_SIMPLE_ARGS
15
34
 
16
35
  class << self
17
36
  def declare_options(options)
@@ -19,38 +38,60 @@ module Aspera
19
38
  end
20
39
  end
21
40
 
41
+ # Read command line arguments (3) and converts to sync_info format
42
+ def sync_args_to_params(async_params)
43
+ # sync session parameters can be provided on command line instead of sync_info
44
+ arguments = {}
45
+ SYNC_ARGUMENTS_INFO.each do |info|
46
+ value = options.get_next_argument(
47
+ info[:conf],
48
+ mandatory: false,
49
+ validation: info[:type],
50
+ accept_list: info[:values])
51
+ break if value.nil?
52
+ arguments[info[:conf]] = value.to_s
53
+ end
54
+ Log.log.debug{Log.dump('arguments', arguments)}
55
+ raise Cli::BadArgument, "Provide 0 or 3 arguments, not #{arguments.keys.length} for: #{SYNC_SIMPLE_ARGS.join(', ')}" unless
56
+ [0, 3].include?(arguments.keys.length)
57
+ if !arguments.empty?
58
+ session_info = async_params
59
+ param_path = :conf
60
+ if async_params.key?('sessions') || async_params.key?('instance')
61
+ async_params['sessions'] ||= [{}]
62
+ Aspera.assert(async_params['sessions'].length == 1){'Only one session is supported with arguments'}
63
+ session_info = async_params['sessions'][0]
64
+ param_path = :args
65
+ end
66
+ SYNC_ARGUMENTS_INFO.each do |info|
67
+ key_path = info[param_path].split('.')
68
+ hash_for_key = session_info
69
+ if key_path.length > 1
70
+ first = key_path.shift
71
+ async_params[first] ||= {}
72
+ hash_for_key = async_params[first]
73
+ end
74
+ raise "Parameter #{info[:conf]} is also set in sync_info, remove from sync_info" if hash_for_key.key?(key_path.last)
75
+ hash_for_key[key_path.last] = arguments[info[:conf]]
76
+ end
77
+ if !session_info.key?('name')
78
+ # if no name is specified, generate one from simple arguments
79
+ session_info['name'] = SYNC_SIMPLE_ARGS.map do |arg_name|
80
+ arguments[arg_name]&.gsub(/[^a-zA-Z0-9]/, '')
81
+ end.compact.reject(&:empty?).join('_')
82
+ end
83
+ end
84
+ end
85
+
22
86
  def execute_sync_action(&block)
23
87
  Aspera.assert(block){'No block given'}
24
88
  command = options.get_next_command(%i[start admin])
25
89
  # try to get 3 arguments as simple arguments
26
90
  case command
27
91
  when :start
28
- simple_session_args = {}
29
- SIMPLE_ARGUMENTS_SYNC.each do |arg, check|
30
- value = options.get_next_argument(
31
- arg,
32
- mandatory: false,
33
- validation: check.is_a?(Class) ? check : nil,
34
- accept_list: check.is_a?(Class) ? nil : check)
35
- break if value.nil?
36
- simple_session_args[arg] = value.to_s
37
- end
38
- async_params = nil
39
- if simple_session_args.empty?
40
- async_params = options.get_option(:sync_info, mandatory: true)
41
- else
42
- raise Cli::BadArgument,
43
- "Provide zero or 3 arguments: #{SIMPLE_ARGUMENTS_SYNC.keys.join(',')}" unless simple_session_args.keys.sort == SIMPLE_ARGUMENTS_SYNC.keys.sort
44
- async_params = options.get_option(
45
- :sync_info,
46
- mandatory: false,
47
- default: {'sessions' => [{'name' => File.basename(simple_session_args['local_dir'])}]})
48
- Aspera.assert_type(async_params, Hash){'sync_info'}
49
- Aspera.assert_type(async_params['sessions'], Array){'sync_info[sessions]'}
50
- Aspera.assert_type(async_params['sessions'].first, Hash){'sync_info[sessions][0]'}
51
- async_params['sessions'].first.merge!(simple_session_args)
52
- end
53
- Log.log.debug{Log.dump('async_params', async_params)}
92
+ # possibilities are:
93
+ async_params = options.get_option(:sync_info, default: {})
94
+ sync_args_to_params(async_params)
54
95
  Transfer::Sync.start(async_params, &block)
55
96
  return Main.result_success
56
97
  when :admin
@@ -116,6 +116,7 @@ module Aspera
116
116
  # by default do not display ascp native progress bar
117
117
  agent_options[:quiet] = true unless agent_options.key?(:quiet)
118
118
  agent_options[:check_ignore_cb] = ->(host, port){@config.ignore_cert?(host, port)}
119
+ # JRuby
119
120
  agent_options[:trusted_certs] = @config.trusted_cert_locations unless agent_options.key?(:trusted_certs)
120
121
  when :httpgw
121
122
  unless agent_options.key?(:url) || @httpgw_url_lambda.nil?
@@ -25,7 +25,7 @@ module Aspera
25
25
  end
26
26
 
27
27
  def event(session_id:, type:, info: nil)
28
- Log.log.debug{"progress: #{type} #{session_id} #{info}"}
28
+ Log.log.trace1{"progress: #{type} #{session_id} #{info}"}
29
29
  Aspera.assert(!session_id.nil? || type.eql?(:pre_start)){'session_id is nil'}
30
30
  return if @completed
31
31
  if @progress_bar.nil?
@@ -4,6 +4,6 @@ module Aspera
4
4
  module Cli
5
5
  # for beta add extension : .beta1
6
6
  # for dev version add extension : .pre
7
- VERSION = '4.18.1'
7
+ VERSION = '4.19.0'
8
8
  end
9
9
  end
@@ -5,6 +5,7 @@ require 'aspera/log'
5
5
  require 'aspera/assert'
6
6
  require 'rbconfig'
7
7
  require 'singleton'
8
+ require 'English'
8
9
 
9
10
  # cspell:words MEBI mswin bccwin
10
11
 
@@ -31,7 +32,6 @@ module Aspera
31
32
  BYTES_PER_MEBIBIT = MEBI / BITS_PER_BYTE
32
33
 
33
34
  class << self
34
- @terminal_supports_unicode = nil
35
35
  def ruby_version
36
36
  return RbConfig::CONFIG['RUBY_PROGRAM_VERSION']
37
37
  end
@@ -66,10 +66,13 @@ module Aspera
66
66
  raise "Unknown CPU: #{RbConfig::CONFIG['host_cpu']}"
67
67
  end
68
68
 
69
+ # normalized architecture name
70
+ # see constants: OS_* and CPU_*
69
71
  def architecture
70
72
  return "#{os}-#{cpu}"
71
73
  end
72
74
 
75
+ # executable file extension for current OS
73
76
  def exe_extension
74
77
  return '.exe' if os.eql?(OS_WINDOWS)
75
78
  return ''
@@ -83,6 +86,7 @@ module Aspera
83
86
  Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"}
84
87
  end
85
88
 
89
+ # empty variable binding for secure eval
86
90
  def empty_binding
87
91
  return Kernel.binding
88
92
  end
@@ -92,7 +96,29 @@ module Aspera
92
96
  Kernel.send('lave'.reverse, code, empty_binding, file, line)
93
97
  end
94
98
 
95
- # value is provided in block
99
+ # start process in background, or raise exception
100
+ # caller can call Process.wait on returned value
101
+ def secure_spawn(env:, exec:, args:, log_only: false)
102
+ Log.log.debug do
103
+ [
104
+ 'execute:'.red,
105
+ env.map{|k, v| "#{k}=#{Shellwords.shellescape(v)}"},
106
+ Shellwords.shellescape(exec),
107
+ args.map{|a|Shellwords.shellescape(a)}
108
+ ].flatten.join(' ')
109
+ end
110
+ return if log_only
111
+ # start ascp in separate process
112
+ ascp_pid = Process.spawn(env, [exec, exec], *args, close_others: true)
113
+ Log.log.debug{"pid: #{ascp_pid}"}
114
+ return ascp_pid
115
+ end
116
+
117
+ # Write content to a file, with restricted access
118
+ # @param path [String] the file path
119
+ # @param force [Boolean] if true, overwrite the file
120
+ # @param mode [Integer] the file mode (permissions)
121
+ # @block [Proc] return the content to write to the file
96
122
  def write_file_restricted(path, force: false, mode: nil)
97
123
  Aspera.assert(block_given?, exception_class: Aspera::InternalError)
98
124
  if force || !File.exist?(path)
@@ -105,6 +131,7 @@ module Aspera
105
131
  return path
106
132
  end
107
133
 
134
+ # restrict access to a file or folder to user only
108
135
  def restrict_file_access(path, mode: nil)
109
136
  if mode.nil?
110
137
  # or FileUtils ?
@@ -121,18 +148,12 @@ module Aspera
121
148
  Log.log.warn(e.message)
122
149
  end
123
150
 
151
+ # @return true if we are in a terminal
124
152
  def terminal?
125
153
  $stdout.tty?
126
154
  end
127
155
 
128
- # @return true if we can display Unicode characters
129
- # https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html
130
- # https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
131
- def terminal_supports_unicode?
132
- @terminal_supports_unicode = terminal? && %w(LC_ALL LC_CTYPE LANG).any?{|var|ENV[var]&.include?('UTF-8')} if @terminal_supports_unicode.nil?
133
- return @terminal_supports_unicode
134
- end
135
-
156
+ # @return :text or :graphical depending on the environment
136
157
  def default_gui_mode
137
158
  # assume not remotely connected on macos and windows
138
159
  return :graphical if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(Environment.os)
@@ -141,6 +162,7 @@ module Aspera
141
162
  return :text
142
163
  end
143
164
 
165
+ # open a URI in a graphical browser
144
166
  # command must be non blocking
145
167
  def open_uri_graphical(uri)
146
168
  case Environment.os
@@ -152,6 +174,7 @@ module Aspera
152
174
  end
153
175
  end
154
176
 
177
+ # open a file in an editor
155
178
  def open_editor(file_path)
156
179
  if ENV.key?('EDITOR')
157
180
  system(ENV['EDITOR'], file_path.to_s)
@@ -166,8 +189,18 @@ module Aspera
166
189
 
167
190
  def initialize
168
191
  @url_method = self.class.default_gui_mode
192
+ @terminal_supports_unicode = nil
169
193
  end
170
194
 
195
+ # @return true if we can display Unicode characters
196
+ # https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html
197
+ # https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
198
+ def terminal_supports_unicode?
199
+ @terminal_supports_unicode = self.class.terminal? && %w(LC_ALL LC_CTYPE LANG).any?{|var|ENV[var]&.include?('UTF-8')} if @terminal_supports_unicode.nil?
200
+ return @terminal_supports_unicode
201
+ end
202
+
203
+
171
204
  # Allows a user to open a Url
172
205
  # if method is "text", then URL is displayed on terminal
173
206
  # if method is "graphical", then the URL will be opened with the default browser.
@@ -29,7 +29,7 @@ module Aspera
29
29
  'recipients' => faspex_pkg_delivery['recipients'],
30
30
  'workspace_id' => @app_context
31
31
  }
32
- created_package = @app_api.create_package_simple(package_data, true, @new_user_option)
32
+ created_package = @app_api.create_package_simple(package_data, true, nil)
33
33
  # but we place it in a Faspex package creation response
34
34
  return {
35
35
  'links' => { 'status' => 'unused' },
@@ -16,10 +16,12 @@ module Aspera
16
16
  FILE_TYPE = 'encrypted_hash_vault'
17
17
  CONTENT_KEYS = %i[label username password url description].freeze
18
18
  FILE_KEYS = %w[version type cipher data].sort.freeze
19
+ private_constant :LEGACY_CIPHER_NAME, :DEFAULT_CIPHER_NAME, :FILE_TYPE, :CONTENT_KEYS, :FILE_KEYS
19
20
  def initialize(path, current_password)
20
21
  Aspera.assert_type(path, String){'path to vault file'}
21
22
  @path = path
22
23
  @all_secrets = {}
24
+ @cipher_name = DEFAULT_CIPHER_NAME
23
25
  vault_encrypted_data = nil
24
26
  if File.exist?(@path)
25
27
  vault_file = File.read(@path)
data/lib/aspera/log.rb CHANGED
@@ -6,6 +6,7 @@ require 'logger'
6
6
  require 'pp'
7
7
  require 'json'
8
8
  require 'singleton'
9
+ require 'stringio'
9
10
 
10
11
  old_verbose = $VERBOSE
11
12
  $VERBOSE = nil
@@ -39,7 +39,7 @@ module Aspera
39
39
  set_json_response(response, {
40
40
  application: 'node',
41
41
  current_time: Time.now.utc.iso8601(0),
42
- version: info['ascp_version'].gsub(/ .*$/, ''),
42
+ version: info['sdk_ascp_version'].gsub(/ .*$/, ''),
43
43
  license_expiration_date: info['expiration_date'],
44
44
  license_max_rate: info['maximum_bandwidth'],
45
45
  os: %x(uname -srv).chomp,
@@ -35,7 +35,7 @@ module Aspera
35
35
  def create_token
36
36
  require 'jwt'
37
37
  seconds_since_epoch = Time.new.to_i
38
- Log.log.info{"seconds=#{seconds_since_epoch}"}
38
+ Log.log.debug{"seconds_since_epoch=#{seconds_since_epoch}"}
39
39
  jwt_payload = {
40
40
  exp: seconds_since_epoch + OAuth::Factory.instance.parameters[:jwt_expiry_offset_sec], # expiration time
41
41
  nbf: seconds_since_epoch - OAuth::Factory.instance.parameters[:jwt_accepted_offset_sec], # not before
@@ -6,6 +6,8 @@ module Aspera
6
6
  module OAuth
7
7
  # This class is used to create a token using a JSON body and a URL
8
8
  class UrlJson < Base
9
+ # @param url URL to send the JSON body
10
+ # @param json JSON body to send
9
11
  def initialize(
10
12
  url:,
11
13
  json:,
@@ -8,8 +8,8 @@ module Aspera
8
8
  module OAuth
9
9
  # Authentication using Web browser
10
10
  class Web < Base
11
- # @param g_o:redirect_uri [M] for type :web
12
- # @param g_o:path_authorize [D] for type :web
11
+ # @param redirect_uri url to receive the code after auth (to be exchanged for token)
12
+ # @param path_authorize path to login page on web app
13
13
  def initialize(
14
14
  redirect_uri:,
15
15
  path_authorize: 'authorize',
@@ -21,11 +21,12 @@ module Aspera
21
21
  uri = URI.parse(@redirect_uri)
22
22
  Aspera.assert(%w[http https].include?(uri.scheme)){'redirect_uri scheme must be http or https'}
23
23
  Aspera.assert(!uri.port.nil?){'redirect_uri must have a port'}
24
- # TODO: we could check that host is localhost or local address
24
+ # TODO: we could check that host is localhost or local address, as we are going to listen locally
25
25
  end
26
26
 
27
27
  def create_token
28
- random_state = SecureRandom.uuid # used to check later
28
+ # generate secure state to check later
29
+ random_state = SecureRandom.uuid
29
30
  login_page_url = Rest.build_uri(
30
31
  "#{@base_url}/#{@path_authorize}",
31
32
  optional_scope_client_id.merge(response_type: 'code', redirect_uri: @redirect_uri, state: random_state))
@@ -16,17 +16,18 @@ module Aspera
16
16
  KEY_SECRETS = %w[password secret passphrase _key apikey crn token].freeze
17
17
  HTTP_SECRETS = %w[Authorization].freeze
18
18
  ALL_SECRETS = [ASCP_ENV_SECRETS, KEY_SECRETS, HTTP_SECRETS].flatten.freeze
19
+ ALL_SECRETS2 = [KEY_SECRETS, HTTP_SECRETS].flatten.freeze
19
20
  KEY_FALSE_POSITIVES = [/^access_key$/, /^fallback_private_key$/].freeze
20
21
  # regex that define named captures :begin and :end
21
22
  REGEX_LOG_REPLACES = [
22
23
  # CLI manager get/set options
23
24
  /(?<begin>[sg]et (?:#{KEY_SECRETS.join('|')})=).*(?<end>)/,
24
25
  # env var ascp exec
25
- /(?<begin> (?:#{ASCP_ENV_SECRETS.join('|')})=)(\\.|[^ ])*(?<end> )/,
26
+ /(?<begin> (?:#{ASCP_ENV_SECRETS.join('|')})=)[^ ]+(?<end> )/,
26
27
  # rendered JSON or Ruby
27
28
  /(?<begin>(?:(?<quote>["'])|:)[^"':=]*(?:#{ALL_SECRETS.join('|')})[^"':=]*\k<quote>?(?:=>|:) *")[^"]+(?<end>")/,
28
29
  # logged data
29
- /(?<begin>(?:#{ALL_SECRETS.join('|')})[ =:]+).*(?<end>$)/,
30
+ /(?<begin>(?:#{ALL_SECRETS2.join('|')})[ =:]+).*(?<end>$)/,
30
31
  # private key values
31
32
  /(?<begin>--+BEGIN [^-]+ KEY--+)[[:ascii:]]+?(?<end>--+?END [^-]+ KEY--+)/,
32
33
  # cred in http dump
data/lib/aspera/ssh.rb CHANGED
@@ -14,7 +14,7 @@ if ENV.fetch('ASCLI_ENABLE_ED25519', 'false').eql?('false')
14
14
  $VERBOSE = old_verbose
15
15
  end
16
16
 
17
- if RUBY_ENGINE == 'jruby' && ENV.fetch('ASCLI_ENABLE_ECDSHA2', 'false').eql?('false')
17
+ if defined?(JRUBY_VERSION) && ENV.fetch('ASCLI_ENABLE_ECDSHA2', 'false').eql?('false')
18
18
  Net::SSH::Transport::Algorithms::ALGORITHMS.each_value { |a| a.reject! { |a| a =~ /^ecd(sa|h)-sha2/ } }
19
19
  Net::SSH::KnownHosts::SUPPORTED_TYPE.reject! { |t| t =~ /^ecd(sa|h)-sha2/ }
20
20
  end
@@ -8,19 +8,21 @@ module Aspera
8
8
  PREFIX = 'faux:///'
9
9
  # size suffix
10
10
  SUFFIX = %w[k m g t p e]
11
+ private_constant :PREFIX, :SUFFIX
11
12
  class << self
12
- def open(name)
13
+ # @return nil if not a faux: scheme, else a FauxFile instance
14
+ def create(name)
13
15
  return nil unless name.start_with?(PREFIX)
14
- parts = name[PREFIX.length..-1].split('?')
15
- raise 'Format: #{PREFIX}<file path>?<size>' unless parts.length.eql?(2)
16
- raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m = parts[1].downcase.match(/^(\d+)([#{SUFFIX.join('')}])$/))
16
+ url_parts = name[PREFIX.length..-1].split('?')
17
+ raise 'Format: #{PREFIX}<file path>?<size>' unless url_parts.length.eql?(2)
18
+ raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m = url_parts[1].downcase.match(/^(\d+)([#{SUFFIX.join('')}])$/))
17
19
  size = m[1].to_i
18
20
  suffix = m[2]
19
21
  SUFFIX.each do |s|
20
22
  size *= 1024
21
23
  break if s.eql?(suffix)
22
24
  end
23
- return FauxFile.new(parts[0], size)
25
+ return FauxFile.new(url_parts[0], size)
24
26
  end
25
27
  end
26
28
  attr_reader :path, :size
@@ -21,9 +21,10 @@ module Aspera
21
21
  class Parameters
22
22
  # Agents shown in manual for parameters (sub list)
23
23
  SUPPORTED_AGENTS = %i[direct node connect trsdk httpgw].freeze
24
+ FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
24
25
  # Short names of columns in manual
25
26
  SUPPORTED_AGENTS_SHORT = SUPPORTED_AGENTS.map{|agent_sym|agent_sym.to_s[0].to_sym}
26
- FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
27
+ HTTP_FALLBACK_ACTIVATION_VALUES = ['1', 1, true, 'force'].freeze
27
28
 
28
29
  private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS
29
30
 
@@ -106,21 +107,25 @@ module Aspera
106
107
  # @param options [Hash] key: :wss: bool, :ascp_args: array of strings
107
108
  def initialize(
108
109
  job_spec,
109
- ascp_args:,
110
- wss:,
111
- quiet:,
112
- trusted_certs:,
113
- check_ignore_cb:
110
+ ascp_args: nil,
111
+ wss: true,
112
+ quiet: true,
113
+ trusted_certs: nil,
114
+ client_ssh_key: nil,
115
+ check_ignore_cb: nil
114
116
  )
115
117
  @job_spec = job_spec
116
- @ascp_args = ascp_args
118
+ @ascp_args = ascp_args.nil? ? [] : ascp_args
117
119
  @wss = wss
118
120
  @quiet = quiet
119
- @trusted_certs = trusted_certs
121
+ @trusted_certs = trusted_certs.nil? ? [] : trusted_certs
122
+ @client_ssh_key = client_ssh_key.nil? ? :rsa : client_ssh_key.to_sym
120
123
  @check_ignore_cb = check_ignore_cb
121
- Aspera.assert_type(job_spec, Hash)
124
+ Aspera.assert_type(@job_spec, Hash)
122
125
  Aspera.assert_type(@ascp_args, Array){'ascp_args'}
123
- Aspera.assert(@ascp_args.all?(String)){'ascp arguments must be Strings'}
126
+ Aspera.assert(@ascp_args.all?(String)){'all ascp arguments must be String'}
127
+ Aspera.assert_type(@trusted_certs, Array){'trusted_certs'}
128
+ Aspera.assert_values(@client_ssh_key, Ascp::Installation::CLIENT_SSH_KEY_OPTIONS)
124
129
  @builder = CommandLineBuilder.new(@job_spec, Spec::DESCRIPTION)
125
130
  end
126
131
 
@@ -162,6 +167,7 @@ module Aspera
162
167
  @builder.add_command_line_options(["#{file_list_option}=#{file_list_file}"]) unless file_list_option.nil?
163
168
  end
164
169
 
170
+ # @return the list of certificates to use when token/ssh or wss are used
165
171
  def remote_certificates
166
172
  certificates_to_use = []
167
173
  # use web socket secure for session ?
@@ -174,7 +180,7 @@ module Aspera
174
180
  @job_spec.delete('fasp_port')
175
181
  @job_spec.delete('sshfp')
176
182
  # set location for CA bundle to be the one of Ruby, see env var SSL_CERT_FILE / SSL_CERT_DIR
177
- certificates_to_use.concat(@trusted_certs) if @trusted_certs.is_a?(Array)
183
+ certificates_to_use.concat(@trusted_certs)
178
184
  # ignore cert for wss ?
179
185
  if @check_ignore_cb&.call(@job_spec['remote_host'], @job_spec['wss_port'])
180
186
  wss_cert_file = TempFileManager.instance.new_file_path_global('wss_cert')
@@ -191,7 +197,7 @@ module Aspera
191
197
  # add SSH bypass keys when authentication is token and no auth is provided
192
198
  if @job_spec.key?('token') && !@job_spec.key?('remote_password')
193
199
  # @job_spec['remote_password'] = Ascp::Installation.instance.ssh_cert_uuid # not used: no passphrase
194
- certificates_to_use.concat(Ascp::Installation.instance.aspera_token_ssh_key_paths)
200
+ certificates_to_use.concat(Ascp::Installation.instance.aspera_token_ssh_key_paths(@client_ssh_key))
195
201
  end
196
202
  end
197
203
  return certificates_to_use
@@ -200,9 +206,9 @@ module Aspera
200
206
  # translate transfer spec to env vars and command line arguments for ascp
201
207
  def ascp_args
202
208
  env_args = {
203
- args: [],
204
- env: {},
205
- ascp_version: :ascp
209
+ args: [],
210
+ env: {},
211
+ name: :ascp
206
212
  }
207
213
 
208
214
  # special cases
@@ -213,7 +219,7 @@ module Aspera
213
219
 
214
220
  # add ssh or wss certificates
215
221
  # (reverse, to keep order, as we unshift)
216
- remote_certificates.reverse_each do |cert|
222
+ remote_certificates&.reverse_each do |cert|
217
223
  env_args[:args].unshift('-i', cert)
218
224
  end
219
225
 
@@ -223,9 +229,9 @@ module Aspera
223
229
  base64_destination = false
224
230
  # symbol must be index of Ascp::Installation.paths
225
231
  if @builder.read_param('use_ascp4')
226
- env_args[:ascp_version] = :ascp4
232
+ env_args[:name] = :ascp4
227
233
  else
228
- env_args[:ascp_version] = :ascp
234
+ env_args[:name] = :ascp
229
235
  base64_destination = true
230
236
  end
231
237
  # destination will be base64 encoded, put this before source path arguments
@@ -243,10 +249,12 @@ module Aspera
243
249
  @builder.add_env_args(env_args)
244
250
  env_args[:args].unshift('-q') if @quiet
245
251
  # add fallback cert and key as arguments if needed
246
- if ['1', 1, true, 'force'].include?(@job_spec['http_fallback'])
252
+ if HTTP_FALLBACK_ACTIVATION_VALUES.include?(@job_spec['http_fallback'])
247
253
  env_args[:args].unshift('-Y', Ascp::Installation.instance.path(:fallback_private_key))
248
254
  env_args[:args].unshift('-I', Ascp::Installation.instance.path(:fallback_certificate))
249
255
  end
256
+ # disable redis in client
257
+ env_args[:env]['ASPERA_TEST_REDIS_DISABLE'] = 'true'
250
258
  Log.log.debug{"ascp args: #{env_args}"}
251
259
  return env_args
252
260
  end
@@ -22,23 +22,21 @@ module Aspera
22
22
  # reserved tag for Aspera
23
23
  TAG_RESERVED = 'aspera'
24
24
  class << self
25
- def action_to_direction(tspec, command)
26
- Aspera.assert_type(tspec, Hash){'transfer spec'}
27
- tspec['direction'] = case command.to_sym
25
+ # translate upload/download to send/receive
26
+ def transfer_type_to_direction(transfer_type)
27
+ case transfer_type.to_sym
28
28
  when :upload then DIRECTION_SEND
29
29
  when :download then DIRECTION_RECEIVE
30
- else Aspera.error_unexpected_value(command.to_sym)
30
+ else Aspera.error_unexpected_value(transfer_type.to_sym)
31
31
  end
32
- return tspec
33
32
  end
34
33
 
35
- def action(tspec)
36
- Aspera.assert_type(tspec, Hash){'transfer spec'}
37
- Aspera.assert_values(tspec['direction'], [DIRECTION_SEND, DIRECTION_RECEIVE]){'direction'}
38
- case tspec['direction']
34
+ # translate send/receive to upload/download
35
+ def direction_to_transfer_type(direction)
36
+ case direction
39
37
  when DIRECTION_SEND then :upload
40
38
  when DIRECTION_RECEIVE then :download
41
- else Aspera.error_unexpected_value(tspec['direction'])
39
+ else Aspera.error_unexpected_value(direction)
42
40
  end
43
41
  end
44
42
  end