aspera-cli 4.15.0 → 4.16.0
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/BUGS.md +29 -3
 - data/CHANGELOG.md +292 -228
 - data/CONTRIBUTING.md +69 -18
 - data/README.md +1102 -952
 - data/bin/ascli +13 -31
 - data/bin/asession +3 -1
 - data/examples/dascli +2 -2
 - data/lib/aspera/aoc.rb +28 -33
 - data/lib/aspera/ascmd.rb +3 -6
 - data/lib/aspera/assert.rb +45 -0
 - data/lib/aspera/cli/extended_value.rb +5 -5
 - data/lib/aspera/cli/formatter.rb +26 -13
 - data/lib/aspera/cli/hints.rb +4 -3
 - data/lib/aspera/cli/main.rb +16 -3
 - data/lib/aspera/cli/manager.rb +45 -36
 - data/lib/aspera/cli/plugin.rb +20 -13
 - data/lib/aspera/cli/plugins/aoc.rb +103 -73
 - data/lib/aspera/cli/plugins/ats.rb +4 -3
 - data/lib/aspera/cli/plugins/config.rb +114 -119
 - data/lib/aspera/cli/plugins/cos.rb +2 -2
 - data/lib/aspera/cli/plugins/faspex.rb +23 -19
 - data/lib/aspera/cli/plugins/faspex5.rb +75 -43
 - data/lib/aspera/cli/plugins/node.rb +28 -15
 - data/lib/aspera/cli/plugins/orchestrator.rb +4 -2
 - data/lib/aspera/cli/plugins/preview.rb +9 -7
 - data/lib/aspera/cli/plugins/server.rb +6 -3
 - data/lib/aspera/cli/plugins/shares.rb +30 -26
 - data/lib/aspera/cli/sync_actions.rb +9 -9
 - data/lib/aspera/cli/transfer_agent.rb +21 -14
 - data/lib/aspera/cli/transfer_progress.rb +2 -3
 - data/lib/aspera/cli/version.rb +1 -1
 - data/lib/aspera/command_line_builder.rb +13 -11
 - data/lib/aspera/cos_node.rb +3 -2
 - data/lib/aspera/coverage.rb +22 -0
 - data/lib/aspera/data_repository.rb +33 -2
 - data/lib/aspera/environment.rb +4 -2
 - data/lib/aspera/fasp/{agent_aspera.rb → agent_alpha.rb} +29 -39
 - data/lib/aspera/fasp/agent_base.rb +17 -7
 - data/lib/aspera/fasp/agent_direct.rb +88 -84
 - data/lib/aspera/fasp/agent_httpgw.rb +4 -3
 - data/lib/aspera/fasp/agent_node.rb +3 -2
 - data/lib/aspera/fasp/agent_trsdk.rb +79 -37
 - data/lib/aspera/fasp/installation.rb +51 -12
 - data/lib/aspera/fasp/management.rb +11 -6
 - data/lib/aspera/fasp/parameters.rb +53 -47
 - data/lib/aspera/fasp/resume_policy.rb +7 -5
 - data/lib/aspera/fasp/sync.rb +273 -0
 - data/lib/aspera/fasp/transfer_spec.rb +10 -8
 - data/lib/aspera/fasp/uri.rb +2 -2
 - data/lib/aspera/faspex_gw.rb +11 -8
 - data/lib/aspera/faspex_postproc.rb +6 -5
 - data/lib/aspera/id_generator.rb +3 -1
 - data/lib/aspera/json_rpc.rb +10 -8
 - data/lib/aspera/keychain/encrypted_hash.rb +46 -11
 - data/lib/aspera/keychain/macos_security.rb +15 -13
 - data/lib/aspera/log.rb +4 -3
 - data/lib/aspera/nagios.rb +7 -2
 - data/lib/aspera/node.rb +17 -16
 - data/lib/aspera/node_simulator.rb +214 -0
 - data/lib/aspera/oauth.rb +22 -19
 - data/lib/aspera/persistency_action_once.rb +13 -14
 - data/lib/aspera/persistency_folder.rb +3 -2
 - data/lib/aspera/preview/file_types.rb +53 -267
 - data/lib/aspera/preview/generator.rb +7 -5
 - data/lib/aspera/preview/terminal.rb +14 -5
 - data/lib/aspera/preview/utils.rb +8 -7
 - data/lib/aspera/proxy_auto_config.rb +6 -3
 - data/lib/aspera/rest.rb +29 -13
 - data/lib/aspera/rest_error_analyzer.rb +1 -0
 - data/lib/aspera/rest_errors_aspera.rb +2 -0
 - data/lib/aspera/secret_hider.rb +5 -2
 - data/lib/aspera/ssh.rb +10 -8
 - data/lib/aspera/temp_file_manager.rb +1 -1
 - data/lib/aspera/web_server_simple.rb +2 -1
 - data.tar.gz.sig +0 -0
 - metadata +96 -45
 - metadata.gz.sig +0 -0
 - data/lib/aspera/sync.rb +0 -219
 
    
        data/lib/aspera/log.rb
    CHANGED
    
    | 
         @@ -3,6 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'aspera/colors'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'aspera/secret_hider'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'aspera/environment'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'aspera/assert'
         
     | 
| 
       6 
7 
     | 
    
         
             
            require 'logger'
         
     | 
| 
       7 
8 
     | 
    
         
             
            require 'pp'
         
     | 
| 
       8 
9 
     | 
    
         
             
            require 'json'
         
     | 
| 
         @@ -39,6 +40,7 @@ class Logger 
     | 
|
| 
       39 
40 
     | 
    
         
             
                  end
         
     | 
| 
       40 
41 
     | 
    
         
             
                EOM
         
     | 
| 
       41 
42 
     | 
    
         
             
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
              # declare methods for all levels
         
     | 
| 
       42 
44 
     | 
    
         
             
              Logger::Severity.constants.each { |severity| make_methods(severity) }
         
     | 
| 
       43 
45 
     | 
    
         
             
            end
         
     | 
| 
       44 
46 
     | 
    
         | 
| 
         @@ -97,8 +99,7 @@ module Aspera 
     | 
|
| 
       97 
99 
     | 
    
         
             
                  Logger::Severity.constants.each do |name|
         
     | 
| 
       98 
100 
     | 
    
         
             
                    return name.downcase.to_sym if @logger.level.eql?(Logger::Severity.const_get(name))
         
     | 
| 
       99 
101 
     | 
    
         
             
                  end
         
     | 
| 
       100 
     | 
    
         
            -
                   
     | 
| 
       101 
     | 
    
         
            -
                  raise "INTERNAL ERROR: unexpected level #{@logger.level}"
         
     | 
| 
      
 102 
     | 
    
         
            +
                  error_unexpected_value(@logger.level){'log level'}
         
     | 
| 
       102 
103 
     | 
    
         
             
                end
         
     | 
| 
       103 
104 
     | 
    
         | 
| 
       104 
105 
     | 
    
         
             
                # change underlying logger, but keep log level
         
     | 
| 
         @@ -123,7 +124,7 @@ module Aspera 
     | 
|
| 
       123 
124 
     | 
    
         
             
                    end
         
     | 
| 
       124 
125 
     | 
    
         
             
                    @logger = Syslog::Logger.new(@program_name, Syslog::LOG_LOCAL2)
         
     | 
| 
       125 
126 
     | 
    
         
             
                  else
         
     | 
| 
       126 
     | 
    
         
            -
                    raise "unknown log type: #{new_log_type 
     | 
| 
      
 127 
     | 
    
         
            +
                    raise "unknown log type: #{new_log_type}, use one of: #{LOG_TYPES.join(', ')}"
         
     | 
| 
       127 
128 
     | 
    
         
             
                  end
         
     | 
| 
       128 
129 
     | 
    
         
             
                  @logger.level = current_severity_integer
         
     | 
| 
       129 
130 
     | 
    
         
             
                  @logger_type = new_log_type
         
     | 
    
        data/lib/aspera/nagios.rb
    CHANGED
    
    | 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'aspera/log'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'aspera/assert'
         
     | 
| 
       3 
5 
     | 
    
         
             
            require 'date'
         
     | 
| 
       4 
6 
     | 
    
         | 
| 
       5 
7 
     | 
    
         
             
            module Aspera
         
     | 
| 
         @@ -21,8 +23,11 @@ module Aspera 
     | 
|
| 
       21 
23 
     | 
    
         
             
                class << self
         
     | 
| 
       22 
24 
     | 
    
         
             
                  # process results of a analysis and display status and exit with code
         
     | 
| 
       23 
25 
     | 
    
         
             
                  def process(data)
         
     | 
| 
       24 
     | 
    
         
            -
                     
     | 
| 
       25 
     | 
    
         
            -
                     
     | 
| 
      
 26 
     | 
    
         
            +
                    assert_type(data, Array)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    assert(!data.empty?){'data is empty'}
         
     | 
| 
      
 28 
     | 
    
         
            +
                    %w[status component message].each do |c|
         
     | 
| 
      
 29 
     | 
    
         
            +
                      assert(data.first.key?(c)){"result must have #{c}"}
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
       26 
31 
     | 
    
         
             
                    res_errors = data.reject{|s|s['status'].eql?('ok')}
         
     | 
| 
       27 
32 
     | 
    
         
             
                    # keep only errors in case of problem, other ok are assumed so
         
     | 
| 
       28 
33 
     | 
    
         
             
                    data = res_errors unless res_errors.empty?
         
     | 
    
        data/lib/aspera/node.rb
    CHANGED
    
    | 
         @@ -5,6 +5,7 @@ require 'aspera/fasp/transfer_spec' 
     | 
|
| 
       5 
5 
     | 
    
         
             
            require 'aspera/rest'
         
     | 
| 
       6 
6 
     | 
    
         
             
            require 'aspera/oauth'
         
     | 
| 
       7 
7 
     | 
    
         
             
            require 'aspera/log'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'aspera/assert'
         
     | 
| 
       8 
9 
     | 
    
         
             
            require 'aspera/environment'
         
     | 
| 
       9 
10 
     | 
    
         
             
            require 'zlib'
         
     | 
| 
       10 
11 
     | 
    
         
             
            require 'base64'
         
     | 
| 
         @@ -50,7 +51,7 @@ module Aspera 
     | 
|
| 
       50 
51 
     | 
    
         
             
                      end
         
     | 
| 
       51 
52 
     | 
    
         
             
                      return lambda{|f|File.fnmatch(match_expression, f['name'], File::FNM_DOTMATCH)}
         
     | 
| 
       52 
53 
     | 
    
         
             
                    when NilClass then return ->(_){true}
         
     | 
| 
       53 
     | 
    
         
            -
                    else  
     | 
| 
      
 54 
     | 
    
         
            +
                    else error_unexpected_value(match_expression.class.name, exception_class: Cli::BadArgument)
         
     | 
| 
       54 
55 
     | 
    
         
             
                    end
         
     | 
| 
       55 
56 
     | 
    
         
             
                  end
         
     | 
| 
       56 
57 
     | 
    
         | 
| 
         @@ -65,8 +66,8 @@ module Aspera 
     | 
|
| 
       65 
66 
     | 
    
         | 
| 
       66 
67 
     | 
    
         
             
                  def decode_scope(scope)
         
     | 
| 
       67 
68 
     | 
    
         
             
                    items = scope.split(SCOPE_SEPARATOR, 2)
         
     | 
| 
       68 
     | 
    
         
            -
                     
     | 
| 
       69 
     | 
    
         
            -
                     
     | 
| 
      
 69 
     | 
    
         
            +
                    assert(items.length.eql?(2)){"invalid scope: #{scope}"}
         
     | 
| 
      
 70 
     | 
    
         
            +
                    assert(items[0].start_with?(SCOPE_PREFIX)){"invalid scope: #{scope}"}
         
     | 
| 
       70 
71 
     | 
    
         
             
                    return {access_key: items[0][SCOPE_PREFIX.length..-1], scope: items[1]}
         
     | 
| 
       71 
72 
     | 
    
         
             
                  end
         
     | 
| 
       72 
73 
     | 
    
         | 
| 
         @@ -74,11 +75,11 @@ module Aspera 
     | 
|
| 
       74 
75 
     | 
    
         
             
                  # @param payload [String] JSON payload to be included in the token
         
     | 
| 
       75 
76 
     | 
    
         
             
                  # @param private_key [OpenSSL::PKey::RSA] Private key to sign the token
         
     | 
| 
       76 
77 
     | 
    
         
             
                  def bearer_token(access_key:, payload:, private_key:)
         
     | 
| 
       77 
     | 
    
         
            -
                     
     | 
| 
       78 
     | 
    
         
            -
                     
     | 
| 
       79 
     | 
    
         
            -
                     
     | 
| 
       80 
     | 
    
         
            -
                     
     | 
| 
       81 
     | 
    
         
            -
                     
     | 
| 
      
 78 
     | 
    
         
            +
                    assert_type(payload, Hash)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    assert(payload.key?('user_id'))
         
     | 
| 
      
 80 
     | 
    
         
            +
                    assert_type(payload['user_id'], String)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    assert(!payload['user_id'].empty?)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    assert_type(private_key, OpenSSL::PKey::RSA)
         
     | 
| 
       82 
83 
     | 
    
         
             
                    # manage convenience parameters
         
     | 
| 
       83 
84 
     | 
    
         
             
                    expiration_sec = payload['_validity'] || BEARER_TOKEN_VALIDITY_DEFAULT
         
     | 
| 
       84 
85 
     | 
    
         
             
                    payload.delete('_validity')
         
     | 
| 
         @@ -104,7 +105,7 @@ module Aspera 
     | 
|
| 
       104 
105 
     | 
    
         
             
                    # if username is not provided, use the access key from the token
         
     | 
| 
       105 
106 
     | 
    
         
             
                    if access_key.nil?
         
     | 
| 
       106 
107 
     | 
    
         
             
                      access_key = Aspera::Node.decode_scope(Aspera::Node.decode_bearer_token(Oauth.bearer_extract(bearer_auth))['scope'])[:access_key]
         
     | 
| 
       107 
     | 
    
         
            -
                       
     | 
| 
      
 108 
     | 
    
         
            +
                      assert(!access_key.nil?)
         
     | 
| 
       108 
109 
     | 
    
         
             
                    end
         
     | 
| 
       109 
110 
     | 
    
         
             
                    return {
         
     | 
| 
       110 
111 
     | 
    
         
             
                      Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => access_key,
         
     | 
| 
         @@ -131,10 +132,10 @@ module Aspera 
     | 
|
| 
       131 
132 
     | 
    
         
             
                  @add_tspec = add_tspec
         
     | 
| 
       132 
133 
     | 
    
         
             
                  if !@app_info.nil?
         
     | 
| 
       133 
134 
     | 
    
         
             
                    REQUIRED_APP_INFO_FIELDS.each do |field|
         
     | 
| 
       134 
     | 
    
         
            -
                       
     | 
| 
      
 135 
     | 
    
         
            +
                      assert(@app_info.key?(field)){"app_info lacks field #{field}"}
         
     | 
| 
       135 
136 
     | 
    
         
             
                    end
         
     | 
| 
       136 
137 
     | 
    
         
             
                    REQUIRED_APP_API_METHODS.each do |method|
         
     | 
| 
       137 
     | 
    
         
            -
                       
     | 
| 
      
 138 
     | 
    
         
            +
                      assert(@app_info[:api].respond_to?(method)){"#{@app_info[:api].class} lacks method #{method}"}
         
     | 
| 
       138 
139 
     | 
    
         
             
                    end
         
     | 
| 
       139 
140 
     | 
    
         
             
                  end
         
     | 
| 
       140 
141 
     | 
    
         
             
                end
         
     | 
| 
         @@ -165,8 +166,8 @@ module Aspera 
     | 
|
| 
       165 
166 
     | 
    
         
             
                # @param top_file_path [String] path of top folder (default = /)
         
     | 
| 
       166 
167 
     | 
    
         
             
                # @param block [Proc] processing method, arguments: entry, path, state
         
     | 
| 
       167 
168 
     | 
    
         
             
                def process_folder_tree(state:, top_file_id:, top_file_path: '/', &block)
         
     | 
| 
       168 
     | 
    
         
            -
                   
     | 
| 
       169 
     | 
    
         
            -
                   
     | 
| 
      
 169 
     | 
    
         
            +
                  assert(!top_file_path.nil?){'top_file_path not set'}
         
     | 
| 
      
 170 
     | 
    
         
            +
                  assert(block){'Missing block'}
         
     | 
| 
       170 
171 
     | 
    
         
             
                  # start at top folder
         
     | 
| 
       171 
172 
     | 
    
         
             
                  folders_to_explore = [{id: top_file_id, path: top_file_path}]
         
     | 
| 
       172 
173 
     | 
    
         
             
                  Log.log.debug{Log.dump(:folders_to_explore, folders_to_explore)}
         
     | 
| 
         @@ -207,7 +208,7 @@ module Aspera 
     | 
|
| 
       207 
208 
     | 
    
         
             
                # @param path [String]  file path
         
     | 
| 
       208 
209 
     | 
    
         
             
                # @return [Hash] {.api,.file_id}
         
     | 
| 
       209 
210 
     | 
    
         
             
                def resolve_api_fid(top_file_id, path)
         
     | 
| 
       210 
     | 
    
         
            -
                   
     | 
| 
      
 211 
     | 
    
         
            +
                  assert_type(top_file_id, String)
         
     | 
| 
       211 
212 
     | 
    
         
             
                  process_last_link = path.end_with?(PATH_SEPARATOR)
         
     | 
| 
       212 
213 
     | 
    
         
             
                  path_elements = path.split(PATH_SEPARATOR).reject(&:empty?)
         
     | 
| 
       213 
214 
     | 
    
         
             
                  return {api: self, file_id: top_file_id} if path_elements.empty?
         
     | 
| 
         @@ -276,14 +277,14 @@ module Aspera 
     | 
|
| 
       276 
277 
     | 
    
         
             
                  case params[:auth][:type]
         
     | 
| 
       277 
278 
     | 
    
         
             
                  when :basic
         
     | 
| 
       278 
279 
     | 
    
         
             
                    ak_name = params[:auth][:username]
         
     | 
| 
       279 
     | 
    
         
            -
                     
     | 
| 
      
 280 
     | 
    
         
            +
                    assert(params[:auth][:password]){'no secret in node object'}
         
     | 
| 
       280 
281 
     | 
    
         
             
                    ak_token = Rest.basic_token(params[:auth][:username], params[:auth][:password])
         
     | 
| 
       281 
282 
     | 
    
         
             
                  when :oauth2
         
     | 
| 
       282 
283 
     | 
    
         
             
                    ak_name = params[:headers][HEADER_X_ASPERA_ACCESS_KEY]
         
     | 
| 
       283 
284 
     | 
    
         
             
                    # TODO: token_generation_lambda = lambda{|do_refresh|oauth_token(force_refresh: do_refresh)}
         
     | 
| 
       284 
285 
     | 
    
         
             
                    # get bearer token, possibly use cache
         
     | 
| 
       285 
286 
     | 
    
         
             
                    ak_token = oauth_token(force_refresh: false)
         
     | 
| 
       286 
     | 
    
         
            -
                  else  
     | 
| 
      
 287 
     | 
    
         
            +
                  else error_unexpected_value(params[:auth][:type])
         
     | 
| 
       287 
288 
     | 
    
         
             
                  end
         
     | 
| 
       288 
289 
     | 
    
         
             
                  transfer_spec = {
         
     | 
| 
       289 
290 
     | 
    
         
             
                    'direction' => direction,
         
     | 
| 
         @@ -0,0 +1,214 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'aspera/log'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'aspera/fasp/installation'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'aspera/fasp/agent_direct'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'webrick'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Aspera
         
     | 
| 
      
 10 
     | 
    
         
            +
              # this class answers the Faspex /send API and creates a package on Aspera on Cloud
         
     | 
| 
      
 11 
     | 
    
         
            +
              class NodeSimulatorServlet < WEBrick::HTTPServlet::AbstractServlet
         
     | 
| 
      
 12 
     | 
    
         
            +
                PATH_TRANSFERS = '/ops/transfers'
         
     | 
| 
      
 13 
     | 
    
         
            +
                PATH_ONE_TRANSFER = %r{/ops/transfers/(.+)$}
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @param app_api [Aspera::AoC]
         
     | 
| 
      
 15 
     | 
    
         
            +
                # @param app_context [String]
         
     | 
| 
      
 16 
     | 
    
         
            +
                def initialize(server, credentials, transfer)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  super(server)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @credentials = credentials
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @xfer_manager = Aspera::Fasp::AgentDirect.new
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def do_POST(request, response)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  case request.path
         
     | 
| 
      
 24 
     | 
    
         
            +
                  when PATH_TRANSFERS
         
     | 
| 
      
 25 
     | 
    
         
            +
                    job_id = @xfer_manager.start_transfer(JSON.parse(request.body))
         
     | 
| 
      
 26 
     | 
    
         
            +
                    session = @xfer_manager.sessions_by_job(job_id).first
         
     | 
| 
      
 27 
     | 
    
         
            +
                    result = session[:ts].clone
         
     | 
| 
      
 28 
     | 
    
         
            +
                    result['id'] = job_id
         
     | 
| 
      
 29 
     | 
    
         
            +
                    set_json_response(response, result)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    Log.log.debug{">>> transfer started: #{job_id}"}
         
     | 
| 
      
 31 
     | 
    
         
            +
                  else
         
     | 
| 
      
 32 
     | 
    
         
            +
                    set_json_response(response, [{error: 'Bad request'}], code: 400)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def do_GET(request, response)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  case request.path
         
     | 
| 
      
 38 
     | 
    
         
            +
                  when '/info'
         
     | 
| 
      
 39 
     | 
    
         
            +
                    info = Aspera::Fasp::Installation.instance.ascp_info
         
     | 
| 
      
 40 
     | 
    
         
            +
                    set_json_response(response, {
         
     | 
| 
      
 41 
     | 
    
         
            +
                      application:                           'node',
         
     | 
| 
      
 42 
     | 
    
         
            +
                      current_time:                          Time.now.utc.iso8601(0),
         
     | 
| 
      
 43 
     | 
    
         
            +
                      version:                               info['ascp_version'].gsub(/ .*$/, ''),
         
     | 
| 
      
 44 
     | 
    
         
            +
                      license_expiration_date:               info['expiration_date'],
         
     | 
| 
      
 45 
     | 
    
         
            +
                      license_max_rate:                      info['maximum_bandwidth'],
         
     | 
| 
      
 46 
     | 
    
         
            +
                      os:                                    %x(uname -srv).chomp,
         
     | 
| 
      
 47 
     | 
    
         
            +
                      aej_status:                            'disconnected',
         
     | 
| 
      
 48 
     | 
    
         
            +
                      async_reporting:                       'yes',
         
     | 
| 
      
 49 
     | 
    
         
            +
                      transfer_activity_reporting:           'yes',
         
     | 
| 
      
 50 
     | 
    
         
            +
                      transfer_user:                         'xfer',
         
     | 
| 
      
 51 
     | 
    
         
            +
                      docroot:                               'file:////data/aoc/eudemo-sedemo',
         
     | 
| 
      
 52 
     | 
    
         
            +
                      node_id:                               '2bbdcc39-f789-4d47-8163-6767fc14f421',
         
     | 
| 
      
 53 
     | 
    
         
            +
                      cluster_id:                            '6dae2844-d1a9-47a5-916d-9b3eac3ea466',
         
     | 
| 
      
 54 
     | 
    
         
            +
                      acls:                                  [],
         
     | 
| 
      
 55 
     | 
    
         
            +
                      access_key_configuration_capabilities: {
         
     | 
| 
      
 56 
     | 
    
         
            +
                        transfer: %w[
         
     | 
| 
      
 57 
     | 
    
         
            +
                          cipher
         
     | 
| 
      
 58 
     | 
    
         
            +
                          policy
         
     | 
| 
      
 59 
     | 
    
         
            +
                          target_rate_cap_kbps
         
     | 
| 
      
 60 
     | 
    
         
            +
                          target_rate_kbps
         
     | 
| 
      
 61 
     | 
    
         
            +
                          preserve_timestamps
         
     | 
| 
      
 62 
     | 
    
         
            +
                          content_protection_secret
         
     | 
| 
      
 63 
     | 
    
         
            +
                          aggressiveness
         
     | 
| 
      
 64 
     | 
    
         
            +
                          token_encryption_key
         
     | 
| 
      
 65 
     | 
    
         
            +
                          byok_enabled
         
     | 
| 
      
 66 
     | 
    
         
            +
                          bandwidth_flow_network_rc_module
         
     | 
| 
      
 67 
     | 
    
         
            +
                          file_checksum_type],
         
     | 
| 
      
 68 
     | 
    
         
            +
                        server:   %w[
         
     | 
| 
      
 69 
     | 
    
         
            +
                          activity_event_logging
         
     | 
| 
      
 70 
     | 
    
         
            +
                          activity_file_event_logging
         
     | 
| 
      
 71 
     | 
    
         
            +
                          recursive_counts
         
     | 
| 
      
 72 
     | 
    
         
            +
                          aej_logging
         
     | 
| 
      
 73 
     | 
    
         
            +
                          wss_enabled
         
     | 
| 
      
 74 
     | 
    
         
            +
                          activity_transfer_ignore_skipped_files
         
     | 
| 
      
 75 
     | 
    
         
            +
                          activity_files_max
         
     | 
| 
      
 76 
     | 
    
         
            +
                          access_key_credentials_encryption_type
         
     | 
| 
      
 77 
     | 
    
         
            +
                          discovery
         
     | 
| 
      
 78 
     | 
    
         
            +
                          auto_delete
         
     | 
| 
      
 79 
     | 
    
         
            +
                          allow
         
     | 
| 
      
 80 
     | 
    
         
            +
                          deny]
         
     | 
| 
      
 81 
     | 
    
         
            +
                      },
         
     | 
| 
      
 82 
     | 
    
         
            +
                      capabilities:                          [
         
     | 
| 
      
 83 
     | 
    
         
            +
                        {name:  'sync', value: true},
         
     | 
| 
      
 84 
     | 
    
         
            +
                        {name:  'watchfolder', value: true},
         
     | 
| 
      
 85 
     | 
    
         
            +
                        {name:  'symbolic_links', value: true},
         
     | 
| 
      
 86 
     | 
    
         
            +
                        {name:  'move_file', value: true},
         
     | 
| 
      
 87 
     | 
    
         
            +
                        {name:  'move_directory', value: true},
         
     | 
| 
      
 88 
     | 
    
         
            +
                        {name:  'filelock', value: false},
         
     | 
| 
      
 89 
     | 
    
         
            +
                        {name:  'ssh_fingerprint', value: false},
         
     | 
| 
      
 90 
     | 
    
         
            +
                        {name:  'aej_version', value: '1.0'},
         
     | 
| 
      
 91 
     | 
    
         
            +
                        {name:  'page', value: true},
         
     | 
| 
      
 92 
     | 
    
         
            +
                        {name:  'file_id_version', value: '2.0'},
         
     | 
| 
      
 93 
     | 
    
         
            +
                        {name:  'auto_delete', value: false}],
         
     | 
| 
      
 94 
     | 
    
         
            +
                      settings:                              [
         
     | 
| 
      
 95 
     | 
    
         
            +
                        {name:  'content_protection_required', value: false},
         
     | 
| 
      
 96 
     | 
    
         
            +
                        {name:  'content_protection_strong_pass_required', value: false},
         
     | 
| 
      
 97 
     | 
    
         
            +
                        {name:  'filelock_restriction', value: 'none'},
         
     | 
| 
      
 98 
     | 
    
         
            +
                        {name:  'ssh_fingerprint', value: nil},
         
     | 
| 
      
 99 
     | 
    
         
            +
                        {name:  'wss_enabled', value: false},
         
     | 
| 
      
 100 
     | 
    
         
            +
                        {name:  'wss_port', value: 443}
         
     | 
| 
      
 101 
     | 
    
         
            +
                      ]})
         
     | 
| 
      
 102 
     | 
    
         
            +
                  when PATH_TRANSFERS
         
     | 
| 
      
 103 
     | 
    
         
            +
                    result = @xfer_manager.sessions.map { |session| job_to_transfer(session) }
         
     | 
| 
      
 104 
     | 
    
         
            +
                    set_json_response(response, result)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  when PATH_ONE_TRANSFER
         
     | 
| 
      
 106 
     | 
    
         
            +
                    job_id = request.path.match(PATH_ONE_TRANSFER)[1]
         
     | 
| 
      
 107 
     | 
    
         
            +
                    set_json_response(response, job_to_transfer(@xfer_manager.sessions_by_job(job_id).first))
         
     | 
| 
      
 108 
     | 
    
         
            +
                  else
         
     | 
| 
      
 109 
     | 
    
         
            +
                    set_json_response(response, [{error: 'Unknown request'}], code: 400)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def set_json_response(response, json, code: 200)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  response.status = code
         
     | 
| 
      
 115 
     | 
    
         
            +
                  response['Content-Type'] = 'application/json'
         
     | 
| 
      
 116 
     | 
    
         
            +
                  response.body = json.to_json
         
     | 
| 
      
 117 
     | 
    
         
            +
                  Log.log.trace1{Log.dump('response', json)}
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def job_to_transfer(job)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  session = {
         
     | 
| 
      
 122 
     | 
    
         
            +
                    id:                    'bafc72b8-366c-4501-8095-47208183d6b8',
         
     | 
| 
      
 123 
     | 
    
         
            +
                    client_node_id:        '',
         
     | 
| 
      
 124 
     | 
    
         
            +
                    server_node_id:        '2bbdcc39-f789-4d47-8163-6767fc14f421',
         
     | 
| 
      
 125 
     | 
    
         
            +
                    client_ip_address:     '192.168.0.100',
         
     | 
| 
      
 126 
     | 
    
         
            +
                    server_ip_address:     '5.10.114.4',
         
     | 
| 
      
 127 
     | 
    
         
            +
                    status:                'running',
         
     | 
| 
      
 128 
     | 
    
         
            +
                    retry_timeout:         3600,
         
     | 
| 
      
 129 
     | 
    
         
            +
                    retry_count:           0,
         
     | 
| 
      
 130 
     | 
    
         
            +
                    start_time_usec:       1701094040000000,
         
     | 
| 
      
 131 
     | 
    
         
            +
                    end_time_usec:         nil,
         
     | 
| 
      
 132 
     | 
    
         
            +
                    elapsed_usec:          405312,
         
     | 
| 
      
 133 
     | 
    
         
            +
                    bytes_transferred:     26,
         
     | 
| 
      
 134 
     | 
    
         
            +
                    bytes_written:         26,
         
     | 
| 
      
 135 
     | 
    
         
            +
                    bytes_lost:            0,
         
     | 
| 
      
 136 
     | 
    
         
            +
                    files_completed:       1,
         
     | 
| 
      
 137 
     | 
    
         
            +
                    directories_completed: 0,
         
     | 
| 
      
 138 
     | 
    
         
            +
                    target_rate_kbps:      500000,
         
     | 
| 
      
 139 
     | 
    
         
            +
                    min_rate_kbps:         0,
         
     | 
| 
      
 140 
     | 
    
         
            +
                    calc_rate_kbps:        9900,
         
     | 
| 
      
 141 
     | 
    
         
            +
                    network_delay_usec:    40000,
         
     | 
| 
      
 142 
     | 
    
         
            +
                    avg_rate_kbps:         0.51,
         
     | 
| 
      
 143 
     | 
    
         
            +
                    error_code:            0,
         
     | 
| 
      
 144 
     | 
    
         
            +
                    error_desc:            '',
         
     | 
| 
      
 145 
     | 
    
         
            +
                    source_statistics:     {
         
     | 
| 
      
 146 
     | 
    
         
            +
                      args_scan_attempted:  1,
         
     | 
| 
      
 147 
     | 
    
         
            +
                      args_scan_completed:  1,
         
     | 
| 
      
 148 
     | 
    
         
            +
                      paths_scan_attempted: 1,
         
     | 
| 
      
 149 
     | 
    
         
            +
                      paths_scan_failed:    0,
         
     | 
| 
      
 150 
     | 
    
         
            +
                      paths_scan_skipped:   0,
         
     | 
| 
      
 151 
     | 
    
         
            +
                      paths_scan_excluded:  0,
         
     | 
| 
      
 152 
     | 
    
         
            +
                      dirs_scan_completed:  0,
         
     | 
| 
      
 153 
     | 
    
         
            +
                      files_scan_completed: 1,
         
     | 
| 
      
 154 
     | 
    
         
            +
                      dirs_xfer_attempted:  0,
         
     | 
| 
      
 155 
     | 
    
         
            +
                      dirs_xfer_fail:       0,
         
     | 
| 
      
 156 
     | 
    
         
            +
                      files_xfer_attempted: 1,
         
     | 
| 
      
 157 
     | 
    
         
            +
                      files_xfer_fail:      0,
         
     | 
| 
      
 158 
     | 
    
         
            +
                      files_xfer_noxfer:    0
         
     | 
| 
      
 159 
     | 
    
         
            +
                    },
         
     | 
| 
      
 160 
     | 
    
         
            +
                    precalc:               {
         
     | 
| 
      
 161 
     | 
    
         
            +
                      enabled:              true,
         
     | 
| 
      
 162 
     | 
    
         
            +
                      status:               'ready',
         
     | 
| 
      
 163 
     | 
    
         
            +
                      bytes_expected:       0,
         
     | 
| 
      
 164 
     | 
    
         
            +
                      directories_expected: 0,
         
     | 
| 
      
 165 
     | 
    
         
            +
                      files_expected:       0,
         
     | 
| 
      
 166 
     | 
    
         
            +
                      files_excluded:       0,
         
     | 
| 
      
 167 
     | 
    
         
            +
                      files_special:        0,
         
     | 
| 
      
 168 
     | 
    
         
            +
                      files_failed:         1
         
     | 
| 
      
 169 
     | 
    
         
            +
                    }}
         
     | 
| 
      
 170 
     | 
    
         
            +
                  return {
         
     | 
| 
      
 171 
     | 
    
         
            +
                    id:                    '609a667d-642e-4290-9312-b4d20d3c0159',
         
     | 
| 
      
 172 
     | 
    
         
            +
                    status:                'running',
         
     | 
| 
      
 173 
     | 
    
         
            +
                    start_spec:            job[:ts],
         
     | 
| 
      
 174 
     | 
    
         
            +
                    sessions:              [session],
         
     | 
| 
      
 175 
     | 
    
         
            +
                    bytes_transferred:     26,
         
     | 
| 
      
 176 
     | 
    
         
            +
                    bytes_written:         26,
         
     | 
| 
      
 177 
     | 
    
         
            +
                    bytes_lost:            0,
         
     | 
| 
      
 178 
     | 
    
         
            +
                    avg_rate_kbps:         0.51,
         
     | 
| 
      
 179 
     | 
    
         
            +
                    files_completed:       1,
         
     | 
| 
      
 180 
     | 
    
         
            +
                    files_skipped:         0,
         
     | 
| 
      
 181 
     | 
    
         
            +
                    directories_completed: 0,
         
     | 
| 
      
 182 
     | 
    
         
            +
                    start_time_usec:       1701094040000000,
         
     | 
| 
      
 183 
     | 
    
         
            +
                    end_time_usec:         1701094040405312,
         
     | 
| 
      
 184 
     | 
    
         
            +
                    elapsed_usec:          405312,
         
     | 
| 
      
 185 
     | 
    
         
            +
                    error_code:            0,
         
     | 
| 
      
 186 
     | 
    
         
            +
                    error_desc:            '',
         
     | 
| 
      
 187 
     | 
    
         
            +
                    precalc:               {
         
     | 
| 
      
 188 
     | 
    
         
            +
                      status:               'ready',
         
     | 
| 
      
 189 
     | 
    
         
            +
                      bytes_expected:       0,
         
     | 
| 
      
 190 
     | 
    
         
            +
                      files_expected:       0,
         
     | 
| 
      
 191 
     | 
    
         
            +
                      directories_expected: 0,
         
     | 
| 
      
 192 
     | 
    
         
            +
                      files_special:        0,
         
     | 
| 
      
 193 
     | 
    
         
            +
                      files_failed:         1
         
     | 
| 
      
 194 
     | 
    
         
            +
                    },
         
     | 
| 
      
 195 
     | 
    
         
            +
                    files:                 [{
         
     | 
| 
      
 196 
     | 
    
         
            +
                      id:              'd1b5c112-82b75425-860745fc-93851671-64541bdd',
         
     | 
| 
      
 197 
     | 
    
         
            +
                      path:            '/workspaces/45071/packages/bYA_ilq73g.asp-package/contents/data_file.bin',
         
     | 
| 
      
 198 
     | 
    
         
            +
                      start_time_usec: 1701094040000000,
         
     | 
| 
      
 199 
     | 
    
         
            +
                      elapsed_usec:    105616,
         
     | 
| 
      
 200 
     | 
    
         
            +
                      end_time_usec:   1701094040001355,
         
     | 
| 
      
 201 
     | 
    
         
            +
                      status:          'completed',
         
     | 
| 
      
 202 
     | 
    
         
            +
                      error_code:      0,
         
     | 
| 
      
 203 
     | 
    
         
            +
                      error_desc:      '',
         
     | 
| 
      
 204 
     | 
    
         
            +
                      size:            26,
         
     | 
| 
      
 205 
     | 
    
         
            +
                      type:            'file',
         
     | 
| 
      
 206 
     | 
    
         
            +
                      checksum_type:   'none',
         
     | 
| 
      
 207 
     | 
    
         
            +
                      checksum:        nil,
         
     | 
| 
      
 208 
     | 
    
         
            +
                      start_byte:      0,
         
     | 
| 
      
 209 
     | 
    
         
            +
                      bytes_written:   26,
         
     | 
| 
      
 210 
     | 
    
         
            +
                      session_id:      'bafc72b8-366c-4501-8095-47208183d6b8'}]
         
     | 
| 
      
 211 
     | 
    
         
            +
                  }
         
     | 
| 
      
 212 
     | 
    
         
            +
                end
         
     | 
| 
      
 213 
     | 
    
         
            +
              end # NodeSimulatorServlet
         
     | 
| 
      
 214 
     | 
    
         
            +
            end # Aspera
         
     | 
    
        data/lib/aspera/oauth.rb
    CHANGED
    
    | 
         @@ -3,6 +3,8 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'aspera/open_application'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'aspera/web_auth'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'aspera/id_generator'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'aspera/log'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'aspera/assert'
         
     | 
| 
       6 
8 
     | 
    
         
             
            require 'base64'
         
     | 
| 
       7 
9 
     | 
    
         
             
            require 'date'
         
     | 
| 
       8 
10 
     | 
    
         
             
            require 'socket'
         
     | 
| 
         @@ -55,7 +57,7 @@ module Aspera 
     | 
|
| 
       55 
57 
     | 
    
         
             
                  end
         
     | 
| 
       56 
58 
     | 
    
         | 
| 
       57 
59 
     | 
    
         
             
                  def bearer_extract(token)
         
     | 
| 
       58 
     | 
    
         
            -
                     
     | 
| 
      
 60 
     | 
    
         
            +
                    assert(bearer?(token)){'not a bearer token, wrong prefix'}
         
     | 
| 
       59 
61 
     | 
    
         
             
                    return token[BEARER_PREFIX.length..-1]
         
     | 
| 
       60 
62 
     | 
    
         
             
                  end
         
     | 
| 
       61 
63 
     | 
    
         | 
| 
         @@ -106,26 +108,28 @@ module Aspera 
     | 
|
| 
       106 
108 
     | 
    
         
             
                  # @param id_create called to generate unique id for token, for cache
         
     | 
| 
       107 
109 
     | 
    
         
             
                  def register_token_creator(id, lambda_create, id_create)
         
     | 
| 
       108 
110 
     | 
    
         
             
                    Log.log.debug{"registering token creator #{id}"}
         
     | 
| 
       109 
     | 
    
         
            -
                     
     | 
| 
      
 111 
     | 
    
         
            +
                    assert_type(id, Symbol)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    assert_type(lambda_create, Proc)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    assert_type(id_create, Proc)
         
     | 
| 
       110 
114 
     | 
    
         
             
                    @create_handlers[id] = lambda_create
         
     | 
| 
       111 
115 
     | 
    
         
             
                    @id_handlers[id] = id_create
         
     | 
| 
       112 
116 
     | 
    
         
             
                  end
         
     | 
| 
       113 
117 
     | 
    
         | 
| 
       114 
118 
     | 
    
         
             
                  # @return one of the registered creators for the given create type
         
     | 
| 
       115 
119 
     | 
    
         
             
                  def token_creator(id)
         
     | 
| 
       116 
     | 
    
         
            -
                     
     | 
| 
      
 120 
     | 
    
         
            +
                    assert(@create_handlers.key?(id)){"token grant method unknown: '#{id}' (#{id.class})"}
         
     | 
| 
       117 
121 
     | 
    
         
             
                    @create_handlers[id]
         
     | 
| 
       118 
122 
     | 
    
         
             
                  end
         
     | 
| 
       119 
123 
     | 
    
         | 
| 
       120 
124 
     | 
    
         
             
                  # list of identifiers found in creation parameters that can be used to uniquely identify the token
         
     | 
| 
       121 
125 
     | 
    
         
             
                  def id_creator(id)
         
     | 
| 
       122 
     | 
    
         
            -
                     
     | 
| 
      
 126 
     | 
    
         
            +
                    assert(@id_handlers.key?(id)){"id creator type unknown: #{id}/#{id.class}"}
         
     | 
| 
       123 
127 
     | 
    
         
             
                    @id_handlers[id]
         
     | 
| 
       124 
128 
     | 
    
         
             
                  end
         
     | 
| 
       125 
129 
     | 
    
         
             
                end # self
         
     | 
| 
       126 
130 
     | 
    
         | 
| 
       127 
131 
     | 
    
         
             
                # JSON Web Signature (JWS) compact serialization: https://datatracker.ietf.org/doc/html/rfc7515
         
     | 
| 
       128 
     | 
    
         
            -
                register_decoder lambda { |token| parts = token.split('.');  
     | 
| 
      
 132 
     | 
    
         
            +
                register_decoder lambda { |token| parts = token.split('.'); assert(parts.length.eql?(3)){'not aoc token'}; JSON.parse(Base64.decode64(parts[1]))} # rubocop:disable Style/Semicolon, Layout/LineLength
         
     | 
| 
       129 
133 
     | 
    
         | 
| 
       130 
134 
     | 
    
         
             
                # generic token creation, parameters are provided in :generic
         
     | 
| 
       131 
135 
     | 
    
         
             
                register_token_creator :generic, lambda { |oauth|
         
     | 
| 
         @@ -152,7 +156,7 @@ module Aspera 
     | 
|
| 
       152 
156 
     | 
    
         
             
                  OpenApplication.instance.uri(login_page_url)
         
     | 
| 
       153 
157 
     | 
    
         
             
                  # wait for code in request
         
     | 
| 
       154 
158 
     | 
    
         
             
                  received_params = web_server.received_request
         
     | 
| 
       155 
     | 
    
         
            -
                   
     | 
| 
      
 159 
     | 
    
         
            +
                  assert(random_state.eql?(received_params['state'])){'wrong received state'}
         
     | 
| 
       156 
160 
     | 
    
         
             
                  # exchange code for token
         
     | 
| 
       157 
161 
     | 
    
         
             
                  return oauth.create_token(oauth.optional_scope_client_id(add_secret: true).merge(
         
     | 
| 
       158 
162 
     | 
    
         
             
                    grant_type:   'authorization_code',
         
     | 
| 
         @@ -169,7 +173,7 @@ module Aspera 
     | 
|
| 
       169 
173 
     | 
    
         
             
                  require 'jwt'
         
     | 
| 
       170 
174 
     | 
    
         
             
                  seconds_since_epoch = Time.new.to_i
         
     | 
| 
       171 
175 
     | 
    
         
             
                  Log.log.info{"seconds=#{seconds_since_epoch}"}
         
     | 
| 
       172 
     | 
    
         
            -
                   
     | 
| 
      
 176 
     | 
    
         
            +
                  assert(oauth.specific_parameters[:payload].is_a?(Hash)){'missing JWT payload'}
         
     | 
| 
       173 
177 
     | 
    
         
             
                  jwt_payload = {
         
     | 
| 
       174 
178 
     | 
    
         
             
                    exp: seconds_since_epoch + @@globals[:jwt_expiry_offset_sec], # expiration time
         
     | 
| 
       175 
179 
     | 
    
         
             
                    nbf: seconds_since_epoch - @@globals[:jwt_accepted_offset_sec], # not before
         
     | 
| 
         @@ -191,14 +195,14 @@ module Aspera 
     | 
|
| 
       191 
195 
     | 
    
         
             
                private
         
     | 
| 
       192 
196 
     | 
    
         | 
| 
       193 
197 
     | 
    
         
             
                # [M]=mandatory [D]=has default value [0]=accept nil
         
     | 
| 
       194 
     | 
    
         
            -
                # :base_url            [M] 
     | 
| 
      
 198 
     | 
    
         
            +
                # :base_url            [M] URL of authentication API
         
     | 
| 
       195 
199 
     | 
    
         
             
                # :auth
         
     | 
| 
       196 
     | 
    
         
            -
                # :grant_method        [M] 
     | 
| 
      
 200 
     | 
    
         
            +
                # :grant_method        [M] :generic, :web, :jwt, [custom types]
         
     | 
| 
       197 
201 
     | 
    
         
             
                # :client_id           [0]
         
     | 
| 
       198 
202 
     | 
    
         
             
                # :client_secret       [0]
         
     | 
| 
       199 
203 
     | 
    
         
             
                # :scope               [0]
         
     | 
| 
       200 
     | 
    
         
            -
                # :path_token          [D] 
     | 
| 
       201 
     | 
    
         
            -
                # :token_field         [D] 
     | 
| 
      
 204 
     | 
    
         
            +
                # :path_token          [D] API end point to create a token
         
     | 
| 
      
 205 
     | 
    
         
            +
                # :token_field         [D] field in result that contains the token
         
     | 
| 
       202 
206 
     | 
    
         
             
                # :jwt:private_key_obj [M] for type :jwt
         
     | 
| 
       203 
207 
     | 
    
         
             
                # :jwt:payload         [M] for type :jwt
         
     | 
| 
       204 
208 
     | 
    
         
             
                # :jwt:headers         [0] for type :jwt
         
     | 
| 
         @@ -207,18 +211,16 @@ module Aspera 
     | 
|
| 
       207 
211 
     | 
    
         
             
                # :generic             [M] for type :generic
         
     | 
| 
       208 
212 
     | 
    
         
             
                def initialize(a_params)
         
     | 
| 
       209 
213 
     | 
    
         
             
                  Log.log.debug{"auth=#{a_params}"}
         
     | 
| 
       210 
     | 
    
         
            -
                  #  
     | 
| 
      
 214 
     | 
    
         
            +
                  # set default values if not set in parameters common to all types
         
     | 
| 
       211 
215 
     | 
    
         
             
                  @generic_parameters = DEFAULT_CREATE_PARAMS.deep_merge(a_params)
         
     | 
| 
       212 
     | 
    
         
            -
                  # legacy
         
     | 
| 
       213 
     | 
    
         
            -
                  @generic_parameters[:grant_method] ||= @generic_parameters.delete(:crtype) if @generic_parameters.key?(:crtype) # cspell: disable-line
         
     | 
| 
       214 
216 
     | 
    
         
             
                  # check that type is known
         
     | 
| 
       215 
217 
     | 
    
         
             
                  self.class.token_creator(@generic_parameters[:grant_method])
         
     | 
| 
       216 
218 
     | 
    
         
             
                  # specific parameters for the creation type
         
     | 
| 
       217 
219 
     | 
    
         
             
                  @specific_parameters = @generic_parameters[@generic_parameters[:grant_method]]
         
     | 
| 
       218 
220 
     | 
    
         
             
                  if @generic_parameters[:grant_method].eql?(:web) && @specific_parameters.key?(:redirect_uri)
         
     | 
| 
       219 
221 
     | 
    
         
             
                    uri = URI.parse(@specific_parameters[:redirect_uri])
         
     | 
| 
       220 
     | 
    
         
            -
                     
     | 
| 
       221 
     | 
    
         
            -
                     
     | 
| 
      
 222 
     | 
    
         
            +
                    assert(%w[http https].include?(uri.scheme)){'redirect_uri scheme must be http or https'}
         
     | 
| 
      
 223 
     | 
    
         
            +
                    assert(!uri.port.nil?){'redirect_uri must have a port'}
         
     | 
| 
       222 
224 
     | 
    
         
             
                    # TODO: we could check that host is localhost or local address
         
     | 
| 
       223 
225 
     | 
    
         
             
                  end
         
     | 
| 
       224 
226 
     | 
    
         
             
                  rest_params = {
         
     | 
| 
         @@ -226,8 +228,9 @@ module Aspera 
     | 
|
| 
       226 
228 
     | 
    
         
             
                    redirect_max: 2
         
     | 
| 
       227 
229 
     | 
    
         
             
                  }
         
     | 
| 
       228 
230 
     | 
    
         
             
                  rest_params[:auth] = a_params[:auth] if a_params.key?(:auth)
         
     | 
| 
      
 231 
     | 
    
         
            +
                  # this is the OAuth API
         
     | 
| 
       229 
232 
     | 
    
         
             
                  @api = Rest.new(rest_params)
         
     | 
| 
       230 
     | 
    
         
            -
                  # if needed use from api
         
     | 
| 
      
 233 
     | 
    
         
            +
                  # if those are needed use from @api
         
     | 
| 
       231 
234 
     | 
    
         
             
                  @generic_parameters.delete(:base_url)
         
     | 
| 
       232 
235 
     | 
    
         
             
                  @generic_parameters.delete(:auth)
         
     | 
| 
       233 
236 
     | 
    
         
             
                  @generic_parameters.delete(@generic_parameters[:grant_method])
         
     | 
| 
         @@ -266,7 +269,7 @@ module Aspera 
     | 
|
| 
       266 
269 
     | 
    
         
             
                    @generic_parameters[:grant_method],
         
     | 
| 
       267 
270 
     | 
    
         
             
                    self.class.id_creator(@generic_parameters[:grant_method]).call(self), # array, so we flatten later
         
     | 
| 
       268 
271 
     | 
    
         
             
                    @generic_parameters[:scope],
         
     | 
| 
       269 
     | 
    
         
            -
                    @api.params.dig( 
     | 
| 
      
 272 
     | 
    
         
            +
                    @api.params.dig(*%i[auth username])
         
     | 
| 
       270 
273 
     | 
    
         
             
                  ].flatten)
         
     | 
| 
       271 
274 
     | 
    
         | 
| 
       272 
275 
     | 
    
         
             
                  # get token_data from cache (or nil), token_data is what is returned by /token
         
     | 
| 
         @@ -322,7 +325,7 @@ module Aspera 
     | 
|
| 
       322 
325 
     | 
    
         
             
                    token_data = JSON.parse(json_data)
         
     | 
| 
       323 
326 
     | 
    
         
             
                    self.class.persist_mgr.put(token_id, json_data)
         
     | 
| 
       324 
327 
     | 
    
         
             
                  end # if ! in_cache
         
     | 
| 
       325 
     | 
    
         
            -
                   
     | 
| 
      
 328 
     | 
    
         
            +
                  assert(token_data.key?(@generic_parameters[:token_field])){"API error: No such field in answer: #{@generic_parameters[:token_field]}"}
         
     | 
| 
       326 
329 
     | 
    
         
             
                  # ok we shall have a token here
         
     | 
| 
       327 
330 
     | 
    
         
             
                  return self.class.bearer_build(token_data[@generic_parameters[:token_field]])
         
     | 
| 
       328 
331 
     | 
    
         
             
                end
         
     | 
| 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'json'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'aspera/log'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'aspera/assert'
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            module Aspera
         
     | 
| 
       7 
8 
     | 
    
         
             
              # Persist data on file system
         
     | 
| 
         @@ -13,21 +14,19 @@ module Aspera 
     | 
|
| 
       13 
14 
     | 
    
         
             
                # @param :parse    Optional  parse method (default to JSON)
         
     | 
| 
       14 
15 
     | 
    
         
             
                # @param :format   Optional  dump method (default to JSON)
         
     | 
| 
       15 
16 
     | 
    
         
             
                # @param :merge    Optional  merge data from file to current data
         
     | 
| 
       16 
     | 
    
         
            -
                def initialize( 
     | 
| 
       17 
     | 
    
         
            -
                   
     | 
| 
       18 
     | 
    
         
            -
                   
     | 
| 
       19 
     | 
    
         
            -
                   
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
                   
     | 
| 
       23 
     | 
    
         
            -
                  @ 
     | 
| 
       24 
     | 
    
         
            -
                  @persisted_object = options[:data]
         
     | 
| 
       25 
     | 
    
         
            -
                  @object_id = options[:id]
         
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(manager:, data:, id:, delete: nil, parse: nil, format: nil, merge: nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  assert(!manager.nil?)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  assert(!data.nil?)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  assert_type(id, String)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  assert(!id.empty?)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @manager = manager
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @persisted_object = data
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @object_id = id
         
     | 
| 
       26 
25 
     | 
    
         
             
                  # by default , at save time, file is deleted if data is nil
         
     | 
| 
       27 
     | 
    
         
            -
                  @delete_condition =  
     | 
| 
       28 
     | 
    
         
            -
                  @persist_format =  
     | 
| 
       29 
     | 
    
         
            -
                  persist_parse =  
     | 
| 
       30 
     | 
    
         
            -
                  persist_merge =  
     | 
| 
      
 26 
     | 
    
         
            +
                  @delete_condition = delete || lambda{|d|d.empty?}
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @persist_format = format || lambda {|h| JSON.generate(h)}
         
     | 
| 
      
 28 
     | 
    
         
            +
                  persist_parse = parse || lambda {|t| JSON.parse(t)}
         
     | 
| 
      
 29 
     | 
    
         
            +
                  persist_merge = merge || lambda {|current, file| current.concat(file).uniq rescue current}
         
     | 
| 
       31 
30 
     | 
    
         
             
                  value = @manager.get(@object_id)
         
     | 
| 
       32 
31 
     | 
    
         
             
                  persist_merge.call(@persisted_object, persist_parse.call(value)) unless value.nil?
         
     | 
| 
       33 
32 
     | 
    
         
             
                end
         
     | 
| 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'fileutils'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'aspera/log'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'aspera/assert'
         
     | 
| 
       5 
6 
     | 
    
         
             
            require 'aspera/environment'
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
8 
     | 
    
         
             
            # search: persistency_folder PersistencyFolder
         
     | 
| 
         @@ -34,7 +35,7 @@ module Aspera 
     | 
|
| 
       34 
35 
     | 
    
         
             
                end
         
     | 
| 
       35 
36 
     | 
    
         | 
| 
       36 
37 
     | 
    
         
             
                def put(object_id, value)
         
     | 
| 
       37 
     | 
    
         
            -
                   
     | 
| 
      
 38 
     | 
    
         
            +
                  assert_type(value, String)
         
     | 
| 
       38 
39 
     | 
    
         
             
                  persist_filepath = id_to_filepath(object_id)
         
     | 
| 
       39 
40 
     | 
    
         
             
                  Log.log.debug{"persistency saving: #{persist_filepath}"}
         
     | 
| 
       40 
41 
     | 
    
         
             
                  FileUtils.rm_f(persist_filepath)
         
     | 
| 
         @@ -67,7 +68,7 @@ module Aspera 
     | 
|
| 
       67 
68 
     | 
    
         | 
| 
       68 
69 
     | 
    
         
             
                # @param object_id String or Array
         
     | 
| 
       69 
70 
     | 
    
         
             
                def id_to_filepath(object_id)
         
     | 
| 
       70 
     | 
    
         
            -
                   
     | 
| 
      
 71 
     | 
    
         
            +
                  assert_type(object_id, String)
         
     | 
| 
       71 
72 
     | 
    
         
             
                  FileUtils.mkdir_p(@folder)
         
     | 
| 
       72 
73 
     | 
    
         
             
                  Environment.restrict_file_access(@folder)
         
     | 
| 
       73 
74 
     | 
    
         
             
                  return File.join(@folder, "#{object_id}#{FILE_SUFFIX}")
         
     |