aspera-cli 4.14.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
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