aspera-cli 4.18.0 → 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 (60) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +23 -0
  4. data/CONTRIBUTING.md +5 -12
  5. data/README.md +152 -84
  6. data/examples/build_exec +85 -0
  7. data/examples/build_package.sh +28 -0
  8. data/lib/aspera/agent/alpha.rb +4 -4
  9. data/lib/aspera/agent/base.rb +2 -0
  10. data/lib/aspera/agent/connect.rb +3 -4
  11. data/lib/aspera/agent/direct.rb +108 -104
  12. data/lib/aspera/agent/httpgw.rb +1 -1
  13. data/lib/aspera/api/aoc.rb +2 -2
  14. data/lib/aspera/api/httpgw.rb +95 -57
  15. data/lib/aspera/api/node.rb +110 -77
  16. data/lib/aspera/ascp/installation.rb +47 -32
  17. data/lib/aspera/ascp/management.rb +4 -1
  18. data/lib/aspera/ascp/products.rb +2 -8
  19. data/lib/aspera/cli/extended_value.rb +27 -14
  20. data/lib/aspera/cli/formatter.rb +35 -28
  21. data/lib/aspera/cli/main.rb +11 -11
  22. data/lib/aspera/cli/manager.rb +109 -94
  23. data/lib/aspera/cli/plugin.rb +4 -7
  24. data/lib/aspera/cli/plugin_factory.rb +10 -1
  25. data/lib/aspera/cli/plugins/aoc.rb +15 -14
  26. data/lib/aspera/cli/plugins/config.rb +35 -29
  27. data/lib/aspera/cli/plugins/faspex.rb +5 -4
  28. data/lib/aspera/cli/plugins/faspex5.rb +16 -13
  29. data/lib/aspera/cli/plugins/node.rb +50 -41
  30. data/lib/aspera/cli/plugins/orchestrator.rb +3 -2
  31. data/lib/aspera/cli/plugins/preview.rb +1 -1
  32. data/lib/aspera/cli/plugins/server.rb +2 -2
  33. data/lib/aspera/cli/plugins/shares.rb +11 -7
  34. data/lib/aspera/cli/special_values.rb +13 -0
  35. data/lib/aspera/cli/sync_actions.rb +73 -32
  36. data/lib/aspera/cli/transfer_agent.rb +3 -2
  37. data/lib/aspera/cli/transfer_progress.rb +1 -1
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/environment.rb +100 -7
  40. data/lib/aspera/faspex_gw.rb +1 -1
  41. data/lib/aspera/keychain/encrypted_hash.rb +2 -0
  42. data/lib/aspera/log.rb +1 -0
  43. data/lib/aspera/node_simulator.rb +1 -1
  44. data/lib/aspera/oauth/jwt.rb +1 -1
  45. data/lib/aspera/oauth/url_json.rb +2 -0
  46. data/lib/aspera/oauth/web.rb +7 -6
  47. data/lib/aspera/rest.rb +46 -15
  48. data/lib/aspera/secret_hider.rb +3 -2
  49. data/lib/aspera/ssh.rb +1 -1
  50. data/lib/aspera/transfer/faux_file.rb +7 -5
  51. data/lib/aspera/transfer/parameters.rb +27 -19
  52. data/lib/aspera/transfer/spec.rb +8 -10
  53. data/lib/aspera/transfer/sync.rb +52 -47
  54. data/lib/aspera/web_auth.rb +0 -1
  55. data/lib/aspera/web_server_simple.rb +24 -13
  56. data.tar.gz.sig +0 -0
  57. metadata +5 -4
  58. metadata.gz.sig +0 -0
  59. data/examples/rubyc +0 -24
  60. data/lib/aspera/open_application.rb +0 -69
@@ -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
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'aspera/command_line_builder'
6
6
  require 'aspera/ascp/installation'
7
+ require 'aspera/agent/direct'
7
8
  require 'aspera/log'
8
9
  require 'aspera/assert'
9
10
  require 'json'
@@ -13,12 +14,12 @@ require 'English'
13
14
 
14
15
  module Aspera
15
16
  module Transfer
16
- # builds command line arg for async
17
+ # builds command line arg for async and execute it
17
18
  module Sync
18
19
  # sync direction, default is push
19
20
  DIRECTIONS = %i[push pull bidi].freeze
20
- # custom JSON for async instance command line options
21
- PARAMS_VX_INSTANCE =
21
+ # JSON for async instance command line options
22
+ CMDLINE_PARAMS_INSTANCE =
22
23
  {
23
24
  'alt_logdir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
24
25
  'watchd' => { cli: { type: :opt_with_arg}, accepted_types: :string},
@@ -28,7 +29,7 @@ module Aspera
28
29
  }.freeze
29
30
 
30
31
  # map sync session parameters to transfer spec: sync -> ts, true if same
31
- PARAMS_VX_SESSION =
32
+ CMDLINE_PARAMS_SESSION =
32
33
  {
33
34
  'name' => { cli: { type: :opt_with_arg}, accepted_types: :string},
34
35
  'local_dir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
@@ -65,13 +66,13 @@ module Aspera
65
66
  'license' => { cli: { type: :envvar, variable: 'ASPERA_SCP_LICENSE'}}
66
67
  }.freeze
67
68
 
68
- CommandLineBuilder.normalize_description(PARAMS_VX_INSTANCE)
69
- CommandLineBuilder.normalize_description(PARAMS_VX_SESSION)
69
+ CommandLineBuilder.normalize_description(CMDLINE_PARAMS_INSTANCE)
70
+ CommandLineBuilder.normalize_description(CMDLINE_PARAMS_SESSION)
70
71
 
71
- PARAMS_VX_KEYS = %w[instance sessions].freeze
72
+ CMDLINE_PARAMS_KEYS = %w[instance sessions].freeze
72
73
 
73
74
  # Translation of transfer spec parameters to async v2 API (asyncs)
74
- TS_TO_PARAMS_V2 = {
75
+ TSPEC_TO_ASYNC_CONF = {
75
76
  'remote_host' => 'remote.host',
76
77
  'remote_user' => 'remote.user',
77
78
  'remote_password' => 'remote.pass',
@@ -83,10 +84,9 @@ module Aspera
83
84
  'tags' => 'tags'
84
85
  }.freeze
85
86
 
86
- ASYNC_EXECUTABLE = 'async'
87
87
  ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
88
88
 
89
- private_constant :PARAMS_VX_INSTANCE, :PARAMS_VX_SESSION, :PARAMS_VX_KEYS, :TS_TO_PARAMS_V2, :ASYNC_EXECUTABLE, :ASYNC_ADMIN_EXECUTABLE
89
+ private_constant :CMDLINE_PARAMS_INSTANCE, :CMDLINE_PARAMS_SESSION, :CMDLINE_PARAMS_KEYS, :TSPEC_TO_ASYNC_CONF, :ASYNC_ADMIN_EXECUTABLE
90
90
 
91
91
  class << self
92
92
  # Set remote_dir in sync parameters based on transfer spec
@@ -126,7 +126,7 @@ module Aspera
126
126
  remote.delete('ws_port')
127
127
  # add SSH bypass keys when authentication is token and no auth is provided
128
128
  if remote.key?('token') && !remote.key?('pass')
129
- certificates_to_use.concat(Ascp::Installation.instance.aspera_token_ssh_key_paths)
129
+ certificates_to_use.concat(Ascp::Installation.instance.aspera_token_ssh_key_paths(:rsa))
130
130
  end
131
131
  end
132
132
  return certificates_to_use
@@ -134,23 +134,27 @@ module Aspera
134
134
 
135
135
  # @param sync_params [Hash] sync parameters, old or new format
136
136
  # @param block [nil, Proc] block to generate transfer spec, takes: direction (one of DIRECTIONS), local_dir, remote_dir
137
- def start(sync_params, &block)
137
+ def start(
138
+ sync_params,
139
+ &block
140
+ )
141
+ Log.log.debug{Log.dump(:sync_params_initial, sync_params)}
138
142
  Aspera.assert_type(sync_params, Hash)
139
143
  env_args = {
140
144
  args: [],
141
145
  env: {}
142
146
  }
143
147
  if sync_params.key?('local')
148
+ # async native JSON format (conf option)
149
+ Aspera.assert_type(sync_params['local'], Hash){'local'}
144
150
  remote = sync_params['remote']
145
- # async native JSON format (v2)
146
- Aspera.assert_type(remote, Hash)
151
+ Aspera.assert_type(remote, Hash){'remote'}
152
+ Aspera.assert_type(remote['path'], String){'remote path'}
147
153
  # get transfer spec if possible, and feed back to new structure
148
154
  if block
149
155
  transfer_spec = yield((sync_params['direction'] || 'push').to_sym, sync_params['local']['path'], remote['path'])
150
- # async native JSON format
151
- Aspera.assert_type(sync_params['local'], Hash)
152
156
  # translate transfer spec to async parameters
153
- TS_TO_PARAMS_V2.each do |ts_param, sy_path|
157
+ TSPEC_TO_ASYNC_CONF.each do |ts_param, sy_path|
154
158
  next unless transfer_spec.key?(ts_param)
155
159
  sy_dig = sy_path.split('.')
156
160
  param = sy_dig.pop
@@ -160,36 +164,41 @@ module Aspera
160
164
  end
161
165
  update_remote_dir(remote, 'path', transfer_spec)
162
166
  end
163
- remote['connect_mode'] ||= remote.key?('ws_port') ? 'ws' : 'ssh'
167
+ remote['connect_mode'] ||= transfer_spec['wss_enabled'] ? 'ws' : 'ssh'
164
168
  add_certificates = remote_certificates(remote)
165
169
  if !add_certificates.empty?
166
170
  remote['private_key_paths'] ||= []
167
171
  remote['private_key_paths'].concat(add_certificates)
168
172
  end
169
- Aspera.assert_type(sync_params, Hash)
173
+ # '--exclusive-mgmt-port=12345', '--arg-err-path=-',
170
174
  env_args[:args] = ["--conf64=#{Base64.strict_encode64(JSON.generate(sync_params))}"]
175
+ Log.log.debug{Log.dump(:sync_params_enriched, sync_params)}
176
+ agent = Agent::Direct.new
177
+ agent.start_and_monitor_process(session: {}, name: :async, **env_args)
171
178
  elsif sync_params.key?('sessions')
172
- # ascli JSON format (v1)
179
+ # ascli JSON format (cmdline)
180
+ raise StandardError, "Only 'sessions', and optionally 'instance' keys are allowed" unless
181
+ sync_params.keys.push('instance').uniq.sort.eql?(CMDLINE_PARAMS_KEYS)
182
+ Aspera.assert_type(sync_params['sessions'], Array)
183
+ Aspera.assert_type(sync_params['sessions'].first, Hash)
173
184
  if block
174
185
  sync_params['sessions'].each do |session|
186
+ Aspera.assert_type(session['local_dir'], String){'local_dir'}
187
+ Aspera.assert_type(session['remote_dir'], String){'remote_dir'}
175
188
  transfer_spec = yield((session['direction'] || 'push').to_sym, session['local_dir'], session['remote_dir'])
176
- PARAMS_VX_SESSION.each do |async_param, behavior|
189
+ CMDLINE_PARAMS_SESSION.each do |async_param, behavior|
177
190
  if behavior.key?(:ts)
178
191
  tspec_param = behavior[:ts].is_a?(TrueClass) ? async_param : behavior[:ts].to_s
179
192
  session[async_param] ||= transfer_spec[tspec_param] if transfer_spec.key?(tspec_param)
180
193
  end
181
194
  end
182
- session['private_key_paths'] = Ascp::Installation.instance.aspera_token_ssh_key_paths if transfer_spec.key?('token')
195
+ session['private_key_paths'] = Ascp::Installation.instance.aspera_token_ssh_key_paths(:rsa) if transfer_spec.key?('token')
183
196
  update_remote_dir(session, 'remote_dir', transfer_spec)
184
197
  end
185
198
  end
186
- raise StandardError, "Only 'sessions', and optionally 'instance' keys are allowed" unless
187
- sync_params.keys.push('instance').uniq.sort.eql?(PARAMS_VX_KEYS)
188
- Aspera.assert_type(sync_params['sessions'], Array)
189
- Aspera.assert_type(sync_params['sessions'].first, Hash)
190
199
  if sync_params.key?('instance')
191
200
  Aspera.assert_type(sync_params['instance'], Hash)
192
- instance_builder = CommandLineBuilder.new(sync_params['instance'], PARAMS_VX_INSTANCE)
201
+ instance_builder = CommandLineBuilder.new(sync_params['instance'], CMDLINE_PARAMS_INSTANCE)
193
202
  instance_builder.process_params
194
203
  instance_builder.add_env_args(env_args)
195
204
  end
@@ -197,23 +206,19 @@ module Aspera
197
206
  sync_params['sessions'].each do |session_params|
198
207
  Aspera.assert_type(session_params, Hash)
199
208
  Aspera.assert(session_params.key?('name')){'session must contain at least name'}
200
- session_builder = CommandLineBuilder.new(session_params, PARAMS_VX_SESSION)
209
+ session_builder = CommandLineBuilder.new(session_params, CMDLINE_PARAMS_SESSION)
201
210
  session_builder.process_params
202
211
  session_builder.add_env_args(env_args)
203
212
  end
213
+ async_exec = Ascp::Installation.instance.path(:async)
214
+ Process.wait(Environment.secure_spawn(env: env_args[:env], exec: async_exec, args: env_args[:args]))
215
+ if $CHILD_STATUS.exitstatus != 0
216
+ raise "Sync failed with exit: #{$CHILD_STATUS.exitstatus}"
217
+ end
204
218
  else
205
219
  raise 'At least one of `local` or `sessions` must be present in async parameters'
206
220
  end
207
- Log.log.debug{Log.dump(:sync_params, sync_params)}
208
- Log.log.debug{"execute: #{env_args[:env].map{|k, v| "#{k}=\"#{v}\""}.join(' ')} \"#{ASYNC_EXECUTABLE}\" \"#{env_args[:args].join('" "')}\""}
209
- res = system(env_args[:env], [ASYNC_EXECUTABLE, ASYNC_EXECUTABLE], *env_args[:args])
210
- Log.log.debug{"result=#{res}"}
211
- case res
212
- when true then return nil
213
- when false then raise "failed: #{$CHILD_STATUS}"
214
- when nil then raise "not started: #{$CHILD_STATUS}"
215
- else Aspera.error_unexpected_value(res)
216
- end
221
+ return nil
217
222
  end
218
223
 
219
224
  def parse_status(stdout)
@@ -234,15 +239,15 @@ module Aspera
234
239
  end
235
240
 
236
241
  def admin_status(sync_params, session_name)
237
- command_line = [ASYNC_ADMIN_EXECUTABLE, '--quiet']
242
+ arguments = ['--quiet']
238
243
  if sync_params.key?('local')
239
244
  Aspera.assert(!sync_params['name'].nil?){'Missing session name'}
240
245
  Aspera.assert(session_name.nil? || session_name.eql?(sync_params['name'])){'Session not found'}
241
- command_line.push("--name=#{sync_params['name']}")
246
+ arguments.push("--name=#{sync_params['name']}")
242
247
  if sync_params.key?('local_db_dir')
243
- command_line.push("--local-db-dir=#{sync_params['local_db_dir']}")
248
+ arguments.push("--local-db-dir=#{sync_params['local_db_dir']}")
244
249
  elsif sync_params.dig('local', 'path')
245
- command_line.push("--local-dir=#{sync_params.dig('local', 'path')}")
250
+ arguments.push("--local-dir=#{sync_params.dig('local', 'path')}")
246
251
  else
247
252
  raise 'Missing either local_db_dir or local.path'
248
253
  end
@@ -250,19 +255,19 @@ module Aspera
250
255
  session = session_name.nil? ? sync_params['sessions'].first : sync_params['sessions'].find{|s|s['name'].eql?(session_name)}
251
256
  raise "Session #{session_name} not found in #{sync_params['sessions'].map{|s|s['name']}.join(',')}" if session.nil?
252
257
  raise 'Missing session name' if session['name'].nil?
253
- command_line.push("--name=#{session['name']}")
258
+ arguments.push("--name=#{session['name']}")
254
259
  if session.key?('local_db_dir')
255
- command_line.push("--local-db-dir=#{session['local_db_dir']}")
260
+ arguments.push("--local-db-dir=#{session['local_db_dir']}")
256
261
  elsif session.key?('local_dir')
257
- command_line.push("--local-dir=#{session['local_dir']}")
262
+ arguments.push("--local-dir=#{session['local_dir']}")
258
263
  else
259
264
  raise 'Missing either local_db_dir or local_dir'
260
265
  end
261
266
  else
262
267
  raise 'At least one of `local` or `sessions` must be present in async parameters'
263
268
  end
264
- Log.log.debug{"execute: #{command_line.join(' ')}"}
265
- stdout, stderr, status = Open3.capture3(*command_line)
269
+ Environment.secure_spawn(env: {}, exec: ASYNC_ADMIN_EXECUTABLE, args: arguments, log_only: true)
270
+ stdout, stderr, status = Open3.capture3(*[ASYNC_ADMIN_EXECUTABLE].concat(arguments))
266
271
  Log.log.debug{"status=#{status}, stderr=#{stderr}"}
267
272
  Log.log.trace1{"stdout=#{stdout}"}
268
273
  raise "Sync failed: #{status.exitstatus} : #{stderr}" unless status.success?
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/web_server_simple'
4
- require 'stringio'
5
4
 
6
5
  module Aspera
7
6
  # servlet called on callback: it records the callback request
@@ -4,12 +4,13 @@ require 'webrick'
4
4
  require 'webrick/https'
5
5
  require 'aspera/log'
6
6
  require 'aspera/assert'
7
+ require 'aspera/hash_ext'
7
8
  require 'openssl'
8
9
 
9
10
  module Aspera
10
11
  # Simple WEBrick server with HTTPS support
11
12
  class WebServerSimple < WEBrick::HTTPServer
12
- CERT_PARAMETERS = %i[key cert chain].freeze
13
+ CERT_PARAMETERS = %i[key cert chain pkcs12].freeze
13
14
  GENERIC_ISSUER = '/C=FR/O=Test/OU=Test/CN=Test'
14
15
  ONE_YEAR_SECONDS = 365 * 24 * 60 * 60
15
16
 
@@ -58,19 +59,29 @@ module Aspera
58
59
  Aspera.assert_type(certificate, Hash)
59
60
  certificate = certificate.symbolize_keys
60
61
  raise "unexpected key in certificate config: only: #{CERT_PARAMETERS.join(', ')}" if certificate.keys.any?{|key|!CERT_PARAMETERS.include?(key)}
61
- webrick_options[:SSLPrivateKey] = if certificate.key?(:key)
62
- OpenSSL::PKey::RSA.new(File.read(certificate[:key]))
62
+ if certificate.key?(:pkcs12)
63
+ Log.log.debug('Using PKCS12 certificate')
64
+ raise 'pkcs12 requires a password' unless certificate.key?(:key)
65
+ pkcs12 = OpenSSL::PKCS12.new(File.read(certificate[:pkcs12]), certificate[:key])
66
+ webrick_options[:SSLCertificate] = pkcs12.certificate
67
+ webrick_options[:SSLPrivateKey] = pkcs12.key
68
+ webrick_options[:SSLExtraChainCert] = pkcs12.ca_certs
63
69
  else
64
- OpenSSL::PKey::RSA.new(4096)
65
- end
66
- if certificate.key?(:cert)
67
- webrick_options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(certificate[:cert]))
68
- else
69
- webrick_options[:SSLCertificate] = OpenSSL::X509::Certificate.new
70
- self.class.fill_self_signed_cert(webrick_options[:SSLCertificate], webrick_options[:SSLPrivateKey])
71
- end
72
- if certificate.key?(:chain)
73
- webrick_options[:SSLExtraChainCert] = [OpenSSL::X509::Certificate.new(File.read(certificate[:chain]))]
70
+ Log.log.debug('Using PEM certificate')
71
+ webrick_options[:SSLPrivateKey] = if certificate.key?(:key)
72
+ OpenSSL::PKey::RSA.new(File.read(certificate[:key]))
73
+ else
74
+ OpenSSL::PKey::RSA.new(4096)
75
+ end
76
+ if certificate.key?(:cert)
77
+ webrick_options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(certificate[:cert]))
78
+ else
79
+ webrick_options[:SSLCertificate] = OpenSSL::X509::Certificate.new
80
+ self.class.fill_self_signed_cert(webrick_options[:SSLCertificate], webrick_options[:SSLPrivateKey])
81
+ end
82
+ if certificate.key?(:chain)
83
+ webrick_options[:SSLExtraChainCert] = [OpenSSL::X509::Certificate.new(File.read(certificate[:chain]))]
84
+ end
74
85
  end
75
86
  end
76
87
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.18.0
4
+ version: 4.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -37,7 +37,7 @@ cert_chain:
37
37
  eTf9kxhVM40wGQOECVNA8UsEEZHD48eF+csUYZtAJOF5oxTI8UyV9T/o6CgO0c9/
38
38
  Gzz+Qm5ULOUcPiJLjSpaiTrkiIVYiDGnqNSr6R1Hb1c=
39
39
  -----END CERTIFICATE-----
40
- date: 2024-07-10 00:00:00.000000000 Z
40
+ date: 2024-10-02 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: blankslate
@@ -390,9 +390,10 @@ files:
390
390
  - README.md
391
391
  - bin/ascli
392
392
  - bin/asession
393
+ - examples/build_exec
394
+ - examples/build_package.sh
393
395
  - examples/dascli
394
396
  - examples/proxy.pac
395
- - examples/rubyc
396
397
  - lib/aspera/agent/alpha.rb
397
398
  - lib/aspera/agent/base.rb
398
399
  - lib/aspera/agent/connect.rb
@@ -435,6 +436,7 @@ files:
435
436
  - lib/aspera/cli/plugins/preview.rb
436
437
  - lib/aspera/cli/plugins/server.rb
437
438
  - lib/aspera/cli/plugins/shares.rb
439
+ - lib/aspera/cli/special_values.rb
438
440
  - lib/aspera/cli/sync_actions.rb
439
441
  - lib/aspera/cli/transfer_agent.rb
440
442
  - lib/aspera/cli/transfer_progress.rb
@@ -468,7 +470,6 @@ files:
468
470
  - lib/aspera/oauth/jwt.rb
469
471
  - lib/aspera/oauth/url_json.rb
470
472
  - lib/aspera/oauth/web.rb
471
- - lib/aspera/open_application.rb
472
473
  - lib/aspera/persistency_action_once.rb
473
474
  - lib/aspera/persistency_folder.rb
474
475
  - lib/aspera/preview/file_types.rb
metadata.gz.sig CHANGED
Binary file
data/examples/rubyc DELETED
@@ -1,24 +0,0 @@
1
- #!/bin/bash
2
- # https://github.com/you54f/ruby-packer
3
- # https://github.com/YOU54F/ruby-packer/releases
4
- set -e
5
- FOLDER="$(dirname $0)/../tmp"
6
- RUBYC="$FOLDER/rubyc"
7
- if test ! -e "$RUBYC"; then
8
- mkdir -p "$FOLDER"
9
- case $(uname -sm|tr ' ' -) in
10
- Darwin-arm64)
11
- curl -L https://github.com/YOU54F/ruby-packer/releases/download/rel-20230812/rubyc-Darwin-arm64.tar.gz | tar -xz -C "$FOLDER"
12
- mv "$FOLDER/rubyc-Darwin-arm64" "$RUBYC"
13
- ;;
14
- Linux-x86_64)
15
- curl -L https://github.com/YOU54F/ruby-packer/releases/download/rel-20230812/rubyc-Linux-x86_64.tar.gz | tar -xz -C "$FOLDER"
16
- mv "$FOLDER/rubyc-Linux-x86_64" "$RUBYC"
17
- ;;
18
- *)
19
- echo "This architecture is not supported." >&2
20
- exit 1
21
- ;;
22
- esac
23
- fi
24
- exec "$RUBYC" "$@"
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/log'
4
- require 'aspera/environment'
5
- require 'rbconfig'
6
- require 'singleton'
7
-
8
- module Aspera
9
- # Allows a user to open a Url
10
- # if method is "text", then URL is displayed on terminal
11
- # if method is "graphical", then the URL will be opened with the default browser.
12
- class OpenApplication
13
- include Singleton
14
- USER_INTERFACES = %i[text graphical].freeze
15
- class << self
16
- def default_gui_mode
17
- # assume not remotely connected on macos and windows
18
- return :graphical if [Environment::OS_WINDOWS, Environment::OS_X].include?(Environment.os)
19
- # unix family
20
- return :graphical if ENV.key?('DISPLAY') && !ENV['DISPLAY'].empty?
21
- return :text
22
- end
23
-
24
- # command must be non blocking
25
- def uri_graphical(uri)
26
- case Environment.os
27
- when Environment::OS_X then return system('open', uri.to_s)
28
- when Environment::OS_WINDOWS then return system('start', 'explorer', %Q{"#{uri}"})
29
- when Environment::OS_LINUX then return system('xdg-open', uri.to_s)
30
- else
31
- raise "no graphical open method for #{Environment.os}"
32
- end
33
- end
34
-
35
- def editor(file_path)
36
- if ENV.key?('EDITOR')
37
- system(ENV['EDITOR'], file_path.to_s)
38
- elsif Environment.os.eql?(Environment::OS_WINDOWS)
39
- system('notepad.exe', %Q{"#{file_path}"})
40
- else
41
- uri_graphical(file_path.to_s)
42
- end
43
- end
44
- end
45
-
46
- attr_accessor :url_method
47
-
48
- def initialize
49
- @url_method = self.class.default_gui_mode
50
- end
51
-
52
- # this is non blocking
53
- def uri(the_url)
54
- case @url_method
55
- when :graphical
56
- self.class.uri_graphical(the_url)
57
- when :text
58
- case the_url.to_s
59
- when /^http/
60
- puts "USER ACTION: please enter this url in a browser:\n#{the_url.to_s.red}\n"
61
- else
62
- puts "USER ACTION: open this:\n#{the_url.to_s.red}\n"
63
- end
64
- else
65
- raise StandardError, "unsupported url open method: #{@url_method}"
66
- end
67
- end
68
- end
69
- end