aspera-cli 4.4.0 → 4.7.0

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