aspera-cli 4.18.1 → 4.19.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 (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