aspera-cli 4.27.2 → 4.27.4
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 +133 -0
- data/CONTRIBUTING.md +5 -2
- data/bin/ascli +3 -1
- data/docs/README.md +1006 -766
- data/lib/aspera/agent/base.rb +7 -2
- data/lib/aspera/agent/connect.rb +6 -8
- data/lib/aspera/agent/desktop.rb +2 -6
- data/lib/aspera/agent/direct.rb +52 -22
- data/lib/aspera/agent/node.rb +9 -8
- data/lib/aspera/agent/transferd.rb +2 -2
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -12
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +11 -7
- data/lib/aspera/api/httpgw.rb +38 -35
- data/lib/aspera/api/node.rb +39 -34
- data/lib/aspera/ascmd.rb +3 -1
- data/lib/aspera/ascp/installation.rb +63 -28
- data/lib/aspera/ascp/management.rb +1 -0
- data/lib/aspera/assert.rb +4 -0
- data/lib/aspera/cli/ascp_actions.rb +20 -41
- data/lib/aspera/cli/async_transfer_store.rb +12 -11
- data/lib/aspera/cli/bootstrapper.rb +14 -16
- data/lib/aspera/cli/command_line.rb +252 -0
- data/lib/aspera/cli/command_registry.rb +215 -37
- data/lib/aspera/cli/command_spec.rb +104 -15
- data/lib/aspera/cli/completion/ascli.bash +12 -0
- data/lib/aspera/cli/completion/ascli.fish +16 -0
- data/lib/aspera/cli/completion/ascli.zsh +19 -0
- data/lib/aspera/cli/context.rb +3 -0
- data/lib/aspera/cli/deprecation.rb +37 -0
- data/lib/aspera/cli/extended_value.rb +6 -3
- data/lib/aspera/cli/formatter.rb +94 -80
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/http.rb +22 -34
- data/lib/aspera/cli/info.rb +3 -0
- data/lib/aspera/cli/mcp_tool.rb +47 -83
- data/lib/aspera/cli/option_declarator.rb +33 -42
- data/lib/aspera/cli/option_registry.rb +69 -0
- data/lib/aspera/cli/option_types.rb +105 -0
- data/lib/aspera/cli/option_value.rb +278 -0
- data/lib/aspera/cli/options.schema.yaml +124 -15
- data/lib/aspera/cli/parser.rb +333 -862
- data/lib/aspera/cli/plugins/alee.rb +7 -4
- data/lib/aspera/cli/plugins/aoc.rb +545 -518
- data/lib/aspera/cli/plugins/ats.rb +59 -80
- data/lib/aspera/cli/plugins/base.rb +221 -265
- data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
- data/lib/aspera/cli/plugins/config.rb +263 -184
- data/lib/aspera/cli/plugins/console.rb +103 -39
- data/lib/aspera/cli/plugins/cos.rb +6 -23
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex5.rb +204 -182
- data/lib/aspera/cli/plugins/faspio.rb +6 -11
- data/lib/aspera/cli/plugins/httpgw.rb +8 -11
- data/lib/aspera/cli/plugins/mcp.rb +20 -55
- data/lib/aspera/cli/plugins/node.rb +300 -327
- data/lib/aspera/cli/plugins/orchestrator.rb +152 -110
- data/lib/aspera/cli/plugins/preview.rb +96 -105
- data/lib/aspera/cli/plugins/server.rb +78 -53
- data/lib/aspera/cli/plugins/shares.rb +80 -131
- data/lib/aspera/cli/preset_actions.rb +44 -27
- data/lib/aspera/cli/preset_manager.rb +44 -19
- data/lib/aspera/cli/prompt.rb +36 -0
- data/lib/aspera/cli/result.rb +42 -36
- data/lib/aspera/cli/runner.rb +32 -59
- data/lib/aspera/cli/special_values.rb +5 -0
- data/lib/aspera/cli/sync_actions.rb +51 -46
- data/lib/aspera/cli/terminal_formatter.rb +9 -3
- data/lib/aspera/cli/transfer_actions.rb +14 -9
- data/lib/aspera/cli/transfer_agent.rb +34 -38
- data/lib/aspera/cli/transfer_progress.rb +290 -55
- data/lib/aspera/cli/vault_manager.rb +0 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +5 -3
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/environment.rb +35 -5
- data/lib/aspera/faspex_gw.rb +2 -1
- data/lib/aspera/faspex_postproc.rb +1 -0
- data/lib/aspera/graphql.rb +5 -5
- data/lib/aspera/json_rpc/client.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +2 -1
- data/lib/aspera/keychain/one_password_api.rb +1 -1
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +47 -27
- data/lib/aspera/markdown.rb +2 -0
- data/lib/aspera/mime.rb +25 -0
- data/lib/aspera/node_emulator.rb +759 -0
- data/lib/aspera/oauth/base.rb +37 -26
- data/lib/aspera/oauth/factory.rb +7 -3
- data/lib/aspera/oauth/generic.rb +1 -1
- data/lib/aspera/oauth/json_credentials.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +4 -5
- data/lib/aspera/oauth/web.rb +9 -8
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_folder.rb +1 -3
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +11 -1
- data/lib/aspera/preview/options.schema.yaml +119 -0
- data/lib/aspera/preview/terminal.rb +4 -3
- data/lib/aspera/preview/utils.rb +9 -6
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/rainbow.rb +7 -0
- data/lib/aspera/rest/aspera_errors.rb +72 -0
- data/lib/aspera/rest/call_error.rb +27 -0
- data/lib/aspera/rest/client.rb +523 -0
- data/lib/aspera/rest/error_analyzer.rb +113 -0
- data/lib/aspera/rest/list.rb +149 -0
- data/lib/aspera/rest/parameters.rb +55 -0
- data/lib/aspera/rest/util.rb +176 -0
- data/lib/aspera/rest.rb +7 -621
- data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
- data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
- data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
- data/lib/aspera/schema/documentation.rb +13 -3
- data/lib/aspera/schema/reader.rb +12 -18
- data/lib/aspera/schema/registry.rb +23 -1
- data/lib/aspera/schema/validator.rb +92 -0
- data/lib/aspera/secret_hider.rb +36 -25
- data/lib/aspera/string_ext.rb +15 -0
- data/lib/aspera/temp_file_manager.rb +6 -5
- data/lib/aspera/transfer/parameters.rb +2 -0
- data/lib/aspera/transfer/spec.rb +1 -0
- data/lib/aspera/uri_reader.rb +11 -11
- data/lib/aspera/web_auth/index.html +147 -0
- data/lib/aspera/web_auth/server.rb +81 -0
- data.tar.gz.sig +0 -0
- metadata +43 -9
- metadata.gz.sig +0 -0
- data/lib/aspera/colors.rb +0 -79
- data/lib/aspera/node_simulator.rb +0 -344
- data/lib/aspera/preview/options.rb +0 -45
- data/lib/aspera/rest_call_error.rb +0 -25
- data/lib/aspera/rest_error_analyzer.rb +0 -111
- data/lib/aspera/rest_errors_aspera.rb +0 -58
- data/lib/aspera/rest_list.rb +0 -136
- data/lib/aspera/web_auth.rb +0 -211
data/lib/aspera/cli/wizard.rb
CHANGED
|
@@ -4,6 +4,8 @@ require 'aspera/oauth/jwt'
|
|
|
4
4
|
require 'aspera/assert'
|
|
5
5
|
require 'aspera/cli/option_declarator'
|
|
6
6
|
require 'aspera/cli/plugins/factory'
|
|
7
|
+
require 'aspera/rainbow'
|
|
8
|
+
using Rainbow
|
|
7
9
|
|
|
8
10
|
module Aspera
|
|
9
11
|
module Cli
|
|
@@ -15,6 +17,7 @@ module Aspera
|
|
|
15
17
|
DEFAULT_PRIV_KEY_FILENAME = 'my_private_key.pem' # pragma: allowlist secret
|
|
16
18
|
private_constant :WIZARD_RESULT_KEYS, :DEFAULT_PRIV_KEY_FILENAME
|
|
17
19
|
|
|
20
|
+
# Declared by the `config` plugin (`use_options Wizard`)
|
|
18
21
|
option :override, description: 'Wizard: override existing value', allowed: Type::BOOLEAN, default: false
|
|
19
22
|
option :default, description: 'Wizard: set as default configuration for specified plugin (also: update)', allowed: Type::BOOLEAN, default: true
|
|
20
23
|
option :key_path, description: 'Wizard: path to private key for JWT'
|
|
@@ -22,7 +25,6 @@ module Aspera
|
|
|
22
25
|
def initialize(parent, main_folder)
|
|
23
26
|
@parent = parent
|
|
24
27
|
@main_folder = main_folder
|
|
25
|
-
self.class.declare_options(options)
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
# @return [Boolean] false if in test mode to avoid interactive input
|
|
@@ -60,7 +62,7 @@ module Aspera
|
|
|
60
62
|
detection_info = nil
|
|
61
63
|
begin
|
|
62
64
|
Log.log.debug { "detecting #{plugin_name_sym} at #{app_url}" }
|
|
63
|
-
|
|
65
|
+
Rest::Parameters.instance.spinner_cb.call(plugin_name_sym.to_s)
|
|
64
66
|
detection_info = plugin_klass.detect(app_url)
|
|
65
67
|
rescue OpenSSL::SSL::SSLError => e
|
|
66
68
|
Log.log.warn(e.message)
|
|
@@ -76,7 +78,7 @@ module Aspera
|
|
|
76
78
|
# If there is a redirect, then the detector can override the url.
|
|
77
79
|
found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
|
|
78
80
|
end
|
|
79
|
-
|
|
81
|
+
Rest::Parameters.instance.spinner_cb.call(action: :success)
|
|
80
82
|
Aspera.assert(!found_apps.empty?) { "No known application found at #{app_url}" }
|
|
81
83
|
Aspera.assert(found_apps.all? { |a| a.keys.all?(Symbol) }, 'all app info keys must be symbols')
|
|
82
84
|
return found_apps
|
data/lib/aspera/coverage.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
if ENV.key?('ENABLE_COVERAGE')
|
|
5
5
|
require 'simplecov'
|
|
6
6
|
require 'securerandom'
|
|
7
|
+
require 'fileutils'
|
|
7
8
|
# compute development top folder based on this source location
|
|
8
9
|
development_root = File.dirname(File.realpath(__FILE__), 3)
|
|
9
10
|
coverage_dir = 'tmp/coverage'
|
|
@@ -32,7 +33,6 @@ if ENV.key?('ENABLE_COVERAGE')
|
|
|
32
33
|
false
|
|
33
34
|
end
|
|
34
35
|
# no coverage in these files
|
|
35
|
-
add_filter 'lib/aspera/node_simulator.rb'
|
|
36
36
|
add_filter 'lib/aspera/keychain/macos_security.rb'
|
|
37
37
|
add_filter 'lib/aspera/assert.rb'
|
|
38
38
|
end
|
data/lib/aspera/environment.rb
CHANGED
|
@@ -5,11 +5,14 @@ require 'aspera/log'
|
|
|
5
5
|
require 'aspera/assert'
|
|
6
6
|
require 'rbconfig'
|
|
7
7
|
require 'singleton'
|
|
8
|
+
require 'openssl'
|
|
8
9
|
require 'open3'
|
|
9
10
|
require 'English'
|
|
10
11
|
require 'shellwords'
|
|
12
|
+
require 'aspera/rainbow'
|
|
13
|
+
using Rainbow
|
|
11
14
|
|
|
12
|
-
# cspell:words
|
|
15
|
+
# cspell:words mswin bccwin
|
|
13
16
|
|
|
14
17
|
module Aspera
|
|
15
18
|
# detect OS, architecture, and specific stuff
|
|
@@ -34,8 +37,6 @@ module Aspera
|
|
|
34
37
|
CPU_LIST = [CPU_X86_64, CPU_ARM64, CPU_PPC64, CPU_PPC64LE, CPU_S390].freeze
|
|
35
38
|
|
|
36
39
|
BITS_PER_BYTE = 8
|
|
37
|
-
MEBI = 1024 * 1024
|
|
38
|
-
BYTES_PER_MEBIBIT = MEBI / BITS_PER_BYTE
|
|
39
40
|
|
|
40
41
|
I18N_VARS = %w(LC_ALL LC_CTYPE LANG).freeze
|
|
41
42
|
|
|
@@ -48,6 +49,14 @@ module Aspera
|
|
|
48
49
|
|
|
49
50
|
PROCESS_MODES = %i[execute background capture].freeze
|
|
50
51
|
|
|
52
|
+
# System CA certificate bundles, used when the default locations of Ruby's OpenSSL do not exist
|
|
53
|
+
CA_BUNDLE_FILES = [
|
|
54
|
+
'/etc/ssl/certs/ca-certificates.crt', # Debian, Ubuntu, Alpine, Arch
|
|
55
|
+
'/etc/pki/tls/certs/ca-bundle.crt', # RHEL, Fedora
|
|
56
|
+
'/etc/ssl/ca-bundle.pem', # SUSE
|
|
57
|
+
'/etc/ssl/cert.pem' # macOS, BSD
|
|
58
|
+
].freeze
|
|
59
|
+
|
|
51
60
|
class << self
|
|
52
61
|
def ruby_version
|
|
53
62
|
return RbConfig::CONFIG['RUBY_PROGRAM_VERSION']
|
|
@@ -163,8 +172,8 @@ module Aspera
|
|
|
163
172
|
if force || !File.exist?(path)
|
|
164
173
|
# Windows may give error
|
|
165
174
|
File.unlink(path) rescue nil
|
|
166
|
-
# content provided by block
|
|
167
|
-
File.write(path, yield)
|
|
175
|
+
# content provided by block, file created with restricted access: never readable by others, even briefly
|
|
176
|
+
File.write(path, yield, perm: mode || 0o600)
|
|
168
177
|
restrict_file_access(path, mode: mode)
|
|
169
178
|
end
|
|
170
179
|
return path
|
|
@@ -201,12 +210,16 @@ module Aspera
|
|
|
201
210
|
I18N_VARS.each { |var| ENV[var] = 'C' }
|
|
202
211
|
end
|
|
203
212
|
|
|
213
|
+
# Override detection of Unicode support: `true` or `false`, or `nil` for auto-detection
|
|
214
|
+
attr_writer :unicode
|
|
215
|
+
|
|
204
216
|
# @return [Boolean] true if we can display Unicode characters
|
|
205
217
|
# Uses Encoding.locale_charmap for OS-independent detection.
|
|
206
218
|
# Falls back to locale env vars for systems where charmap is not available.
|
|
207
219
|
# https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html
|
|
208
220
|
# https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
|
|
209
221
|
def terminal_supports_unicode?
|
|
222
|
+
return @unicode unless @unicode.nil?
|
|
210
223
|
return false unless terminal?
|
|
211
224
|
locale_charmap_utf8? || I18N_VARS.any? { |var| ENV[var]&.include?('UTF-8') }
|
|
212
225
|
end
|
|
@@ -293,6 +306,23 @@ module Aspera
|
|
|
293
306
|
nil
|
|
294
307
|
end
|
|
295
308
|
|
|
309
|
+
# When Ruby's OpenSSL was built on another system (e.g. single executable built on RHEL, used on Ubuntu),
|
|
310
|
+
# its default CA locations may not exist: use the system CA bundle instead.
|
|
311
|
+
# @return [String, nil] CA bundle file used, or nil if no change
|
|
312
|
+
def fix_ca_certificates
|
|
313
|
+
return if defined?(JRUBY_VERSION)
|
|
314
|
+
return if [OpenSSL::X509::DEFAULT_CERT_FILE_ENV, OpenSSL::X509::DEFAULT_CERT_DIR_ENV].any? { |var| ENV.key?(var) }
|
|
315
|
+
return if File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE) || Dir.exist?(OpenSSL::X509::DEFAULT_CERT_DIR)
|
|
316
|
+
ca_file = CA_BUNDLE_FILES.find { |file| File.file?(file) }
|
|
317
|
+
return if ca_file.nil?
|
|
318
|
+
# Used by OpenSSL for new default stores, and by child processes
|
|
319
|
+
ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] = ca_file
|
|
320
|
+
# Default store of SSL contexts is initialized when openssl is loaded
|
|
321
|
+
OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE.add_file(ca_file)
|
|
322
|
+
Log.log.debug { "Using system CA certificates: #{ca_file}" }
|
|
323
|
+
ca_file
|
|
324
|
+
end
|
|
325
|
+
|
|
296
326
|
def graphical?
|
|
297
327
|
@default_gui_mode == :graphical
|
|
298
328
|
end
|
data/lib/aspera/faspex_gw.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'aspera/log'
|
|
4
4
|
require 'aspera/assert'
|
|
5
|
+
require 'aspera/mime'
|
|
5
6
|
require 'webrick'
|
|
6
7
|
require 'json'
|
|
7
8
|
|
|
@@ -10,7 +11,7 @@ module Aspera
|
|
|
10
11
|
class Faspex4GWServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
11
12
|
AOC_API = 'Aspera::Api::AoC'
|
|
12
13
|
FX_API = 'Aspera::Api::Faspex'
|
|
13
|
-
# @param app_api [Rest] API object
|
|
14
|
+
# @param app_api [Rest::Client] API object
|
|
14
15
|
# @param app_context [String] workspace id (aoc only)
|
|
15
16
|
def initialize(server, app_api, app_context)
|
|
16
17
|
Aspera.assert_values(app_api.class.name, [AOC_API, FX_API])
|
data/lib/aspera/graphql.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'aspera/
|
|
3
|
+
require 'aspera/rest/call_error'
|
|
4
4
|
|
|
5
5
|
module Aspera
|
|
6
|
-
# Helper to execute GraphQL queries via a Rest object.
|
|
6
|
+
# Helper to execute GraphQL queries via a Rest::Client object.
|
|
7
7
|
# Follows the GraphQL-over-HTTP convention: POST {query, variables} to endpoint,
|
|
8
8
|
# parse top-level `data` key, raise on top-level `errors` array.
|
|
9
9
|
module GraphQL
|
|
@@ -12,16 +12,16 @@ module Aspera
|
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
14
|
# Execute a GraphQL query loaded from a .graphql file in lib/aspera/api/queries/
|
|
15
|
-
# @param rest [Rest] API object pointing at the GraphQL endpoint
|
|
15
|
+
# @param rest [Rest::Client] API object pointing at the GraphQL endpoint
|
|
16
16
|
# @param name [String] Base filename without extension (e.g. 'my_query')
|
|
17
17
|
# @param variables [Hash] GraphQL variables
|
|
18
18
|
# @return [Hash] The `data` object from the response
|
|
19
|
-
# @raise [
|
|
19
|
+
# @raise [Rest::CallError] if the response contains GraphQL errors
|
|
20
20
|
def execute(rest, name, variables = {})
|
|
21
21
|
query = load_query(name)
|
|
22
22
|
response = rest.create(nil, {query: query, variables: variables})
|
|
23
23
|
errors = response['errors']
|
|
24
|
-
raise
|
|
24
|
+
raise Rest::CallError, errors.map { |e| e['message'] }.join("\n") if errors.is_a?(Array) && !errors.empty?
|
|
25
25
|
response['data']
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
# cspell:ignore blankslate jsonrpc
|
|
4
4
|
|
|
5
|
-
require 'aspera/
|
|
5
|
+
require 'aspera/rest/error_analyzer'
|
|
6
6
|
require 'aspera/assert'
|
|
7
7
|
require 'aspera/json_rpc/version'
|
|
8
8
|
require 'blankslate'
|
|
9
9
|
|
|
10
|
-
Aspera::
|
|
10
|
+
Aspera::Rest::ErrorAnalyzer.instance.add_simple_handler(name: 'JSON RPC', path: %w[error message], always: true)
|
|
11
11
|
|
|
12
12
|
module Aspera
|
|
13
13
|
module JsonRpc
|
|
14
|
-
# JSON-RPC 2.0 client over an Aspera::Rest HTTP endpoint.
|
|
14
|
+
# JSON-RPC 2.0 client over an Aspera::Rest::Client HTTP endpoint.
|
|
15
15
|
# Methods are dispatched dynamically via method_missing.
|
|
16
16
|
# Example:
|
|
17
|
-
# client = JsonRpc::Client.new(Rest.new(base_url: 'http://127.0.0.1:33024'))
|
|
17
|
+
# client = JsonRpc::Client.new(Rest::Client.new(base_url: 'http://127.0.0.1:33024'))
|
|
18
18
|
# client.get_info
|
|
19
19
|
# client.start_transfer(app_id: '...', transfer_spec: {...})
|
|
20
20
|
class Client < BlankSlate
|
|
@@ -22,7 +22,7 @@ module Aspera
|
|
|
22
22
|
reveal :inspect
|
|
23
23
|
reveal :to_s
|
|
24
24
|
|
|
25
|
-
# @param api [Rest] Aspera REST object pointing at the JSON-RPC endpoint
|
|
25
|
+
# @param api [Rest::Client] Aspera REST object pointing at the JSON-RPC endpoint
|
|
26
26
|
# @param namespace [String, nil] optional method prefix, e.g. "myns."
|
|
27
27
|
def initialize(api, namespace = nil)
|
|
28
28
|
super()
|
|
@@ -138,7 +138,7 @@ module Aspera
|
|
|
138
138
|
end
|
|
139
139
|
end
|
|
140
140
|
|
|
141
|
-
# Derive an AES key from
|
|
141
|
+
# Derive an AES key from `new_password`.
|
|
142
142
|
# @legacy_key is true for vaults created by v4.26.0 or earlier (no kdf field):
|
|
143
143
|
# key = password truncated/zero-padded to key_bytes (same as symmetric-encryption did).
|
|
144
144
|
# Otherwise PBKDF2 is used. When @kdf_params is nil (new vault or password change)
|
|
@@ -181,7 +181,7 @@ module Aspera
|
|
|
181
181
|
'kdf' => @kdf_params,
|
|
182
182
|
'data' => @cipher.encrypt(YAML.dump(@all_secrets))
|
|
183
183
|
}
|
|
184
|
-
|
|
184
|
+
Environment.write_file_restricted(@path, force: true) { YAML.dump(vault_info) }
|
|
185
185
|
end
|
|
186
186
|
end
|
|
187
187
|
end
|
|
@@ -18,7 +18,8 @@ module Aspera
|
|
|
18
18
|
Aspera.assert_values(vault_type, LIST.map(&:to_s)) { 'vault.type' }
|
|
19
19
|
case vault_type
|
|
20
20
|
when 'file'
|
|
21
|
-
|
|
21
|
+
# `name` (or `file`), default: `vault.bin`
|
|
22
|
+
info[:file] = info.delete(:name) || info[:file] || 'vault.bin'
|
|
22
23
|
info[:file] = File.join(folder, info[:file]) unless File.absolute_path?(info[:file])
|
|
23
24
|
Aspera.assert(!password.nil?, 'please provide password')
|
|
24
25
|
info[:password] = password
|
data/lib/aspera/link_header.rb
CHANGED
|
@@ -31,7 +31,7 @@ module Aspera
|
|
|
31
31
|
@links = links
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# Return the href of the first link whose
|
|
34
|
+
# Return the href of the first link whose `rel` attribute matches `rel`.
|
|
35
35
|
# Comparison is case-insensitive per RFC 7230 s.3.2 and RFC 8288 s.3.
|
|
36
36
|
# Returns nil if no link with that relation exists.
|
|
37
37
|
# @param rel [String]
|
|
@@ -41,7 +41,7 @@ module Aspera
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
class << self
|
|
44
|
-
# Parse a raw Link header value into a
|
|
44
|
+
# Parse a raw Link header value into a `LinkHeader` instance.
|
|
45
45
|
# Uses StringScanner so that commas inside <URI> are not treated as separators.
|
|
46
46
|
# @param raw [String, nil]
|
|
47
47
|
# @return [LinkHeader]
|
data/lib/aspera/log.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'aspera/assert'
|
|
4
|
-
require 'aspera/colors'
|
|
5
4
|
require 'aspera/secret_hider'
|
|
6
5
|
require 'logger'
|
|
7
6
|
require 'pp'
|
|
8
7
|
require 'json'
|
|
9
8
|
require 'singleton'
|
|
10
9
|
require 'stringio'
|
|
10
|
+
require 'aspera/rainbow'
|
|
11
|
+
using Rainbow
|
|
11
12
|
|
|
12
13
|
# Ignore warnings
|
|
13
14
|
old_verbose = $VERBOSE
|
|
@@ -67,6 +68,26 @@ module Aspera
|
|
|
67
68
|
# Where logs are sent to
|
|
68
69
|
LOG_TYPES = %i[stderr stdout syslog].freeze
|
|
69
70
|
|
|
71
|
+
# Logger device for a terminal: the status line (e.g. progress bar) is suspended while a log line is written
|
|
72
|
+
class TerminalDevice
|
|
73
|
+
# @param io [IO] Output stream
|
|
74
|
+
def initialize(io)
|
|
75
|
+
@io = io
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Called by `Logger`
|
|
79
|
+
# @param message [String] Formatted log line
|
|
80
|
+
def write(message)
|
|
81
|
+
status_line = Log.instance.status_line if @io.tty?
|
|
82
|
+
return @io.write(message) if status_line.nil?
|
|
83
|
+
status_line.suspend { @io.write(message) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Standard streams are not closed
|
|
87
|
+
def close; end
|
|
88
|
+
end
|
|
89
|
+
private_constant :TerminalDevice
|
|
90
|
+
|
|
70
91
|
# Levels are :trace2,:trace1,:debug,:info,:warn,:error,fatal,:unknown
|
|
71
92
|
LEVELS = Logger::Severity.constants.sort { |a, b| Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b) }.map { |c| c.downcase.to_sym }.freeze
|
|
72
93
|
|
|
@@ -118,13 +139,13 @@ module Aspera
|
|
|
118
139
|
|
|
119
140
|
# Returns the last 2 containers (module/class) and method caller
|
|
120
141
|
def caller_method
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
142
|
+
locations = caller_locations
|
|
143
|
+
# Outermost frame of logging code: Ruby logger, or this file (level methods, dump)
|
|
144
|
+
i = locations.rindex { |loc| loc.path.end_with?('/logger.rb') || loc.path.eql?(__FILE__) }
|
|
145
|
+
frame = locations[i + 1] if i
|
|
124
146
|
return '???' unless frame
|
|
125
|
-
#
|
|
126
|
-
full = frame
|
|
127
|
-
return '???' unless full
|
|
147
|
+
# "Class::Module#method" (Ruby >= 3.4) or "method" (Ruby < 3.4)
|
|
148
|
+
full = frame.label
|
|
128
149
|
# Split into class/module and method parts
|
|
129
150
|
parts = full.split(/(::|#)/)
|
|
130
151
|
# Reconstruct keeping only last two class/module names + separator + method
|
|
@@ -138,12 +159,15 @@ module Aspera
|
|
|
138
159
|
end
|
|
139
160
|
class_parts = classes.split('::')
|
|
140
161
|
selected_classes = class_parts.last(2).join('::')
|
|
162
|
+
return method if selected_classes.empty?
|
|
141
163
|
"#{selected_classes}.#{method}"
|
|
142
164
|
end
|
|
143
165
|
end
|
|
144
166
|
|
|
145
167
|
attr_reader :logger_type, :logger
|
|
146
168
|
attr_accessor :dump_format
|
|
169
|
+
# [#suspend, nil] Object displaying a status line on the terminal (e.g. progress bar), see TerminalDevice
|
|
170
|
+
attr_accessor :status_line
|
|
147
171
|
|
|
148
172
|
# Set the program name used in log output
|
|
149
173
|
# @param value [String] Program name
|
|
@@ -195,9 +219,9 @@ module Aspera
|
|
|
195
219
|
current_severity_integer = @logger&.level || ENV['AS_LOG_LEVEL']&.to_i || Logger::Severity::INFO
|
|
196
220
|
case new_log_type
|
|
197
221
|
when :stderr
|
|
198
|
-
@logger = Logger.new($stderr, progname: @program_name, formatter: DEFAULT_FORMATTER)
|
|
222
|
+
@logger = Logger.new(TerminalDevice.new($stderr), progname: @program_name, formatter: DEFAULT_FORMATTER)
|
|
199
223
|
when :stdout
|
|
200
|
-
@logger = Logger.new($stdout, progname: @program_name, formatter: DEFAULT_FORMATTER)
|
|
224
|
+
@logger = Logger.new(TerminalDevice.new($stdout), progname: @program_name, formatter: DEFAULT_FORMATTER)
|
|
201
225
|
when :syslog
|
|
202
226
|
require 'syslog/logger'
|
|
203
227
|
# The syslog class automatically creates methods from the severity names.
|
|
@@ -223,6 +247,7 @@ module Aspera
|
|
|
223
247
|
|
|
224
248
|
def initialize
|
|
225
249
|
@logger = nil
|
|
250
|
+
@status_line = nil
|
|
226
251
|
@program_name = 'aspera'
|
|
227
252
|
@dump_format = :json
|
|
228
253
|
@logger_type = :stderr
|
|
@@ -230,34 +255,29 @@ module Aspera
|
|
|
230
255
|
self.logger_type = @logger_type
|
|
231
256
|
end
|
|
232
257
|
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
TRACE2:
|
|
236
|
-
TRACE1:
|
|
237
|
-
DEBUG:
|
|
238
|
-
INFO:
|
|
239
|
-
WARN:
|
|
240
|
-
ERROR:
|
|
241
|
-
FATAL:
|
|
242
|
-
UNKNOWN:
|
|
258
|
+
# Short (4-letters) levels with color (evaluated on each call, as colors can be enabled or disabled by option)
|
|
259
|
+
LVL_COLOR = {
|
|
260
|
+
TRACE2: -> { short_levl(:TRACE2).faint },
|
|
261
|
+
TRACE1: -> { short_levl(:TRACE1).blue },
|
|
262
|
+
DEBUG: -> { short_levl(:DEBUG).cyan },
|
|
263
|
+
INFO: -> { short_levl(:INFO).green },
|
|
264
|
+
WARN: -> { short_levl(:WARN).bg(:yellow).black },
|
|
265
|
+
ERROR: -> { short_levl(:ERROR).bg(:red).blink },
|
|
266
|
+
FATAL: -> { short_levl(:FATAL).magenta },
|
|
267
|
+
UNKNOWN: -> { short_levl(:UNKNOWN).blink }
|
|
243
268
|
}.freeze
|
|
244
269
|
|
|
245
|
-
|
|
246
|
-
LVL_COLOR = LVL_DECO.to_h do |k, v|
|
|
247
|
-
[k, short_levl(k).apply(*v)]
|
|
248
|
-
end.freeze
|
|
249
|
-
|
|
250
|
-
DEFAULT_FORMATTER = ->(s, _d, _p, m) { "#{LVL_COLOR[s]} #{m}\n" }
|
|
270
|
+
DEFAULT_FORMATTER = ->(s, _d, _p, m) { "#{LVL_COLOR[s]&.call} #{m}\n" }
|
|
251
271
|
|
|
252
272
|
# pre-defined formatters
|
|
253
273
|
FORMATTER_LAMBDAS = {
|
|
254
274
|
standard: Logger::Formatter.new,
|
|
255
275
|
default: DEFAULT_FORMATTER,
|
|
256
|
-
caller: ->(s, _d, _p, m) { "#{LVL_COLOR[s]} #{Log.caller_method}\n#{m}\n" }
|
|
276
|
+
caller: ->(s, _d, _p, m) { "#{LVL_COLOR[s]&.call} #{Log.caller_method}\n#{m}\n" }
|
|
257
277
|
}.freeze
|
|
258
278
|
|
|
259
279
|
FORMATTERS = FORMATTER_LAMBDAS.keys
|
|
260
280
|
|
|
261
|
-
private_constant :
|
|
281
|
+
private_constant :DEFAULT_FORMATTER, :FORMATTER_LAMBDAS
|
|
262
282
|
end
|
|
263
283
|
end
|
data/lib/aspera/markdown.rb
CHANGED
|
@@ -82,6 +82,8 @@ module Aspera
|
|
|
82
82
|
col_widths = table.transpose.map do |col|
|
|
83
83
|
[col.flat_map { |c| c.to_s.delete('`').split(HTML_BREAK).map(&:size) }.max, COL_WIDTH].min
|
|
84
84
|
end
|
|
85
|
+
# do not modify caller's table
|
|
86
|
+
table = table.dup
|
|
85
87
|
headings = table.shift
|
|
86
88
|
table.unshift(col_widths.map { |col_width| '-' * col_width })
|
|
87
89
|
table.unshift(headings)
|
data/lib/aspera/mime.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aspera
|
|
4
|
+
# MIME types used in `Content-Type` and `Accept`
|
|
5
|
+
module Mime
|
|
6
|
+
# JSON body
|
|
7
|
+
JSON = 'application/json'
|
|
8
|
+
# URL encoded form body
|
|
9
|
+
WWW = 'application/x-www-form-urlencoded'
|
|
10
|
+
# Plain text body
|
|
11
|
+
TEXT = 'text/plain'
|
|
12
|
+
# JSON:API body (https://jsonapi.org)
|
|
13
|
+
JSON_API = 'application/vnd.api+json'
|
|
14
|
+
# Check if a media type is JSON: `application/json` or structured syntax suffix `+json` (RFC 6839)
|
|
15
|
+
# @param mime [String] Media type, without parameters (see `Rest.parse_header`)
|
|
16
|
+
# @return [Boolean] `true` if JSON
|
|
17
|
+
def json?(mime) = mime == JSON || mime.end_with?(JSON_SUFFIX) || mime == LEGACY_JSON
|
|
18
|
+
module_function :json?
|
|
19
|
+
# Structured syntax suffix for JSON
|
|
20
|
+
JSON_SUFFIX = '+json'
|
|
21
|
+
# Non standard type used by some servers for JSON
|
|
22
|
+
LEGACY_JSON = 'application/x-javascript'
|
|
23
|
+
private_constant :JSON_SUFFIX, :LEGACY_JSON
|
|
24
|
+
end
|
|
25
|
+
end
|