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,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# spellchecker: ignore workgroups mypackages passcode
|
|
4
4
|
|
|
5
|
-
require 'aspera/cli/
|
|
5
|
+
require 'aspera/cli/plugins/oauth'
|
|
6
6
|
require 'aspera/cli/extended_value'
|
|
7
7
|
require 'aspera/cli/special_values'
|
|
8
|
+
require 'aspera/cli/wizard'
|
|
9
|
+
require 'aspera/api/faspex'
|
|
8
10
|
require 'aspera/persistency_action_once'
|
|
9
11
|
require 'aspera/id_generator'
|
|
10
12
|
require 'aspera/nagios'
|
|
@@ -15,60 +17,7 @@ require 'securerandom'
|
|
|
15
17
|
module Aspera
|
|
16
18
|
module Cli
|
|
17
19
|
module Plugins
|
|
18
|
-
class Faspex5 <
|
|
19
|
-
RECIPIENT_TYPES = %w[user workgroup external_user distribution_list shared_inbox].freeze
|
|
20
|
-
PACKAGE_TERMINATED = %w[completed failed].freeze
|
|
21
|
-
# list of supported mailbox types (to list packages)
|
|
22
|
-
API_LIST_MAILBOX_TYPES = %w[inbox inbox_history inbox_all inbox_all_history outbox outbox_history pending pending_history all].freeze
|
|
23
|
-
PACKAGE_SEND_FROM_REMOTE_SOURCE = 'remote_source'
|
|
24
|
-
# Faspex API v5: get transfer spec for connect
|
|
25
|
-
TRANSFER_CONNECT = 'connect'
|
|
26
|
-
ADMIN_RESOURCES = %i[
|
|
27
|
-
accounts distribution_lists contacts jobs workgroups shared_inboxes nodes oauth_clients registrations saml_configs
|
|
28
|
-
metadata_profiles email_notifications alternate_addresses webhooks
|
|
29
|
-
].freeze
|
|
30
|
-
# states for jobs not in final state
|
|
31
|
-
JOB_RUNNING = %w[queued working].freeze
|
|
32
|
-
PATH_STANDARD_ROOT = '/aspera/faspex'
|
|
33
|
-
PATH_API_V5 = 'api/v5'
|
|
34
|
-
# endpoint for authentication API
|
|
35
|
-
PATH_AUTH = 'auth'
|
|
36
|
-
PATH_HEALTH = 'configuration/ping'
|
|
37
|
-
PATH_API_DETECT = "#{PATH_API_V5}/#{PATH_HEALTH}"
|
|
38
|
-
# OAuth methods supported
|
|
39
|
-
STD_AUTH_TYPES = %i[web jwt boot].freeze
|
|
40
|
-
HEADER_ITERATION_TOKEN = 'X-Aspera-Next-Iteration-Token'
|
|
41
|
-
HEADER_FASPEX_VERSION = 'X-IBM-Aspera'
|
|
42
|
-
EMAIL_NOTIF_LIST = %w[
|
|
43
|
-
welcome_email
|
|
44
|
-
forgot_password
|
|
45
|
-
package_received
|
|
46
|
-
package_received_cc
|
|
47
|
-
package_sent_cc
|
|
48
|
-
package_downloaded
|
|
49
|
-
package_downloaded_cc
|
|
50
|
-
workgroup_package
|
|
51
|
-
upload_result
|
|
52
|
-
upload_result_cc
|
|
53
|
-
relay_started_cc
|
|
54
|
-
relay_finished_cc
|
|
55
|
-
relay_error_cc
|
|
56
|
-
shared_inbox_invitation
|
|
57
|
-
shared_inbox_submit
|
|
58
|
-
personal_invitation
|
|
59
|
-
personal_submit
|
|
60
|
-
account_approved
|
|
61
|
-
account_denied
|
|
62
|
-
package_file_processing_failed_sender
|
|
63
|
-
package_file_processing_failed_recipient
|
|
64
|
-
relay_failed_admin
|
|
65
|
-
relay_failed
|
|
66
|
-
admin_sync_failed
|
|
67
|
-
sync_failed
|
|
68
|
-
account_exist
|
|
69
|
-
mfa_code
|
|
70
|
-
]
|
|
71
|
-
private_constant :JOB_RUNNING, :RECIPIENT_TYPES, :PACKAGE_TERMINATED, :PATH_HEALTH, :API_LIST_MAILBOX_TYPES, :PACKAGE_SEND_FROM_REMOTE_SOURCE, :STD_AUTH_TYPES, :HEADER_ITERATION_TOKEN, :HEADER_FASPEX_VERSION, :EMAIL_NOTIF_LIST
|
|
20
|
+
class Faspex5 < Oauth
|
|
72
21
|
class << self
|
|
73
22
|
def application_name
|
|
74
23
|
'Faspex'
|
|
@@ -78,19 +27,19 @@ module Aspera
|
|
|
78
27
|
# add scheme if missing
|
|
79
28
|
address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
|
|
80
29
|
urls = [address_or_url]
|
|
81
|
-
urls.push("#{address_or_url}#{PATH_STANDARD_ROOT}") unless address_or_url.end_with?(PATH_STANDARD_ROOT)
|
|
30
|
+
urls.push("#{address_or_url}#{Api::Faspex::PATH_STANDARD_ROOT}") unless address_or_url.end_with?(Api::Faspex::PATH_STANDARD_ROOT)
|
|
82
31
|
error = nil
|
|
83
32
|
urls.each do |base_url|
|
|
84
33
|
# Faspex is always HTTPS
|
|
85
34
|
next unless base_url.start_with?('https://')
|
|
86
35
|
api = Rest.new(base_url: base_url, redirect_max: 1)
|
|
87
|
-
response = api.
|
|
36
|
+
response = api.read(Api::Faspex::PATH_API_DETECT, ret: :resp)
|
|
88
37
|
next unless response.code.start_with?('2') && response.body.strip.empty?
|
|
89
38
|
# end is at -1, and subtract 1 for "/"
|
|
90
|
-
url_length = -2 - PATH_API_DETECT.length
|
|
39
|
+
url_length = -2 - Api::Faspex::PATH_API_DETECT.length
|
|
91
40
|
# take redirect if any
|
|
92
41
|
return {
|
|
93
|
-
version: response[HEADER_FASPEX_VERSION] || '5',
|
|
42
|
+
version: response[Api::Faspex::HEADER_FASPEX_VERSION] || '5',
|
|
94
43
|
url: response.uri.to_s[0..url_length]
|
|
95
44
|
}
|
|
96
45
|
rescue StandardError => e
|
|
@@ -100,122 +49,57 @@ module Aspera
|
|
|
100
49
|
raise error if error
|
|
101
50
|
return
|
|
102
51
|
end
|
|
52
|
+
end
|
|
103
53
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
formatter.display_status('- JWT: enabled')
|
|
122
|
-
formatter.display_status("Log in as user #{wiz_username.red}. Navigate to your profile:")
|
|
123
|
-
formatter.display_status('👤 → Account Settings → Preferences → Public Key in PEM:')
|
|
124
|
-
formatter.display_status(pub_key_pem)
|
|
125
|
-
formatter.display_status('Once set, fill in the parameters:')
|
|
126
|
-
end
|
|
127
|
-
return {preset_value: {}, test_args: ''} if options.get_option(:test_mode)
|
|
128
|
-
return {
|
|
129
|
-
preset_value: {
|
|
130
|
-
url: instance_url,
|
|
131
|
-
username: wiz_username,
|
|
132
|
-
auth: :jwt.to_s,
|
|
133
|
-
private_key: "@file:#{private_key_path}",
|
|
134
|
-
client_id: options.get_option(:client_id, mandatory: true),
|
|
135
|
-
client_secret: options.get_option(:client_secret, mandatory: true)
|
|
136
|
-
},
|
|
137
|
-
test_args: 'user profile show'
|
|
138
|
-
}
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# @return true if the URL is a public link
|
|
142
|
-
def public_link?(url)
|
|
143
|
-
url.include?('?context=')
|
|
54
|
+
# @param wizard [Wizard] The wizard object
|
|
55
|
+
# @param app_url [Wizard] The wizard object
|
|
56
|
+
# @return [Hash] :preset_value, :test_args
|
|
57
|
+
def wizard(wizard, app_url)
|
|
58
|
+
client_id = options.get_option(:client_id)
|
|
59
|
+
client_secret = options.get_option(:client_secret)
|
|
60
|
+
if client_id.nil? || client_secret.nil?
|
|
61
|
+
formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
|
|
62
|
+
formatter.display_status("Log in as an admin user at: #{app_url}")
|
|
63
|
+
Environment.instance.open_uri(app_url)
|
|
64
|
+
formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
|
|
65
|
+
formatter.display_status('Create an API client with:')
|
|
66
|
+
formatter.display_status('- name: ascli')
|
|
67
|
+
formatter.display_status('- JWT: enabled')
|
|
68
|
+
formatter.display_status('Upon creation, the admin shall get those parameters:')
|
|
69
|
+
client_id = options.get_option(:client_id, mandatory: wizard.required)
|
|
70
|
+
client_secret = options.get_option(:client_secret, mandatory: wizard.required)
|
|
144
71
|
end
|
|
72
|
+
wiz_username = options.get_option(:username, mandatory: true)
|
|
73
|
+
wizard.check_email(wiz_username)
|
|
74
|
+
private_key_path = wizard.ask_private_key(
|
|
75
|
+
user: wiz_username,
|
|
76
|
+
url: app_url,
|
|
77
|
+
page: '👤 → Account Settings → Preferences → Public Key in PEM'
|
|
78
|
+
)
|
|
79
|
+
return {
|
|
80
|
+
preset_value: {
|
|
81
|
+
url: app_url,
|
|
82
|
+
username: wiz_username,
|
|
83
|
+
auth: :jwt.to_s,
|
|
84
|
+
private_key: "@file:#{private_key_path}",
|
|
85
|
+
client_id: client_id,
|
|
86
|
+
client_secret: client_secret
|
|
87
|
+
},
|
|
88
|
+
test_args: 'user profile show'
|
|
89
|
+
}
|
|
145
90
|
end
|
|
146
91
|
|
|
147
92
|
def initialize(**_)
|
|
148
93
|
super
|
|
149
|
-
options.declare(:
|
|
150
|
-
options.declare(:client_secret, 'OAuth client secret')
|
|
151
|
-
options.declare(:redirect_uri, 'OAuth redirect URI for web authentication')
|
|
152
|
-
options.declare(:auth, 'OAuth type of authentication', values: STD_AUTH_TYPES, default: :jwt)
|
|
153
|
-
options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
|
|
154
|
-
options.declare(:passphrase, 'OAuth JWT RSA private key passphrase')
|
|
155
|
-
options.declare(:box, "Package inbox, either shared inbox name or one of: #{API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox')
|
|
94
|
+
options.declare(:box, "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox')
|
|
156
95
|
options.declare(:shared_folder, 'Send package with files from shared folder')
|
|
157
|
-
options.declare(:group_type, 'Type of shared box',
|
|
96
|
+
options.declare(:group_type, 'Type of shared box', allowed: %i[shared_inboxes workgroups], default: :shared_inboxes)
|
|
158
97
|
options.parse_options!
|
|
159
|
-
@pub_link_context = nil
|
|
160
98
|
end
|
|
161
99
|
|
|
162
100
|
def set_api
|
|
163
|
-
#
|
|
164
|
-
@
|
|
165
|
-
auth_type = self.class.public_link?(@faspex5_api_base_url) ? :public_link : options.get_option(:auth, mandatory: true)
|
|
166
|
-
case auth_type
|
|
167
|
-
when :public_link
|
|
168
|
-
# resolve any redirect
|
|
169
|
-
@faspex5_api_base_url = Rest.new(base_url: @faspex5_api_base_url, redirect_max: 3).call(operation: 'GET')[:http].uri.to_s
|
|
170
|
-
encoded_context = Rest.query_to_h(URI.parse(@faspex5_api_base_url).query)['context']
|
|
171
|
-
raise BadArgument, 'Bad faspex5 public link, missing context in query' if encoded_context.nil?
|
|
172
|
-
# public link information (allowed usage)
|
|
173
|
-
@pub_link_context = JSON.parse(Base64.decode64(encoded_context))
|
|
174
|
-
Log.dump(:@pub_link_context, @pub_link_context, level: :trace1)
|
|
175
|
-
# ok, we have the additional parameters, get the base url
|
|
176
|
-
@faspex5_api_base_url = @faspex5_api_base_url.gsub(%r{/public/.*}, '').gsub(/\?.*/, '')
|
|
177
|
-
@api_v5 = Rest.new(
|
|
178
|
-
base_url: "#{@faspex5_api_base_url}/#{PATH_API_V5}",
|
|
179
|
-
headers: {'Passcode' => @pub_link_context['passcode']}
|
|
180
|
-
)
|
|
181
|
-
when :boot
|
|
182
|
-
# the password here is the token copied directly from browser in developer mode
|
|
183
|
-
@api_v5 = Rest.new(
|
|
184
|
-
base_url: "#{@faspex5_api_base_url}/#{PATH_API_V5}",
|
|
185
|
-
headers: {'Authorization' => options.get_option(:password, mandatory: true)}
|
|
186
|
-
)
|
|
187
|
-
when :web
|
|
188
|
-
# opens a browser and ask user to auth using web
|
|
189
|
-
@api_v5 = Rest.new(
|
|
190
|
-
base_url: "#{@faspex5_api_base_url}/#{PATH_API_V5}",
|
|
191
|
-
auth: {
|
|
192
|
-
type: :oauth2,
|
|
193
|
-
base_url: "#{@faspex5_api_base_url}/#{PATH_AUTH}",
|
|
194
|
-
grant_method: :web,
|
|
195
|
-
client_id: options.get_option(:client_id, mandatory: true),
|
|
196
|
-
redirect_uri: options.get_option(:redirect_uri, mandatory: true)
|
|
197
|
-
}
|
|
198
|
-
)
|
|
199
|
-
when :jwt
|
|
200
|
-
app_client_id = options.get_option(:client_id, mandatory: true)
|
|
201
|
-
@api_v5 = Rest.new(
|
|
202
|
-
base_url: "#{@faspex5_api_base_url}/#{PATH_API_V5}",
|
|
203
|
-
auth: {
|
|
204
|
-
type: :oauth2,
|
|
205
|
-
grant_method: :jwt,
|
|
206
|
-
base_url: "#{@faspex5_api_base_url}/#{PATH_AUTH}",
|
|
207
|
-
client_id: app_client_id,
|
|
208
|
-
payload: {
|
|
209
|
-
iss: app_client_id, # issuer
|
|
210
|
-
aud: app_client_id, # audience (this field is not clear...)
|
|
211
|
-
sub: "user:#{options.get_option(:username, mandatory: true)}" # subject is a user
|
|
212
|
-
},
|
|
213
|
-
private_key_obj: OpenSSL::PKey::RSA.new(options.get_option(:private_key, mandatory: true), options.get_option(:passphrase)),
|
|
214
|
-
headers: {typ: 'JWT'}
|
|
215
|
-
}
|
|
216
|
-
)
|
|
217
|
-
else Aspera.error_unexpected_value(auth_type)
|
|
218
|
-
end
|
|
101
|
+
# create an API object with the same options, but with a different subpath
|
|
102
|
+
@api_v5 = new_with_options(Api::Faspex)
|
|
219
103
|
# in case user wants to use HTTPGW tell transfer agent how to get address
|
|
220
104
|
transfer.httpgw_url_cb = lambda{@api_v5.read('account')['gateway_url']}
|
|
221
105
|
end
|
|
@@ -224,7 +108,7 @@ module Aspera
|
|
|
224
108
|
def normalize_recipients(parameters)
|
|
225
109
|
return unless parameters.key?('recipients')
|
|
226
110
|
Aspera.assert_type(parameters['recipients'], Array){'recipients'}
|
|
227
|
-
recipient_types = RECIPIENT_TYPES
|
|
111
|
+
recipient_types = Api::Faspex::RECIPIENT_TYPES
|
|
228
112
|
if parameters.key?('recipient_types')
|
|
229
113
|
recipient_types = parameters['recipient_types']
|
|
230
114
|
parameters.delete('recipient_types')
|
|
@@ -233,7 +117,7 @@ module Aspera
|
|
|
233
117
|
parameters['recipients'].map! do |recipient_data|
|
|
234
118
|
# if just a string, make a general lookup and build expected name/type hash
|
|
235
119
|
if recipient_data.is_a?(String)
|
|
236
|
-
matched = @api_v5.lookup_by_name('contacts', recipient_data, query: {context: 'packages', type:
|
|
120
|
+
matched = @api_v5.lookup_by_name('contacts', recipient_data, query: Rest.php_style({context: 'packages', type: recipient_types}))
|
|
237
121
|
recipient_data = {
|
|
238
122
|
name: matched['name'],
|
|
239
123
|
recipient_type: matched['type']
|
|
@@ -245,7 +129,7 @@ module Aspera
|
|
|
245
129
|
end
|
|
246
130
|
|
|
247
131
|
# wait for package status to be in provided list
|
|
248
|
-
def wait_package_status(id, status_list: PACKAGE_TERMINATED)
|
|
132
|
+
def wait_package_status(id, status_list: Api::Faspex::PACKAGE_TERMINATED)
|
|
249
133
|
total_sent = false
|
|
250
134
|
loop do
|
|
251
135
|
status = @api_v5.read("packages/#{id}/upload_details")
|
|
@@ -276,7 +160,7 @@ module Aspera
|
|
|
276
160
|
result = nil
|
|
277
161
|
loop do
|
|
278
162
|
result = @api_v5.read("jobs/#{job_id}", {type: :formatted})
|
|
279
|
-
break unless JOB_RUNNING.include?(result['status'])
|
|
163
|
+
break unless Api::Faspex::JOB_RUNNING.include?(result['status'])
|
|
280
164
|
formatter.long_operation_running(result['status'])
|
|
281
165
|
sleep(0.5)
|
|
282
166
|
end
|
|
@@ -292,7 +176,7 @@ module Aspera
|
|
|
292
176
|
entity =
|
|
293
177
|
case box
|
|
294
178
|
when SpecialValues::ALL then 'packages' # only admin can list all packages globally
|
|
295
|
-
when *API_LIST_MAILBOX_TYPES then "#{box}/packages"
|
|
179
|
+
when *Api::Faspex::API_LIST_MAILBOX_TYPES then "#{box}/packages"
|
|
296
180
|
else
|
|
297
181
|
group_type = options.get_option(:group_type)
|
|
298
182
|
"#{group_type}/#{lookup_entity_by_field(api: @api_v5, entity: group_type, value: box)['id']}/packages"
|
|
@@ -338,8 +222,7 @@ module Aspera
|
|
|
338
222
|
else
|
|
339
223
|
# a single id was provided, or a list of ids
|
|
340
224
|
package_ids = [package_ids] unless package_ids.is_a?(Array)
|
|
341
|
-
Aspera.
|
|
342
|
-
Aspera.assert(package_ids.all?(String)){'Package id shall be String'}
|
|
225
|
+
Aspera.assert_array_all(package_ids, String){'Package id(s)'}
|
|
343
226
|
# packages = package_ids.map{|pkg_id|@api_v5.read("packages/#{pkg_id}")}
|
|
344
227
|
packages = package_ids.map{ |pkg_id| {'id'=>pkg_id}}
|
|
345
228
|
end
|
|
@@ -352,12 +235,12 @@ module Aspera
|
|
|
352
235
|
end
|
|
353
236
|
download_params = {
|
|
354
237
|
type: 'received',
|
|
355
|
-
transfer_type: TRANSFER_CONNECT
|
|
238
|
+
transfer_type: Api::Faspex::TRANSFER_CONNECT
|
|
356
239
|
}
|
|
357
240
|
box = options.get_option(:box)
|
|
358
241
|
case box
|
|
359
242
|
when /outbox/ then download_params[:type] = 'sent'
|
|
360
|
-
when *API_LIST_MAILBOX_TYPES then nil # nothing to do
|
|
243
|
+
when *Api::Faspex::API_LIST_MAILBOX_TYPES then nil # nothing to do
|
|
361
244
|
else # shared inbox / workgroup
|
|
362
245
|
download_params[:recipient_workgroup_id] = lookup_entity_by_field(api: @api_v5, entity: options.get_option(:group_type), value: box)['id']
|
|
363
246
|
end
|
|
@@ -372,7 +255,7 @@ module Aspera
|
|
|
372
255
|
content_type: Rest::MIME_JSON,
|
|
373
256
|
body: param_file_list,
|
|
374
257
|
headers: {'Accept' => Rest::MIME_JSON}
|
|
375
|
-
)
|
|
258
|
+
)
|
|
376
259
|
# delete flag for Connect Client
|
|
377
260
|
transfer_spec.delete('authentication')
|
|
378
261
|
statuses = transfer.start(transfer_spec)
|
|
@@ -410,28 +293,29 @@ module Aspera
|
|
|
410
293
|
until folders_to_process.empty?
|
|
411
294
|
path = folders_to_process.shift
|
|
412
295
|
loop do
|
|
413
|
-
|
|
296
|
+
data, http = @api_v5.call(
|
|
414
297
|
operation: 'POST',
|
|
415
298
|
subpath: browse_endpoint,
|
|
416
299
|
query: query,
|
|
417
300
|
content_type: Rest::MIME_JSON,
|
|
418
301
|
body: {'path' => path, 'filters' => filters},
|
|
419
|
-
headers: {'Accept' => Rest::MIME_JSON}
|
|
302
|
+
headers: {'Accept' => Rest::MIME_JSON},
|
|
303
|
+
ret: :both
|
|
420
304
|
)
|
|
421
|
-
all_items.concat(
|
|
305
|
+
all_items.concat(data['items'])
|
|
422
306
|
if !max_items.nil? && (all_items.count >= max_items)
|
|
423
307
|
all_items = all_items.slice(0, max_items) if all_items.count > max_items
|
|
424
308
|
break
|
|
425
309
|
end
|
|
426
|
-
folders_to_process.concat(
|
|
310
|
+
folders_to_process.concat(data['items'].select{ |i| i['type'].eql?('directory')}.map{ |i| i['path']}) if recursive
|
|
427
311
|
if use_paging
|
|
428
|
-
iteration_token =
|
|
312
|
+
iteration_token = http[Api::Faspex::HEADER_ITERATION_TOKEN]
|
|
429
313
|
break if iteration_token.nil? || iteration_token.empty?
|
|
430
314
|
query['iteration_token'] = iteration_token
|
|
431
315
|
else
|
|
432
|
-
total_count =
|
|
433
|
-
break if
|
|
434
|
-
query['offset'] +=
|
|
316
|
+
total_count = data['total_count'] if total_count.nil?
|
|
317
|
+
break if data['item_count'].eql?(0)
|
|
318
|
+
query['offset'] += data['item_count']
|
|
435
319
|
end
|
|
436
320
|
formatter.long_operation_running(all_items.count)
|
|
437
321
|
end
|
|
@@ -446,7 +330,7 @@ module Aspera
|
|
|
446
330
|
command = options.get_next_command(%i[show browse status delete receive send list])
|
|
447
331
|
package_id =
|
|
448
332
|
if %i[receive show browse status delete].include?(command)
|
|
449
|
-
@pub_link_context&.key?('package_id') ? @pub_link_context['package_id'] : instance_identifier
|
|
333
|
+
@api_v5.pub_link_context&.key?('package_id') ? @api_v5.pub_link_context['package_id'] : instance_identifier
|
|
450
334
|
end
|
|
451
335
|
case command
|
|
452
336
|
when :show
|
|
@@ -465,8 +349,7 @@ module Aspera
|
|
|
465
349
|
when :delete
|
|
466
350
|
ids = package_id
|
|
467
351
|
ids = [ids] unless ids.is_a?(Array)
|
|
468
|
-
Aspera.
|
|
469
|
-
Aspera.assert(ids.all?(String)){"Package id(s) shall be String, but have: #{ids.map(&:class).uniq.join(', ')}"}
|
|
352
|
+
Aspera.assert_array_all(ids, String){'Package id(s)'}
|
|
470
353
|
# API returns 204, empty on success
|
|
471
354
|
@api_v5.call(
|
|
472
355
|
operation: 'DELETE',
|
|
@@ -481,36 +364,39 @@ module Aspera
|
|
|
481
364
|
when :send
|
|
482
365
|
parameters = value_create_modify(command: command)
|
|
483
366
|
# autofill recipient for public url
|
|
484
|
-
if @pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
|
|
367
|
+
if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
|
|
485
368
|
parameters['recipients'] = [{
|
|
486
|
-
name: @pub_link_context['name'],
|
|
487
|
-
recipient_type: @pub_link_context['recipient_type']
|
|
369
|
+
name: @api_v5.pub_link_context['name'],
|
|
370
|
+
recipient_type: @api_v5.pub_link_context['recipient_type']
|
|
488
371
|
}]
|
|
489
372
|
end
|
|
490
373
|
normalize_recipients(parameters)
|
|
374
|
+
# User specified content prot in tspec, but faspex requires in package creation
|
|
375
|
+
# `transfer_spec/upload` will set `content_protection`
|
|
376
|
+
if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
|
|
377
|
+
transfer.user_transfer_spec.delete('content_protection')
|
|
378
|
+
parameters['ear_enabled'] = true
|
|
379
|
+
end
|
|
491
380
|
package = @api_v5.create('packages', parameters)
|
|
492
381
|
shared_folder = options.get_option(:shared_folder)
|
|
493
382
|
if shared_folder.nil?
|
|
494
383
|
# send from local files
|
|
495
|
-
transfer_spec = @api_v5.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
query:
|
|
499
|
-
|
|
500
|
-
body: {paths: transfer.source_list},
|
|
501
|
-
headers: {'Accept' => Rest::MIME_JSON}
|
|
502
|
-
)[:data]
|
|
384
|
+
transfer_spec = @api_v5.create(
|
|
385
|
+
"packages/#{package['id']}/transfer_spec/upload",
|
|
386
|
+
{paths: transfer.source_list},
|
|
387
|
+
query: {transfer_type: Api::Faspex::TRANSFER_CONNECT}
|
|
388
|
+
)
|
|
503
389
|
# well, we asked a TS for connect, but we actually want a generic one
|
|
504
390
|
transfer_spec.delete('authentication')
|
|
505
391
|
return Main.result_transfer(transfer.start(transfer_spec))
|
|
506
392
|
else
|
|
507
393
|
# send from remote shared folder
|
|
508
|
-
if (m =
|
|
394
|
+
if (m = Base.percent_selector(shared_folder))
|
|
509
395
|
shared_folder = lookup_entity_by_field(
|
|
510
396
|
api: @api_v5,
|
|
511
397
|
entity: 'shared_folders',
|
|
512
|
-
field: m[
|
|
513
|
-
value:
|
|
398
|
+
field: m[:field],
|
|
399
|
+
value: m[:value]
|
|
514
400
|
)['id']
|
|
515
401
|
end
|
|
516
402
|
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
|
@@ -536,7 +422,7 @@ module Aspera
|
|
|
536
422
|
tclo: true
|
|
537
423
|
}
|
|
538
424
|
res_id_query = :default
|
|
539
|
-
available_commands =
|
|
425
|
+
available_commands = ALL_OPS
|
|
540
426
|
case res_sym
|
|
541
427
|
when :metadata_profiles
|
|
542
428
|
exec_args[:entity] = 'configuration/metadata_profiles'
|
|
@@ -554,7 +440,7 @@ module Aspera
|
|
|
554
440
|
available_commands += [:reset_password]
|
|
555
441
|
when :oauth_clients
|
|
556
442
|
exec_args[:display_fields] = Formatter.all_but('public_key')
|
|
557
|
-
exec_args[:api] =
|
|
443
|
+
exec_args[:api] = @api_v5.auth_api
|
|
558
444
|
exec_args[:list_query] = {'expand': true, 'no_api_path': true, 'client_types[]': 'public'}
|
|
559
445
|
when :shared_inboxes, :workgroups
|
|
560
446
|
available_commands += %i[members saml_groups invite_external_collaborator]
|
|
@@ -563,9 +449,9 @@ module Aspera
|
|
|
563
449
|
available_commands += %i[shared_folders browse]
|
|
564
450
|
end
|
|
565
451
|
res_command = options.get_next_command(available_commands)
|
|
566
|
-
return Main.result_value_list(EMAIL_NOTIF_LIST, name: 'email_id') if res_command.eql?(:list) && res_sym.eql?(:email_notifications)
|
|
452
|
+
return Main.result_value_list(Api::Faspex::EMAIL_NOTIF_LIST, name: 'email_id') if res_command.eql?(:list) && res_sym.eql?(:email_notifications)
|
|
567
453
|
case res_command
|
|
568
|
-
when *
|
|
454
|
+
when *ALL_OPS
|
|
569
455
|
return entity_execute(command: res_command, **exec_args) do |field, value|
|
|
570
456
|
lookup_entity_by_field(api: @api_v5, entity: exec_args[:entity], value: value, field: field, items_key: exec_args[:items_key], query: res_id_query)['id']
|
|
571
457
|
end
|
|
@@ -575,9 +461,9 @@ module Aspera
|
|
|
575
461
|
lookup_entity_by_field(api: @api_v5, entity: 'nodes', field: field, value: value)['id']
|
|
576
462
|
end
|
|
577
463
|
shfld_entity = "nodes/#{node_id}/shared_folders"
|
|
578
|
-
sh_command = options.get_next_command(
|
|
464
|
+
sh_command = options.get_next_command(ALL_OPS + [:user])
|
|
579
465
|
case sh_command
|
|
580
|
-
when *
|
|
466
|
+
when *ALL_OPS
|
|
581
467
|
return entity_execute(
|
|
582
468
|
api: @api_v5,
|
|
583
469
|
entity: shfld_entity,
|
|
@@ -628,20 +514,20 @@ module Aspera
|
|
|
628
514
|
users = options.get_next_argument('user id, %name:, or Array')
|
|
629
515
|
users = [users] unless users.is_a?(Array)
|
|
630
516
|
users = users.map do |user|
|
|
631
|
-
if (m =
|
|
517
|
+
if (m = Base.percent_selector(user))
|
|
632
518
|
lookup_entity_by_field(
|
|
633
519
|
api: @api_v5,
|
|
634
520
|
entity: 'accounts',
|
|
635
|
-
field: m[
|
|
636
|
-
value:
|
|
637
|
-
query:
|
|
521
|
+
field: m[:field],
|
|
522
|
+
value: m[:value],
|
|
523
|
+
query: Rest.php_style({type: ACCOUNT_TYPES})
|
|
638
524
|
)['id']
|
|
639
525
|
else
|
|
640
526
|
# it's the user id (not member id...)
|
|
641
527
|
user
|
|
642
528
|
end
|
|
643
529
|
end
|
|
644
|
-
access = options.get_next_argument('level', mandatory: false, accept_list:
|
|
530
|
+
access = options.get_next_argument('level', mandatory: false, accept_list: SHARED_INBOX_MEMBER_LEVELS, default: :standard)
|
|
645
531
|
options.unshift_next_argument({user: users.map{ |u| {id: u, access: access}}})
|
|
646
532
|
end
|
|
647
533
|
return entity_execute(
|
|
@@ -652,10 +538,10 @@ module Aspera
|
|
|
652
538
|
) do |field, value|
|
|
653
539
|
lookup_entity_by_field(
|
|
654
540
|
api: @api_v5,
|
|
655
|
-
entity: '
|
|
541
|
+
entity: 'contacts',
|
|
656
542
|
field: field,
|
|
657
543
|
value: value,
|
|
658
|
-
query: {type:
|
|
544
|
+
query: Rest.php_style({type: %w[user]})
|
|
659
545
|
)['id']
|
|
660
546
|
end
|
|
661
547
|
when :reset_password
|
|
@@ -668,13 +554,9 @@ module Aspera
|
|
|
668
554
|
end
|
|
669
555
|
|
|
670
556
|
def execute_admin
|
|
671
|
-
command = options.get_next_command(%i[configuration smtp
|
|
557
|
+
command = options.get_next_command(%i[configuration smtp events clean_deleted].concat(Api::Faspex::ADMIN_RESOURCES).freeze)
|
|
672
558
|
case command
|
|
673
|
-
when
|
|
674
|
-
# resource will be deprecated
|
|
675
|
-
Log.log.warn('resource command is deprecated (4.18), directly use the specific command instead')
|
|
676
|
-
return execute_resource(options.get_next_command(ADMIN_RESOURCES))
|
|
677
|
-
when *ADMIN_RESOURCES
|
|
559
|
+
when *Api::Faspex::ADMIN_RESOURCES
|
|
678
560
|
return execute_resource(command)
|
|
679
561
|
when :clean_deleted
|
|
680
562
|
delete_data = value_create_modify(command: command, default: {})
|
|
@@ -739,25 +621,27 @@ module Aspera
|
|
|
739
621
|
|
|
740
622
|
def execute_action
|
|
741
623
|
command = options.get_next_command(ACTIONS)
|
|
742
|
-
set_api unless
|
|
624
|
+
set_api unless %i{postprocessing health}.include?(command)
|
|
743
625
|
case command
|
|
744
626
|
when :version
|
|
745
627
|
return Main.result_single_object(@api_v5.read('version'))
|
|
746
628
|
when :health
|
|
747
629
|
nagios = Nagios.new
|
|
748
630
|
begin
|
|
749
|
-
|
|
750
|
-
|
|
631
|
+
data, http = Rest.new(base_url: options.get_option(:url, mandatory: true))
|
|
632
|
+
.read('health', ret: :both)
|
|
633
|
+
data.each do |k, v|
|
|
751
634
|
nagios.add_ok(k, v.to_s)
|
|
752
635
|
end
|
|
636
|
+
nagios.add_ok('version', http['X-IBM-Aspera']) if http['X-IBM-Aspera']
|
|
753
637
|
rescue StandardError => e
|
|
754
|
-
nagios.add_critical('
|
|
638
|
+
nagios.add_critical('core', e.to_s)
|
|
755
639
|
end
|
|
756
|
-
|
|
640
|
+
Main.result_object_list(nagios.status_list)
|
|
757
641
|
when :user
|
|
758
642
|
case options.get_next_command(%i[account profile])
|
|
759
643
|
when :account
|
|
760
|
-
return Main.result_single_object(@api_v5.read('account'))
|
|
644
|
+
return Main.result_single_object(@api_v5.read('account', query_read_delete))
|
|
761
645
|
when :profile
|
|
762
646
|
case options.get_next_command(%i[show modify])
|
|
763
647
|
when :show
|
|
@@ -791,7 +675,7 @@ module Aspera
|
|
|
791
675
|
return execute_admin
|
|
792
676
|
when :invitations
|
|
793
677
|
invitation_endpoint = 'invitations'
|
|
794
|
-
invitation_command = options.get_next_command(%i[resend].concat(
|
|
678
|
+
invitation_command = options.get_next_command(%i[resend].concat(ALL_OPS))
|
|
795
679
|
case invitation_command
|
|
796
680
|
when :create
|
|
797
681
|
return do_bulk_operation(command: invitation_command, descr: 'data') do |params|
|
|
@@ -832,6 +716,10 @@ module Aspera
|
|
|
832
716
|
return Main.result_status('Gateway terminated')
|
|
833
717
|
end
|
|
834
718
|
end
|
|
719
|
+
SHARED_INBOX_MEMBER_LEVELS = %i[submit_only standard shared_inbox_admin].freeze
|
|
720
|
+
ACCOUNT_TYPES = %w{local_user saml_user self_registered_user external_user}.freeze
|
|
721
|
+
CONTACT_TYPES = %w{workgroup shared_inbox distribution_list user external_user}.freeze
|
|
722
|
+
private_constant :SHARED_INBOX_MEMBER_LEVELS, :ACCOUNT_TYPES, :CONTACT_TYPES
|
|
835
723
|
end
|
|
836
724
|
end
|
|
837
725
|
end
|