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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +13 -0
- data/CONTRIBUTING.md +5 -12
- data/README.md +60 -29
- data/examples/build_exec +85 -0
- data/lib/aspera/agent/base.rb +2 -0
- data/lib/aspera/agent/direct.rb +108 -104
- data/lib/aspera/api/aoc.rb +2 -2
- data/lib/aspera/api/httpgw.rb +91 -56
- data/lib/aspera/ascp/installation.rb +47 -32
- data/lib/aspera/ascp/management.rb +4 -1
- data/lib/aspera/ascp/products.rb +1 -7
- data/lib/aspera/cli/formatter.rb +24 -18
- data/lib/aspera/cli/manager.rb +10 -10
- data/lib/aspera/cli/plugin.rb +2 -2
- data/lib/aspera/cli/plugin_factory.rb +10 -1
- data/lib/aspera/cli/plugins/config.rb +15 -10
- data/lib/aspera/cli/plugins/node.rb +4 -3
- data/lib/aspera/cli/plugins/server.rb +1 -1
- data/lib/aspera/cli/plugins/shares.rb +11 -7
- data/lib/aspera/cli/sync_actions.rb +72 -31
- data/lib/aspera/cli/transfer_agent.rb +1 -0
- data/lib/aspera/cli/transfer_progress.rb +1 -1
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/environment.rb +43 -10
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +2 -0
- data/lib/aspera/log.rb +1 -0
- data/lib/aspera/node_simulator.rb +1 -1
- data/lib/aspera/oauth/jwt.rb +1 -1
- data/lib/aspera/oauth/url_json.rb +2 -0
- data/lib/aspera/oauth/web.rb +5 -4
- data/lib/aspera/secret_hider.rb +3 -2
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +7 -5
- data/lib/aspera/transfer/parameters.rb +27 -19
- data/lib/aspera/transfer/spec.rb +8 -10
- data/lib/aspera/transfer/sync.rb +52 -47
- data/lib/aspera/web_auth.rb +0 -1
- data/lib/aspera/web_server_simple.rb +24 -13
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
- 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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
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.
|
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?
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/environment.rb
CHANGED
@@ -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
|
-
#
|
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
|
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.
|
data/lib/aspera/faspex_gw.rb
CHANGED
@@ -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,
|
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
@@ -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['
|
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,
|
data/lib/aspera/oauth/jwt.rb
CHANGED
@@ -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.
|
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
|
data/lib/aspera/oauth/web.rb
CHANGED
@@ -8,8 +8,8 @@ module Aspera
|
|
8
8
|
module OAuth
|
9
9
|
# Authentication using Web browser
|
10
10
|
class Web < Base
|
11
|
-
# @param
|
12
|
-
# @param
|
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
|
-
|
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))
|
data/lib/aspera/secret_hider.rb
CHANGED
@@ -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('|')})=)
|
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>(?:#{
|
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
|
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
|
-
|
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
|
-
|
15
|
-
raise 'Format: #{PREFIX}<file path>?<size>' unless
|
16
|
-
raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m =
|
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(
|
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
|
-
|
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
|
-
|
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
|
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)
|
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
|
-
|
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
|
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[:
|
232
|
+
env_args[:name] = :ascp4
|
227
233
|
else
|
228
|
-
env_args[:
|
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
|
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
|
data/lib/aspera/transfer/spec.rb
CHANGED
@@ -22,23 +22,21 @@ module Aspera
|
|
22
22
|
# reserved tag for Aspera
|
23
23
|
TAG_RESERVED = 'aspera'
|
24
24
|
class << self
|
25
|
-
|
26
|
-
|
27
|
-
|
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(
|
30
|
+
else Aspera.error_unexpected_value(transfer_type.to_sym)
|
31
31
|
end
|
32
|
-
return tspec
|
33
32
|
end
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
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(
|
39
|
+
else Aspera.error_unexpected_value(direction)
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|