aspera-cli 4.7.0 → 4.9.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 (96) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1267 -999
  4. data/bin/ascli +20 -1
  5. data/bin/asession +37 -34
  6. data/docs/test_env.conf +7 -3
  7. data/examples/aoc.rb +13 -12
  8. data/examples/dascli +23 -0
  9. data/examples/faspex4.rb +34 -29
  10. data/examples/{transfer.rb → node.rb} +31 -59
  11. data/examples/server.rb +93 -0
  12. data/lib/aspera/aoc.rb +153 -143
  13. data/lib/aspera/ascmd.rb +56 -45
  14. data/lib/aspera/ats_api.rb +9 -6
  15. data/lib/aspera/cli/basic_auth_plugin.rb +18 -16
  16. data/lib/aspera/cli/extended_value.rb +33 -30
  17. data/lib/aspera/cli/formater.rb +105 -111
  18. data/lib/aspera/cli/info.rb +3 -2
  19. data/lib/aspera/cli/listener/line_dump.rb +1 -0
  20. data/lib/aspera/cli/listener/logger.rb +1 -0
  21. data/lib/aspera/cli/listener/progress.rb +13 -12
  22. data/lib/aspera/cli/listener/progress_multi.rb +21 -20
  23. data/lib/aspera/cli/main.rb +110 -90
  24. data/lib/aspera/cli/manager.rb +99 -88
  25. data/lib/aspera/cli/plugin.rb +98 -39
  26. data/lib/aspera/cli/plugins/alee.rb +6 -5
  27. data/lib/aspera/cli/plugins/aoc.rb +581 -450
  28. data/lib/aspera/cli/plugins/ats.rb +84 -83
  29. data/lib/aspera/cli/plugins/bss.rb +30 -27
  30. data/lib/aspera/cli/plugins/config.rb +488 -397
  31. data/lib/aspera/cli/plugins/console.rb +17 -15
  32. data/lib/aspera/cli/plugins/cos.rb +26 -35
  33. data/lib/aspera/cli/plugins/faspex.rb +206 -172
  34. data/lib/aspera/cli/plugins/faspex5.rb +109 -74
  35. data/lib/aspera/cli/plugins/node.rb +379 -189
  36. data/lib/aspera/cli/plugins/orchestrator.rb +71 -65
  37. data/lib/aspera/cli/plugins/preview.rb +131 -122
  38. data/lib/aspera/cli/plugins/server.rb +50 -150
  39. data/lib/aspera/cli/plugins/shares.rb +61 -27
  40. data/lib/aspera/cli/plugins/sync.rb +15 -14
  41. data/lib/aspera/cli/transfer_agent.rb +75 -64
  42. data/lib/aspera/cli/version.rb +2 -1
  43. data/lib/aspera/colors.rb +29 -28
  44. data/lib/aspera/command_line_builder.rb +50 -43
  45. data/lib/aspera/cos_node.rb +64 -38
  46. data/lib/aspera/data_repository.rb +1 -0
  47. data/lib/aspera/environment.rb +33 -10
  48. data/lib/aspera/fasp/agent_base.rb +35 -30
  49. data/lib/aspera/fasp/agent_connect.rb +35 -30
  50. data/lib/aspera/fasp/agent_direct.rb +68 -60
  51. data/lib/aspera/fasp/agent_httpgw.rb +71 -64
  52. data/lib/aspera/fasp/agent_node.rb +24 -23
  53. data/lib/aspera/fasp/agent_trsdk.rb +19 -20
  54. data/lib/aspera/fasp/error.rb +2 -1
  55. data/lib/aspera/fasp/error_info.rb +79 -68
  56. data/lib/aspera/fasp/installation.rb +130 -126
  57. data/lib/aspera/fasp/listener.rb +1 -0
  58. data/lib/aspera/fasp/parameters.rb +71 -60
  59. data/lib/aspera/fasp/parameters.yaml +69 -17
  60. data/lib/aspera/fasp/resume_policy.rb +14 -11
  61. data/lib/aspera/fasp/transfer_spec.rb +6 -5
  62. data/lib/aspera/fasp/uri.rb +25 -24
  63. data/lib/aspera/faspex_gw.rb +83 -72
  64. data/lib/aspera/hash_ext.rb +23 -13
  65. data/lib/aspera/id_generator.rb +16 -13
  66. data/lib/aspera/keychain/encrypted_hash.rb +61 -46
  67. data/lib/aspera/keychain/macos_security.rb +26 -24
  68. data/lib/aspera/log.rb +35 -39
  69. data/lib/aspera/nagios.rb +36 -28
  70. data/lib/aspera/node.rb +19 -19
  71. data/lib/aspera/oauth.rb +120 -100
  72. data/lib/aspera/open_application.rb +25 -22
  73. data/lib/aspera/persistency_action_once.rb +9 -8
  74. data/lib/aspera/persistency_folder.rb +13 -9
  75. data/lib/aspera/preview/file_types.rb +261 -266
  76. data/lib/aspera/preview/generator.rb +74 -73
  77. data/lib/aspera/preview/image_error.png +0 -0
  78. data/lib/aspera/preview/options.rb +7 -6
  79. data/lib/aspera/preview/utils.rb +30 -33
  80. data/lib/aspera/preview/video_error.png +0 -0
  81. data/lib/aspera/proxy_auto_config.rb +27 -23
  82. data/lib/aspera/rest.rb +73 -74
  83. data/lib/aspera/rest_call_error.rb +1 -0
  84. data/lib/aspera/rest_error_analyzer.rb +23 -19
  85. data/lib/aspera/rest_errors_aspera.rb +43 -40
  86. data/lib/aspera/secret_hider.rb +74 -0
  87. data/lib/aspera/ssh.rb +13 -10
  88. data/lib/aspera/sync.rb +49 -47
  89. data/lib/aspera/temp_file_manager.rb +7 -5
  90. data/lib/aspera/timer_limiter.rb +9 -8
  91. data/lib/aspera/uri_reader.rb +17 -18
  92. data/lib/aspera/web_auth.rb +17 -15
  93. data.tar.gz.sig +5 -0
  94. metadata +119 -35
  95. metadata.gz.sig +0 -0
  96. data/bin/dascli +0 -13
data/lib/aspera/log.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'aspera/colors'
4
+ require 'aspera/secret_hider'
3
5
  require 'logger'
4
6
  require 'pp'
5
7
  require 'json'
@@ -8,17 +10,14 @@ require 'singleton'
8
10
  module Aspera
9
11
  # Singleton object for logging
10
12
  class Log
11
- # display string for hidden secrets
12
- HIDDEN_PASSWORD='🔑'
13
- private_constant :HIDDEN_PASSWORD
14
13
  include Singleton
15
14
  # class methods
16
15
  class << self
17
16
  # levels are :debug,:info,:warn,:error,fatal,:unknown
18
- def levels; Logger::Severity.constants.sort{|a,b|Logger::Severity.const_get(a)<=>Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym};end
17
+ def levels; Logger::Severity.constants.sort{|a,b|Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym};end
19
18
 
20
19
  # where logs are sent to
21
- def logtypes; [:stderr,:stdout,:syslog];end
20
+ def logtypes; %i[stderr stdout syslog];end
22
21
 
23
22
  # get the logger object of singleton
24
23
  def log; instance.logger;end
@@ -27,28 +26,36 @@ module Aspera
27
26
  # @param name string or symbol
28
27
  # @param format either pp or json format
29
28
  def dump(name,object,format=:json)
30
- log.debug() do
31
- result=
32
- case format
33
- when :json
34
- JSON.pretty_generate(object) rescue PP.pp(object,'')
35
- when :ruby
36
- PP.pp(object,'')
37
- else
38
- raise 'wrong parameter, expect pp or json'
39
- end
29
+ log.debug do
30
+ result =
31
+ case format
32
+ when :json
33
+ JSON.pretty_generate(object) rescue PP.pp(object,+'')
34
+ when :ruby
35
+ PP.pp(object,+'')
36
+ else
37
+ raise 'wrong parameter, expect pp or json'
38
+ end
40
39
  "#{name.to_s.green} (#{format})=\n#{result}"
41
40
  end
42
41
  end
42
+
43
+ def capture_stderr
44
+ real_stderr = $stderr
45
+ $stderr = StringIO.new
46
+ yield
47
+ log.debug($stderr.string)
48
+ ensure
49
+ $stderr = real_stderr
50
+ end
43
51
  end # class
44
52
 
45
53
  attr_reader :logger_type, :logger
46
54
  attr_writer :program_name
47
- attr_accessor :log_secrets
48
55
 
49
56
  # set log level of underlying logger given symbol level
50
57
  def level=(new_level)
51
- @logger.level=Logger::Severity.const_get(new_level.to_sym.upcase)
58
+ @logger.level = Logger::Severity.const_get(new_level.to_sym.upcase)
52
59
  end
53
60
 
54
61
  # get symbol of debug level of underlying logger
@@ -62,9 +69,9 @@ module Aspera
62
69
 
63
70
  # change underlying logger, but keep log level
64
71
  def logger_type=(new_logtype)
65
- current_severity_integer=@logger.level unless @logger.nil?
66
- current_severity_integer=ENV['AS_LOG_LEVEL'] if current_severity_integer.nil? && ENV.has_key?('AS_LOG_LEVEL')
67
- current_severity_integer=Logger::Severity::WARN if current_severity_integer.nil?
72
+ current_severity_integer = @logger.level unless @logger.nil?
73
+ current_severity_integer = ENV['AS_LOG_LEVEL'] if current_severity_integer.nil? && ENV.has_key?('AS_LOG_LEVEL')
74
+ current_severity_integer = Logger::Severity::WARN if current_severity_integer.nil?
68
75
  case new_logtype
69
76
  when :stderr
70
77
  @logger = Logger.new($stderr)
@@ -72,34 +79,23 @@ module Aspera
72
79
  @logger = Logger.new($stdout)
73
80
  when :syslog
74
81
  require 'syslog/logger'
75
- @logger = Syslog::Logger.new(@program_name)
82
+ @logger = Syslog::Logger.new(@program_name, Syslog::LOG_LOCAL2)
76
83
  else
77
84
  raise "unknown log type: #{new_logtype.class} #{new_logtype}"
78
85
  end
79
- @logger.level=current_severity_integer
80
- @logger_type=new_logtype
81
- original_formatter = @logger.formatter || Logger::Formatter.new
82
- # update formatter with password hiding, note that @log_secrets may be set AFTER this init is done, so it's done at runtime
83
- @logger.formatter=lambda do |severity, datetime, progname, msg|
84
- if msg.is_a?(String) && !@log_secrets
85
- msg=msg
86
- .gsub(/(["':][^"]*(password|secret|private_key)[^"]*["']?[=>: ]+")([^"]+)(")/){"#{Regexp.last_match(1)}#{HIDDEN_PASSWORD}#{Regexp.last_match(4)}"}
87
- .gsub(/("[^"]*(secret)[^"]*"=>{)([^}]+)(})/){"#{Regexp.last_match(1)}#{HIDDEN_PASSWORD}#{Regexp.last_match(4)}"}
88
- .gsub(/((secrets)={)([^}]+)(})/){"#{Regexp.last_match(1)}#{HIDDEN_PASSWORD}#{Regexp.last_match(4)}"}
89
- .gsub(/--+BEGIN[A-Z ]+KEY--+.+--+END[A-Z ]+KEY--+/m){HIDDEN_PASSWORD}
90
- end
91
- original_formatter.call(severity, datetime, progname, msg)
92
- end
86
+ @logger.level = current_severity_integer
87
+ @logger_type = new_logtype
88
+ # update formatter with password hiding
89
+ @logger.formatter = SecretHider.log_formatter(@logger.formatter)
93
90
  end
94
91
 
95
92
  private
96
93
 
97
94
  def initialize
98
- @logger=nil
99
- @program_name='aspera'
100
- @log_secrets=false
95
+ @logger = nil
96
+ @program_name = 'aspera'
101
97
  # this sets @logger and @logger_type (self needed to call method instead of local var)
102
- self.logger_type=:stderr
98
+ self.logger_type = :stderr
103
99
  raise 'error logger shall be defined' if @logger.nil?
104
100
  end
105
101
  end
data/lib/aspera/nagios.rb CHANGED
@@ -1,25 +1,52 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'date'
3
4
 
4
5
  module Aspera
5
6
  class Nagios
6
7
  # nagios levels
7
- LEVELS=[:ok,:warning,:critical,:unknown,:dependent]
8
- ADD_PREFIX='add_'
8
+ LEVELS = %i[ok warning critical unknown dependent].freeze
9
+ ADD_PREFIX = 'add_'
9
10
  # date offset levels
10
- DATE_WARN_OFFSET=2
11
- DATE_CRIT_OFFSET=5
11
+ DATE_WARN_OFFSET = 2
12
+ DATE_CRIT_OFFSET = 5
12
13
  private_constant :LEVELS,:ADD_PREFIX,:DATE_WARN_OFFSET,:DATE_CRIT_OFFSET
13
14
 
14
15
  # add methods to add nagios error levels, each take component name and message
15
16
  LEVELS.each_index do |code|
16
- name="#{ADD_PREFIX}#{LEVELS[code]}".to_sym
17
+ name = "#{ADD_PREFIX}#{LEVELS[code]}".to_sym
17
18
  define_method(name){|comp,msg|@data.push({code: code,comp: comp,msg: msg})}
18
19
  end
19
20
 
21
+ class << self
22
+ # process results of a analysis and display status and exit with code
23
+ def process(data)
24
+ raise 'INTERNAL ERROR, result must be list and not empty' unless data.is_a?(Array) && !data.empty?
25
+ %w[status component message].each{|c|raise "INTERNAL ERROR, result must have #{c}" unless data.first.has_key?(c)}
26
+ res_errors = data.reject{|s|s['status'].eql?('ok')}
27
+ # keep only errors in case of problem, other ok are assumed so
28
+ data = res_errors unless res_errors.empty?
29
+ # first is most critical
30
+ data.sort!{|a,b|LEVELS.index(a['status'].to_sym) <=> LEVELS.index(b['status'].to_sym)}
31
+ # build message: if multiple components: concatenate
32
+ #message = data.map{|i|"#{i['component']}:#{i['message']}"}.join(', ').gsub("\n",' ')
33
+ message = data.
34
+ map{|i|i['component']}.
35
+ uniq.
36
+ map{|comp|comp + ':' + data.select{|d|d['component'].eql?(comp)}.map{|d|d['message']}.join(',')}.
37
+ join(', ').
38
+ tr("\n",' ')
39
+ status = data.first['status'].upcase
40
+ # display status for nagios
41
+ puts("#{status} - [#{message}]\n")
42
+ # provide exit code to nagios
43
+ Process.exit(LEVELS.index(data.first['status'].to_sym))
44
+ end
45
+ end
46
+
20
47
  attr_reader :data
21
48
  def initialize
22
- @data=[]
49
+ @data = []
23
50
  end
24
51
 
25
52
  # comparte remote time with local time
@@ -27,9 +54,9 @@ module Aspera
27
54
  # check date if specified : 2015-10-13T07:32:01Z
28
55
  rtime = DateTime.strptime(remote_date)
29
56
  diff_time = (rtime - DateTime.now).abs
30
- diff_disp=diff_time.round(-2)
57
+ diff_disp = diff_time.round(-2)
31
58
  Log.log.debug("DATE: #{remote_date} #{rtime} diff=#{diff_disp}")
32
- msg="offset #{diff_disp} sec"
59
+ msg = "offset #{diff_disp} sec"
33
60
  if diff_time >= DATE_CRIT_OFFSET
34
61
  add_critical(component,msg)
35
62
  elsif diff_time >= DATE_WARN_OFFSET
@@ -47,26 +74,7 @@ module Aspera
47
74
  # translate for display
48
75
  def result
49
76
  raise 'missing result' if @data.empty?
50
- {type: :object_list,data: @data.map{|i|{'status'=>LEVELS[i[:code]].to_s,'component'=>i[:comp],'message'=>i[:msg]}}}
51
- end
52
-
53
- # process results of a analysis and display status and exit with code
54
- def self.process(data)
55
- raise 'INTERNAL ERROR, result must be list and not empty' unless data.is_a?(Array) && !data.empty?
56
- ['status','component','message'].each{|c|raise "INTERNAL ERROR, result must have #{c}" unless data.first.has_key?(c)}
57
- res_errors = data.reject{|s|s['status'].eql?('ok')}
58
- # keep only errors in case of problem, other ok are assumed so
59
- data = res_errors unless res_errors.empty?
60
- # first is most critical
61
- data.sort!{|a,b|LEVELS.index(a['status'].to_sym)<=>LEVELS.index(b['status'].to_sym)}
62
- # build message: if multiple components: concatenate
63
- #message = data.map{|i|"#{i['component']}:#{i['message']}"}.join(', ').gsub("\n",' ')
64
- message = data.map{|i|i['component']}.uniq.map{|comp|comp+':'+data.select{|d|d['component'].eql?(comp)}.map{|d|d['message']}.join(',')}.join(', ').gsub("\n",' ')
65
- status=data.first['status'].upcase
66
- # display status for nagios
67
- puts("#{status} - [#{message}]\n")
68
- # provide exit code to nagios
69
- Process.exit(LEVELS.index(data.first['status'].to_sym))
77
+ {type: :object_list,data: @data.map{|i|{'status' => LEVELS[i[:code]].to_s,'component' => i[:comp],'message' => i[:msg]}}}
70
78
  end
71
79
  end
72
80
  end
data/lib/aspera/node.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'aspera/fasp/transfer_spec'
3
4
  require 'aspera/rest'
4
5
  require 'aspera/oauth'
5
6
  require 'aspera/log'
7
+ require 'aspera/environment'
6
8
  require 'zlib'
7
9
  require 'base64'
8
10
 
@@ -10,21 +12,18 @@ module Aspera
10
12
  # Provides additional functions using node API.
11
13
  class Node < Rest
12
14
  # permissions
13
- ACCESS_LEVELS=%w[delete list mkdir preview read rename write].freeze
15
+ ACCESS_LEVELS = %w[delete list mkdir preview read rename write].freeze
14
16
  # prefix for ruby code for filter
15
- MATCH_EXEC_PREFIX='exec:'
17
+ MATCH_EXEC_PREFIX = 'exec:'
16
18
 
17
19
  # register node special token decoder
18
20
  Oauth.register_decoder(lambda{|token|JSON.parse(Zlib::Inflate.inflate(Base64.decode64(token)).partition('==SIGNATURE==').first)})
19
21
 
20
- class<<self
22
+ class << self
21
23
  def set_ak_basic_token(ts,ak,secret)
22
- Log.log.warn("Expected transfer user: #{Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER}, but have #{ts['remote_user']}") unless ts['remote_user'].eql?(Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
23
- ts['token']="Basic #{Base64.strict_encode64("#{ak}:#{secret}")}"
24
- end
25
-
26
- def empty_binding
27
- return Kernel.binding
24
+ Log.log.warn("Expected transfer user: #{Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER}, "\
25
+ "but have #{ts['remote_user']}") unless ts['remote_user'].eql?(Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
26
+ ts['token'] = Rest.basic_creds(ak,secret)
28
27
  end
29
28
 
30
29
  # for access keys: provide expression to match entry in folder
@@ -32,9 +31,9 @@ module Aspera
32
31
  # if prefix: ruby code
33
32
  # if filder is nil, then always match
34
33
  def file_matcher(match_expression)
35
- match_expression||="#{MATCH_EXEC_PREFIX}true"
34
+ match_expression ||= "#{MATCH_EXEC_PREFIX}true"
36
35
  if match_expression.start_with?(MATCH_EXEC_PREFIX)
37
- return eval("lambda{|f|#{match_expression[MATCH_EXEC_PREFIX.length..-1]}}", empty_binding, __FILE__, __LINE__)
36
+ return Environment.secure_eval("lambda{|f|#{match_expression[MATCH_EXEC_PREFIX.length..-1]}}")
38
37
  end
39
38
  return lambda{|f|f['name'].match(/#{match_expression}/)}
40
39
  end
@@ -60,21 +59,22 @@ module Aspera
60
59
  raise "processor must have #{opt[:method]}" unless processor.respond_to?(opt[:method])
61
60
  Log.log.debug("crawl #{opt}")
62
61
  #top_info=read("files/#{opt[:top_file_id]}")[:data]
63
- folders_to_explore=[{id: opt[:top_file_id], relpath: opt[:top_file_path]}]
62
+ folders_to_explore = [{id: opt[:top_file_id], relpath: opt[:top_file_path]}]
64
63
  Log.dump(:folders_to_explore,folders_to_explore)
65
64
  while !folders_to_explore.empty?
66
65
  current_item = folders_to_explore.shift
67
66
  Log.log.debug("searching #{current_item[:relpath]}".bg_green)
68
67
  # get folder content
69
- folder_contents = begin
70
- read("files/#{current_item[:id]}/files")[:data]
71
- rescue StandardError => e
72
- Log.log.warn("#{current_item[:relpath]}: #{e.class} #{e.message}")
73
- []
74
- end
68
+ folder_contents =
69
+ begin
70
+ read("files/#{current_item[:id]}/files")[:data]
71
+ rescue StandardError => e
72
+ Log.log.warn("#{current_item[:relpath]}: #{e.class} #{e.message}")
73
+ []
74
+ end
75
75
  Log.dump(:folder_contents,folder_contents)
76
76
  folder_contents.each do |entry|
77
- relative_path=File.join(current_item[:relpath],entry['name'])
77
+ relative_path = File.join(current_item[:relpath],entry['name'])
78
78
  Log.log.debug("looking #{relative_path}".bg_green)
79
79
  # entry type is file, folder or link
80
80
  if processor.send(opt[:method],entry,relative_path) && entry['type'].eql?('folder')