aspera-cli 4.25.4 → 4.25.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34cb77d2badea1e3650f2aed72fb3a91c419e6457a08acd62808c9a6d9ab0e74
4
- data.tar.gz: 31e1e1a28d99afebd6b6a3f7b9e848bf18256d47fdc7108368a6e964eef03b04
3
+ metadata.gz: a886cb3a02f285c84728b216f1cd906b8440db041a8240ed09ed1969d81ec6c3
4
+ data.tar.gz: a05a7d9fc94a5d748c52d659426a18cf567a9e303231c86d1ec2312abc3465ae
5
5
  SHA512:
6
- metadata.gz: e15bfb65919b8b8a6112120eca273dac962ff6d6b29d890df1e9293f4321e557bdd68697c394cb6ef738a3d5bcc3a75a2f7f177494b66542c2e970c4da99e9d0
7
- data.tar.gz: 1d3570c590e7bfbda3ce5fc49396637471f48ff6fccce7235ae31adcc23c1e1ccc4a978d6bdbe9a4c1c06f06e02ed5336043267891c0b56a995b4d0199ab0d92
6
+ metadata.gz: 87988d7b4252134e772c7c5420e59471cfb89355627cb3bf49fe6f5cf19382644856dfee3494adb3db2a879fc42b77f5754f8fee995eae8ca45229fc68ccd769
7
+ data.tar.gz: ace70306ca85a12b17d12dcf146f280d313dcbef491576c265a5a20f70199d1c03ef3834cc62de30b20949f2cfdea39821b09b232c8bd826709f5622c7f631c3
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  <!-- markdownlint-configure-file { "no-duplicate-heading": { "siblings_only": true } } -->
4
4
 
5
+ ## 4.25.5
6
+
7
+ Released: 2026-03-25
8
+
9
+ ### New Features
10
+
11
+ * `faspex5`: When sending a package, recipients specified in fields `private_recipients`, `notified_on_upload`, `notified_on_download` and `notified_on_receipt` are now also expanded like field `recipients`.
12
+
13
+ ### Issues Fixed
14
+
15
+ * `config`: Fixed `preset update` with dot-path options.
16
+ * `sync`: #251 in `admin` command: Fixed database path on Windows: Use `~private-asp` instead of `.private-asp`.
17
+ * `httpgw`: #252 HTTPGW transfer showed success even in case of error code `403`. Now raises error with message.
18
+
19
+ ### Breaking Changes
20
+
5
21
  ## 4.25.4
6
22
 
7
23
  Released: 2026-03-04
@@ -301,7 +317,7 @@ Released: 2025-01-21
301
317
 
302
318
  * Internal: Basic REST calls now return data directly. (no more `data` key). For advanced calls, use `call`.
303
319
  * Internal: Transfer SDK download is now a 2-step procedure: First get the YAML file from GitHub with URLs for the various platforms and versions, and then download the archive from the official IBM repository.
304
- **global**: Option `format=multi` is replaced with option `multi_table=yes`.
320
+ * **global**: Option `format=multi` is replaced with option `multi_table=yes`.
305
321
  * `faspex5`: Removed deprecated option `value` replaced with positional parameter.
306
322
 
307
323
  ## 4.19.0
@@ -1065,7 +1081,7 @@ Released: 2021-02-03
1065
1081
 
1066
1082
  ### New Features
1067
1083
 
1068
- * The option `value` of command `find`, to filter on name, is not optional.
1084
+ * The option `value` of command `find` to filter on name is mandatory.
1069
1085
  * `find` now also reports all types (file, folder, link).
1070
1086
  * `find` now is able to report all fields (type, size, etc...).
1071
1087
 
@@ -1095,7 +1111,7 @@ Released: 2021-02-03
1095
1111
  ### New Features
1096
1112
 
1097
1113
  * Improved wizard (prepare for AoC global client id).
1098
- * Preview generator: Added option : --skip-format=&lt;png,mp4&gt;.
1114
+ * Preview generator: Added option : `skip_format` with values `png`, `mp4`.
1099
1115
  * Removed outdated pictures from this doc.
1100
1116
 
1101
1117
  ## 0.9.19
@@ -1163,7 +1179,7 @@ Released: 2021-02-03
1163
1179
 
1164
1180
  ### New Features
1165
1181
 
1166
- * `faspex`: Use option `once_only` set to `yes` to enable cargo like function. id=NEW deprecated.
1182
+ * `faspex`: Use option `once_only` set to `yes` to enable cargo like function. `id=NEW` deprecated.
1167
1183
  * `aoc`: Share to share transfer with command `transfer`.
1168
1184
 
1169
1185
  ## 0.9.7
@@ -1172,8 +1188,8 @@ Released: 2021-02-03
1172
1188
 
1173
1189
  * Homogeneous transfer spec for `node` and [`direct`](README.md#agt_direct) transfer agents.
1174
1190
  * Preview persistency goes to unique file by default.
1175
- * Catch mxf extension in preview as video.
1176
- * Faspex: Possibility to download all packages by specifying id=ALL.
1191
+ * Catch `mxf` extension in preview as video.
1192
+ * Faspex: Possibility to download all packages by specifying `id=ALL`.
1177
1193
  * Faspex: To come: Cargo-like function to download only new packages with id=NEW.
1178
1194
 
1179
1195
  ## 0.9.6
data/CONTRIBUTING.md CHANGED
@@ -12,7 +12,7 @@ To help us diagnose and resolve the issue efficiently, please include the follow
12
12
 
13
13
  - The `ascli` version you are using:
14
14
 
15
- ```bash
15
+ ```shell
16
16
  ascli -v
17
17
  ```
18
18
 
@@ -20,7 +20,7 @@ To help us diagnose and resolve the issue efficiently, please include the follow
20
20
 
21
21
  - **Your Ruby environment details**:
22
22
 
23
- ```bash
23
+ ```shell
24
24
  ruby -v
25
25
  ```
26
26
 
@@ -32,7 +32,7 @@ We welcome contributions to improve the `aspera-cli` project!
32
32
 
33
33
  Clone the repository to initialize the development environment:
34
34
 
35
- ```bash
35
+ ```shell
36
36
  git clone https://github.com/IBM/aspera-cli.git
37
37
  cd aspera-cli
38
38
  bundle install
@@ -91,7 +91,7 @@ You can manage your Ruby installation using your preferred tool (e.g., `rbenv`,
91
91
 
92
92
  To start with a clean state and remove all installed gems:
93
93
 
94
- ```bash
94
+ ```shell
95
95
  bundle exec rake tools:clean_gems
96
96
  ```
97
97
 
@@ -109,9 +109,8 @@ The following environment variables and macros control specific build behaviors:
109
109
  | Environment variable | Contents | Description |
110
110
  |-----------------------------|------------| -------------------------------------------------------------|
111
111
  | `ASPERA_CLI_TEST_CONF_URL` | URL | URL for the configuration file containing secrets for tests. |
112
- | `ASPERA_CLI_DOC_CHECK_LINKS`| yes/no | Validates that links exist during documentation generation. |
113
- | `LOG_SECRETS` | yes/no | Toggles the logging of secrets in `rake` tasks. |
114
- | `LOG_LEVEL` | debug, ... | Sets the logging verbosity for `rake` tasks. |
112
+ | `LOG_SECRETS` | `yes`/`no` | Toggles the logging of secrets in `rake` tasks. |
113
+ | `LOG_LEVEL` | `debug`, ... | Sets the logging verbosity for `rake` tasks. |
115
114
  | `ENABLE_COVERAGE` | set/unset | Enables test coverage analysis when defined. |
116
115
  | `SIGNING_KEY` | File path | Path to the signing key used for building the gem file. |
117
116
  | `SIGNING_KEY_PEM` | PEM Value | The PEM content of the signing key. |
@@ -121,11 +120,11 @@ These values can be set as standard environment variables or passed directly to
121
120
  Setting `SIGNING_KEY_PEM` automatically generates a file at `$HOME/.gem/signing_key.pem` and sets the `SIGNING_KEY` variable accordingly.
122
121
 
123
122
  > [!NOTE]
124
- > `ASPERA_CLI_*` variables are typically defined in your shell profile for development, while others are intended for ad-hoc command-line use.
123
+ > `ASPERA_CLI_TEST_CONF_URL` is typically defined in your shell profile for development, while others are usually for ad-hoc command-line use.
125
124
 
126
125
  To run the CLI directly from your source directory, add the following to your shell profile (adjust the path as necessary):
127
126
 
128
- ```bash
127
+ ```shell
129
128
  dev_ascli=$HOME/github/aspera-cli
130
129
  export PATH=$dev_ascli/bin:$PATH
131
130
  export RUBYLIB=$dev_ascli/lib:$RUBYLIB
@@ -140,24 +139,28 @@ Installation instructions can be found at [IBM Plex](https://www.ibm.com/plex/).
140
139
 
141
140
  On macOS, install `lualatex` and required packages via Homebrew:
142
141
 
143
- ```bash
142
+ ```shell
144
143
  brew install texlive
145
144
  ```
146
145
 
147
146
  If using an alternative installation method, ensure the following packages are present:
148
147
 
149
- ```bash
148
+ ```shell
150
149
  tlmgr update --self
151
150
  tlmgr install fvextra selnolig lualatex-math
152
151
  ```
153
152
 
154
- - To validate URLs during generation: `ASPERA_CLI_DOC_CHECK_LINKS=1`.
153
+ - Validate URLs during generation with:
155
154
 
156
- - To debug the generation process: `ASPERA_CLI_DOC_DEBUG=debug`.
155
+ ```shell
156
+ rake doc:check_links
157
+ ```
158
+
159
+ - Debug the generation process: `ASPERA_CLI_DOC_DEBUG=debug`.
157
160
 
158
- - To build the documentation:
161
+ - Build the documentation:
159
162
 
160
- ```bash
163
+ ```shell
161
164
  rake doc:build
162
165
  ```
163
166
 
@@ -169,14 +172,14 @@ Detailed testing information can be found in <tests/README.md>.
169
172
 
170
173
  To build an unsigned gem:
171
174
 
172
- ```bash
175
+ ```shell
173
176
  bundle install
174
177
  bundle exec rake unsigned
175
178
  ```
176
179
 
177
180
  To exclude optional gems from the installation:
178
181
 
179
- ```bash
182
+ ```shell
180
183
  bundle config set without optional
181
184
  ```
182
185
 
@@ -185,7 +188,7 @@ bundle config set without optional
185
188
  Generating a signed gem requires a **private key**, specified via the `SIGNING_KEY` environment variable.
186
189
  The gem is signed using the public certificate in `certs` and the **private key**.
187
190
 
188
- ```bash
191
+ ```shell
189
192
  bundle exec rake SIGNING_KEY=/path/to/vault/gem-private_key.pem
190
193
  ```
191
194
 
@@ -195,7 +198,7 @@ For more details, see <certs/README.md>.
195
198
 
196
199
  To update the stubs:
197
200
 
198
- ```bash
201
+ ```shell
199
202
  bundle exec rake tools:grpc
200
203
  ```
201
204
 
@@ -211,7 +214,7 @@ Refer to the [Executable build guide](build/binary/README.md).
211
214
 
212
215
  To list related `rake` tasks:
213
216
 
214
- ```bash
217
+ ```shell
215
218
  bundle exec rake -T ^binary:
216
219
  ```
217
220
 
@@ -234,17 +237,23 @@ Before a new release, ensure the following:
234
237
 
235
238
  - **Pass all tests**:
236
239
 
237
- ```bash
240
+ ```shell
238
241
  bundle exec rake test:run
239
242
  ```
240
243
 
241
244
  - **Verify container builds** (using the local gem):
242
245
 
243
- ```bash
246
+ ```shell
244
247
  bundle exec rake container:build'[local]'
245
248
  bundle exec rake container:test
246
249
  ```
247
250
 
251
+ - **Check documentation links**:
252
+
253
+ ```shell
254
+ bundle exec rake doc:check_links
255
+ ```
256
+
248
257
  ### Automated Release Process
249
258
 
250
259
  Releases are managed through the GitHub Actions UI via the **New Release on GitHub** workflow (`.github/workflows/release.yml`).
data/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5861/badge)](https://bestpractices.coreinfrastructure.org/projects/5861)
6
6
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
7
7
 
8
+ ![Hootput the Owl](docs/mascot.svg)
9
+
8
10
  **`ascli`** is the command-line interface for IBM Aspera products.
9
11
  Use it from the terminal or in scripts to:
10
12
 
@@ -18,8 +20,8 @@ Choose what best suits you:
18
20
 
19
21
  | Resource | Link |
20
22
  | -------- | ---- |
21
- | **Online manual** | [docs/README.md](docs/README.md) |
22
- | **PDF manual** | In [releases](https://github.com/IBM/aspera-cli/releases) |
23
+ | **Online manual** | [GitHub docs/README.md](https://github.com/IBM/aspera-cli/blob/main/docs/README.md) |
24
+ | **PDF manual** | In [GitHub Releases](https://github.com/IBM/aspera-cli/releases) |
23
25
  | **RubyGems** | [rubygems.org/gems/aspera-cli](https://rubygems.org/gems/aspera-cli) |
24
26
  | **RubyDoc** | [rubydoc.info/gems/aspera-cli](https://www.rubydoc.info/gems/aspera-cli) |
25
27
  | **Docsify** | [online](https://docsify-this.net/?basePath=https://raw.githubusercontent.com/IBM/aspera-cli/main/docs&homepage=README.md&sidebar=true&browser-tab-title=Aspera%20CLI%20Manual&hide-credits=true&maxLevel=4&externalLinkTarget=_blank&image-captions=true&dark-mode=auto) |
@@ -34,7 +36,7 @@ ascli config transferd install
34
36
  ```
35
37
 
36
38
  The second command installs the **FASP** transfer engine (`ascp`).
37
- For other install methods (single executable, Docker, Chocolatey, Homebrew), see the [user manual](docs/README.md#installation).
39
+ For other install methods (single executable, Docker, Chocolatey, Homebrew), see the [user manual](https://github.com/IBM/aspera-cli/blob/main/docs/README.md#installation).
38
40
 
39
41
  **Quick check:**
40
42
 
@@ -44,13 +46,13 @@ ascli -v
44
46
 
45
47
  ## Contributing
46
48
 
47
- - **Bugs & features:** [BUGS.md](BUGS.md)
48
- - **How to contribute:** [CONTRIBUTING.md](CONTRIBUTING.md)
49
- - **Release notes:** [CHANGELOG.md](CHANGELOG.md)
49
+ - **Bugs & features:** [BUGS.md](https://github.com/IBM/aspera-cli/blob/main/BUGS.md)
50
+ - **How to contribute:** [CONTRIBUTING.md](https://github.com/IBM/aspera-cli/blob/main/CONTRIBUTING.md)
51
+ - **Release notes:** [CHANGELOG.md](https://github.com/IBM/aspera-cli/blob/main/CHANGELOG.md)
50
52
 
51
53
  Commands map to Aspera REST APIs; see the manual for options.
52
54
  For debugging, use `--log-level=debug`.
53
55
 
54
56
  ## License
55
57
 
56
- [Apache-2.0](LICENSE)
58
+ [Apache-2.0](https://github.com/IBM/aspera-cli/blob/main/LICENSE)
@@ -25,25 +25,27 @@ module Aspera
25
25
  private_constant :LISTEN_LOCAL_ADDRESS, :SELECT_AVAILABLE_PORT
26
26
 
27
27
  # Options: same as values in option `transfer_info`
28
- # @param ascp_args [Array] (Params) Optional Additional arguments to ascp
29
- # @param wss [Boolean] (Params) `true`: if both SSH and wss in ts: prefer wss
30
- # @param quiet [Boolean] (Params) By default no native `ascp` progress bar
31
- # @param monitor [Boolean] (Params) Set to `false` to eliminate management port
32
- # @param trusted_certs [Array] (Params) Optional list of files with trusted certificates (stores)
33
- # @param client_ssh_key [String] (Params) Client SSH key option (from CLIENT_SSH_KEY_OPTIONS)
34
- # @param check_ignore_cb [Proc] (Params) Callback with host,port
28
+ # (Args) : Options that influence `ascp` arguments.
29
+ #
30
+ # @param ascp_args [Array] (Args) Optional Additional arguments to ascp
31
+ # @param wss [Boolean] (Args) `true`: if both SSH and wss in ts: prefer wss
32
+ # @param quiet [Boolean] (Args) By default no native `ascp` progress bar
33
+ # @param client_ssh_key [String] (Args) Client SSH key option (from CLIENT_SSH_KEY_OPTIONS)
34
+ # @param trusted_certs [Array<String>] (Args) (WSS) Optional list of files with trusted certificates (stores)
35
+ # @param check_ignore_cb [Proc] (Args) (WSS) Callback with host,port to check if WSS connection shall ignore certificate validity
35
36
  # @param spawn_timeout_sec [Integer] Timeout for ascp spawn
36
37
  # @param spawn_delay_sec [Integer] Optional delay to start between sessions
37
38
  # @param multi_incr_udp [Boolean] Optional `true`: increment UDP port for each session
38
39
  # @param resume [Hash] Optional Resume policy
40
+ # @param monitor [Boolean] Set to `false` to eliminate management port
39
41
  # @param management_cb [Proc] callback for management events
40
42
  # @param base_options [Hash] other options for base class
41
43
  def initialize(
42
44
  ascp_args: nil,
43
45
  wss: true,
44
46
  quiet: true,
45
- trusted_certs: nil,
46
47
  client_ssh_key: nil,
48
+ trusted_certs: nil,
47
49
  check_ignore_cb: nil,
48
50
  spawn_timeout_sec: 2,
49
51
  spawn_delay_sec: 2,
@@ -22,11 +22,11 @@ module Aspera
22
22
  Dir.children(File.dirname(File.expand_path(__FILE__)))
23
23
  .select{ |file| file.end_with?(Environment::RB_EXT)}
24
24
  .map{ |file| File.basename(file, Environment::RB_EXT).to_sym}
25
- .reject{ |item| IGNORED_ITEMS.include?(item)}.each_with_object({}) do |agent_sym, hash|
26
- hash[agent_sym] = {
25
+ .reject{ |item| IGNORED_ITEMS.include?(item)}.to_h do |agent_sym|
26
+ [agent_sym, {
27
27
  long: agent_sym.to_s.capitalize,
28
28
  short: agent_sym.eql?(:direct) ? :a : agent_sym.to_s[0].to_sym
29
- }.freeze
29
+ }.freeze]
30
30
  end.freeze
31
31
  private_constant :IGNORED_ITEMS
32
32
  end
@@ -59,7 +59,7 @@ module Aspera
59
59
  ts_tags = transfer_spec['tags']
60
60
  ts_tags[Transfer::Spec::TAG_RESERVED]['xfer_retry'] ||= 150 if ts_tags.is_a?(Hash) && ts_tags[Transfer::Spec::TAG_RESERVED].is_a?(Hash)
61
61
  # Optimization in case of sending to the same node
62
- # TODO: probably remove this, as /etc/hosts shall be used for that
62
+ # TODO: probably remove this, as /etc/hosts shall be used for that on server side.
63
63
  transfer_spec['remote_host'] = '127.0.0.1' if !transfer_spec['wss_enabled'] && transfer_spec['remote_host'].eql?(URI.parse(node_api_.base_url).host)
64
64
  resp = node_api_.create('ops/transfers', transfer_spec)
65
65
  @transfer_id = resp['id']
@@ -3,6 +3,7 @@
3
3
  require 'aspera/api/aoc.rb'
4
4
  module Aspera
5
5
  module Api
6
+ # Aspera License Entitlement Engine API client
6
7
  class Alee < Aspera::Rest
7
8
  def initialize(entitlement_id, customer_id, api_domain: AoC::SAAS_DOMAIN_PROD, version: 'v1')
8
9
  super(
@@ -7,11 +7,15 @@ require 'aspera/hash_ext'
7
7
  require 'aspera/data_repository'
8
8
  require 'aspera/transfer/spec'
9
9
  require 'aspera/api/node'
10
+ require 'aspera/rest_list'
10
11
  require 'base64'
11
12
 
12
13
  module Aspera
13
14
  module Api
15
+ # Aspera on Cloud API client
14
16
  class AoC < Rest
17
+ include RestList
18
+
15
19
  PRODUCT_NAME = 'Aspera on Cloud'
16
20
  # use default workspace if it is set, else none
17
21
  DEFAULT_WORKSPACE = ''
@@ -144,7 +148,7 @@ module Aspera
144
148
 
145
149
  # Call `block` with same query using paging and response information.
146
150
  # Block must return a 2 element `Array` with data and http response
147
- # @param query [Hash] Additionnal query parameters
151
+ # @param query [Hash] Additional query parameters
148
152
  # @return [Hash] Items and total number of items
149
153
  # @option return [Array<Hash>] :items The list of items
150
154
  # @option return [Integer] :total The total number of items
@@ -153,8 +157,8 @@ module Aspera
153
157
  Aspera.assert(block_given?)
154
158
  # set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
155
159
  query['per_page'] = 1000 unless query.key?('per_page')
156
- max_items = query.delete(Rest::MAX_ITEMS)
157
- max_pages = query.delete(Rest::MAX_PAGES)
160
+ max_items = query.delete(RestList::MAX_ITEMS)
161
+ max_pages = query.delete(RestList::MAX_PAGES)
158
162
  item_list = []
159
163
  total_count = nil
160
164
  current_page = query['page']
@@ -209,7 +213,7 @@ module Aspera
209
213
  when 'upload' then %w[mkdir write]
210
214
  when Array
211
215
  Aspera.assert_array_all(levels, String){'access_levels'}
212
- levels.each{ |level| Aspera.assert_value(level, Node::ACCESS_LEVELS){'access_level'}}
216
+ levels.each{ |level| Aspera.assert_values(level, Node::ACCESS_LEVELS){'access_level'}}
213
217
  levels
214
218
  else Aspera.error_unexpected_value(levels){"access_levels must be a list of #{Node::ACCESS_LEVELS.join(', ')} or one of edit, preview, download, upload"}
215
219
  end
@@ -492,7 +496,7 @@ module Aspera
492
496
  # in that case, the name is resolved and replaced with {type: , id: }
493
497
  # @param package_data [Hash] The whole package creation payload
494
498
  # @param rcpt_lst_field [String] The field in structure, i.e. recipients or bcc_recipients
495
- # @param new_user_option [Hash] Additionnal fields for contact creation
499
+ # @param new_user_option [Hash] Additional fields for contact creation
496
500
  # @return nil, `package_data` is modified
497
501
  def resolve_package_recipients(package_data, rcpt_lst_field, new_user_option)
498
502
  return unless package_data.key?(rcpt_lst_field)
@@ -510,7 +514,7 @@ module Aspera
510
514
  # email: user, else dropbox
511
515
  entity_type = short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
512
516
  begin
513
- full_recipient_info = lookup_by_name(entity_type, short_recipient_info, query: {'current_workspace_id' => ws_id})
517
+ full_recipient_info = lookup_by_name(entity_type, short_recipient_info, query: {'workspace_id' => ws_id})
514
518
  rescue EntityNotFound
515
519
  # dropboxes cannot be created on the fly
516
520
  Aspera.assert_values(entity_type, 'contacts', type: Error){"No such shared inbox in workspace #{ws_id}"}
@@ -5,7 +5,7 @@ require 'aspera/rest'
5
5
 
6
6
  module Aspera
7
7
  module Api
8
- # ATS API without authentication
8
+ # Aspera Transfer Service API client without authentication
9
9
  class Ats < Aspera::Rest
10
10
  SERVICE_BASE_URL = 'https://ats.aspera.io'
11
11
  # currently supported clouds
@@ -7,6 +7,7 @@ require 'xmlsimple'
7
7
 
8
8
  module Aspera
9
9
  module Api
10
+ # Cloud Object Storage Node API Client
10
11
  class CosNode < Node
11
12
  IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
12
13
  TOKEN_FIELD = 'delegated_refresh_token'
@@ -2,10 +2,11 @@
2
2
 
3
3
  require 'aspera/rest'
4
4
  require 'aspera/oauth/base'
5
+ require 'aspera/rest_list'
5
6
  require 'digest'
6
7
 
7
8
  module Aspera
8
- # Implement OAuth for Faspex public link
9
+ # OAuth for Faspex public link
9
10
  class FaspexPubLink < OAuth::Base
10
11
  class << self
11
12
  attr_accessor :additional_info
@@ -55,14 +56,14 @@ module Aspera
55
56
  end
56
57
  OAuth::Factory.instance.register_token_creator(FaspexPubLink)
57
58
  module Api
59
+ # Aspera Faspex 5 API Client
58
60
  class Faspex < Aspera::Rest
61
+ include RestList
62
+
59
63
  # endpoint for authentication API
60
64
  PATH_AUTH = 'auth'
61
65
  PATH_API_V5 = 'api/v5'
62
66
  PATH_HEALTH = 'configuration/ping'
63
- private_constant :PATH_AUTH,
64
- :PATH_API_V5,
65
- :PATH_HEALTH
66
67
  RECIPIENT_TYPES = %w[user workgroup external_user distribution_list shared_inbox].freeze
67
68
  PACKAGE_TERMINATED = %w[completed failed].freeze
68
69
  # list of supported mailbox types (to list packages)
@@ -148,6 +149,7 @@ module Aspera
148
149
  def initialize(
149
150
  url:,
150
151
  auth:,
152
+ root: PATH_API_V5,
151
153
  password: nil,
152
154
  client_id: nil,
153
155
  client_secret: nil,
@@ -161,23 +163,23 @@ module Aspera
161
163
  super(**
162
164
  case auth
163
165
  when :public_link
164
- # Get URL of final redirect of public link
165
- redir_url = Rest.new(base_url: url, redirect_max: 3).call(operation: 'GET', ret: :resp).uri.to_s
166
- Log.dump(:redir_url, redir_url, level: :trace1)
167
- # get context from query
168
- encoded_context = Rest.query_to_h(URI.parse(redir_url).query)['context']
166
+ # Get URL of final redirect of provided public link
167
+ final_url = Rest.new(base_url: url, redirect_max: 3).call(operation: 'GET', ret: :resp).uri.to_s
168
+ Log.dump(:final_url, final_url, level: :trace1)
169
+ # Get context from query
170
+ encoded_context = Rest.query_to_h(URI.parse(final_url).query)['context']
169
171
  raise ParameterError, 'Bad faspex5 public link, missing context in query' if encoded_context.nil?
170
172
  # public link information (contains passcode and allowed usage)
171
173
  @pub_link_context = JSON.parse(Base64.decode64(encoded_context))
172
174
  Log.dump(:pub_link_context, @pub_link_context, level: :trace1)
173
175
  # Get the base url, i.e. .../aspera/faspex
174
- base_url = redir_url.gsub(%r{/public/.*}, '').gsub(/\?.*/, '')
176
+ base_url = final_url.gsub(%r{/public/.*}, '').gsub(/\?.*/, '')
175
177
  # Get web UI client_id and redirect_uri
176
178
  # TODO: change this for something more reliable
177
179
  config = JSON.parse(Rest.new(base_url: "#{base_url}/config.js", redirect_max: 3).call(operation: 'GET').sub(/^[^=]+=/, '').gsub(/([a-z_]+):/, '"\1":').delete("\n ").tr("'", '"')).symbolize_keys
178
- Log.dump(:configjs, config)
180
+ Log.dump(:config_js, config)
179
181
  {
180
- base_url: "#{base_url}/#{PATH_API_V5}",
182
+ base_url: "#{base_url}/#{root}",
181
183
  auth: {
182
184
  type: :oauth2,
183
185
  base_url: "#{base_url}/#{PATH_AUTH}",
@@ -194,7 +196,7 @@ module Aspera
194
196
  Aspera.assert(password, type: ParameterError){'Missing password'}
195
197
  # the password here is the token copied directly from browser in developer mode
196
198
  {
197
- base_url: "#{url}/#{PATH_API_V5}",
199
+ base_url: "#{url}/#{root}",
198
200
  headers: {'Authorization' => password}
199
201
  }
200
202
  when :web
@@ -202,7 +204,7 @@ module Aspera
202
204
  Aspera.assert(redirect_uri, type: ParameterError){'Missing redirect_uri'}
203
205
  # opens a browser and ask user to auth using web
204
206
  {
205
- base_url: "#{url}/#{PATH_API_V5}",
207
+ base_url: "#{url}/#{root}",
206
208
  auth: {
207
209
  type: :oauth2,
208
210
  base_url: "#{url}/#{PATH_AUTH}",
@@ -217,7 +219,7 @@ module Aspera
217
219
  Aspera.assert(client_id, type: ParameterError){'Missing client_id'}
218
220
  Aspera.assert(private_key, type: ParameterError){'Missing private_key'}
219
221
  {
220
- base_url: "#{url}/#{PATH_API_V5}",
222
+ base_url: "#{url}/#{root}",
221
223
  auth: {
222
224
  type: :oauth2,
223
225
  base_url: "#{url}/#{PATH_AUTH}",
@@ -238,10 +240,6 @@ module Aspera
238
240
  end
239
241
  )
240
242
  end
241
-
242
- def auth_api
243
- Rest.new(**params, base_url: base_url.sub(PATH_API_V5, PATH_AUTH))
244
- end
245
243
  end
246
244
  end
247
245
  end
@@ -4,13 +4,18 @@ require 'aspera/log'
4
4
  require 'aspera/rest'
5
5
  require 'aspera/transfer/faux_file'
6
6
  require 'aspera/assert'
7
+ require 'net/protocol'
7
8
  require 'securerandom'
8
9
  require 'websocket'
9
10
  require 'base64'
10
11
  require 'json'
11
12
 
13
+ # throw exception on error, instead of error code
14
+ WebSocket.should_raise = true
15
+
12
16
  module Aspera
13
17
  module Api
18
+ # Aspera HTTP Gateway API client
14
19
  # Start a transfer using Aspera HTTP Gateway, using web socket secure for uploads
15
20
  # ref: https://api.ibm.com/explorer/catalog/aspera/product/ibm-aspera/api/http-gateway-api/doc/guides-toc
16
21
  # https://developer.ibm.com/apis/catalog?search=%22aspera%20http%22
@@ -75,6 +80,15 @@ module Aspera
75
80
  Log.log.trace2{"#{LOG_WS_SEND}counts: #{@shared_info[:count]}"}
76
81
  end
77
82
 
83
+ # Check header ourself and give precise error message, as websocket will only throw error without details
84
+ # @param [String] Response Header
85
+ # @return [String] Response Header
86
+ def validated_ws_response_header(header)
87
+ first_line = header.split("\r\n").first
88
+ raise RestCallError.new({messages: ["Unexpected: #{first_line}", 'Expected: 101 Switching Protocols']}) unless first_line.split(/\s+/, 3)[1].eql?('101')
89
+ header
90
+ end
91
+
78
92
  # message processing for read thread
79
93
  def process_received_message(message)
80
94
  Log.log.debug{"#{LOG_WS_RECV}message: [#{message}] (#{message.class})"}
@@ -153,8 +167,9 @@ module Aspera
153
167
  @ws_handshake = ::WebSocket::Handshake::Client.new(url: upload_url, headers: {})
154
168
  @ws_io.write(@ws_handshake.to_s)
155
169
  sleep(0.1)
156
- @ws_handshake << @ws_io.readuntil("\r\n\r\n")
157
- Aspera.assert(@ws_handshake.finished?){'Error in websocket handshake'}
170
+ # Get whole HTTP response header, Check and process
171
+ # no need to check `finished?` or result of `<<` (true), as we give the whole header at once
172
+ @ws_handshake << validated_ws_response_header(@ws_io.readuntil("\r\n\r\n"))
158
173
  Log.log.debug{"#{LOG_WS_SEND}handshake success"}
159
174
  # data shared between main thread and read thread
160
175
  @shared_info = {
@@ -232,14 +247,15 @@ module Aspera
232
247
  # throttling may have skipped last one
233
248
  @notify_cb&.call(:transfer, session_id: session_id, info: session_sent_bytes)
234
249
  @notify_cb&.call(:session_end, session_id: session_id)
235
- @notify_cb&.call(:end)
236
250
  ws_send(ws_type: :close, data: nil)
237
251
  Log.log.debug("Finished upload, waiting for end of #{THR_RECV} thread.")
238
252
  @ws_read_thread.join
239
253
  Log.log.debug{'Read thread joined'}
254
+ ensure
240
255
  # session no more used
241
256
  @ws_io = nil
242
257
  http_session&.finish
258
+ @notify_cb&.call(:end)
243
259
  end
244
260
 
245
261
  def download(transfer_spec)