aspera-cli 4.24.1 → 4.25.0.pre
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 +1064 -745
- data/CONTRIBUTING.md +43 -100
- data/README.md +1281 -720
- data/bin/ascli +20 -1
- data/bin/asession +23 -27
- data/lib/aspera/agent/base.rb +10 -21
- data/lib/aspera/agent/connect.rb +2 -3
- data/lib/aspera/agent/desktop.rb +2 -2
- data/lib/aspera/agent/direct.rb +49 -32
- data/lib/aspera/agent/factory.rb +31 -0
- data/lib/aspera/api/aoc.rb +134 -76
- data/lib/aspera/api/cos_node.rb +3 -2
- data/lib/aspera/api/faspex.rb +213 -0
- data/lib/aspera/api/node.rb +107 -94
- data/lib/aspera/ascmd.rb +1 -2
- data/lib/aspera/ascp/installation.rb +73 -58
- data/lib/aspera/ascp/management.rb +119 -23
- data/lib/aspera/assert.rb +39 -11
- data/lib/aspera/cli/error.rb +4 -2
- data/lib/aspera/cli/extended_value.rb +91 -67
- data/lib/aspera/cli/formatter.rb +62 -27
- data/lib/aspera/cli/hints.rb +8 -0
- data/lib/aspera/cli/info.rb +4 -4
- data/lib/aspera/cli/main.rb +76 -84
- data/lib/aspera/cli/manager.rb +352 -248
- data/lib/aspera/cli/plugins/alee.rb +5 -4
- data/lib/aspera/cli/plugins/aoc.rb +175 -195
- data/lib/aspera/cli/plugins/ats.rb +4 -4
- data/lib/aspera/cli/plugins/base.rb +343 -0
- data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
- data/lib/aspera/cli/plugins/config.rb +283 -269
- data/lib/aspera/cli/plugins/console.rb +27 -22
- data/lib/aspera/cli/plugins/cos.rb +3 -3
- data/lib/aspera/cli/plugins/factory.rb +78 -0
- data/lib/aspera/cli/plugins/faspex.rb +49 -46
- data/lib/aspera/cli/plugins/faspex5.rb +113 -225
- data/lib/aspera/cli/plugins/faspio.rb +19 -18
- data/lib/aspera/cli/plugins/httpgw.rb +14 -13
- data/lib/aspera/cli/plugins/node.rb +162 -149
- data/lib/aspera/cli/plugins/oauth.rb +48 -0
- data/lib/aspera/cli/plugins/orchestrator.rb +129 -45
- data/lib/aspera/cli/plugins/preview.rb +30 -50
- data/lib/aspera/cli/plugins/server.rb +21 -21
- data/lib/aspera/cli/plugins/shares.rb +45 -47
- data/lib/aspera/cli/sync_actions.rb +50 -39
- data/lib/aspera/cli/transfer_agent.rb +35 -49
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +3 -3
- data/lib/aspera/cli/wizard.rb +70 -55
- data/lib/aspera/colors.rb +6 -0
- data/lib/aspera/command_line_builder.rb +59 -61
- data/lib/aspera/command_line_converter.rb +2 -1
- data/lib/aspera/coverage.rb +2 -2
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +51 -41
- data/lib/aspera/faspex_gw.rb +7 -5
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/keychain/factory.rb +1 -2
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +37 -9
- data/lib/aspera/markdown.rb +31 -0
- data/lib/aspera/nagios.rb +7 -6
- data/lib/aspera/oauth/base.rb +25 -28
- data/lib/aspera/oauth/factory.rb +9 -9
- data/lib/aspera/oauth/url_json.rb +2 -1
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/preview/file_types.rb +23 -37
- data/lib/aspera/products/connect.rb +7 -6
- data/lib/aspera/products/desktop.rb +1 -4
- data/lib/aspera/products/other.rb +9 -1
- data/lib/aspera/products/transferd.rb +0 -1
- data/lib/aspera/rest.rb +168 -113
- data/lib/aspera/rest_error_analyzer.rb +4 -4
- data/lib/aspera/ssh.rb +7 -4
- data/lib/aspera/ssl.rb +41 -0
- data/lib/aspera/sync/args.schema.yaml +46 -3
- data/lib/aspera/sync/conf.schema.yaml +307 -123
- data/lib/aspera/sync/database.rb +2 -1
- data/lib/aspera/sync/operations.rb +135 -79
- data/lib/aspera/temp_file_manager.rb +17 -5
- data/lib/aspera/transfer/error.rb +16 -7
- data/lib/aspera/transfer/parameters.rb +35 -22
- data/lib/aspera/transfer/resumer.rb +74 -0
- data/lib/aspera/transfer/spec.rb +5 -5
- data/lib/aspera/transfer/spec.schema.yaml +170 -59
- data/lib/aspera/transfer/spec_doc.rb +49 -43
- data/lib/aspera/uri_reader.rb +2 -2
- data/lib/aspera/web_auth.rb +6 -6
- data/lib/transferd_pb.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +26 -11
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
- data/lib/aspera/cli/plugin.rb +0 -333
- data/lib/aspera/cli/plugin_factory.rb +0 -81
- data/lib/aspera/resumer.rb +0 -77
- data/lib/aspera/transfer/error_info.rb +0 -91
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
require 'aspera/api/alee'
|
|
4
4
|
require 'aspera/nagios'
|
|
5
|
+
require 'aspera/cli/plugins/basic_auth'
|
|
5
6
|
|
|
6
7
|
module Aspera
|
|
7
8
|
module Cli
|
|
8
9
|
module Plugins
|
|
9
|
-
class Alee <
|
|
10
|
+
class Alee < BasicAuth
|
|
10
11
|
ACTIONS = %i[health entitlement].freeze
|
|
11
12
|
|
|
12
13
|
def execute_action
|
|
@@ -16,13 +17,13 @@ module Aspera
|
|
|
16
17
|
nagios = Nagios.new
|
|
17
18
|
begin
|
|
18
19
|
api = Api::Alee.new(nil, nil, version: 'ping')
|
|
19
|
-
|
|
20
|
-
raise "unexpected response: #{
|
|
20
|
+
http = api.read(nil, ret: :resp)
|
|
21
|
+
raise "unexpected response: #{http.body}" unless http.body.eql?('pong')
|
|
21
22
|
nagios.add_ok('api', 'answered ok')
|
|
22
23
|
rescue StandardError => e
|
|
23
24
|
nagios.add_critical('api', e.to_s)
|
|
24
25
|
end
|
|
25
|
-
|
|
26
|
+
Main.result_object_list(nagios.status_list)
|
|
26
27
|
when :entitlement
|
|
27
28
|
entitlement_id = options.get_option(:username, mandatory: true)
|
|
28
29
|
customer_id = options.get_option(:password, mandatory: true)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'aspera/cli/plugins/oauth'
|
|
3
4
|
require 'aspera/cli/plugins/node'
|
|
4
5
|
require 'aspera/cli/plugins/ats'
|
|
5
|
-
require 'aspera/cli/basic_auth_plugin'
|
|
6
6
|
require 'aspera/cli/transfer_agent'
|
|
7
7
|
require 'aspera/cli/special_values'
|
|
8
|
+
require 'aspera/cli/wizard'
|
|
8
9
|
require 'aspera/agent/node'
|
|
9
10
|
require 'aspera/transfer/spec'
|
|
10
11
|
require 'aspera/api/aoc'
|
|
@@ -18,11 +19,9 @@ require 'date'
|
|
|
18
19
|
module Aspera
|
|
19
20
|
module Cli
|
|
20
21
|
module Plugins
|
|
21
|
-
class Aoc <
|
|
22
|
+
class Aoc < Oauth
|
|
22
23
|
# default redirect for AoC web auth
|
|
23
24
|
REDIRECT_LOCALHOST = 'http://localhost:12345'
|
|
24
|
-
# OAuth methods supported
|
|
25
|
-
STD_AUTH_TYPES = %i[web jwt].freeze
|
|
26
25
|
# admin objects that can be manipulated
|
|
27
26
|
ADMIN_OBJECTS = %i[
|
|
28
27
|
self
|
|
@@ -57,7 +56,7 @@ module Aspera
|
|
|
57
56
|
# options and parameters for Api::AoC.new
|
|
58
57
|
OPTIONS_NEW = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password workspace].freeze
|
|
59
58
|
|
|
60
|
-
private_constant :REDIRECT_LOCALHOST, :
|
|
59
|
+
private_constant :REDIRECT_LOCALHOST, :ADMIN_OBJECTS, :PACKAGE_RECEIVED_BASE_QUERY, :OPTIONS_NEW, :PACKAGE_LIST_DEFAULT_FIELDS
|
|
61
60
|
class << self
|
|
62
61
|
def application_name
|
|
63
62
|
'Aspera on Cloud'
|
|
@@ -70,9 +69,9 @@ module Aspera
|
|
|
70
69
|
base_url = "#{base_url}.#{Api::AoC::SAAS_DOMAIN_PROD}" unless base_url.include?('.')
|
|
71
70
|
# AoC is only https
|
|
72
71
|
return unless base_url.start_with?('https://')
|
|
73
|
-
|
|
74
|
-
return if
|
|
75
|
-
redirect_uri = URI.parse(
|
|
72
|
+
location = Rest.new(base_url: base_url, redirect_max: 0).call(operation: 'GET', subpath: 'auth/ping', exception: false, ret: :resp)['Location']
|
|
73
|
+
return if location.nil?
|
|
74
|
+
redirect_uri = URI.parse(location)
|
|
76
75
|
od = Api::AoC.split_org_domain(URI.parse(base_url))
|
|
77
76
|
return unless redirect_uri.path.end_with?("oauth2/#{od[:organization]}/login")
|
|
78
77
|
# either in standard domain, or product name in page
|
|
@@ -82,108 +81,6 @@ module Aspera
|
|
|
82
81
|
}
|
|
83
82
|
end
|
|
84
83
|
|
|
85
|
-
# @param url [String] url to check
|
|
86
|
-
# @return [Bool] true if private key is required for the url (i.e. no passcode)
|
|
87
|
-
def private_key_required?(url)
|
|
88
|
-
# pub link do not need private key
|
|
89
|
-
return Api::AoC.link_info(url)[:token].nil?
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# @param object [Plugin] An instance of this class
|
|
93
|
-
# @param private_key_path [String] path to private key
|
|
94
|
-
# @param pub_key_pem [String] PEM of public key
|
|
95
|
-
# @return [Hash] :preset_value, :test_args
|
|
96
|
-
def wizard(object:, private_key_path: nil, pub_key_pem: nil)
|
|
97
|
-
# set vars to look like object
|
|
98
|
-
options = object.options
|
|
99
|
-
formatter = object.formatter
|
|
100
|
-
instance_url = options.get_option(:url, mandatory: true)
|
|
101
|
-
pub_link_info = Api::AoC.link_info(instance_url)
|
|
102
|
-
if !pub_link_info[:token].nil?
|
|
103
|
-
pub_api = Rest.new(base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1")
|
|
104
|
-
pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})
|
|
105
|
-
preset_value = {
|
|
106
|
-
link: instance_url
|
|
107
|
-
}
|
|
108
|
-
preset_value[:password] = options.get_option(:password, mandatory: true) if pub_info['password_protected']
|
|
109
|
-
return {
|
|
110
|
-
preset_value: preset_value,
|
|
111
|
-
test_args: 'organization'
|
|
112
|
-
}
|
|
113
|
-
end
|
|
114
|
-
options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: Api::AoC.saas_url?(instance_url))
|
|
115
|
-
options.parse_options!
|
|
116
|
-
# make username mandatory for jwt, this triggers interactive input
|
|
117
|
-
wiz_username = options.get_option(:username, mandatory: true)
|
|
118
|
-
raise "Username shall be an email in AoC: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
|
|
119
|
-
# Set the pub key and jwt tag in the user's profile automatically
|
|
120
|
-
auto_set_pub_key = false
|
|
121
|
-
auto_set_jwt = false
|
|
122
|
-
# use browser authentication to bootstrap
|
|
123
|
-
use_browser_authentication = false
|
|
124
|
-
if options.get_option(:use_generic_client)
|
|
125
|
-
formatter.display_status('Using global client_id.')
|
|
126
|
-
formatter.display_status('Please Login to your Aspera on Cloud instance.')
|
|
127
|
-
formatter.display_status('Navigate to: 👤 → Account Settings → Profile → Public Key')
|
|
128
|
-
formatter.display_status('Check or update the value to:'.red.blink)
|
|
129
|
-
formatter.display_status(pub_key_pem, hide_secrets: false)
|
|
130
|
-
if !options.get_option(:test_mode)
|
|
131
|
-
formatter.display_status('Once updated or validated, press enter.')
|
|
132
|
-
Environment.instance.open_uri(instance_url)
|
|
133
|
-
$stdin.gets
|
|
134
|
-
end
|
|
135
|
-
else
|
|
136
|
-
formatter.display_status('Using organization specific client_id.')
|
|
137
|
-
if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
|
|
138
|
-
formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
|
|
139
|
-
formatter.display_status('Navigate to: 𓃑 → Admin → Integrations → API Clients')
|
|
140
|
-
formatter.display_status('Check or create in integration:')
|
|
141
|
-
formatter.display_status('- name: cli')
|
|
142
|
-
formatter.display_status("- redirect uri: #{REDIRECT_LOCALHOST}")
|
|
143
|
-
formatter.display_status('- origin: localhost')
|
|
144
|
-
formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
|
|
145
|
-
end
|
|
146
|
-
Environment.instance.open_uri("#{instance_url}/admin/integrations/api-clients")
|
|
147
|
-
options.get_option(:client_id, mandatory: true)
|
|
148
|
-
options.get_option(:client_secret, mandatory: true)
|
|
149
|
-
# use_browser_authentication = true
|
|
150
|
-
end
|
|
151
|
-
if use_browser_authentication
|
|
152
|
-
formatter.display_status('We will use web authentication to bootstrap.')
|
|
153
|
-
auto_set_pub_key = true
|
|
154
|
-
auto_set_jwt = true
|
|
155
|
-
Aspera.error_not_implemented
|
|
156
|
-
# aoc_api.oauth.grant_method = :web
|
|
157
|
-
# aoc_api.oauth.scope = Api::AoC::SCOPE_FILES_ADMIN
|
|
158
|
-
# aoc_api.oauth.specific_parameters[:redirect_uri] = REDIRECT_LOCALHOST
|
|
159
|
-
end
|
|
160
|
-
myself = object.aoc_api.read('self')
|
|
161
|
-
if auto_set_pub_key
|
|
162
|
-
Aspera.assert(myself['public_key'].empty?, type: Cli::Error){'Public key is already set in profile (use --override=yes)'} unless option_override
|
|
163
|
-
formatter.display_status('Updating profile with the public key.')
|
|
164
|
-
aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
|
|
165
|
-
end
|
|
166
|
-
if auto_set_jwt
|
|
167
|
-
formatter.display_status('Enabling JWT for client')
|
|
168
|
-
aoc_api.update("clients/#{options.get_option(:client_id)}", {'jwt_grant_enabled' => true, 'explicit_authorization_required' => false})
|
|
169
|
-
end
|
|
170
|
-
preset_result = {
|
|
171
|
-
url: instance_url,
|
|
172
|
-
username: myself['email'],
|
|
173
|
-
auth: :jwt.to_s,
|
|
174
|
-
private_key: "@file:#{private_key_path}"
|
|
175
|
-
}
|
|
176
|
-
# set only if non nil
|
|
177
|
-
%i[client_id client_secret].each do |s|
|
|
178
|
-
o = options.get_option(s)
|
|
179
|
-
preset_result[s.to_s] = o unless o.nil?
|
|
180
|
-
end
|
|
181
|
-
return {
|
|
182
|
-
preset_value: preset_result,
|
|
183
|
-
test_args: 'user profile show'
|
|
184
|
-
}
|
|
185
|
-
end
|
|
186
|
-
|
|
187
84
|
# @param base [String] Base folder path
|
|
188
85
|
# @return [String] Folder path that does jot exist, with possible .<number> extension
|
|
189
86
|
def next_available_folder(base, always: false)
|
|
@@ -217,44 +114,123 @@ module Aspera
|
|
|
217
114
|
end
|
|
218
115
|
end
|
|
219
116
|
|
|
117
|
+
# @param wizard [Wizard] The wizard object
|
|
118
|
+
# @param app_url [Wizard] The wizard object
|
|
119
|
+
# @return [Hash] :preset_value, :test_args
|
|
120
|
+
def wizard(wizard, app_url)
|
|
121
|
+
pub_link_info = Api::AoC.link_info(app_url)
|
|
122
|
+
# public link case
|
|
123
|
+
if pub_link_info.key?(:token)
|
|
124
|
+
pub_api = Rest.new(base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1")
|
|
125
|
+
pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})
|
|
126
|
+
preset_value = {
|
|
127
|
+
link: app_url
|
|
128
|
+
}
|
|
129
|
+
preset_value[:password] = options.get_option(:password, mandatory: true) if pub_info['password_protected']
|
|
130
|
+
return {
|
|
131
|
+
preset_value: preset_value,
|
|
132
|
+
test_args: 'organization'
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', allowed: Allowed::TYPES_BOOLEAN, default: Api::AoC.saas_url?(app_url))
|
|
136
|
+
options.parse_options!
|
|
137
|
+
# make username mandatory for jwt, this triggers interactive input
|
|
138
|
+
wiz_username = options.get_option(:username, mandatory: true)
|
|
139
|
+
wizard.check_email(wiz_username)
|
|
140
|
+
# Set the pub key and jwt tag in the user's profile automatically
|
|
141
|
+
auto_set_pub_key = false
|
|
142
|
+
auto_set_jwt = false
|
|
143
|
+
# use browser authentication to bootstrap
|
|
144
|
+
use_browser_authentication = false
|
|
145
|
+
private_key_path = wizard.ask_private_key(
|
|
146
|
+
user: wiz_username,
|
|
147
|
+
url: app_url,
|
|
148
|
+
page: '👤 → Account Settings → Profile → Public Key'
|
|
149
|
+
)
|
|
150
|
+
client_id = options.get_option(:client_id)
|
|
151
|
+
client_secret = options.get_option(:client_secret)
|
|
152
|
+
if client_id.nil? || client_secret.nil?
|
|
153
|
+
if options.get_option(:use_generic_client)
|
|
154
|
+
client_id = client_secret = nil
|
|
155
|
+
formatter.display_status('Using global client_id.')
|
|
156
|
+
else
|
|
157
|
+
formatter.display_status('Using organization specific client_id.')
|
|
158
|
+
formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
|
|
159
|
+
formatter.display_status('Navigate to: 𓃑 → Admin → Integrations → API Clients')
|
|
160
|
+
formatter.display_status('Check or create in integration:')
|
|
161
|
+
formatter.display_status('- name: cli')
|
|
162
|
+
formatter.display_status("- redirect uri: #{REDIRECT_LOCALHOST}")
|
|
163
|
+
formatter.display_status('- origin: localhost')
|
|
164
|
+
formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
|
|
165
|
+
Environment.instance.open_uri("#{app_url}/admin/integrations/api-clients")
|
|
166
|
+
client_id = options.get_option(:client_id, mandatory: true)
|
|
167
|
+
client_secret = options.get_option(:client_secret, mandatory: true)
|
|
168
|
+
# use_browser_authentication = true
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
if use_browser_authentication
|
|
172
|
+
formatter.display_status('We will use web authentication to bootstrap.')
|
|
173
|
+
auto_set_pub_key = true
|
|
174
|
+
auto_set_jwt = true
|
|
175
|
+
Aspera.error_not_implemented
|
|
176
|
+
# aoc_api.oauth.grant_method = :web
|
|
177
|
+
# aoc_api.oauth.scope = Api::AoC::SCOPE_FILES_ADMIN
|
|
178
|
+
# aoc_api.oauth.specific_parameters[:redirect_uri] = REDIRECT_LOCALHOST
|
|
179
|
+
end
|
|
180
|
+
myself = aoc_api.read('self')
|
|
181
|
+
if auto_set_pub_key
|
|
182
|
+
Aspera.assert(myself['public_key'].empty?, type: Cli::Error){'Public key is already set in profile (use --override=yes)'} unless option_override
|
|
183
|
+
formatter.display_status('Updating profile with the public key.')
|
|
184
|
+
aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
|
|
185
|
+
end
|
|
186
|
+
if auto_set_jwt
|
|
187
|
+
formatter.display_status('Enabling JWT for client')
|
|
188
|
+
aoc_api.update("clients/#{options.get_option(:client_id)}", {'jwt_grant_enabled' => true, 'explicit_authorization_required' => false})
|
|
189
|
+
end
|
|
190
|
+
return {
|
|
191
|
+
preset_value: {
|
|
192
|
+
url: app_url,
|
|
193
|
+
username: myself['email'],
|
|
194
|
+
auth: :jwt.to_s,
|
|
195
|
+
private_key: "@file:#{private_key_path}",
|
|
196
|
+
client_id: client_id,
|
|
197
|
+
client_secret: client_secret
|
|
198
|
+
}.compact,
|
|
199
|
+
test_args: 'user profile show'
|
|
200
|
+
}
|
|
201
|
+
end
|
|
202
|
+
|
|
220
203
|
def initialize(**_)
|
|
221
204
|
super
|
|
222
205
|
@cache_workspace_info = nil
|
|
223
206
|
@cache_home_node_file = nil
|
|
224
207
|
@cache_api_aoc = nil
|
|
225
|
-
options.declare(:
|
|
226
|
-
options.declare(:
|
|
227
|
-
options.declare(:
|
|
228
|
-
options.declare(:
|
|
229
|
-
options.declare(:redirect_uri, 'OAuth API client redirect URI')
|
|
230
|
-
options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
|
|
231
|
-
options.declare(:passphrase, 'RSA private key passphrase', types: String)
|
|
232
|
-
options.declare(:workspace, 'Name of workspace', types: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE)
|
|
233
|
-
options.declare(:new_user_option, 'New user creation option for unknown package recipients', types: Hash)
|
|
234
|
-
options.declare(:validate_metadata, 'Validate shared inbox metadata', values: :bool, default: true)
|
|
235
|
-
options.declare(:package_folder, 'Field of package to use as folder name, or @none:', types: [String, NilClass])
|
|
208
|
+
options.declare(:workspace, 'Name of workspace', allowed: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE)
|
|
209
|
+
options.declare(:new_user_option, 'New user creation option for unknown package recipients', allowed: Hash)
|
|
210
|
+
options.declare(:validate_metadata, 'Validate shared inbox metadata', allowed: Allowed::TYPES_BOOLEAN, default: true)
|
|
211
|
+
options.declare(:package_folder, 'Field of package to use as folder name, or @none:', allowed: [String, NilClass])
|
|
236
212
|
options.parse_options!
|
|
237
213
|
# add node plugin options (for manual)
|
|
238
214
|
Node.declare_options(options)
|
|
239
215
|
end
|
|
240
216
|
|
|
241
217
|
def api_from_options(aoc_base_path)
|
|
242
|
-
create_values = OPTIONS_NEW.each_with_object({
|
|
243
|
-
subpath: aoc_base_path,
|
|
244
|
-
secret_finder: config
|
|
245
|
-
}) do |i, m|
|
|
246
|
-
m[i] = options.get_option(i) unless options.get_option(i).nil?
|
|
247
|
-
end
|
|
248
|
-
create_values[:scope] = Api::AoC::SCOPE_FILES_USER if create_values[:scope].nil?
|
|
249
218
|
# create an API object with the same options, but with a different subpath
|
|
250
|
-
return
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
219
|
+
return new_with_options(
|
|
220
|
+
Api::AoC,
|
|
221
|
+
base: {
|
|
222
|
+
subpath: aoc_base_path,
|
|
223
|
+
secret_finder: config
|
|
224
|
+
},
|
|
225
|
+
add: {
|
|
226
|
+
scope: Api::AoC::SCOPE_FILES_USER,
|
|
227
|
+
workspace: nil
|
|
228
|
+
}
|
|
229
|
+
)
|
|
256
230
|
end
|
|
257
231
|
|
|
232
|
+
# AoC Rest object
|
|
233
|
+
# @return [Rest]
|
|
258
234
|
def aoc_api
|
|
259
235
|
if @cache_api_aoc.nil?
|
|
260
236
|
@cache_api_aoc = api_from_options(Api::AoC::API_V1)
|
|
@@ -300,56 +276,11 @@ module Aspera
|
|
|
300
276
|
return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
|
|
301
277
|
end
|
|
302
278
|
|
|
303
|
-
# Call block with same query using paging and response information
|
|
304
|
-
# block must return a hash with :data and :http keys
|
|
305
|
-
# @return [Hash] {items: , total: }
|
|
306
|
-
def api_call_paging(base_query = {})
|
|
307
|
-
Aspera.assert_type(base_query, Hash){'query'}
|
|
308
|
-
Aspera.assert(block_given?)
|
|
309
|
-
# set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
|
|
310
|
-
base_query['per_page'] = 1000 unless base_query.key?('per_page')
|
|
311
|
-
max_items = base_query.delete(MAX_ITEMS)
|
|
312
|
-
max_pages = base_query.delete(MAX_PAGES)
|
|
313
|
-
item_list = []
|
|
314
|
-
total_count = nil
|
|
315
|
-
current_page = base_query['page']
|
|
316
|
-
current_page = 1 if current_page.nil?
|
|
317
|
-
page_count = 0
|
|
318
|
-
loop do
|
|
319
|
-
query = base_query.clone
|
|
320
|
-
query['page'] = current_page
|
|
321
|
-
result = yield(query)
|
|
322
|
-
Aspera.assert(result[:data])
|
|
323
|
-
Aspera.assert(result[:http])
|
|
324
|
-
total_count = result[:http]['X-Total-Count']
|
|
325
|
-
page_count += 1
|
|
326
|
-
current_page += 1
|
|
327
|
-
add_items = result[:data]
|
|
328
|
-
break if add_items.empty?
|
|
329
|
-
# append new items to full list
|
|
330
|
-
item_list += add_items
|
|
331
|
-
break if !max_items.nil? && item_list.count >= max_items
|
|
332
|
-
break if !max_pages.nil? && page_count >= max_pages
|
|
333
|
-
formatter.long_operation_running("#{item_list.count} / #{total_count}") unless total_count.eql?(item_list.count.to_s)
|
|
334
|
-
end
|
|
335
|
-
formatter.long_operation_terminated
|
|
336
|
-
item_list = item_list[0..max_items - 1] if !max_items.nil? && item_list.count > max_items
|
|
337
|
-
return {items: item_list, total: total_count}
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
# read using the query and paging
|
|
341
|
-
# @return [Hash] {data: , total: }
|
|
342
|
-
def api_read_all(resource_class_path, base_query = {})
|
|
343
|
-
return api_call_paging(base_query) do |query|
|
|
344
|
-
aoc_api.call(operation: 'GET', subpath: resource_class_path, headers: {'Accept' => Rest::MIME_JSON}, query: query)
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
|
|
348
279
|
# List all entities, given additional, default and user's queries
|
|
349
280
|
# @param resource_class_path path to query on API
|
|
350
281
|
# @param fields fields to display
|
|
351
282
|
# @param base_query a query applied always
|
|
352
|
-
# @param default_query default query unless
|
|
283
|
+
# @param default_query default query unless overridden by user
|
|
353
284
|
# @param &block (Optional) calls block with user's or default query
|
|
354
285
|
def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
|
|
355
286
|
Aspera.assert_type(base_query, Hash)
|
|
@@ -357,7 +288,7 @@ module Aspera
|
|
|
357
288
|
query = query_read_delete(default: default_query)
|
|
358
289
|
# caller may add specific modifications or checks to query
|
|
359
290
|
yield(query) if block_given?
|
|
360
|
-
result =
|
|
291
|
+
result = aoc_api.read_with_paging(resource_class_path, base_query.merge(query).compact, formatter: formatter)
|
|
361
292
|
return Main.result_object_list(result[:items], fields: fields, total: result[:total])
|
|
362
293
|
end
|
|
363
294
|
|
|
@@ -382,7 +313,7 @@ module Aspera
|
|
|
382
313
|
Aspera.assert_type(query, Hash){'query'}
|
|
383
314
|
PACKAGE_RECEIVED_BASE_QUERY.each{ |k, v| query[k] = v unless query.key?(k)}
|
|
384
315
|
resolve_dropbox_name_default_ws_id(query)
|
|
385
|
-
return
|
|
316
|
+
return aoc_api.read_with_paging('packages', query.compact, formatter: formatter)
|
|
386
317
|
end
|
|
387
318
|
|
|
388
319
|
NODE4_EXT_COMMANDS = %i[transfer].concat(Node::COMMANDS_GEN4).freeze
|
|
@@ -443,6 +374,7 @@ module Aspera
|
|
|
443
374
|
Aspera.error_unreachable_line
|
|
444
375
|
end
|
|
445
376
|
|
|
377
|
+
# @param resource_type [Symbol] One of ADMIN_OBJECTS
|
|
446
378
|
def execute_resource_action(resource_type)
|
|
447
379
|
# get path on API, resource type is singular, but api is plural
|
|
448
380
|
resource_class_path =
|
|
@@ -460,8 +392,9 @@ module Aspera
|
|
|
460
392
|
global_operations = %i[create list]
|
|
461
393
|
supported_operations = %i[show modify]
|
|
462
394
|
supported_operations.push(:delete, *global_operations) unless singleton_object
|
|
463
|
-
supported_operations.push(:do) if resource_type.eql?(:node)
|
|
395
|
+
supported_operations.push(:do, :bearer_token) if resource_type.eql?(:node)
|
|
464
396
|
supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
|
|
397
|
+
supported_operations.push(:shared_folder, :dropbox) if resource_type.eql?(:workspace)
|
|
465
398
|
command = options.get_next_command(supported_operations)
|
|
466
399
|
# require identifier for non global commands
|
|
467
400
|
if !singleton_object && !global_operations.include?(command)
|
|
@@ -522,9 +455,61 @@ module Aspera
|
|
|
522
455
|
return Main.result_success
|
|
523
456
|
when :do
|
|
524
457
|
command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
|
|
525
|
-
# init context
|
|
526
|
-
aoc_api.context = :files
|
|
527
458
|
return execute_nodegen4_command(command_repo, res_id)
|
|
459
|
+
when :bearer_token
|
|
460
|
+
node_api = aoc_api.node_api_from(
|
|
461
|
+
node_id: res_id,
|
|
462
|
+
scope: options.get_next_argument('scope')
|
|
463
|
+
)
|
|
464
|
+
return Main.result_text(node_api.oauth.authorization)
|
|
465
|
+
when :dropbox
|
|
466
|
+
command_shared = options.get_next_command(%i[list])
|
|
467
|
+
case command_shared
|
|
468
|
+
when :list
|
|
469
|
+
query = options.get_option(:query) || {}
|
|
470
|
+
res_data = aoc_api.read('dropboxes', query.merge({'workspace_id'=>res_id}))
|
|
471
|
+
return Main.result_object_list(res_data, fields: %w[id name description])
|
|
472
|
+
end
|
|
473
|
+
when :shared_folder
|
|
474
|
+
query = options.get_option(:query) || Api::AoC.workspace_access(res_id).merge({'admin' => true})
|
|
475
|
+
shared_folders = aoc_api.read_with_paging("#{resource_instance_path}/permissions", query)[:items]
|
|
476
|
+
# inside a workspace
|
|
477
|
+
command_shared = options.get_next_command(%i[list member])
|
|
478
|
+
case command_shared
|
|
479
|
+
when :list
|
|
480
|
+
return Main.result_object_list(shared_folders, fields: %w[id node_name node_id file_id file.path tags.aspera.files.workspace.share_as])
|
|
481
|
+
when :member
|
|
482
|
+
shared_folder_id = instance_identifier
|
|
483
|
+
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
484
|
+
Aspera.assert(shared_folder)
|
|
485
|
+
command_shared_member = options.get_next_command(%i[list])
|
|
486
|
+
case command_shared_member
|
|
487
|
+
when :list
|
|
488
|
+
node_api = aoc_api.node_api_from(
|
|
489
|
+
node_id: shared_folder['node_id'],
|
|
490
|
+
workspace_id: res_id,
|
|
491
|
+
workspace_name: nil,
|
|
492
|
+
scope: Api::Node::SCOPE_USER
|
|
493
|
+
)
|
|
494
|
+
result = node_api.read(
|
|
495
|
+
'permissions',
|
|
496
|
+
{'file_id' => shared_folder['file_id'], 'tag' => "aspera.files.workspace.id=#{res_id}"}
|
|
497
|
+
)
|
|
498
|
+
result.each do |item|
|
|
499
|
+
item['member'] = begin
|
|
500
|
+
if Api::AoC.workspace_access?(item)
|
|
501
|
+
{'name'=>'[Internal permission]'}
|
|
502
|
+
else
|
|
503
|
+
aoc_api.read("admin/#{item['access_type']}s/#{item['access_id']}") rescue {'name': 'not found'}
|
|
504
|
+
end
|
|
505
|
+
rescue => e
|
|
506
|
+
{'name'=>e.to_s}
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
# TODO : read users and group name and add, if query "include_members"
|
|
510
|
+
return Main.result_object_list(result, fields: %w[access_type access_id access_level last_updated_at member.name member.email member.system_group_type member.system_group])
|
|
511
|
+
end
|
|
512
|
+
end
|
|
528
513
|
else Aspera.error_unexpected_value(command)
|
|
529
514
|
end
|
|
530
515
|
end
|
|
@@ -691,7 +676,6 @@ module Aspera
|
|
|
691
676
|
filter = query_read_delete(default: {})
|
|
692
677
|
filter['limit'] ||= 100
|
|
693
678
|
if options.get_option(:once_only, mandatory: true)
|
|
694
|
-
aoc_api.context = :files
|
|
695
679
|
saved_date = []
|
|
696
680
|
start_date_persistency = PersistencyActionOnce.new(
|
|
697
681
|
manager: persistency,
|
|
@@ -736,7 +720,6 @@ module Aspera
|
|
|
736
720
|
return Main.result_object_list(events)
|
|
737
721
|
end
|
|
738
722
|
when :usage_reports
|
|
739
|
-
aoc_api.context = :files
|
|
740
723
|
return result_list('usage_reports', base_query: workspace_id_hash)
|
|
741
724
|
end
|
|
742
725
|
end
|
|
@@ -809,7 +792,7 @@ module Aspera
|
|
|
809
792
|
}
|
|
810
793
|
return result_list('short_links', fields: Formatter.all_but('data'), base_query: list_params) if command.eql?(:list)
|
|
811
794
|
one_id = instance_identifier
|
|
812
|
-
found =
|
|
795
|
+
found = aoc_api.read_with_paging('short_links', list_params, formatter: formatter)[:items].find{ |item| item['id'].eql?(one_id)}
|
|
813
796
|
raise Cli::BadIdentifier.new('Short link', one_id) if found.nil?
|
|
814
797
|
return Main.result_single_object(found, fields: Formatter.all_but('data'))
|
|
815
798
|
when :modify
|
|
@@ -876,7 +859,6 @@ module Aspera
|
|
|
876
859
|
command = options.get_next_command(ACTIONS)
|
|
877
860
|
if %i[files packages].include?(command)
|
|
878
861
|
default_flag = ' (default)' if options.get_option(:workspace).eql?(:default)
|
|
879
|
-
aoc_api.context = command
|
|
880
862
|
formatter.display_status("Workspace: #{aoc_api.workspace[:name].to_s.red}#{default_flag}")
|
|
881
863
|
if !aoc_api.private_link.nil?
|
|
882
864
|
folder_name = aoc_api.node_api_from(node_id: aoc_api.home[:node_id]).read("files/#{aoc_api.home[:file_id]}")['name']
|
|
@@ -908,7 +890,6 @@ module Aspera
|
|
|
908
890
|
when :list
|
|
909
891
|
return result_list('workspaces', fields: %w[id name])
|
|
910
892
|
when :current
|
|
911
|
-
aoc_api.context = :files
|
|
912
893
|
return Main.result_single_object(aoc_api.workspace)
|
|
913
894
|
end
|
|
914
895
|
when :profile
|
|
@@ -961,7 +942,7 @@ module Aspera
|
|
|
961
942
|
# enforce workspace id from link (should be already ok, but in case user wanted to override)
|
|
962
943
|
package_data['workspace_id'] = aoc_api.public_link['data']['workspace_id']
|
|
963
944
|
end
|
|
964
|
-
package_data['encryption_at_rest'] = true if transfer.
|
|
945
|
+
package_data['encryption_at_rest'] = true if transfer.user_transfer_spec['content_protection'].eql?('encrypt')
|
|
965
946
|
# transfer may raise an error
|
|
966
947
|
created_package = aoc_api.create_package_simple(package_data, option_validate, new_user_option)
|
|
967
948
|
Main.result_transfer(transfer.start(created_package[:spec], rest_token: created_package[:node]))
|
|
@@ -1024,7 +1005,7 @@ module Aspera
|
|
|
1024
1005
|
destination_folder,
|
|
1025
1006
|
Environment.instance.sanitized_filename(package_info[per_package_field1])
|
|
1026
1007
|
)
|
|
1027
|
-
transfer.
|
|
1008
|
+
transfer.user_transfer_spec['destination_root'] = self.class.unique_folder(
|
|
1028
1009
|
folder,
|
|
1029
1010
|
extension: per_package_field2.eql?('seq') ? :seq : package_info[per_package_field2],
|
|
1030
1011
|
always: per_package_sub_always
|
|
@@ -1119,9 +1100,9 @@ module Aspera
|
|
|
1119
1100
|
when :instances
|
|
1120
1101
|
return entity_execute(api: aoc_api, entity: 'workflow_instances')
|
|
1121
1102
|
when :workflows
|
|
1122
|
-
wf_command = options.get_next_command(%i[action launch].concat(
|
|
1103
|
+
wf_command = options.get_next_command(%i[action launch].concat(ALL_OPS))
|
|
1123
1104
|
case wf_command
|
|
1124
|
-
when *
|
|
1105
|
+
when *ALL_OPS
|
|
1125
1106
|
return entity_execute(
|
|
1126
1107
|
api: automation_api,
|
|
1127
1108
|
entity: 'workflows',
|
|
@@ -1152,7 +1133,6 @@ module Aspera
|
|
|
1152
1133
|
uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
|
|
1153
1134
|
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
1154
1135
|
Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?)
|
|
1155
|
-
aoc_api.context = :files
|
|
1156
1136
|
server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.workspace[:id])
|
|
1157
1137
|
server.start
|
|
1158
1138
|
return Main.result_status('Gateway terminated')
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# cspell:ignore trustpolicy
|
|
4
4
|
|
|
5
|
+
require 'aspera/cli/plugins/base'
|
|
5
6
|
require 'aspera/cli/plugins/node'
|
|
6
7
|
require 'aspera/api/ats'
|
|
7
8
|
require 'aspera/api/aoc'
|
|
@@ -13,7 +14,7 @@ module Aspera
|
|
|
13
14
|
module Plugins
|
|
14
15
|
# Access Aspera Transfer Service
|
|
15
16
|
# https://developer.ibm.com/aspera/docs/ats-api-reference/creating-ats-api-keys/
|
|
16
|
-
class Ats <
|
|
17
|
+
class Ats < Base
|
|
17
18
|
# columns for list of cloud providers
|
|
18
19
|
CLOUD_TABLE = %w[id name].freeze
|
|
19
20
|
private_constant :CLOUD_TABLE
|
|
@@ -25,7 +26,6 @@ module Aspera
|
|
|
25
26
|
options.declare(:instance, 'ATS instance in ibm cloud')
|
|
26
27
|
options.declare(:ats_key, 'ATS key identifier (ats_xxx)')
|
|
27
28
|
options.declare(:ats_secret, 'ATS key secret')
|
|
28
|
-
options.declare(:params, 'Parameters access key creation (@json:)')
|
|
29
29
|
options.declare(:cloud, 'Cloud provider')
|
|
30
30
|
options.declare(:region, 'Cloud region')
|
|
31
31
|
options.parse_options!
|
|
@@ -59,7 +59,7 @@ module Aspera
|
|
|
59
59
|
access_key_id = instance_identifier unless %i[create list].include?(command)
|
|
60
60
|
case command
|
|
61
61
|
when :create
|
|
62
|
-
params =
|
|
62
|
+
params = value_create_modify(command: command, default: {})
|
|
63
63
|
server_data = nil
|
|
64
64
|
# if transfer_server_id not provided, get it from command line options
|
|
65
65
|
if !params.key?('transfer_server_id')
|
|
@@ -92,7 +92,7 @@ module Aspera
|
|
|
92
92
|
return Main.result_single_object(res)
|
|
93
93
|
# TODO : action : modify, with "PUT"
|
|
94
94
|
when :list
|
|
95
|
-
params =
|
|
95
|
+
params = query_read_delete(default: {'offset' => 0, 'max_results' => 1000})
|
|
96
96
|
res = ats_api_pub_v1.read('access_keys', params)
|
|
97
97
|
return Main.result_object_list(res['data'], fields: ['name', 'id', 'created.at', 'modified.at'])
|
|
98
98
|
when :show
|