aspera-cli 4.14.0 → 4.15.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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -3
  4. data/CONTRIBUTING.md +7 -7
  5. data/README.md +1457 -880
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/proxy.pac +1 -1
  9. data/lib/aspera/aoc.rb +198 -127
  10. data/lib/aspera/ascmd.rb +24 -14
  11. data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
  12. data/lib/aspera/cli/error.rb +17 -0
  13. data/lib/aspera/cli/extended_value.rb +47 -12
  14. data/lib/aspera/cli/formatter.rb +260 -171
  15. data/lib/aspera/cli/hints.rb +80 -0
  16. data/lib/aspera/cli/main.rb +101 -147
  17. data/lib/aspera/cli/manager.rb +160 -124
  18. data/lib/aspera/cli/plugin.rb +70 -59
  19. data/lib/aspera/cli/plugins/alee.rb +0 -1
  20. data/lib/aspera/cli/plugins/aoc.rb +239 -273
  21. data/lib/aspera/cli/plugins/ats.rb +8 -5
  22. data/lib/aspera/cli/plugins/bss.rb +2 -2
  23. data/lib/aspera/cli/plugins/config.rb +516 -375
  24. data/lib/aspera/cli/plugins/console.rb +40 -0
  25. data/lib/aspera/cli/plugins/cos.rb +4 -5
  26. data/lib/aspera/cli/plugins/faspex.rb +99 -84
  27. data/lib/aspera/cli/plugins/faspex5.rb +179 -148
  28. data/lib/aspera/cli/plugins/node.rb +219 -153
  29. data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
  30. data/lib/aspera/cli/plugins/preview.rb +46 -32
  31. data/lib/aspera/cli/plugins/server.rb +57 -17
  32. data/lib/aspera/cli/plugins/shares.rb +34 -12
  33. data/lib/aspera/cli/sync_actions.rb +68 -0
  34. data/lib/aspera/cli/transfer_agent.rb +45 -55
  35. data/lib/aspera/cli/transfer_progress.rb +74 -0
  36. data/lib/aspera/cli/version.rb +1 -1
  37. data/lib/aspera/colors.rb +3 -1
  38. data/lib/aspera/command_line_builder.rb +14 -11
  39. data/lib/aspera/cos_node.rb +3 -2
  40. data/lib/aspera/environment.rb +17 -6
  41. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  42. data/lib/aspera/fasp/agent_base.rb +31 -77
  43. data/lib/aspera/fasp/agent_connect.rb +21 -22
  44. data/lib/aspera/fasp/agent_direct.rb +88 -102
  45. data/lib/aspera/fasp/agent_httpgw.rb +196 -192
  46. data/lib/aspera/fasp/agent_node.rb +41 -34
  47. data/lib/aspera/fasp/agent_trsdk.rb +75 -34
  48. data/lib/aspera/fasp/error_info.rb +2 -2
  49. data/lib/aspera/fasp/faux_file.rb +52 -0
  50. data/lib/aspera/fasp/installation.rb +43 -184
  51. data/lib/aspera/fasp/management.rb +244 -0
  52. data/lib/aspera/fasp/parameters.rb +59 -26
  53. data/lib/aspera/fasp/parameters.yaml +75 -8
  54. data/lib/aspera/fasp/products.rb +162 -0
  55. data/lib/aspera/fasp/transfer_spec.rb +1 -1
  56. data/lib/aspera/fasp/uri.rb +4 -4
  57. data/lib/aspera/faspex_gw.rb +2 -2
  58. data/lib/aspera/faspex_postproc.rb +2 -2
  59. data/lib/aspera/hash_ext.rb +2 -2
  60. data/lib/aspera/json_rpc.rb +49 -0
  61. data/lib/aspera/line_logger.rb +23 -0
  62. data/lib/aspera/log.rb +57 -16
  63. data/lib/aspera/node.rb +97 -14
  64. data/lib/aspera/oauth.rb +36 -18
  65. data/lib/aspera/open_application.rb +4 -4
  66. data/lib/aspera/persistency_folder.rb +2 -2
  67. data/lib/aspera/preview/file_types.rb +4 -2
  68. data/lib/aspera/preview/generator.rb +22 -35
  69. data/lib/aspera/preview/options.rb +2 -0
  70. data/lib/aspera/preview/terminal.rb +24 -13
  71. data/lib/aspera/preview/utils.rb +19 -26
  72. data/lib/aspera/rest.rb +103 -72
  73. data/lib/aspera/rest_call_error.rb +1 -1
  74. data/lib/aspera/rest_error_analyzer.rb +15 -14
  75. data/lib/aspera/rest_errors_aspera.rb +37 -34
  76. data/lib/aspera/secret_hider.rb +14 -16
  77. data/lib/aspera/ssh.rb +4 -1
  78. data/lib/aspera/sync.rb +128 -122
  79. data/lib/aspera/temp_file_manager.rb +10 -3
  80. data/lib/aspera/web_auth.rb +10 -7
  81. data/lib/aspera/web_server_simple.rb +9 -4
  82. data.tar.gz.sig +0 -0
  83. metadata +33 -15
  84. metadata.gz.sig +0 -0
  85. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  86. data/lib/aspera/cli/listener/logger.rb +0 -22
  87. data/lib/aspera/cli/listener/progress.rb +0 -50
  88. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  89. data/lib/aspera/cli/plugins/sync.rb +0 -44
  90. data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,49 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # cspell:words Transfersdk
4
-
5
3
  require 'aspera/fasp/agent_base'
6
4
  require 'aspera/fasp/installation'
7
5
  require 'json'
6
+ require 'uri'
8
7
 
9
8
  module Aspera
10
9
  module Fasp
11
10
  class AgentTrsdk < Aspera::Fasp::AgentBase
12
11
  DEFAULT_OPTIONS = {
13
- address: '127.0.0.1',
14
- port: 55_002
12
+ url: 'grpc://127.0.0.1:0',
13
+ external: false,
14
+ keep: false
15
15
  }.freeze
16
16
  private_constant :DEFAULT_OPTIONS
17
17
 
18
18
  # options come from transfer_info
19
- def initialize(user_opts)
20
- raise "expecting Hash (or nil), but have #{user_opts.class}" unless user_opts.nil? || user_opts.is_a?(Hash)
21
- # set default options and override if specified
22
- options = DEFAULT_OPTIONS.dup
23
- user_opts&.each do |k, v|
24
- raise "Unknown local agent parameter: #{k}, expect one of #{DEFAULT_OPTIONS.keys.map(&:to_s).join(',')}" unless DEFAULT_OPTIONS.key?(k)
25
- options[k] = v
26
- end
27
- Log.log.debug{"options= #{options}"}
28
- super()
19
+ def initialize(user_opts={})
20
+ super(user_opts)
21
+ @options = AgentBase.options(default: DEFAULT_OPTIONS, options: user_opts)
22
+ daemon_uri = URI.parse(@options[:url])
23
+ raise Fasp::Error, "invalid url #{@options[:url]}" unless daemon_uri.scheme.eql?('grpc')
24
+ Log.log.debug{Log.dump(:agent_options, @options)}
29
25
  # load and create SDK stub
30
26
  $LOAD_PATH.unshift(Installation.instance.sdk_ruby_folder)
31
27
  require 'transfer_services_pb'
32
- @transfer_client = Transfersdk::TransferService::Stub.new("#{options[:address]}:#{options[:port]}", :this_channel_is_insecure)
28
+ # it stays
29
+ @daemon_pid = nil
33
30
  begin
31
+ @transfer_client = Transfersdk::TransferService::Stub.new("#{daemon_uri.host}:#{daemon_uri.port}", :this_channel_is_insecure)
34
32
  get_info_response = @transfer_client.get_info(Transfersdk::InstanceInfoRequest.new)
35
33
  Log.log.debug{"daemon info: #{get_info_response}"}
34
+ Log.log.warn{'attached to existing daemon'} unless @options[:external] || @options[:keep]
35
+ at_exit{shutdown}
36
36
  rescue GRPC::Unavailable
37
- Log.log.warn('no daemon present, starting daemon...')
37
+ raise if @options[:external]
38
+ raise "daemon started with PID #{@daemon_pid}, but connection failed to #{daemon_uri}}" unless @daemon_pid.nil?
39
+ Log.log.warn('no daemon present, starting daemon...') if @options[:external]
38
40
  # location of daemon binary
39
41
  bin_folder = File.realpath(File.join(Installation.instance.sdk_ruby_folder, '..'))
40
42
  # config file and logs are created in same folder
41
- conf_file = File.join(bin_folder, 'sdk.conf')
43
+ generated_config_file_path = File.join(bin_folder, 'sdk.conf')
42
44
  log_base = File.join(bin_folder, 'transferd')
43
45
  # create a config file for daemon
44
46
  config = {
45
- address: options[:address],
46
- port: options[:port],
47
+ address: daemon_uri.host,
48
+ port: daemon_uri.port,
47
49
  fasp_runtime: {
48
50
  use_embedded: false,
49
51
  user_defined: {
@@ -52,10 +54,30 @@ module Aspera
52
54
  }
53
55
  }
54
56
  }
55
- File.write(conf_file, config.to_json)
56
- trd_pid = Process.spawn(Installation.instance.path(:transferd), '--config', conf_file, out: "#{log_base}.out", err: "#{log_base}.err")
57
- Process.detach(trd_pid)
58
- sleep(2.0)
57
+ File.write(generated_config_file_path, config.to_json)
58
+ @daemon_pid = Process.spawn(Installation.instance.path(:transferd), '--config', generated_config_file_path, out: "#{log_base}.out", err: "#{log_base}.err")
59
+ begin
60
+ # wait for process to initialize
61
+ Timeout.timeout(2.0) do
62
+ _, status = Process.wait2(@daemon_pid)
63
+ raise "transfer daemon exited with status #{status.exitstatus}. Check files: #{log_base}.out #{log_base}.err"
64
+ end
65
+ rescue Timeout::Error
66
+ nil
67
+ end
68
+ Log.log.debug{"daemon started with pid #{@daemon_pid}"}
69
+ Process.detach(@daemon_pid) if @options[:keep]
70
+ if daemon_uri.port.eql?(0)
71
+ # if port is zero, a dynamic port was created, get it
72
+ File.open("#{log_base}.out", 'r') do |file|
73
+ file.each_line do |line|
74
+ if (m = line.match(/Info: API Server: Listening on ([^:]+):(\d+) /))
75
+ daemon_uri.port = m[2].to_i
76
+ # no "break" , need to keep last one
77
+ end
78
+ end
79
+ end
80
+ end
59
81
  retry
60
82
  end
61
83
  end
@@ -74,25 +96,34 @@ module Aspera
74
96
  end
75
97
 
76
98
  def wait_for_transfers_completion
77
- started = false
99
+ # set to true when we know the total size of the transfer
100
+ session_started = false
101
+ bytes_expected = nil
78
102
  # monitor transfer status
79
103
  @transfer_client.monitor_transfers(Transfersdk::RegistrationRequest.new(transferId: [@transfer_id])) do |response|
80
- Log.dump(:response, response.to_h)
104
+ Log.log.debug{Log.dump(:response, response.to_h)}
81
105
  # Log.log.debug{"#{response.sessionInfo.preTransferBytes} #{response.transferInfo.bytesTransferred}"}
82
106
  case response.status
83
107
  when :RUNNING
84
- if !started && !response.sessionInfo.preTransferBytes.eql?(0)
85
- notify_begin(@transfer_id, response.sessionInfo.preTransferBytes)
86
- started = true
87
- elsif started
88
- notify_progress(@transfer_id, response.transferInfo.bytesTransferred)
108
+ if !session_started
109
+ notify_progress(session_id: @transfer_id, type: :session_start)
110
+ session_started = true
111
+ end
112
+ if bytes_expected.nil? &&
113
+ !response.sessionInfo.preTransferBytes.eql?(0)
114
+ bytes_expected = response.sessionInfo.preTransferBytes
115
+ notify_progress(type: :session_size, session_id: @transfer_id, info: bytes_expected)
89
116
  end
90
- when :FAILED, :COMPLETED, :CANCELED
91
- notify_end(@transfer_id)
92
- raise Fasp::Error, JSON.parse(response.message)['Description'] unless :COMPLETED.eql?(response.status)
117
+ notify_progress(type: :transfer, session_id: @transfer_id, info: response.transferInfo.bytesTransferred)
118
+ when :COMPLETED
119
+ notify_progress(type: :transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
120
+ notify_progress(type: :end, session_id: @transfer_id)
93
121
  break
122
+ when :FAILED, :CANCELED
123
+ notify_progress(type: :end, session_id: @transfer_id)
124
+ raise Fasp::Error, JSON.parse(response.message)['Description']
94
125
  when :QUEUED, :UNKNOWN_STATUS, :PAUSED, :ORPHANED
95
- # ignore
126
+ notify_progress(session_id: nil, type: :pre_start, info: response.status.to_s.downcase)
96
127
  else
97
128
  Log.log.error{"unknown status#{response.status}"}
98
129
  end
@@ -100,6 +131,16 @@ module Aspera
100
131
  # TODO: return status
101
132
  return []
102
133
  end
134
+
135
+ def shutdown
136
+ if !@options[:keep] && !@daemon_pid.nil?
137
+ Log.log.debug("stopping daemon #{@daemon_pid}")
138
+ Process.kill('INT', @daemon_pid)
139
+ _, status = Process.wait2(@daemon_pid)
140
+ Log.log.debug("daemon stopped #{status}")
141
+ @daemon_pid = nil
142
+ end
143
+ end
103
144
  end
104
145
  end
105
146
  end
@@ -5,11 +5,11 @@
5
5
  module Aspera
6
6
  module Fasp
7
7
  # from https://www.google.com/search?q=FASP+error+codes
8
- # Note that the fact that an error is retryable is not internally defined by protocol, it's client-side responsibility
8
+ # Note that the fact that an error is retry-able is not internally defined by protocol, it's client-side responsibility
9
9
  # rubocop:disable Layout/MultilineHashKeyLineBreaks
10
10
  # rubocop:disable Layout/FirstHashElementLineBreak
11
11
  ERROR_INFO = {
12
- # id retryable mnemo message additional info
12
+ # id retry-able mnemo message additional info
13
13
  1 => { r: false, c: 'FASP_PROTO', m: 'Generic fasp(tm) protocol error', a: 'fasp(tm) error'},
14
14
  2 => { r: false, c: 'ASCP', m: 'Generic SCP error', a: 'ASCP error'},
15
15
  3 => { r: false, c: 'AMBIGUOUS_TARGET', m: 'Target incorrectly specified', a: 'Ambiguous target'},
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspera
4
+ module Fasp
5
+ # generates a pseudo file stream
6
+ class FauxFile
7
+ # marker for faux file
8
+ PREFIX = 'faux:///'
9
+ # size suffix
10
+ SUFFIX = %w[k m g t p e]
11
+ class << self
12
+ def open(name)
13
+ return nil unless name.start_with?(PREFIX)
14
+ parts = name[PREFIX.length..-1].split('?')
15
+ raise 'Format: #{PREFIX}<file path>?<size>' unless parts.length.eql?(2)
16
+ raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m = parts[1].downcase.match(/^(\d+)([#{SUFFIX.join('')}])$/))
17
+ size = m[1].to_i
18
+ suffix = m[2]
19
+ SUFFIX.each do |s|
20
+ size *= 1024
21
+ break if s.eql?(suffix)
22
+ end
23
+ return FauxFile.new(parts[0], size)
24
+ end
25
+ end
26
+ attr_reader :path, :size
27
+
28
+ def initialize(path, size)
29
+ @path = path
30
+ @size = size
31
+ @offset = 0
32
+ # we cache large chunks, anyway most of them will be the same size
33
+ @chunk_by_size = {}
34
+ end
35
+
36
+ def read(chunk_size)
37
+ return nil if eof?
38
+ bytes_to_read = [chunk_size, @size - @offset].min
39
+ @offset += bytes_to_read
40
+ @chunk_by_size[bytes_to_read] = "\x00" * bytes_to_read unless @chunk_by_size.key?(bytes_to_read)
41
+ return @chunk_by_size[bytes_to_read]
42
+ end
43
+
44
+ def close
45
+ end
46
+
47
+ def eof?
48
+ return @offset >= @size
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'English'
4
- require 'singleton'
5
- require 'aspera/log'
3
+ # cspell:ignore protobuf ckpt
6
4
  require 'aspera/environment'
7
5
  require 'aspera/data_repository'
6
+ require 'aspera/fasp/products'
7
+ require 'aspera/log'
8
+ require 'aspera/web_server_simple'
9
+ require 'English'
10
+ require 'singleton'
8
11
  require 'xmlsimple'
9
12
  require 'zlib'
10
13
  require 'base64'
@@ -13,24 +16,18 @@ require 'openssl'
13
16
 
14
17
  module Aspera
15
18
  module Fasp
16
- # Singleton that tells where to find ascp and other local resources (keys..) , using the "path(symb)" method.
19
+ # Singleton that tells where to find ascp and other local resources (keys..) , using the "path(:name)" method.
17
20
  # It is used by object : AgentDirect to find necessary resources
18
- # By default it takes the first Aspera product found specified in product_locations
21
+ # By default it takes the first Aspera product found
19
22
  # but the user can specify ascp location by calling:
20
23
  # Installation.instance.use_ascp_from_product(product_name)
21
24
  # or
22
25
  # Installation.instance.ascp_path=""
23
26
  class Installation
24
27
  include Singleton
25
- # known product names
26
- PRODUCT_CONNECT = 'Aspera Connect'
27
- PRODUCT_CLI_V1 = 'Aspera CLI'
28
- PRODUCT_DRIVE = 'Aspera Drive'
29
- PRODUCT_ENTSRV = 'Enterprise Server'
30
28
  # protobuf generated files from sdk
31
29
  EXT_RUBY_PROTOBUF = '_pb.rb'
32
30
  RB_SDK_FOLDER = 'lib'
33
- ONE_YEAR_SECONDS = 365 * 24 * 60 * 60
34
31
  DEFAULT_ASPERA_CONF = <<~END_OF_CONFIG_FILE
35
32
  <?xml version='1.0' encoding='UTF-8'?>
36
33
  <CONF version="2">
@@ -42,9 +39,9 @@ module Aspera
42
39
  </default>
43
40
  </CONF>
44
41
  END_OF_CONFIG_FILE
45
- DUMMY_CERT_INFO = '/C=US/ST=California/L=Emeryville/O=Aspera Inc./OU=Corporate/CN=Aspera Inc./emailAddress=info@asperasoft.com'
46
- private_constant :PRODUCT_CONNECT, :PRODUCT_CLI_V1, :PRODUCT_DRIVE, :PRODUCT_ENTSRV, :EXT_RUBY_PROTOBUF, :RB_SDK_FOLDER,
47
- :ONE_YEAR_SECONDS, :DEFAULT_ASPERA_CONF, :DUMMY_CERT_INFO
42
+ # all ascp files (in SDK)
43
+ FILES = %i[ascp ascp4 transferd ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].freeze
44
+ private_constant :EXT_RUBY_PROTOBUF, :RB_SDK_FOLDER, :DEFAULT_ASPERA_CONF, :FILES
48
45
  # set ascp executable path
49
46
  def ascp_path=(v)
50
47
  @path_to_ascp = v
@@ -56,7 +53,7 @@ module Aspera
56
53
 
57
54
  def sdk_ruby_folder
58
55
  ruby_pb_folder = File.join(sdk_folder, RB_SDK_FOLDER)
59
- FileUtils.mkdir_p(ruby_pb_folder) unless Dir.exist?(ruby_pb_folder)
56
+ FileUtils.mkdir_p(ruby_pb_folder)
60
57
  return ruby_pb_folder
61
58
  end
62
59
 
@@ -73,61 +70,36 @@ module Aspera
73
70
  # @return the path to folder where SDK is installed
74
71
  def sdk_folder
75
72
  raise 'SDK path was ot initialized' if @sdk_dir.nil?
76
- FileUtils.mkdir_p(@sdk_dir) unless Dir.exist?(@sdk_dir)
73
+ FileUtils.mkdir_p(@sdk_dir)
77
74
  @sdk_dir
78
75
  end
79
76
 
80
77
  # find ascp in named product (use value : FIRST_FOUND='FIRST' to just use first one)
81
- # or select one from installed_products()
78
+ # or select one from Products.installed_products()
82
79
  def use_ascp_from_product(product_name)
83
80
  if product_name.eql?(FIRST_FOUND)
84
- pl = installed_products.first
81
+ pl = Products.installed_products.first
85
82
  raise "no FASP installation found\nPlease check manual on how to install FASP." if pl.nil?
86
83
  else
87
- pl = installed_products.find{|i|i[:name].eql?(product_name)}
84
+ pl = Products.installed_products.find{|i|i[:name].eql?(product_name)}
88
85
  raise "no such product installed: #{product_name}" if pl.nil?
89
86
  end
90
87
  self.ascp_path = pl[:ascp_path]
91
88
  Log.log.debug{"ascp_path=#{@path_to_ascp}"}
92
89
  end
93
90
 
94
- # @return the list of installed products in format of product_locations
95
- def installed_products
96
- if @found_products.nil?
97
- scan_locations = product_locations.clone
98
- # add SDK as first search path
99
- scan_locations.unshift({
100
- expected: 'SDK',
101
- app_root: sdk_folder,
102
- sub_bin: ''
103
- })
104
- # search installed products: with ascp
105
- @found_products = scan_locations.select! do |item|
106
- # skip if not main folder
107
- next false unless Dir.exist?(item[:app_root])
108
- Log.log.debug{"Found #{item[:app_root]}"}
109
- sub_bin = item[:sub_bin] || BIN_SUBFOLDER
110
- item[:ascp_path] = File.join(item[:app_root], sub_bin, ascp_filename)
111
- # skip if no ascp
112
- next false unless File.exist?(item[:ascp_path])
113
- # read info from product info file if present
114
- product_info_file = "#{item[:app_root]}/#{PRODUCT_INFO}"
115
- if File.exist?(product_info_file)
116
- res_s = XmlSimple.xml_in(File.read(product_info_file), {'ForceArray' => false})
117
- item[:name] = res_s['name']
118
- item[:version] = res_s['version']
119
- else
120
- item[:name] = item[:expected]
91
+ # @return [Hash] with key = file name (String), and value = path to file
92
+ def file_paths
93
+ return FILES.each_with_object({}) do |v, m|
94
+ m[v.to_s] =
95
+ begin
96
+ path(v)
97
+ rescue => e
98
+ e.message
121
99
  end
122
- true # select this version
123
- end
124
100
  end
125
- return @found_products
126
101
  end
127
102
 
128
- # all ascp files (in SDK)
129
- FILES = %i[ascp ascp4 ssh_bypass_dsa_privkey ssh_bypass_rsa_privkey aspera_license aspera_conf fallback_certificate fallback_cert_privkey].freeze
130
-
131
103
  def check_or_create_sdk_file(filename, force: false, &block)
132
104
  return Environment.write_file_restricted(File.join(sdk_folder, filename), force: force, mode: 0o644, &block)
133
105
  end
@@ -143,31 +115,28 @@ module Aspera
143
115
  file = file.gsub('ascp', 'ascp4') if k.eql?(:ascp4)
144
116
  when :transferd
145
117
  file = transferd_filepath
146
- when :ssh_bypass_dsa_privkey
147
- file = check_or_create_sdk_file('aspera_bypass_dsa.pem') {get_key('dsa', 1)}
148
- when :ssh_bypass_rsa_privkey
149
- file = check_or_create_sdk_file('aspera_bypass_rsa.pem') {get_key('rsa', 2)}
118
+ when :ssh_private_dsa, :ssh_private_rsa
119
+ # assume last 3 letters are type
120
+ type = k.to_s[-3..-1]
121
+ file = check_or_create_sdk_file("aspera_bypass_#{type}.pem") do
122
+ # generate PEM from DER
123
+ OpenSSL::PKey.const_get(type.upcase).new(DataRepository.instance.data(type.eql?('dsa') ? 1 : 2)).to_pem
124
+ end
150
125
  when :aspera_license
151
126
  file = check_or_create_sdk_file('aspera-license') do
152
127
  Zlib::Inflate.inflate(DataRepository.instance.data(6))
153
128
  end
154
129
  when :aspera_conf
155
130
  file = check_or_create_sdk_file('aspera.conf') {DEFAULT_ASPERA_CONF}
156
- when :fallback_certificate, :fallback_cert_privkey
131
+ when :fallback_certificate, :fallback_private_key
157
132
  file_key = File.join(sdk_folder, 'aspera_fallback_cert_private_key.pem')
158
133
  file_cert = File.join(sdk_folder, 'aspera_fallback_cert.pem')
159
134
  if !File.exist?(file_key) || !File.exist?(file_cert)
160
135
  require 'openssl'
161
136
  # create new self signed certificate for http fallback
162
- private_key = OpenSSL::PKey::RSA.new(1024)
163
137
  cert = OpenSSL::X509::Certificate.new
164
- cert.subject = cert.issuer = OpenSSL::X509::Name.parse(DUMMY_CERT_INFO)
165
- cert.not_before = Time.now
166
- cert.not_after = Time.now + ONE_YEAR_SECONDS
167
- cert.public_key = private_key.public_key
168
- cert.serial = 0x0
169
- cert.version = 2
170
- cert.sign(private_key, OpenSSL::Digest.new('SHA1'))
138
+ private_key = OpenSSL::PKey::RSA.new(4096)
139
+ WebServerSimple.fill_self_signed_cert(cert, private_key)
171
140
  check_or_create_sdk_file('aspera_fallback_cert_private_key.pem', force: true) {private_key.to_pem}
172
141
  check_or_create_sdk_file('aspera_fallback_cert.pem', force: true) {cert.to_pem}
173
142
  end
@@ -179,33 +148,13 @@ module Aspera
179
148
  return file
180
149
  end
181
150
 
182
- # @return the file path of local connect where API's URI can be read
183
- def connect_uri
184
- connect = get_product_folders(PRODUCT_CONNECT)
185
- folder = File.join(connect[:run_root], VAR_RUN_SUBFOLDER)
186
- ['', 's'].each do |ext|
187
- uri_file = File.join(folder, "http#{ext}.uri")
188
- Log.log.debug{"checking connect port file: #{uri_file}"}
189
- if File.exist?(uri_file)
190
- return File.open(uri_file, &:gets).strip
191
- end
192
- end
193
- raise "no connect uri file found in #{folder}"
194
- end
195
-
196
- # @ return path to configuration file of aspera CLI
197
- def cli_conf_file
198
- connect = get_product_folders(PRODUCT_CLI_V1)
199
- return File.join(connect[:app_root], BIN_SUBFOLDER, '.aspera_cli_conf')
200
- end
201
-
202
151
  # default bypass key phrase
203
- def bypass_pass
152
+ def ssh_cert_uuid
204
153
  return format('%08x-%04x-%04x-%04x-%04x%08x', *DataRepository.instance.data(3).unpack('NnnnnN'))
205
154
  end
206
155
 
207
- def bypass_keys
208
- return %i[ssh_bypass_dsa_privkey ssh_bypass_rsa_privkey].map{|i|Installation.instance.path(i)}
156
+ def aspera_token_ssh_key_paths
157
+ return %i[ssh_private_dsa ssh_private_rsa].map{|i|Installation.instance.path(i)}
209
158
  end
210
159
 
211
160
  # use in plugin `config`
@@ -267,123 +216,33 @@ module Aspera
267
216
  # ensure license file are generated so that ascp invocation for version works
268
217
  path(:aspera_license)
269
218
  path(:aspera_conf)
270
- ascp_path = File.join(sdk_folder, ascp_filename)
271
- raise "No #{ascp_filename} found in SDK archive" unless File.exist?(ascp_path)
219
+ ascp_file = Products.ascp_filename
220
+ ascp_path = File.join(sdk_folder, ascp_file)
221
+ raise "No #{ascp_file} found in SDK archive" unless File.exist?(ascp_path)
272
222
  Environment.restrict_file_access(ascp_path, mode: 0o755)
273
223
  Environment.restrict_file_access(ascp_path.gsub('ascp', 'ascp4'), mode: 0o755)
274
- ascp_version = get_ascp_version(File.join(sdk_folder, ascp_filename))
224
+ ascp_version = get_ascp_version(ascp_path)
275
225
  trd_path = transferd_filepath
276
226
  Log.log.warn{"No #{trd_path} in SDK archive"} unless File.exist?(trd_path)
277
227
  Environment.restrict_file_access(trd_path, mode: 0o755) if File.exist?(trd_path)
278
228
  transferd_version = get_exe_version(trd_path, 'version')
279
229
  sdk_version = transferd_version || ascp_version
280
- File.write(File.join(sdk_folder, PRODUCT_INFO), "<product><name>IBM Aspera SDK</name><version>#{sdk_version}</version></product>")
230
+ File.write(File.join(sdk_folder, Products::INFO_META_FILE), "<product><name>IBM Aspera SDK</name><version>#{sdk_version}</version></product>")
281
231
  return sdk_version
282
232
  end
283
233
 
284
234
  private
285
235
 
286
- BIN_SUBFOLDER = 'bin'
287
- ETC_SUBFOLDER = 'etc'
288
- VAR_RUN_SUBFOLDER = File.join('var', 'run')
289
- # product information manifest: XML (part of aspera product)
290
- PRODUCT_INFO = 'product-info.mf'
291
236
  # policy for product selection
292
237
  FIRST_FOUND = 'FIRST'
293
238
 
294
- private_constant :BIN_SUBFOLDER, :ETC_SUBFOLDER, :VAR_RUN_SUBFOLDER, :PRODUCT_INFO
295
-
296
239
  def initialize
297
240
  @path_to_ascp = nil
298
241
  @sdk_dir = nil
299
- @found_products = nil
300
- end
301
-
302
- # @return folder paths for specified applications
303
- # @param name Connect or CLI
304
- def get_product_folders(name)
305
- found = installed_products.select{|i|i[:expected].eql?(name) || i[:name].eql?(name)}
306
- raise "Product: #{name} not found, please install." if found.empty?
307
- return found.first
308
- end
309
-
310
- # filename for ascp with optional extension (Windows)
311
- def ascp_filename
312
- return 'ascp' + Environment.exe_extension
313
242
  end
314
243
 
315
244
  def transferd_filepath
316
- return File.join(sdk_folder, 'asperatransferd' + Environment.exe_extension)
317
- end
318
-
319
- # @return product folders depending on OS fields
320
- # :expected M app name is taken from the manifest if present, else defaults to this value
321
- # :app_root M main folder for the application
322
- # :log_root O location of log files (Linux uses syslog)
323
- # :run_root O only for Connect Client, location of http port file
324
- # :sub_bin O subfolder with executables, default : bin
325
- def product_locations
326
- case Aspera::Environment.os
327
- when Aspera::Environment::OS_WINDOWS; return [{
328
- expected: PRODUCT_CONNECT,
329
- app_root: File.join(ENV['LOCALAPPDATA'], 'Programs', 'Aspera', 'Aspera Connect'),
330
- log_root: File.join(ENV['LOCALAPPDATA'], 'Aspera', 'Aspera Connect', 'var', 'log'),
331
- run_root: File.join(ENV['LOCALAPPDATA'], 'Aspera', 'Aspera Connect')
332
- }, {
333
- expected: PRODUCT_CLI_V1,
334
- app_root: File.join('C:', 'Program Files', 'Aspera', 'cli'),
335
- log_root: File.join('C:', 'Program Files', 'Aspera', 'cli', 'var', 'log')
336
- }, {
337
- expected: PRODUCT_ENTSRV,
338
- app_root: File.join('C:', 'Program Files', 'Aspera', 'Enterprise Server'),
339
- log_root: File.join('C:', 'Program Files', 'Aspera', 'Enterprise Server', 'var', 'log')
340
- }]
341
- when Aspera::Environment::OS_X; return [{
342
- expected: PRODUCT_CONNECT,
343
- app_root: File.join(Dir.home, 'Applications', 'Aspera Connect.app'),
344
- log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Connect'),
345
- run_root: File.join(Dir.home, 'Library', 'Application Support', 'Aspera', 'Aspera Connect'),
346
- sub_bin: File.join('Contents', 'Resources')
347
- }, {
348
- expected: PRODUCT_CONNECT,
349
- app_root: File.join('', 'Applications', 'Aspera Connect.app'),
350
- log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Connect'),
351
- run_root: File.join(Dir.home, 'Library', 'Application Support', 'Aspera', 'Aspera Connect'),
352
- sub_bin: File.join('Contents', 'Resources')
353
- }, {
354
- expected: PRODUCT_CLI_V1,
355
- app_root: File.join(Dir.home, 'Applications', 'Aspera CLI'),
356
- log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera')
357
- }, {
358
- expected: PRODUCT_ENTSRV,
359
- app_root: File.join('', 'Library', 'Aspera'),
360
- log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera')
361
- }, {
362
- expected: PRODUCT_DRIVE,
363
- app_root: File.join('', 'Applications', 'Aspera Drive.app'),
364
- log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Drive'),
365
- sub_bin: File.join('Contents', 'Resources')
366
- }]
367
- else; return [{ # other: Linux and Unix family
368
- expected: PRODUCT_CONNECT,
369
- app_root: File.join(Dir.home, '.aspera', 'connect'),
370
- run_root: File.join(Dir.home, '.aspera', 'connect')
371
- }, {
372
- expected: PRODUCT_CLI_V1,
373
- app_root: File.join(Dir.home, '.aspera', 'cli')
374
- }, {
375
- expected: PRODUCT_ENTSRV,
376
- app_root: File.join('', 'opt', 'aspera')
377
- }]
378
- end
379
- end
380
-
381
- # @return a standard bypass key
382
- # @param type rsa or dsa
383
- # @param id in repository 1 for dsa, 2 for rsa
384
- def get_key(type, id)
385
- # generate PEM from DER
386
- OpenSSL::PKey.const_get(type.upcase).new(DataRepository.instance.data(id)).to_pem
245
+ return File.join(sdk_folder, 'asperatransferd' + Environment.exe_extension) # cspell:disable-line
387
246
  end
388
247
  end # Installation
389
248
  end