aspera-cli 4.25.1 → 4.25.2
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +46 -1
- data/CONTRIBUTING.md +6 -11
- data/README.md +31 -9742
- data/bin/asession +111 -88
- data/lib/aspera/agent/connect.rb +1 -1
- data/lib/aspera/agent/desktop.rb +1 -1
- data/lib/aspera/agent/direct.rb +19 -18
- data/lib/aspera/agent/node.rb +1 -1
- data/lib/aspera/api/aoc.rb +25 -8
- data/lib/aspera/api/node.rb +16 -14
- data/lib/aspera/ascp/installation.rb +32 -51
- data/lib/aspera/assert.rb +1 -1
- data/lib/aspera/cli/extended_value.rb +1 -0
- data/lib/aspera/cli/formatter.rb +0 -4
- data/lib/aspera/cli/hints.rb +11 -4
- data/lib/aspera/cli/main.rb +3 -6
- data/lib/aspera/cli/manager.rb +8 -4
- data/lib/aspera/cli/plugins/aoc.rb +11 -14
- data/lib/aspera/cli/plugins/base.rb +1 -1
- data/lib/aspera/cli/plugins/config.rb +50 -48
- data/lib/aspera/cli/plugins/factory.rb +2 -2
- data/lib/aspera/cli/plugins/faspex5.rb +4 -3
- data/lib/aspera/cli/plugins/preview.rb +6 -11
- data/lib/aspera/cli/transfer_agent.rb +2 -2
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/environment.rb +30 -16
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/faspex_postproc.rb +16 -10
- data/lib/aspera/hash_ext.rb +8 -0
- data/lib/aspera/log.rb +3 -4
- data/lib/aspera/markdown.rb +17 -0
- data/lib/aspera/oauth/base.rb +1 -1
- data/lib/aspera/oauth/web.rb +1 -1
- data/lib/aspera/preview/generator.rb +9 -9
- data/lib/aspera/rest_call_error.rb +16 -8
- data/lib/aspera/rest_error_analyzer.rb +1 -1
- data/lib/aspera/transfer/resumer.rb +2 -2
- data/lib/aspera/yaml.rb +49 -0
- data.tar.gz.sig +0 -0
- metadata +16 -2
- metadata.gz.sig +0 -0
- data/release_notes.md +0 -8
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -30,10 +30,6 @@ module Aspera
|
|
|
30
30
|
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
31
31
|
|
|
32
32
|
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES
|
|
33
|
-
# prefix to display error messages in user messages (terminal)
|
|
34
|
-
ERROR_FLASH = 'ERROR:'.bg_red.gray.blink.freeze
|
|
35
|
-
WARNING_FLASH = 'WARNING:'.bg_brown.black.blink.freeze
|
|
36
|
-
HINT_FLASH = 'HINT:'.bg_green.gray.blink.freeze
|
|
37
33
|
|
|
38
34
|
class << self
|
|
39
35
|
# nicer display for boolean
|
data/lib/aspera/cli/hints.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Aspera
|
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
exception: RestCallError,
|
|
29
|
+
exception: Aspera::RestCallError,
|
|
30
30
|
match: /Signature has expired/,
|
|
31
31
|
remediation: [
|
|
32
32
|
'There is too much time difference between your computer and the server',
|
|
@@ -108,6 +108,13 @@ module Aspera
|
|
|
108
108
|
'Transfer user shall have those parameters in aspera.conf set to: token',
|
|
109
109
|
'authorization_transfer_in_value authorization_transfer_out_value'
|
|
110
110
|
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
exception: Aspera::RestCallError,
|
|
114
|
+
match: /invalid_grant/,
|
|
115
|
+
remediation: [
|
|
116
|
+
'Check your public key in your AoC user profile.'
|
|
117
|
+
]
|
|
111
118
|
}
|
|
112
119
|
]
|
|
113
120
|
private_constant :ERROR_HINTS
|
|
@@ -129,9 +136,9 @@ module Aspera
|
|
|
129
136
|
next unless message.match?(m)
|
|
130
137
|
else Aspera.error_unexpected_value(m)
|
|
131
138
|
end
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
hint[:remediation].each do |r|
|
|
140
|
+
Log.log.info{"#{'HINT:'.bg_green.gray.blink.freeze} #{r}"}
|
|
141
|
+
end
|
|
135
142
|
break
|
|
136
143
|
end
|
|
137
144
|
end
|
data/lib/aspera/cli/main.rb
CHANGED
|
@@ -228,7 +228,7 @@ module Aspera
|
|
|
228
228
|
# 1- processing of error condition
|
|
229
229
|
unless exception_info.nil?
|
|
230
230
|
Log.log.warn(exception_info[:e].message) if Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
|
|
231
|
-
|
|
231
|
+
Log.log.error{"#{exception_info[:t]}: #{exception_info[:e].message}"}
|
|
232
232
|
Log.log.debug{(['Backtrace:'] + exception_info[:e].backtrace).join("\n")} if exception_info[:debug]
|
|
233
233
|
@context.formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
|
|
234
234
|
# Is that a known error condition with proposal for remediation ?
|
|
@@ -237,7 +237,7 @@ module Aspera
|
|
|
237
237
|
# 2- processing of command not processed (due to exception or bad command line)
|
|
238
238
|
if execute_command || @option_show_config
|
|
239
239
|
@context.options.final_errors.each do |msg|
|
|
240
|
-
|
|
240
|
+
Log.log.error{"Argument: #{msg}"}
|
|
241
241
|
# Add code as exception if there is not already an error
|
|
242
242
|
exception_info = {e: Exception.new(msg), t: 'UnusedArg'} if exception_info.nil?
|
|
243
243
|
end
|
|
@@ -291,10 +291,7 @@ module Aspera
|
|
|
291
291
|
@context.formatter.declare_options(@context.options)
|
|
292
292
|
# Compare $0 with expected name
|
|
293
293
|
current_prog_name = File.basename($PROGRAM_NAME)
|
|
294
|
-
|
|
295
|
-
:error,
|
|
296
|
-
"#{Formatter::WARNING_FLASH} Please use '#{Info::CMD_NAME}' instead of '#{current_prog_name}'"
|
|
297
|
-
) unless current_prog_name.eql?(Info::CMD_NAME)
|
|
294
|
+
Aspera.assert(current_prog_name.eql?(Info::CMD_NAME), type: :warn){"Please use '#{Info::CMD_NAME}' instead of '#{current_prog_name}'"}
|
|
298
295
|
# Declare and parse global options
|
|
299
296
|
declare_global_options
|
|
300
297
|
# Do not display config commands if help is asked
|
data/lib/aspera/cli/manager.rb
CHANGED
|
@@ -35,7 +35,8 @@ module Aspera
|
|
|
35
35
|
attr_accessor :values
|
|
36
36
|
|
|
37
37
|
# @param option [Symbol] Name of option
|
|
38
|
-
# @param
|
|
38
|
+
# @param description [String] Description for help
|
|
39
|
+
# @param allowed [nil,Class,Array<Class>,Array<Symbol>] Allowed values
|
|
39
40
|
# @param handler [Hash] Accessor: keys: :o(object) and :m(method)
|
|
40
41
|
# @param deprecation [String] Deprecation message
|
|
41
42
|
# `allowed`:
|
|
@@ -61,7 +62,7 @@ module Aspera
|
|
|
61
62
|
else
|
|
62
63
|
:setter
|
|
63
64
|
end
|
|
64
|
-
Aspera.assert(@object.respond_to?(@read_method)){"#{@object} does not respond to #{
|
|
65
|
+
Aspera.assert(@object.respond_to?(@read_method)){"#{@object} does not respond to #{@read_method}"} unless @access.eql?(:local)
|
|
65
66
|
@types = nil
|
|
66
67
|
@values = nil
|
|
67
68
|
if !allowed.nil?
|
|
@@ -600,9 +601,12 @@ module Aspera
|
|
|
600
601
|
end
|
|
601
602
|
|
|
602
603
|
# TODO: use formatter
|
|
603
|
-
#
|
|
604
|
+
# Highlight current value in list
|
|
605
|
+
# @param list [Array<Symbol>] List of possible values
|
|
606
|
+
# @param current [Symbol] Current value
|
|
607
|
+
# @return [String] comma separated sorted list of values, with the current value highlighted
|
|
604
608
|
def highlight_current_in_list(list, current)
|
|
605
|
-
list.map do |i|
|
|
609
|
+
list.sort.map do |i|
|
|
606
610
|
if i.eql?(current)
|
|
607
611
|
$stdout.isatty ? i.to_s.red.bold : "[#{i}]"
|
|
608
612
|
else
|
|
@@ -470,11 +470,11 @@ module Aspera
|
|
|
470
470
|
return Main.result_success
|
|
471
471
|
when :do
|
|
472
472
|
command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
|
|
473
|
-
return execute_nodegen4_command(command_repo, res_id, scope: Api::Node::
|
|
473
|
+
return execute_nodegen4_command(command_repo, res_id, scope: Api::Node::Scope::ADMIN)
|
|
474
474
|
when :bearer_token
|
|
475
475
|
node_api = aoc_api.node_api_from(
|
|
476
476
|
node_id: res_id,
|
|
477
|
-
scope: options.get_next_argument('scope')
|
|
477
|
+
scope: options.get_next_argument('scope', default: Api::Node::Scope::ADMIN)
|
|
478
478
|
)
|
|
479
479
|
return Main.result_text(node_api.oauth.authorization)
|
|
480
480
|
when :dropbox
|
|
@@ -504,7 +504,7 @@ module Aspera
|
|
|
504
504
|
node_id: shared_folder['node_id'],
|
|
505
505
|
workspace_id: res_id,
|
|
506
506
|
workspace_name: nil,
|
|
507
|
-
scope: Api::Node::
|
|
507
|
+
scope: Api::Node::Scope::USER
|
|
508
508
|
)
|
|
509
509
|
result = node_api.read(
|
|
510
510
|
'permissions',
|
|
@@ -742,9 +742,9 @@ module Aspera
|
|
|
742
742
|
end
|
|
743
743
|
|
|
744
744
|
# Create a shared link for the given entity
|
|
745
|
-
# @param purpose_public [
|
|
745
|
+
# @param purpose_public [String] send_package_to_dropbox or view_shared_file
|
|
746
746
|
# @param shared_data [Hash] information for shared data
|
|
747
|
-
# @param block
|
|
747
|
+
# @param &block [Proc] Optional: called on creation
|
|
748
748
|
def short_link_command(purpose_public:, **shared_data)
|
|
749
749
|
link_type = options.get_next_argument('link type', accept_list: %i[public private])
|
|
750
750
|
purpose_local = case link_type
|
|
@@ -780,6 +780,7 @@ module Aspera
|
|
|
780
780
|
}
|
|
781
781
|
end
|
|
782
782
|
custom_data = value_create_modify(command: command, default: {})
|
|
783
|
+
access_levels = custom_data.delete('access_levels')
|
|
783
784
|
if (pass = custom_data.delete('password'))
|
|
784
785
|
entity_data[:data][:url_token_data][:password] = pass
|
|
785
786
|
entity_data[:password_enabled] = true
|
|
@@ -787,7 +788,7 @@ module Aspera
|
|
|
787
788
|
entity_data.deep_merge!(custom_data)
|
|
788
789
|
result_create_short_link = aoc_api.create('short_links', entity_data)
|
|
789
790
|
# public: Creation: permission on node
|
|
790
|
-
yield(result_create_short_link['resource_id']) if block_given? && link_type.eql?(:public)
|
|
791
|
+
yield(result_create_short_link['resource_id'], access_levels) if block_given? && link_type.eql?(:public)
|
|
791
792
|
return Main.result_single_object(result_create_short_link)
|
|
792
793
|
when :list, :show
|
|
793
794
|
query = if link_type.eql?(:private)
|
|
@@ -1052,13 +1053,13 @@ module Aspera
|
|
|
1052
1053
|
when *Node::NODE4_READ_ACTIONS
|
|
1053
1054
|
package_id = instance_identifier
|
|
1054
1055
|
package_info = aoc_api.read("packages/#{package_id}")
|
|
1055
|
-
return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::
|
|
1056
|
+
return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::Scope::USER)
|
|
1056
1057
|
end
|
|
1057
1058
|
when :files
|
|
1058
1059
|
command_repo = options.get_next_command([:short_link].concat(NODE4_EXT_COMMANDS))
|
|
1059
1060
|
case command_repo
|
|
1060
1061
|
when *NODE4_EXT_COMMANDS
|
|
1061
|
-
return execute_nodegen4_command(command_repo, aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::
|
|
1062
|
+
return execute_nodegen4_command(command_repo, aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::Scope::USER)
|
|
1062
1063
|
when :short_link
|
|
1063
1064
|
folder_dest = options.get_next_argument('path', validation: String)
|
|
1064
1065
|
home_node_api = aoc_api.node_api_from(
|
|
@@ -1071,17 +1072,13 @@ module Aspera
|
|
|
1071
1072
|
node_id: shared_apfid[:api].app_info[:node_info]['id'],
|
|
1072
1073
|
file_id: shared_apfid[:file_id],
|
|
1073
1074
|
**workspace_id_hash
|
|
1074
|
-
) do |resource_id|
|
|
1075
|
-
# TODO: merge with node permissions ?
|
|
1076
|
-
# TODO: access level as arg
|
|
1077
|
-
access_levels = Api::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
|
|
1075
|
+
) do |resource_id, access_levels|
|
|
1078
1076
|
perm_data = {
|
|
1079
1077
|
'file_id' => shared_apfid[:file_id],
|
|
1080
1078
|
'access_id' => resource_id,
|
|
1081
1079
|
'access_type' => 'user',
|
|
1082
|
-
'access_levels' => access_levels,
|
|
1080
|
+
'access_levels' => Api::AoC.expand_access_levels(access_levels),
|
|
1083
1081
|
'tags' => {
|
|
1084
|
-
# TODO: really just here ? not in tags.aspera.files.workspace ?
|
|
1085
1082
|
'url_token' => true,
|
|
1086
1083
|
'folder_name' => File.basename(folder_dest),
|
|
1087
1084
|
'created_by_name' => aoc_api.current_user_info['name'],
|
|
@@ -40,7 +40,7 @@ module Aspera
|
|
|
40
40
|
def initialize(context:)
|
|
41
41
|
# Check presence in descendant of mandatory method and constant
|
|
42
42
|
Aspera.assert(respond_to?(:execute_action), type: InternalError){"Missing method 'execute_action' in #{self.class}"}
|
|
43
|
-
Aspera.assert(self.class.
|
|
43
|
+
Aspera.assert(self.class.const_defined?(:ACTIONS), type: InternalError){"Missing constant 'ACTIONS' in #{self.class}"}
|
|
44
44
|
@context = context
|
|
45
45
|
add_manual_header if @context.man_header
|
|
46
46
|
end
|
|
@@ -36,6 +36,7 @@ require 'digest'
|
|
|
36
36
|
require 'open3'
|
|
37
37
|
require 'date'
|
|
38
38
|
require 'erb'
|
|
39
|
+
require 'net/http'
|
|
39
40
|
|
|
40
41
|
module Aspera
|
|
41
42
|
module Cli
|
|
@@ -107,11 +108,7 @@ module Aspera
|
|
|
107
108
|
)
|
|
108
109
|
options.parse_options!
|
|
109
110
|
Log.log.debug{"#{Info::CMD_NAME} folder: #{@main_folder}"}
|
|
110
|
-
|
|
111
|
-
context.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
|
112
|
-
# Set folders for plugin lookup
|
|
113
|
-
Plugins::Factory.instance.add_lookup_folder(self.class.gem_plugins_folder)
|
|
114
|
-
Plugins::Factory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
|
111
|
+
setup_persistency_and_plugin_folders
|
|
115
112
|
# Option to set config file
|
|
116
113
|
options.declare(
|
|
117
114
|
:config_file, 'Path to YAML file with preset configuration',
|
|
@@ -121,12 +118,7 @@ module Aspera
|
|
|
121
118
|
options.parse_options!
|
|
122
119
|
# Read config file (set @config_presets)
|
|
123
120
|
read_config_file
|
|
124
|
-
|
|
125
|
-
ExtendedValue.instance.on(EXTEND_PRESET){ |v| preset_by_name(v)}
|
|
126
|
-
ExtendedValue.instance.on(EXTEND_VAULT){ |v| vault_value(v)}
|
|
127
|
-
ExtendedValue.instance.on(EXTEND_ARGS){ |v| options.args_as_extended(v)}
|
|
128
|
-
# Load defaults before it can be overridden
|
|
129
|
-
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
|
121
|
+
setup_extended_value_handlers
|
|
130
122
|
# Vault options
|
|
131
123
|
options.declare(:secret, 'Secret for access keys')
|
|
132
124
|
options.declare(:vault, 'Vault for secrets', allowed: Hash)
|
|
@@ -145,9 +137,8 @@ module Aspera
|
|
|
145
137
|
options.declare(:sdk_url, 'Ascp: URL to get Aspera Transfer Executables', default: SpecialValues::DEF)
|
|
146
138
|
options.parse_options!
|
|
147
139
|
set_sdk_dir
|
|
148
|
-
options.declare(:ascp_path, 'Ascp: Path to ascp (or product with "product:")', handler: {o: Ascp::Installation.instance, m: :ascp_path}, default: "#{Ascp::Installation::USE_PRODUCT_PREFIX}#{Ascp::Installation::FIRST_FOUND}")
|
|
149
140
|
options.declare(:locations_url, 'Ascp: URL to get download locations of Aspera Transfer Daemon', handler: {o: Ascp::Installation.instance, m: :transferd_urls})
|
|
150
|
-
options.declare(:sdk_folder, 'Ascp:
|
|
141
|
+
options.declare(:sdk_folder, 'Ascp: Path to folder with ascp (or product with "product:")', handler: {o: Products::Transferd, m: :sdk_directory})
|
|
151
142
|
options.declare(:progress_bar, 'Display progress bar', allowed: Allowed::TYPES_BOOLEAN, default: Environment.terminal?)
|
|
152
143
|
# Email options
|
|
153
144
|
options.declare(:smtp, 'Email: SMTP configuration', allowed: Hash)
|
|
@@ -165,17 +156,39 @@ module Aspera
|
|
|
165
156
|
options.declare(:proxy_credentials, 'HTTP proxy credentials for fpac: user, password', allowed: [Array, NilClass])
|
|
166
157
|
options.parse_options!
|
|
167
158
|
@progress_bar = TransferProgress.new if options.get_option(:progress_bar)
|
|
159
|
+
setup_pac_executor
|
|
160
|
+
setup_rest_and_transfer_runtime
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def setup_persistency_and_plugin_folders
|
|
166
|
+
context.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
|
167
|
+
Plugins::Factory.instance.add_lookup_folder(self.class.gem_plugins_folder)
|
|
168
|
+
Plugins::Factory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def setup_extended_value_handlers
|
|
172
|
+
ExtendedValue.instance.on(EXTEND_PRESET){ |v| preset_by_name(v)}
|
|
173
|
+
ExtendedValue.instance.on(EXTEND_VAULT){ |v| vault_value(v)}
|
|
174
|
+
ExtendedValue.instance.on(EXTEND_ARGS){ |v| options.args_as_extended(v)}
|
|
175
|
+
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def setup_pac_executor
|
|
168
179
|
pac_script = options.get_option(:fpac)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
end
|
|
180
|
+
return unless pac_script
|
|
181
|
+
|
|
182
|
+
@pac_exec = ProxyAutoConfig.new(pac_script).register_uri_generic
|
|
183
|
+
proxy_user_pass = options.get_option(:proxy_credentials)
|
|
184
|
+
if proxy_user_pass
|
|
185
|
+
Aspera.assert(proxy_user_pass.length.eql?(2), type: Cli::BadArgument){"proxy_credentials shall have two elements (#{proxy_user_pass.length})"}
|
|
186
|
+
@pac_exec.proxy_user = proxy_user_pass[0]
|
|
187
|
+
@pac_exec.proxy_pass = proxy_user_pass[1]
|
|
178
188
|
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def setup_rest_and_transfer_runtime
|
|
179
192
|
RestParameters.instance.user_agent = Info::CMD_NAME
|
|
180
193
|
RestParameters.instance.progress_bar = @progress_bar
|
|
181
194
|
RestParameters.instance.session_cb = lambda{ |http_session| update_http_session(http_session)}
|
|
@@ -197,12 +210,14 @@ module Aspera
|
|
|
197
210
|
keys_to_delete.each{ |k| @option_http_options.delete(k)}
|
|
198
211
|
OAuth::Factory.instance.persist_mgr = persistency if @option_cache_tokens
|
|
199
212
|
OAuth::Web.additional_info = "#{Info::CMD_NAME} v#{Cli::VERSION}"
|
|
200
|
-
Transfer::Parameters.file_list_folder = File.join(@main_folder,
|
|
201
|
-
RestErrorAnalyzer.instance.log_file = File.join(@main_folder,
|
|
213
|
+
Transfer::Parameters.file_list_folder = File.join(@main_folder, FILELIST_FOLDERNAME)
|
|
214
|
+
RestErrorAnalyzer.instance.log_file = File.join(@main_folder, REST_EXCEPTIONS_LOG_FILENAME)
|
|
202
215
|
# Register aspera REST call error handlers
|
|
203
216
|
RestErrorsAspera.register_handlers
|
|
204
217
|
end
|
|
205
218
|
|
|
219
|
+
public
|
|
220
|
+
|
|
206
221
|
attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_warn_insecure_cert, :option_http_options
|
|
207
222
|
attr_reader :option_ignore_cert_host_port, :progress_bar
|
|
208
223
|
|
|
@@ -307,10 +322,7 @@ module Aspera
|
|
|
307
322
|
if @option_warn_insecure_cert
|
|
308
323
|
base_url = "https://#{address}:#{port}"
|
|
309
324
|
if !@ssl_warned_urls.include?(base_url)
|
|
310
|
-
|
|
311
|
-
:error,
|
|
312
|
-
"#{Formatter::WARNING_FLASH} Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production."
|
|
313
|
-
)
|
|
325
|
+
Log.log.warn{"Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production."}
|
|
314
326
|
@ssl_warned_urls.push(base_url)
|
|
315
327
|
end
|
|
316
328
|
end
|
|
@@ -416,7 +428,7 @@ module Aspera
|
|
|
416
428
|
raise Cli::Error, "Preset already exists: #{preset_name} (use --override=yes or provide alternate name on command line)" \
|
|
417
429
|
if !option_override && @config_presets.key?(preset_name)
|
|
418
430
|
if option_default
|
|
419
|
-
|
|
431
|
+
Log.log.info("Setting config preset as default for #{plugin_name}")
|
|
420
432
|
@config_presets[CONF_PRESET_DEFAULTS][plugin_name.to_s] = preset_name
|
|
421
433
|
end
|
|
422
434
|
@config_presets[preset_name] = preset_values
|
|
@@ -439,7 +451,7 @@ module Aspera
|
|
|
439
451
|
Log.log.warn{"overwriting value for #{param_name}: #{selected_preset[param_name]}"}
|
|
440
452
|
end
|
|
441
453
|
selected_preset[param_name] = param_value
|
|
442
|
-
|
|
454
|
+
Log.log.info("Updated: #{preset}: #{param_name} <- #{param_value}")
|
|
443
455
|
nil
|
|
444
456
|
end
|
|
445
457
|
|
|
@@ -591,24 +603,16 @@ module Aspera
|
|
|
591
603
|
end
|
|
592
604
|
|
|
593
605
|
def install_transfer_sdk
|
|
594
|
-
# Reset to default location, if older default was used
|
|
595
|
-
Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
|
|
596
606
|
asked_version = options.get_next_argument('transferd version', mandatory: false)
|
|
597
607
|
name, version, folder = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: asked_version)
|
|
598
608
|
return Main.result_status("Installed #{name} version #{version} in #{folder}")
|
|
599
609
|
end
|
|
600
610
|
|
|
601
611
|
def execute_action_ascp
|
|
602
|
-
command = options.get_next_command(%i[connect
|
|
612
|
+
command = options.get_next_command(%i[connect show products info install spec schema errors])
|
|
603
613
|
case command
|
|
604
614
|
when :connect
|
|
605
615
|
return execute_connect_action
|
|
606
|
-
when :use
|
|
607
|
-
ascp_path = options.get_next_argument('path to ascp')
|
|
608
|
-
Ascp::Installation.instance.ascp_path = ascp_path
|
|
609
|
-
formatter.display_status("ascp version: #{Ascp::Installation.instance.get_ascp_version(ascp_path)}")
|
|
610
|
-
set_global_default(:ascp_path, ascp_path)
|
|
611
|
-
return Main.result_nothing
|
|
612
616
|
when :show
|
|
613
617
|
return Main.result_text(Ascp::Installation.instance.path(:ascp))
|
|
614
618
|
when :info
|
|
@@ -622,15 +626,10 @@ module Aspera
|
|
|
622
626
|
SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
|
|
623
627
|
return Main.result_single_object(data)
|
|
624
628
|
when :products
|
|
625
|
-
command = options.get_next_command(%i[list
|
|
629
|
+
command = options.get_next_command(%i[list])
|
|
626
630
|
case command
|
|
627
631
|
when :list
|
|
628
632
|
return Main.result_object_list(Ascp::Installation.instance.installed_products, fields: %w[name app_root])
|
|
629
|
-
when :use
|
|
630
|
-
default_product = options.get_next_argument('product name')
|
|
631
|
-
Ascp::Installation.instance.use_ascp_from_product(default_product)
|
|
632
|
-
set_global_default(:ascp_path, "#{Ascp::Installation::USE_PRODUCT_PREFIX}#{default_product}")
|
|
633
|
-
return Main.result_nothing
|
|
634
633
|
end
|
|
635
634
|
when :install
|
|
636
635
|
return install_transfer_sdk
|
|
@@ -848,7 +847,7 @@ module Aspera
|
|
|
848
847
|
file_url = options.get_next_argument('source URL').chomp
|
|
849
848
|
file_dest = options.get_next_argument('file path', mandatory: false)
|
|
850
849
|
file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
|
|
851
|
-
|
|
850
|
+
Log.log.info("Downloading: #{file_url}")
|
|
852
851
|
Rest.new(base_url: file_url).call(operation: 'GET', save_to_file: file_dest)
|
|
853
852
|
return Main.result_status("Saved to: #{file_dest}")
|
|
854
853
|
when :tokens
|
|
@@ -1047,8 +1046,7 @@ module Aspera
|
|
|
1047
1046
|
return false if @config_checksum_on_disk.eql?(current_checksum)
|
|
1048
1047
|
FileUtils.mkdir_p(@main_folder)
|
|
1049
1048
|
Environment.restrict_file_access(@main_folder)
|
|
1050
|
-
Log.log.info{"
|
|
1051
|
-
formatter.display_status('Saving config file.')
|
|
1049
|
+
Log.log.info{"Saving config file: #{@option_config_file}"}
|
|
1052
1050
|
Environment.write_file_restricted(@option_config_file, force: true){@config_presets.to_yaml}
|
|
1053
1051
|
@config_checksum_on_disk = current_checksum
|
|
1054
1052
|
return true
|
|
@@ -1200,6 +1198,8 @@ module Aspera
|
|
|
1200
1198
|
# Folder containing custom plugins in user's config folder
|
|
1201
1199
|
ASPERA_PLUGINS_FOLDERNAME = 'plugins'
|
|
1202
1200
|
PERSISTENCY_FOLDER = 'persist_store'
|
|
1201
|
+
FILELIST_FOLDERNAME = 'filelists'
|
|
1202
|
+
REST_EXCEPTIONS_LOG_FILENAME = 'rest_exceptions.log'
|
|
1203
1203
|
ASPERA = 'aspera'
|
|
1204
1204
|
SERVER_COMMAND = 'server'
|
|
1205
1205
|
TRANSFERD_APP_NAME = 'sdk'
|
|
@@ -1232,6 +1232,8 @@ module Aspera
|
|
|
1232
1232
|
:CONF_PRESET_DEFAULTS,
|
|
1233
1233
|
:CONF_PRESET_GLOBAL,
|
|
1234
1234
|
:ASPERA_PLUGINS_FOLDERNAME,
|
|
1235
|
+
:FILELIST_FOLDERNAME,
|
|
1236
|
+
:REST_EXCEPTIONS_LOG_FILENAME,
|
|
1235
1237
|
:ASPERA,
|
|
1236
1238
|
:DEMO_SERVER,
|
|
1237
1239
|
:DEMO_PRESET,
|
|
@@ -20,9 +20,9 @@ module Aspera
|
|
|
20
20
|
@plugins = {}
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# @return list of registered plugins
|
|
23
|
+
# @return [Array<Symbol>] Sorted list of registered plugins
|
|
24
24
|
def plugin_list
|
|
25
|
-
@plugins.keys
|
|
25
|
+
@plugins.keys.sort
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# add a folder to the list of folders to look for plugins
|
|
@@ -506,7 +506,7 @@ module Aspera
|
|
|
506
506
|
)
|
|
507
507
|
return Main.result_single_object(result)
|
|
508
508
|
when :members, :saml_groups
|
|
509
|
-
# :shared_inboxes, :workgroups
|
|
509
|
+
# res_command := :shared_inboxes, :workgroups
|
|
510
510
|
res_id = instance_identifier{ |field, value| lookup_entity_by_field(api: @api_v5, entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
|
|
511
511
|
res_path = "#{res_sym}/#{res_id}/#{res_command}"
|
|
512
512
|
list_key = res_command.to_s
|
|
@@ -533,6 +533,7 @@ module Aspera
|
|
|
533
533
|
access = options.get_next_argument('level', mandatory: false, accept_list: SHARED_INBOX_MEMBER_LEVELS, default: :standard)
|
|
534
534
|
options.unshift_next_argument({user: users.map{ |u| {id: u, access: access}}})
|
|
535
535
|
end
|
|
536
|
+
# TODO: test SAML group
|
|
536
537
|
return entity_execute(
|
|
537
538
|
api: @api_v5,
|
|
538
539
|
entity: res_path,
|
|
@@ -541,11 +542,11 @@ module Aspera
|
|
|
541
542
|
) do |field, value|
|
|
542
543
|
lookup_entity_by_field(
|
|
543
544
|
api: @api_v5,
|
|
544
|
-
entity:
|
|
545
|
+
entity: res_path,
|
|
545
546
|
field: field,
|
|
546
547
|
value: value,
|
|
547
548
|
query: Rest.php_style({type: %w[user]})
|
|
548
|
-
)['
|
|
549
|
+
)['user_id']
|
|
549
550
|
end
|
|
550
551
|
when :reset_password
|
|
551
552
|
# :accounts
|
|
@@ -106,17 +106,12 @@ module Aspera
|
|
|
106
106
|
Log.log.debug{"tmpdir: #{@tmp_folder}"}
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
-
# /files/id/files is normally cached in
|
|
109
|
+
# /files/id/files is normally cached in Redis, but we can discard the cache
|
|
110
110
|
# but /files/id is not cached
|
|
111
111
|
def get_folder_entries(file_id, request_args = nil)
|
|
112
112
|
headers = {'Accept' => Rest::MIME_JSON}
|
|
113
113
|
headers['X-Aspera-Cache-Control'] = 'no-cache' if @option_folder_reset_cache.eql?(:header)
|
|
114
|
-
return @api_node.
|
|
115
|
-
operation: 'GET',
|
|
116
|
-
subpath: "files/#{file_id}/files",
|
|
117
|
-
headers: headers,
|
|
118
|
-
query: request_args
|
|
119
|
-
)
|
|
114
|
+
return @api_node.read("files/#{file_id}/files", request_args, headers: headers)
|
|
120
115
|
end
|
|
121
116
|
|
|
122
117
|
# old version based on folders
|
|
@@ -153,7 +148,7 @@ module Aspera
|
|
|
153
148
|
end
|
|
154
149
|
# log/persist periodically or last one
|
|
155
150
|
next unless @periodic.trigger? || event.equal?(events.last)
|
|
156
|
-
Log.log.
|
|
151
|
+
Log.log.debug{"Processed event #{event['id']}"}
|
|
157
152
|
# save checkpoint to avoid losing processing in case of error
|
|
158
153
|
if !iteration_persistency.nil?
|
|
159
154
|
iteration_persistency.data[0] = event['id'].to_s
|
|
@@ -186,7 +181,7 @@ module Aspera
|
|
|
186
181
|
end
|
|
187
182
|
# log/persist periodically or last one
|
|
188
183
|
next unless @periodic.trigger? || event.equal?(events.last)
|
|
189
|
-
Log.log.
|
|
184
|
+
Log.log.debug{"Processing event #{event['id']}"}
|
|
190
185
|
# save checkpoint to avoid losing processing in case of error
|
|
191
186
|
if !iteration_persistency.nil?
|
|
192
187
|
iteration_persistency.data[0] = event['id'].to_s
|
|
@@ -303,7 +298,7 @@ module Aspera
|
|
|
303
298
|
# download original file to temp folder
|
|
304
299
|
do_transfer(Transfer::Spec::DIRECTION_RECEIVE, entry['parent_file_id'], entry['name'], @tmp_folder)
|
|
305
300
|
end
|
|
306
|
-
Log.log.
|
|
301
|
+
Log.log.debug{"source: #{entry['id']}: #{entry['path']}"}
|
|
307
302
|
gen_infos.each do |gen_info|
|
|
308
303
|
gen_info[:generator].generate rescue nil
|
|
309
304
|
end
|
|
@@ -335,7 +330,7 @@ module Aspera
|
|
|
335
330
|
entry = entries_to_process.shift
|
|
336
331
|
# process this entry only if it is within the top_path
|
|
337
332
|
entry_path_with_slash = entry['path']
|
|
338
|
-
Log.log.
|
|
333
|
+
Log.log.debug{"processing entry #{entry_path_with_slash}"} if @periodic.trigger?
|
|
339
334
|
entry_path_with_slash = "#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
|
|
340
335
|
if !top_path.nil? && !top_path.start_with?(entry_path_with_slash) && !entry_path_with_slash.start_with?(top_path)
|
|
341
336
|
Log.log.debug{"#{entry['path']} folder (skip start)".bg_red}
|
|
@@ -33,8 +33,8 @@ module Aspera
|
|
|
33
33
|
:DEFAULT_TRANSFER_NOTIFY_TEMPLATE
|
|
34
34
|
|
|
35
35
|
class << self
|
|
36
|
-
# @return :success
|
|
37
|
-
#
|
|
36
|
+
# @return [:success] if all sessions statuses returned by "start" are success
|
|
37
|
+
# @return [Exception] if one sessions statuses returned by "start" is failed
|
|
38
38
|
def session_status(statuses)
|
|
39
39
|
error_statuses = statuses.reject{ |i| i.eql?(:success)}
|
|
40
40
|
return :success if error_statuses.empty?
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/environment.rb
CHANGED
|
@@ -58,8 +58,8 @@ module Aspera
|
|
|
58
58
|
# @param code [String] Ruby code to execute
|
|
59
59
|
# @param file [String] File name for error reporting
|
|
60
60
|
# @param line [Integer] Line number for error reporting
|
|
61
|
-
def secure_eval(code, file, line)
|
|
62
|
-
Kernel.send('lave'.reverse, code, empty_binding, file, line)
|
|
61
|
+
def secure_eval(code, file, line, user_binding = nil)
|
|
62
|
+
Kernel.send('lave'.reverse, code, user_binding || empty_binding, file, line)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Build argv for Process.spawn / Kernel.system (no shell)
|
|
@@ -74,26 +74,40 @@ module Aspera
|
|
|
74
74
|
argv
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
#
|
|
77
|
+
# like `Shellwords.shellescape`, but does not escape `=`
|
|
78
|
+
def shell_escape_pretty(str)
|
|
79
|
+
# Safe unquoted characters + '=' explicitly allowed
|
|
80
|
+
return str if str.match?(%r{\A[A-Za-z0-9_.,:/@+=-]+\z})
|
|
81
|
+
# return str if Shellwords.shellescape(str) == str
|
|
82
|
+
|
|
83
|
+
# Otherwise use single quotes
|
|
84
|
+
"'#{str.gsub("'", %q('\'\''))}'"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Execute a process securely without shell.
|
|
88
|
+
#
|
|
89
|
+
# Execution `mode` can be:
|
|
90
|
+
# - :execute -> Kernel.system, return nil
|
|
91
|
+
# - :background -> Process.spawn, return pid
|
|
92
|
+
# - :capture -> Open3.capture3, return stdout
|
|
93
|
+
#
|
|
94
|
+
# @param cmd [Array<String>] Executable and arguments (mapped "to_s")
|
|
95
|
+
# @param mode [:execute,:background,:capture] Execution mode
|
|
96
|
+
# @param kwargs [Hash] Additional arguments to underlying method
|
|
97
|
+
# @option kwargs [Hash] :env Environment variables
|
|
98
|
+
# @option kwargs [Boolean] :exception for :capture mode, raise error if process fails
|
|
99
|
+
# @option kwargs [Boolean] :close_others for :background mode
|
|
100
|
+
# @return [nil] for :execute mode
|
|
101
|
+
# @return [Integer] pid for :background mode
|
|
102
|
+
# @return [String] stdout for :capture mode
|
|
89
103
|
def secure_execute(*cmd, mode: :execute, **kwargs)
|
|
90
104
|
cmd = cmd.map(&:to_s)
|
|
91
105
|
Aspera.assert(cmd.size.positive?, type: ArgumentError){'executable must be present'}
|
|
92
106
|
Aspera.assert_values(mode, PROCESS_MODES, type: ArgumentError){'mode'}
|
|
93
107
|
Log.log.debug do
|
|
94
108
|
parts = [mode.to_s, 'command:']
|
|
95
|
-
kwargs[:env]&.each{ |k, v| parts << "#{k}=#{
|
|
96
|
-
cmd.each{ |a| parts <<
|
|
109
|
+
kwargs[:env]&.each{ |k, v| parts << "#{k}=#{shell_escape_pretty(v.to_s)}"}
|
|
110
|
+
cmd.each{ |a| parts << shell_escape_pretty(a)}
|
|
97
111
|
parts.join(' ')
|
|
98
112
|
end
|
|
99
113
|
case mode
|
data/lib/aspera/faspex_gw.rb
CHANGED
|
@@ -80,7 +80,7 @@ module Aspera
|
|
|
80
80
|
faspex4_send_to_faspex5(faspex_pkg_parameters)
|
|
81
81
|
else Aspera.error_unexpected_value(@app_api.class.name)
|
|
82
82
|
end
|
|
83
|
-
Log.log.
|
|
83
|
+
Log.log.debug{"faspex_package_create_result=#{faspex_package_create_result}"}
|
|
84
84
|
response.status = 200
|
|
85
85
|
response.content_type = Rest::MIME_JSON
|
|
86
86
|
response.body = JSON.generate(faspex_package_create_result)
|