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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'singleton'
2
3
  require 'aspera/log'
3
4
 
@@ -5,13 +6,12 @@ module Aspera
5
6
  module Fasp
6
7
  # implements a simple resume policy
7
8
  class ResumePolicy
8
-
9
9
  # list of supported parameters and default values
10
10
  DEFAULTS={
11
- :iter_max => 7,
12
- :sleep_initial => 2,
13
- :sleep_factor => 2,
14
- :sleep_max => 60
11
+ iter_max: 7,
12
+ sleep_initial: 2,
13
+ sleep_factor: 2,
14
+ sleep_max: 60
15
15
  }
16
16
 
17
17
  # @param params see DEFAULTS
@@ -20,12 +20,9 @@ module Aspera
20
20
  if !params.nil?
21
21
  raise "expecting Hash (or nil), but have #{params.class}" unless params.is_a?(Hash)
22
22
  params.each do |k,v|
23
- if DEFAULTS.has_key?(k)
24
- raise "#{k} must be Integer" unless v.is_a?(Integer)
25
- @parameters[k]=v
26
- else
27
- raise "unknown resume parameter: #{k}, expect one of #{DEFAULTS.keys.map{|i|i.to_s}.join(",")}"
28
- end
23
+ raise "unknown resume parameter: #{k}, expect one of #{DEFAULTS.keys.map(&:to_s).join(',')}" unless DEFAULTS.has_key?(k)
24
+ raise "#{k} must be Integer" unless v.is_a?(Integer)
25
+ @parameters[k]=v
29
26
  end
30
27
  end
31
28
  Log.log.debug("resume params=#{@parameters}")
@@ -45,9 +42,9 @@ module Aspera
45
42
  block.call
46
43
  break
47
44
  rescue Fasp::Error => e
48
- Log.log.warn("An error occured: #{e.message}" );
45
+ Log.log.warn("An error occured: #{e.message}");
49
46
  # failure in ascp
50
- if e.retryable? then
47
+ if e.retryable?
51
48
  # exit if we exceed the max number of retry
52
49
  raise Fasp::Error,'Maximum number of retry reached' if remaining_resumes <= 0
53
50
  else
@@ -61,7 +58,7 @@ module Aspera
61
58
 
62
59
  # take this retry in account
63
60
  remaining_resumes-=1
64
- Log.log.warn( "resuming in #{sleep_seconds} seconds (retry left:#{remaining_resumes})" );
61
+ Log.log.warn("resuming in #{sleep_seconds} seconds (retry left:#{remaining_resumes})");
65
62
 
66
63
  # wait a bit before retrying, maybe network condition will be better
67
64
  sleep(sleep_seconds)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ require 'aspera/fasp/parameters'
3
+
4
+ module Aspera
5
+ module Fasp
6
+ # parameters for Transfer Spec
7
+ class TransferSpec
8
+ # default transfer username for access key based transfers
9
+ ACCESS_KEY_TRANSFER_USER='xfer'
10
+ SSH_PORT=33_001
11
+ UDP_PORT=33_001
12
+ AK_TSPEC_BASE={
13
+ 'remote_user' => ACCESS_KEY_TRANSFER_USER,
14
+ 'ssh_port' => SSH_PORT,
15
+ 'fasp_port' => UDP_PORT
16
+ }
17
+ # define constants for enums of parameters: <paramater>_<enum>, e.g. CIPHER_AES_128
18
+ Aspera::Fasp::Parameters.description.each do |k,v|
19
+ next unless v[:enum].is_a?(Array)
20
+ v[:enum].each do |enum|
21
+ TransferSpec.const_set("#{k.to_s.upcase}_#{enum.upcase.gsub(/[^A-Z0-9]/,'_')}", enum.freeze)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'aspera/log'
2
3
  require 'aspera/command_line_builder'
3
4
 
@@ -15,33 +16,33 @@ module Aspera
15
16
  result_ts['remote_host']=@fasp_uri.host
16
17
  result_ts['remote_user']=@fasp_uri.user
17
18
  result_ts['ssh_port']=@fasp_uri.port
18
- result_ts['paths']=[{"source"=>URI.decode_www_form_component(@fasp_uri.path)}]
19
+ result_ts['paths']=[{'source'=>URI.decode_www_form_component(@fasp_uri.path)}]
19
20
  # faspex does not encode trailing base64 encoded tags, fix that
20
21
  fixed_query = @fasp_uri.query.gsub(/(=+)$/){|x|'%3D'*x.length}
21
22
 
22
- URI::decode_www_form(fixed_query).each do |i|
23
+ URI.decode_www_form(fixed_query).each do |i|
23
24
  name=i[0]
24
25
  value=i[1]
25
26
  case name
26
- when 'cookie'; result_ts['cookie']=value
27
- when 'token'; result_ts['token']=value
28
- when 'policy'; result_ts['rate_policy']=value
29
- when 'httpport'; result_ts['http_fallback_port']=value.to_i
30
- when 'targetrate'; result_ts['target_rate_kbps']=value.to_i
31
- when 'minrate'; result_ts['min_rate_kbps']=value.to_i
32
- when 'port'; result_ts['fasp_port']=value.to_i
33
- when 'enc'; result_ts['cipher']=value.gsub('-','') # aes-128 -> aes128
34
- when 'tags64'; result_ts['tags']=JSON.parse(Base64.strict_decode64(value))
35
- when 'bwcap'; result_ts['target_rate_cap_kbps']=value.to_i
36
- when 'createpath'; result_ts['create_dir']=CommandLineBuilder.yes_to_true(value)
37
- when 'fallback'; result_ts['http_fallback']=CommandLineBuilder.yes_to_true(value)
38
- when 'lockpolicy'; result_ts['lock_rate_policy']=CommandLineBuilder.yes_to_true(value)
39
- when 'lockminrate'; result_ts['lock_min_rate']=CommandLineBuilder.yes_to_true(value)
40
- when 'sshfp'; result_ts['sshfp']=value
41
- when 'auth'; Log.log.debug("ignoring #{name}=#{value}") # TODO: translate into transfer spec ? yes/no
42
- when 'v'; Log.log.debug("ignoring #{name}=#{value}") # TODO: translate into transfer spec ? 2
43
- when 'protect'; Log.log.debug("ignoring #{name}=#{value}") # TODO: translate into transfer spec ?
44
- else Log.log.error("non managed URI value: #{name} = #{value}")
27
+ when 'cookie' then result_ts['cookie']=value
28
+ when 'token' then result_ts['token']=value
29
+ when 'sshfp' then result_ts['sshfp']=value
30
+ when 'policy' then result_ts['rate_policy']=value
31
+ when 'httpport' then result_ts['http_fallback_port']=value.to_i
32
+ when 'targetrate' then result_ts['target_rate_kbps']=value.to_i
33
+ when 'minrate' then result_ts['min_rate_kbps']=value.to_i
34
+ when 'port' then result_ts['fasp_port']=value.to_i
35
+ when 'bwcap' then result_ts['target_rate_cap_kbps']=value.to_i
36
+ when 'enc' then result_ts['cipher']=value.gsub(/^aes/,'aes-').gsub(/cfb$/,'-cfb').gsub(/gcm$/,'-gcm').gsub(/--/,'-')
37
+ when 'tags64' then result_ts['tags']=JSON.parse(Base64.strict_decode64(value))
38
+ when 'createpath' then result_ts['create_dir']=CommandLineBuilder.yes_to_true(value)
39
+ when 'fallback' then result_ts['http_fallback']=CommandLineBuilder.yes_to_true(value)
40
+ when 'lockpolicy' then result_ts['lock_rate_policy']=CommandLineBuilder.yes_to_true(value)
41
+ when 'lockminrate' then result_ts['lock_min_rate']=CommandLineBuilder.yes_to_true(value)
42
+ when 'auth' then Log.log.debug("ignoring auth #{name}=#{value}") # TODO: translate into transfer spec ? yes/no
43
+ when 'v' then Log.log.debug("ignoring v #{name}=#{value}") # TODO: translate into transfer spec ? 2
44
+ when 'protect' then Log.log.debug("ignoring protect #{name}=#{value}") # TODO: translate into transfer spec ?
45
+ else Log.log.warn("URI parameter ignored: #{name} = #{value}")
45
46
  end
46
47
  end
47
48
  return result_ts
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require 'aspera/log'
2
3
  require 'aspera/aoc'
3
- require 'aspera/node'
4
+ require 'aspera/fasp/transfer_spec'
4
5
  require 'aspera/cli/main'
5
6
  require 'webrick'
6
7
  require 'webrick/https'
@@ -12,7 +13,8 @@ module Aspera
12
13
  # this class answers the Faspex /send API and creates a package on Aspera on Cloud
13
14
  class FaspexGW
14
15
  class FxGwServlet < WEBrick::HTTPServlet::AbstractServlet
15
- def initialize(server,a_aoc_api_user,a_workspace_id)
16
+ def initialize(_server,a_aoc_api_user,a_workspace_id)
17
+ super
16
18
  @aoc_api_user=a_aoc_api_user
17
19
  @aoc_workspace_id=a_workspace_id
18
20
  end
@@ -30,27 +32,27 @@ module Aspera
30
32
  # }
31
33
  # }
32
34
  def process_faspex_send(request, response)
33
- raise "no payload" if request.body.nil?
35
+ raise 'no payload' if request.body.nil?
34
36
  faspex_pkg_parameters=JSON.parse(request.body)
35
37
  faspex_pkg_delivery=faspex_pkg_parameters['delivery']
36
- Log.log.debug "faspex pkg create parameters=#{faspex_pkg_parameters}"
38
+ Log.log.debug("faspex pkg create parameters=#{faspex_pkg_parameters}")
37
39
 
38
40
  # get recipient ids
39
41
  files_pkg_recipients=[]
40
42
  faspex_pkg_delivery['recipients'].each do |recipient_email|
41
- user_lookup=@aoc_api_user.read("contacts",{'current_workspace_id'=>@aoc_workspace_id,'q'=>recipient_email})[:data]
42
- raise StandardError,"no such unique user: #{recipient_email} / #{user_lookup}" unless !user_lookup.nil? and user_lookup.length == 1
43
+ user_lookup=@aoc_api_user.read('contacts',{'current_workspace_id'=>@aoc_workspace_id,'q'=>recipient_email})[:data]
44
+ raise StandardError,"no such unique user: #{recipient_email} / #{user_lookup}" unless !user_lookup.nil? && user_lookup.length.eql?(1)
43
45
  recipient_user_info=user_lookup.first
44
- files_pkg_recipients.push({"id"=>recipient_user_info['source_id'],"type"=>recipient_user_info['source_type']})
46
+ files_pkg_recipients.push({'id'=>recipient_user_info['source_id'],'type'=>recipient_user_info['source_type']})
45
47
  end
46
48
 
47
49
  # create a new package with one file
48
- the_package=@aoc_api_user.create("packages",{
49
- "file_names"=>faspex_pkg_delivery['sources'][0]['paths'],
50
- "name"=>faspex_pkg_delivery['title'],
51
- "note"=>faspex_pkg_delivery['note'],
52
- "recipients"=>files_pkg_recipients,
53
- "workspace_id"=>@aoc_workspace_id})[:data]
50
+ the_package=@aoc_api_user.create('packages',{
51
+ 'file_names' =>faspex_pkg_delivery['sources'][0]['paths'],
52
+ 'name' =>faspex_pkg_delivery['title'],
53
+ 'note' =>faspex_pkg_delivery['note'],
54
+ 'recipients' =>files_pkg_recipients,
55
+ 'workspace_id'=>@aoc_workspace_id})[:data]
54
56
 
55
57
  # get node information for the node on which package must be created
56
58
  node_info=@aoc_api_user.read("nodes/#{the_package['node_id']}")[:data]
@@ -59,50 +61,50 @@ module Aspera
59
61
  node_auth_bearer_token=@aoc_api_user.oauth_token(scope: AoC.node_scope(node_info['access_key'],AoC::SCOPE_NODE_USER))
60
62
 
61
63
  # tell Files what to expect in package: 1 transfer (can also be done after transfer)
62
- @aoc_api_user.update("packages/#{the_package['id']}",{"sent"=>true,"transfers_expected"=>1})
64
+ @aoc_api_user.update("packages/#{the_package['id']}",{'sent'=>true,'transfers_expected'=>1})
65
+
66
+ # to return an error:
67
+ # response.status=400
68
+ # return 'ERROR HERE'
63
69
 
64
- if false
65
- response.status=400
66
- return "ERROR HERE"
67
- end
68
70
  # TODO: check about xfer_*
69
71
  ts_tags={
70
- "aspera" => {
71
- "files" => { "package_id" => the_package['id'], "package_operation" => "upload" },
72
- "node" => { "access_key" => node_info['access_key'], "file_id" => the_package['contents_file_id'] },
73
- "xfer_id" => SecureRandom.uuid,
74
- "xfer_retry" => 3600 } }
72
+ 'aspera' => {
73
+ 'files' => { 'package_id' => the_package['id'], 'package_operation' => 'upload' },
74
+ 'node' => { 'access_key' => node_info['access_key'], 'file_id' => the_package['contents_file_id'] },
75
+ 'xfer_id' => SecureRandom.uuid,
76
+ 'xfer_retry' => 3600 } }
75
77
  # this transfer spec is for transfer to AoC
76
78
  faspex_transfer_spec={
77
79
  'direction' => 'send',
78
80
  'remote_host' => node_info['host'],
79
- 'remote_user' => Node::ACCESS_KEY_TRANSFER_USER,
80
- 'ssh_port' => Node::SSH_PORT_DEFAULT,
81
- 'fasp_port' => Node::UDP_PORT_DEFAULT
81
+ 'remote_user' => Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER,
82
+ 'ssh_port' => Fasp::TransferSpec::SSH_PORT,
83
+ 'fasp_port' => Fasp::TransferSpec::UDP_PORT,
82
84
  'tags' => ts_tags,
83
85
  'token' => node_auth_bearer_token,
84
86
  'paths' => [{'destination' => '/'}],
85
87
  'cookie' => 'unused',
86
88
  'create_dir' => true,
87
89
  'rate_policy' => 'fair',
88
- 'rate_policy_allowed' => 'fixed',
89
- 'min_rate_cap_kbps' => nil,
90
- 'min_rate_kbps' => 0,
90
+ 'rate_policy_allowed' => 'fixed',
91
+ 'min_rate_cap_kbps' => nil,
92
+ 'min_rate_kbps' => 0,
91
93
  'target_rate_percentage' => nil,
92
- 'lock_target_rate' => nil,
93
- 'fasp_url' => 'unused',
94
- 'lock_min_rate' => true,
95
- 'lock_rate_policy' => true,
96
- 'source_root' => '',
97
- 'content_protection' => nil,
98
- 'target_rate_cap_kbps' => 20000, # TODO
99
- 'target_rate_kbps' => 10000, # TODO
100
- 'cipher' => 'aes-128',
101
- 'cipher_allowed' => nil,
102
- 'http_fallback' => false,
103
- 'http_fallback_port' => nil,
104
- 'https_fallback_port' => nil,
105
- 'destination_root' => '/'
94
+ 'lock_target_rate' => nil,
95
+ 'fasp_url' => 'unused',
96
+ 'lock_min_rate' => true,
97
+ 'lock_rate_policy' => true,
98
+ 'source_root' => '',
99
+ 'content_protection' => nil,
100
+ 'target_rate_cap_kbps' => 20_000, # TODO: is this value useful ?
101
+ 'target_rate_kbps' => 10_000, # TODO: get from where?
102
+ 'cipher' => 'aes-128',
103
+ 'cipher_allowed' => nil,
104
+ 'http_fallback' => false,
105
+ 'http_fallback_port' => nil,
106
+ 'https_fallback_port' => nil,
107
+ 'destination_root' => '/'
106
108
  }
107
109
  # but we place it in a Faspex package creation response
108
110
  faspex_package_create_result={
@@ -111,28 +113,27 @@ module Aspera
111
113
  }
112
114
  Log.log.info("faspex_package_create_result=#{faspex_package_create_result}")
113
115
  response.status=200
114
- response.content_type = "application/json"
116
+ response.content_type = 'application/json'
115
117
  response.body=JSON.generate(faspex_package_create_result)
116
118
  end
117
119
 
118
- def do_GET (request, response)
120
+ def do_GET(request, response) # rubocop:disable Naming/MethodName
119
121
  case request.path
120
122
  when '/aspera/faspex/send'
121
123
  process_faspex_send(request, response)
122
124
  else
123
125
  response.status=400
124
- return "ERROR HERE"
125
- raise "unsupported path: #{request.path}"
126
+ return 'ERROR HERE'
126
127
  end
127
128
  end
128
129
  end # FxGwServlet
129
130
 
130
131
  class NewUserServlet < WEBrick::HTTPServlet::AbstractServlet
131
- def do_GET (request, response)
132
+ def do_GET(request, response) # rubocop:disable Naming/MethodName
132
133
  case request.path
133
134
  when '/newuser'
134
135
  response.status=200
135
- response.content_type = "text/html"
136
+ response.content_type = 'text/html'
136
137
  response.body='<html><body>hello world</body></html>'
137
138
  else
138
139
  raise "unsupported path: [#{request.path}]"
@@ -140,49 +141,14 @@ module Aspera
140
141
  end
141
142
  end
142
143
 
143
- def fill_self_signed_cert(options)
144
- key = OpenSSL::PKey::RSA.new(4096)
145
- cert = OpenSSL::X509::Certificate.new
146
- cert.subject = cert.issuer = OpenSSL::X509::Name.parse("/C=FR/O=Test/OU=Test/CN=Test")
147
- cert.not_before = Time.now
148
- cert.not_after = Time.now + 365 * 24 * 60 * 60
149
- cert.public_key = key.public_key
150
- cert.serial = 0x0
151
- cert.version = 2
152
- ef = OpenSSL::X509::ExtensionFactory.new
153
- ef.issuer_certificate = cert
154
- ef.subject_certificate = cert
155
- cert.extensions = [
156
- ef.create_extension("basicConstraints","CA:TRUE", true),
157
- ef.create_extension("subjectKeyIdentifier", "hash"),
158
- # ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
159
- ]
160
- cert.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always,issuer:always"))
161
- cert.sign(key, OpenSSL::Digest::SHA256.new)
162
- options[:SSLPrivateKey] = key
163
- options[:SSLCertificate] = cert
164
- end
165
-
166
144
  def initialize(a_aoc_api_user,a_workspace_id)
167
145
  webrick_options = {
168
- :app => FaspexGW,
169
- :Port => 9443,
170
- :Logger => Log.log,
171
- #:DocumentRoot => Cli::Main.gem_root,
172
- :SSLEnable => true,
173
- :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
146
+ Port: 9443,
147
+ Logger: Log.log,
148
+ SSLEnable: true,
149
+ SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
150
+ SSLCertName: [['CN',WEBrick::Utils.getservername]]
174
151
  }
175
- case 2
176
- when 0
177
- # generate self signed cert
178
- webrick_options[:SSLCertName] = [ [ 'CN',WEBrick::Utils::getservername ] ]
179
- Log.log.error(">>>#{webrick_options[:SSLCertName]}")
180
- when 1
181
- fill_self_signed_cert(webrick_options)
182
- when 2
183
- webrick_options[:SSLPrivateKey] =OpenSSL::PKey::RSA.new(File.read('/Users/laurent/workspace/Tools/certificate/myserver.key'))
184
- webrick_options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read('/Users/laurent/workspace/Tools/certificate/myserver.crt'))
185
- end
186
152
  Log.log.info("Server started on port #{webrick_options[:Port]}")
187
153
  @server = WEBrick::HTTPServer.new(webrick_options)
188
154
  @server.mount('/aspera/faspex', FxGwServlet,a_aoc_api_user,a_workspace_id)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # for older rubies
2
3
  unless Hash.method_defined?(:dig)
3
4
  class Hash
@@ -11,18 +12,18 @@ end
11
12
 
12
13
  class ::Hash
13
14
  def deep_merge(second)
14
- self.merge(second){|key,v1,v2|Hash===v1&&Hash===v2 ? v1.deep_merge(v2) : v2}
15
+ merge(second){|_key,v1,v2|Hash===v1&&Hash===v2 ? v1.deep_merge(v2) : v2}
15
16
  end
16
17
 
17
18
  def deep_merge!(second)
18
- self.merge!(second){|key,v1,v2|Hash===v1&&Hash===v2 ? v1.deep_merge!(v2) : v2}
19
+ merge!(second){|_key,v1,v2|Hash===v1&&Hash===v2 ? v1.deep_merge!(v2) : v2}
19
20
  end
20
21
  end
21
22
 
22
23
  unless Hash.method_defined?(:symbolize_keys)
23
24
  class Hash
24
25
  def symbolize_keys
25
- return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
26
+ return each_with_object({}){|(k,v),memo| memo[k.to_sym] = v; }
26
27
  end
27
28
  end
28
29
  end
@@ -1,22 +1,23 @@
1
+ # frozen_string_literal: true
1
2
  require 'uri'
2
3
 
3
4
  module Aspera
4
5
  class IdGenerator
5
6
  ID_SEPARATOR='_'
6
- WINDOWS_PROTECTED_CHAR=%r{[/:"<>\\\*\?]}
7
+ WINDOWS_PROTECTED_CHAR=%r{[/:"<>\\*?]}
7
8
  PROTECTED_CHAR_REPLACE='_'
8
9
  private_constant :ID_SEPARATOR,:PROTECTED_CHAR_REPLACE,:WINDOWS_PROTECTED_CHAR
9
10
  def self.from_list(object_id)
10
11
  if object_id.is_a?(Array)
11
- object_id=object_id.select{|i|!i.nil?}.map do |i|
12
- (i.is_a?(String) and i.start_with?('https://')) ? URI.parse(i).host : i.to_s
12
+ object_id=object_id.reject(&:nil?).map do |i|
13
+ i.is_a?(String) && i.start_with?('https://') ? URI.parse(i).host : i.to_s
13
14
  end.join(ID_SEPARATOR)
14
15
  end
15
- raise "id must be a String" unless object_id.is_a?(String)
16
+ raise 'id must be a String' unless object_id.is_a?(String)
16
17
  return object_id.
17
- gsub(WINDOWS_PROTECTED_CHAR,PROTECTED_CHAR_REPLACE). # remove windows forbidden chars
18
- gsub('.',PROTECTED_CHAR_REPLACE). # keep dot for extension only (nicer)
19
- downcase
18
+ gsub(WINDOWS_PROTECTED_CHAR,PROTECTED_CHAR_REPLACE). # remove windows forbidden chars
19
+ gsub('.',PROTECTED_CHAR_REPLACE). # keep dot for extension only (nicer)
20
+ downcase
20
21
  end
21
22
  end
22
23
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+ require 'openssl'
3
+
4
+ module Aspera
5
+ module Keychain
6
+ class SimpleCipher
7
+ def initialize(key)
8
+ @key=Digest::SHA1.hexdigest(key)[0..23]
9
+ @cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
10
+ end
11
+
12
+ def encrypt(value)
13
+ @cipher.encrypt
14
+ @cipher.key = @key
15
+ s = @cipher.update(value) + @cipher.final
16
+ s.unpack1('H*')
17
+ end
18
+
19
+ def decrypt(value)
20
+ @cipher.decrypt
21
+ @cipher.key = @key
22
+ s = [value].pack('H*').unpack('C*').pack('c*')
23
+ @cipher.update(s) + @cipher.final
24
+ end
25
+ end
26
+
27
+ # Manage secrets in a simple Hash
28
+ class EncryptedHash
29
+ SEPARATOR='%'
30
+ private_constant :SEPARATOR
31
+ def initialize(values)
32
+ raise 'values shall be Hash' unless values.is_a?(Hash)
33
+ @all_secrets=values
34
+ end
35
+
36
+ def set(options)
37
+ raise 'options shall be Hash' unless options.is_a?(Hash)
38
+ unsupported=options.keys-[:username,:url,:secret,:description]
39
+ raise "unsupported options: #{unsupported}" unless unsupported.empty?
40
+ username=options[:username]
41
+ raise 'options shall have username' if username.nil?
42
+ url=options[:url]
43
+ raise 'options shall have username' if url.nil?
44
+ secret=options[:secret]
45
+ raise 'options shall have secret' if secret.nil?
46
+ key=[url,username].join(SEPARATOR)
47
+ raise "secret #{key} already exist, delete first" if @all_secrets.has_key?(key)
48
+ obj={username: username, url: url, secret: SimpleCipher.new(key).encrypt(secret)}
49
+ obj[:description]=options[:description] if options.has_key?(:description)
50
+ @all_secrets[key]=obj
51
+ nil
52
+ end
53
+
54
+ def list
55
+ result=[]
56
+ @all_secrets.each do |k,v|
57
+ case v
58
+ when String
59
+ o={username: k, url: '', description: ''}
60
+ when Hash
61
+ o=v.clone
62
+ o.delete(:secret)
63
+ o[:description]||=''
64
+ else raise 'error'
65
+ end
66
+ o[:description]=v[:description] if v.is_a?(Hash) && v[:description].is_a?(String)
67
+ result.push(o)
68
+ end
69
+ return result
70
+ end
71
+
72
+ def delete(options)
73
+ raise 'options shall be Hash' unless options.is_a?(Hash)
74
+ unsupported=options.keys-[:username,:url]
75
+ raise "unsupported options: #{unsupported}" unless unsupported.empty?
76
+ username=options[:username]
77
+ raise 'options shall have username' if username.nil?
78
+ url=options[:url]
79
+ key=nil
80
+ if !url.nil?
81
+ extk=[url,username].join(SEPARATOR)
82
+ key=extk if @all_secrets.has_key?(extk)
83
+ end
84
+ # backward compatibility: TODO: remove in future ? (make url mandatory ?)
85
+ key=username if key.nil? && @all_secrets.has_key?(username)
86
+ raise 'no such secret' if key.nil?
87
+ @all_secrets.delete(key)
88
+ end
89
+
90
+ def get(options)
91
+ raise 'options shall be Hash' unless options.is_a?(Hash)
92
+ unsupported=options.keys-[:username,:url]
93
+ raise "unsupported options: #{unsupported}" unless unsupported.empty?
94
+ username=options[:username]
95
+ raise 'options shall have username' if username.nil?
96
+ url=options[:url]
97
+ val=nil
98
+ if !url.nil?
99
+ val=@all_secrets[[url,username].join(SEPARATOR)]
100
+ end
101
+ # backward compatibility: TODO: remove in future ? (make url mandatory ?)
102
+ if val.nil?
103
+ val=@all_secrets[username]
104
+ end
105
+ result=options.clone
106
+ case val
107
+ when NilClass
108
+ raise 'no such secret'
109
+ when String
110
+ result.merge!({secret: val, description: ''})
111
+ when Hash
112
+ key=[url,username].join(SEPARATOR)
113
+ plain=SimpleCipher.new(key).decrypt(val[:secret])
114
+ result.merge!({secret: plain, description: val[:description]})
115
+ else raise 'error'
116
+ end
117
+ return result
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+ require 'security'
3
+
4
+ # enhance the gem to support other keychains
5
+ module Security
6
+ class Keychain
7
+ class << self
8
+ def by_name(name)
9
+ keychains_from_output('security list-keychains').select{|kc|kc.filename.end_with?("/#{name}.keychain-db")}.first
10
+ end
11
+ end
12
+ end
13
+
14
+ class Password
15
+ class << self
16
+ # add some login to original method
17
+ alias orig_flags_for_options flags_for_options
18
+ def flags_for_options(options = {})
19
+ keychain=options.delete(:keychain)
20
+ url=options.delete(:url)
21
+ if !url.nil?
22
+ uri=URI.parse(url)
23
+ raise 'only https' unless uri.scheme.eql?('https')
24
+ options[:r]='htps'
25
+ raise 'host required in URL' if uri.host.nil?
26
+ options[:s]=uri.host
27
+ options[:p]=uri.path unless ['','/'].include?(uri.path)
28
+ options[:P]=uri.port unless uri.port.eql?(443) && !url.include?(':443/')
29
+ end
30
+ flags=[orig_flags_for_options(options)]
31
+ flags.push(keychain.filename) unless keychain.nil?
32
+ flags.join(' ')
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module Aspera
39
+ module Keychain
40
+ # keychain based on macOS keychain, using `security` cmmand line
41
+ class MacosSecurity
42
+ def initialize(name=nil)
43
+ @keychain=name.nil? ? Security::Keychain.default_keychain : Security::Keychain.by_name(name)
44
+ raise "no such keychain #{name}" if @keychain.nil?
45
+ end
46
+
47
+ def set(options)
48
+ raise 'options shall be Hash' unless options.is_a?(Hash)
49
+ unsupported=options.keys-[:username,:url,:secret,:description]
50
+ raise "unsupported options: #{unsupported}" unless unsupported.empty?
51
+ username=options[:username]
52
+ raise 'options shall have username' if username.nil?
53
+ url=options[:url]
54
+ raise 'options shall have url' if url.nil?
55
+ secret=options[:secret]
56
+ raise 'options shall have secret' if secret.nil?
57
+ raise 'set not implemented'
58
+ end
59
+
60
+ def get(options)
61
+ raise 'options shall be Hash' unless options.is_a?(Hash)
62
+ unsupported=options.keys-[:username,:url]
63
+ raise "unsupported options: #{unsupported}" unless unsupported.empty?
64
+ username=options[:username]
65
+ raise 'options shall have username' if username.nil?
66
+ url=options[:url]
67
+ raise 'options shall have url' if url.nil?
68
+ info=Security::InternetPassword.find(keychain: @keychain, url: url, account: username)
69
+ raise 'not found' if info.nil?
70
+ result=options.clone
71
+ result.merge!({secret: info.password, description: info.attributes['icmt']})
72
+ return result
73
+ end
74
+
75
+ def list
76
+ raise 'list not implemented'
77
+ end
78
+
79
+ def delete(options)
80
+ raise 'options shall be Hash' unless options.is_a?(Hash)
81
+ unsupported=options.keys-[:username,:url]
82
+ raise "unsupported options: #{unsupported}" unless unsupported.empty?
83
+ username=options[:username]
84
+ raise 'options shall have username' if username.nil?
85
+ url=options[:url]
86
+ raise "delete not implemented #{url}"
87
+ end
88
+ end
89
+ end
90
+ end