aspera-cli 4.4.0 → 4.7.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2095 -1503
  3. data/bin/ascli +2 -1
  4. data/bin/asession +4 -5
  5. data/docs/test_env.conf +3 -0
  6. data/examples/aoc.rb +4 -3
  7. data/examples/faspex4.rb +25 -25
  8. data/examples/proxy.pac +1 -1
  9. data/examples/transfer.rb +17 -17
  10. data/lib/aspera/aoc.rb +238 -185
  11. data/lib/aspera/ascmd.rb +93 -83
  12. data/lib/aspera/ats_api.rb +11 -10
  13. data/lib/aspera/cli/basic_auth_plugin.rb +13 -14
  14. data/lib/aspera/cli/extended_value.rb +42 -33
  15. data/lib/aspera/cli/formater.rb +142 -108
  16. data/lib/aspera/cli/info.rb +17 -0
  17. data/lib/aspera/cli/listener/line_dump.rb +3 -2
  18. data/lib/aspera/cli/listener/logger.rb +2 -1
  19. data/lib/aspera/cli/listener/progress.rb +16 -18
  20. data/lib/aspera/cli/listener/progress_multi.rb +18 -21
  21. data/lib/aspera/cli/main.rb +173 -149
  22. data/lib/aspera/cli/manager.rb +163 -168
  23. data/lib/aspera/cli/plugin.rb +43 -31
  24. data/lib/aspera/cli/plugins/alee.rb +6 -6
  25. data/lib/aspera/cli/plugins/aoc.rb +405 -370
  26. data/lib/aspera/cli/plugins/ats.rb +86 -79
  27. data/lib/aspera/cli/plugins/bss.rb +14 -16
  28. data/lib/aspera/cli/plugins/config.rb +580 -362
  29. data/lib/aspera/cli/plugins/console.rb +23 -19
  30. data/lib/aspera/cli/plugins/cos.rb +18 -18
  31. data/lib/aspera/cli/plugins/faspex.rb +201 -158
  32. data/lib/aspera/cli/plugins/faspex5.rb +80 -57
  33. data/lib/aspera/cli/plugins/node.rb +183 -166
  34. data/lib/aspera/cli/plugins/orchestrator.rb +71 -67
  35. data/lib/aspera/cli/plugins/preview.rb +92 -96
  36. data/lib/aspera/cli/plugins/server.rb +79 -75
  37. data/lib/aspera/cli/plugins/shares.rb +35 -19
  38. data/lib/aspera/cli/plugins/sync.rb +20 -22
  39. data/lib/aspera/cli/transfer_agent.rb +76 -113
  40. data/lib/aspera/cli/version.rb +2 -1
  41. data/lib/aspera/colors.rb +35 -27
  42. data/lib/aspera/command_line_builder.rb +48 -34
  43. data/lib/aspera/cos_node.rb +29 -21
  44. data/lib/aspera/data_repository.rb +3 -2
  45. data/lib/aspera/environment.rb +50 -45
  46. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +28 -25
  47. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +52 -43
  48. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +58 -72
  49. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +37 -43
  50. data/lib/aspera/fasp/{node.rb → agent_node.rb} +35 -16
  51. data/lib/aspera/fasp/agent_trsdk.rb +104 -0
  52. data/lib/aspera/fasp/error.rb +2 -1
  53. data/lib/aspera/fasp/error_info.rb +68 -52
  54. data/lib/aspera/fasp/installation.rb +152 -124
  55. data/lib/aspera/fasp/listener.rb +1 -0
  56. data/lib/aspera/fasp/parameters.rb +87 -92
  57. data/lib/aspera/fasp/parameters.yaml +305 -249
  58. data/lib/aspera/fasp/resume_policy.rb +11 -14
  59. data/lib/aspera/fasp/transfer_spec.rb +26 -0
  60. data/lib/aspera/fasp/uri.rb +22 -21
  61. data/lib/aspera/faspex_gw.rb +55 -89
  62. data/lib/aspera/hash_ext.rb +4 -3
  63. data/lib/aspera/id_generator.rb +8 -7
  64. data/lib/aspera/keychain/encrypted_hash.rb +121 -0
  65. data/lib/aspera/keychain/macos_security.rb +90 -0
  66. data/lib/aspera/log.rb +55 -37
  67. data/lib/aspera/nagios.rb +13 -12
  68. data/lib/aspera/node.rb +30 -25
  69. data/lib/aspera/oauth.rb +175 -226
  70. data/lib/aspera/open_application.rb +4 -3
  71. data/lib/aspera/persistency_action_once.rb +6 -6
  72. data/lib/aspera/persistency_folder.rb +5 -9
  73. data/lib/aspera/preview/file_types.rb +6 -5
  74. data/lib/aspera/preview/generator.rb +25 -24
  75. data/lib/aspera/preview/options.rb +16 -14
  76. data/lib/aspera/preview/utils.rb +98 -98
  77. data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
  78. data/lib/aspera/proxy_auto_config.rb +111 -20
  79. data/lib/aspera/rest.rb +154 -135
  80. data/lib/aspera/rest_call_error.rb +2 -2
  81. data/lib/aspera/rest_error_analyzer.rb +23 -25
  82. data/lib/aspera/rest_errors_aspera.rb +15 -14
  83. data/lib/aspera/ssh.rb +12 -10
  84. data/lib/aspera/sync.rb +42 -41
  85. data/lib/aspera/temp_file_manager.rb +18 -14
  86. data/lib/aspera/timer_limiter.rb +2 -1
  87. data/lib/aspera/uri_reader.rb +7 -5
  88. data/lib/aspera/web_auth.rb +79 -76
  89. metadata +116 -29
  90. data/docs/Makefile +0 -66
  91. data/docs/README.erb.md +0 -3973
  92. data/docs/README.md +0 -13
  93. data/docs/diagrams.txt +0 -49
  94. data/docs/doc_tools.rb +0 -58
  95. data/lib/aspera/api_detector.rb +0 -60
  96. data/lib/aspera/cli/plugins/shares2.rb +0 -114
  97. data/lib/aspera/secrets.rb +0 -20
data/lib/aspera/log.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'aspera/colors'
2
3
  require 'logger'
3
4
  require 'pp'
@@ -7,32 +8,43 @@ require 'singleton'
7
8
  module Aspera
8
9
  # Singleton object for logging
9
10
  class Log
10
-
11
- public
11
+ # display string for hidden secrets
12
+ HIDDEN_PASSWORD='🔑'
13
+ private_constant :HIDDEN_PASSWORD
12
14
  include Singleton
15
+ # class methods
16
+ class << self
17
+ # 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
13
19
 
14
- attr_reader :logger
15
- attr_reader :logger_type
16
- # levels are :debug,:info,:warn,:error,fatal,:unknown
17
- def self.levels; Logger::Severity.constants.sort{|a,b|Logger::Severity.const_get(a)<=>Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym};end
18
-
19
- # where logs are sent to
20
- def self.logtypes; [:stderr,:stdout,:syslog];end
20
+ # where logs are sent to
21
+ def logtypes; [:stderr,:stdout,:syslog];end
21
22
 
22
- # get the logger object of singleton
23
- def self.log; self.instance.logger; end
23
+ # get the logger object of singleton
24
+ def log; instance.logger;end
24
25
 
25
- # dump object in debug mode
26
- # @param name string or symbol
27
- # @param format either pp or json format
28
- def self.dump(name,object,format=:json)
29
- result=case format
30
- when :ruby;PP.pp(object,'')
31
- when :json;JSON.pretty_generate(object) rescue PP.pp(object,'')
32
- else raise "wrong parameter, expect pp or json"
26
+ # dump object in debug mode
27
+ # @param name string or symbol
28
+ # @param format either pp or json format
29
+ 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
40
+ "#{name.to_s.green} (#{format})=\n#{result}"
41
+ end
33
42
  end
34
- self.log.debug("#{name.to_s.green} (#{format})=\n#{result}")
35
- end
43
+ end # class
44
+
45
+ attr_reader :logger_type, :logger
46
+ attr_writer :program_name
47
+ attr_accessor :log_secrets
36
48
 
37
49
  # set log level of underlying logger given symbol level
38
50
  def level=(new_level)
@@ -44,25 +56,20 @@ module Aspera
44
56
  Logger::Severity.constants.each do |name|
45
57
  return name.downcase.to_sym if @logger.level.eql?(Logger::Severity.const_get(name))
46
58
  end
47
- raise "error"
59
+ # should not happen
60
+ raise "INTERNAL ERROR: unexpected level #{@logger.level}"
48
61
  end
49
62
 
50
63
  # change underlying logger, but keep log level
51
64
  def logger_type=(new_logtype)
52
- current_severity_integer=if @logger.nil?
53
- if ENV.has_key?('AS_LOG_LEVEL')
54
- ENV['AS_LOG_LEVEL']
55
- else
56
- Logger::Severity::WARN
57
- end
58
- else
59
- @logger.level
60
- end
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?
61
68
  case new_logtype
62
69
  when :stderr
63
- @logger = Logger.new(STDERR)
70
+ @logger = Logger.new($stderr)
64
71
  when :stdout
65
- @logger = Logger.new(STDOUT)
72
+ @logger = Logger.new($stdout)
66
73
  when :syslog
67
74
  require 'syslog/logger'
68
75
  @logger = Syslog::Logger.new(@program_name)
@@ -71,18 +78,29 @@ module Aspera
71
78
  end
72
79
  @logger.level=current_severity_integer
73
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
74
93
  end
75
94
 
76
- attr_writer :program_name
77
-
78
95
  private
79
96
 
80
97
  def initialize
81
98
  @logger=nil
82
99
  @program_name='aspera'
83
- # this sets @logger and @logger_type
100
+ @log_secrets=false
101
+ # this sets @logger and @logger_type (self needed to call method instead of local var)
84
102
  self.logger_type=:stderr
103
+ raise 'error logger shall be defined' if @logger.nil?
85
104
  end
86
-
87
105
  end
88
106
  end
data/lib/aspera/nagios.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'date'
2
3
 
3
4
  module Aspera
@@ -5,24 +6,24 @@ module Aspera
5
6
  # nagios levels
6
7
  LEVELS=[:ok,:warning,:critical,:unknown,:dependent]
7
8
  ADD_PREFIX='add_'
8
- # add methods to add nagios error levels, each take component name and message
9
- LEVELS.each_index do |code|
10
- name="#{ADD_PREFIX}#{LEVELS[code]}".to_sym
11
- define_method(name){|comp,msg|@data.push({:code=>code,:comp=>comp,:msg=>msg})}
12
- public name
13
- end
14
9
  # date offset levels
15
10
  DATE_WARN_OFFSET=2
16
11
  DATE_CRIT_OFFSET=5
17
12
  private_constant :LEVELS,:ADD_PREFIX,:DATE_WARN_OFFSET,:DATE_CRIT_OFFSET
18
13
 
14
+ # add methods to add nagios error levels, each take component name and message
15
+ LEVELS.each_index do |code|
16
+ name="#{ADD_PREFIX}#{LEVELS[code]}".to_sym
17
+ define_method(name){|comp,msg|@data.push({code: code,comp: comp,msg: msg})}
18
+ end
19
+
19
20
  attr_reader :data
20
21
  def initialize
21
22
  @data=[]
22
23
  end
23
24
 
24
25
  # comparte remote time with local time
25
- def check_time_offset( remote_date, component )
26
+ def check_time_offset(remote_date, component)
26
27
  # check date if specified : 2015-10-13T07:32:01Z
27
28
  rtime = DateTime.strptime(remote_date)
28
29
  diff_time = (rtime - DateTime.now).abs
@@ -38,22 +39,22 @@ module Aspera
38
39
  end
39
40
  end
40
41
 
41
- def check_product_version( component, product, version )
42
+ def check_product_version(component, _product, version)
42
43
  add_ok(component,"version #{version}")
43
44
  # TODO check on database if latest version
44
45
  end
45
46
 
46
47
  # translate for display
47
48
  def result
48
- raise "missing result" if @data.empty?
49
- {:type=>:object_list,:data=>@data.map{|i|{'status'=>LEVELS[i[:code]].to_s,'component'=>i[:comp],'message'=>i[:msg]}}}
49
+ 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]}}}
50
51
  end
51
52
 
52
53
  # process results of a analysis and display status and exit with code
53
54
  def self.process(data)
54
- raise "INTERNAL ERROR, result must be list and not empty" unless data.is_a?(Array) and !data.empty?
55
+ raise 'INTERNAL ERROR, result must be list and not empty' unless data.is_a?(Array) && !data.empty?
55
56
  ['status','component','message'].each{|c|raise "INTERNAL ERROR, result must have #{c}" unless data.first.has_key?(c)}
56
- res_errors = data.select{|s|!s['status'].eql?('ok')}
57
+ res_errors = data.reject{|s|s['status'].eql?('ok')}
57
58
  # keep only errors in case of problem, other ok are assumed so
58
59
  data = res_errors unless res_errors.empty?
59
60
  # first is most critical
data/lib/aspera/node.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+ require 'aspera/fasp/transfer_spec'
1
3
  require 'aspera/rest'
2
4
  require 'aspera/oauth'
3
5
  require 'aspera/log'
@@ -8,37 +10,40 @@ module Aspera
8
10
  # Provides additional functions using node API.
9
11
  class Node < Rest
10
12
  # permissions
11
- ACCESS_LEVELS=['delete','list','mkdir','preview','read','rename','write']
13
+ ACCESS_LEVELS=%w[delete list mkdir preview read rename write].freeze
14
+ # prefix for ruby code for filter
12
15
  MATCH_EXEC_PREFIX='exec:'
13
- # (public) default transfer username for access key based transfers
14
- ACCESS_KEY_TRANSFER_USER='xfer'
15
- SSH_PORT_DEFAULT=33001
16
- UDP_PORT_DEFAULT=33001
17
16
 
18
17
  # register node special token decoder
19
18
  Oauth.register_decoder(lambda{|token|JSON.parse(Zlib::Inflate.inflate(Base64.decode64(token)).partition('==SIGNATURE==').first)})
20
19
 
21
- def self.set_ak_basic_token(ts,ak,secret)
22
- raise "ERROR: expected xfer" unless ts['remote_user'].eql?(ACCESS_KEY_TRANSFER_USER)
23
- ts['token']="Basic #{Base64.strict_encode64("#{ak}:#{secret}")}"
24
- end
20
+ class<<self
21
+ 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
25
 
26
- # for access keys: provide expression to match entry in folder
27
- # if no prefix: regex
28
- # if prefix: ruby code
29
- # if filder is nil, then always match
30
- def self.file_matcher(match_expression)
31
- match_expression||="#{MATCH_EXEC_PREFIX}true"
32
- if match_expression.start_with?(MATCH_EXEC_PREFIX)
33
- return eval "lambda{|f|#{match_expression[MATCH_EXEC_PREFIX.length..-1]}}"
26
+ def empty_binding
27
+ return Kernel.binding
34
28
  end
35
- return lambda{|f|f['name'].match(/#{match_expression}/)}
36
- end
37
29
 
38
- def initialize(rest_params)
39
- super(rest_params)
30
+ # for access keys: provide expression to match entry in folder
31
+ # if no prefix: regex
32
+ # if prefix: ruby code
33
+ # if filder is nil, then always match
34
+ def file_matcher(match_expression)
35
+ match_expression||="#{MATCH_EXEC_PREFIX}true"
36
+ if match_expression.start_with?(MATCH_EXEC_PREFIX)
37
+ return eval("lambda{|f|#{match_expression[MATCH_EXEC_PREFIX.length..-1]}}", empty_binding, __FILE__, __LINE__)
38
+ end
39
+ return lambda{|f|f['name'].match(/#{match_expression}/)}
40
+ end
40
41
  end
41
42
 
43
+ # def initialize(rest_params)
44
+ # super(rest_params)
45
+ # end
46
+
42
47
  # recursively crawl in a folder.
43
48
  # subfolders a processed if the processing method returns true
44
49
  # @param processor must provide a method to process each entry
@@ -57,13 +62,13 @@ module Aspera
57
62
  #top_info=read("files/#{opt[:top_file_id]}")[:data]
58
63
  folders_to_explore=[{id: opt[:top_file_id], relpath: opt[:top_file_path]}]
59
64
  Log.dump(:folders_to_explore,folders_to_explore)
60
- while !folders_to_explore.empty? do
65
+ while !folders_to_explore.empty?
61
66
  current_item = folders_to_explore.shift
62
67
  Log.log.debug("searching #{current_item[:relpath]}".bg_green)
63
68
  # get folder content
64
69
  folder_contents = begin
65
70
  read("files/#{current_item[:id]}/files")[:data]
66
- rescue => e
71
+ rescue StandardError => e
67
72
  Log.log.warn("#{current_item[:relpath]}: #{e.class} #{e.message}")
68
73
  []
69
74
  end
@@ -72,8 +77,8 @@ module Aspera
72
77
  relative_path=File.join(current_item[:relpath],entry['name'])
73
78
  Log.log.debug("looking #{relative_path}".bg_green)
74
79
  # entry type is file, folder or link
75
- if processor.send(opt[:method],entry,relative_path) and entry['type'].eql?('folder')
76
- folders_to_explore.push({:id=>entry['id'],:relpath=>relative_path})
80
+ if processor.send(opt[:method],entry,relative_path) && entry['type'].eql?('folder')
81
+ folders_to_explore.push({id: entry['id'],relpath: relative_path})
77
82
  end
78
83
  end
79
84
  end