hammer_cli 0.10.1 → 0.10.2

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/bin/hammer +1 -1
  3. data/config/cli_config.template.yml +4 -1
  4. data/doc/release_notes.md +5 -0
  5. data/lib/hammer_cli/abstract.rb +9 -4
  6. data/lib/hammer_cli/apipie/api_connection.rb +1 -1
  7. data/lib/hammer_cli/ca_cert_fetcher.rb +15 -4
  8. data/lib/hammer_cli/ca_cert_manager.rb +18 -33
  9. data/lib/hammer_cli/exceptions.rb +1 -0
  10. data/lib/hammer_cli/exit_codes.rb +2 -1
  11. data/lib/hammer_cli/ssloptions.rb +19 -10
  12. data/lib/hammer_cli/version.rb +1 -1
  13. data/lib/hammer_cli.rb +1 -0
  14. data/man/hammer.1.gz +0 -0
  15. data/test/unit/apipie/api_connection_test.rb +0 -1
  16. data/test/unit/ca_cert_manager_test.rb +23 -33
  17. data/test/unit/fixtures/certs/non_ca_cert.pem +27 -0
  18. metadata +50 -322
  19. data/test/reports/TEST-Fields-ContainerField-display-.xml +0 -7
  20. data/test/reports/TEST-Fields-ContainerField-display-blank-is-allowed.xml +0 -15
  21. data/test/reports/TEST-Fields-ContainerField-display-blank-is-not-allowed.xml +0 -15
  22. data/test/reports/TEST-Fields-ContainerField.xml +0 -7
  23. data/test/reports/TEST-Fields-Field-display-.xml +0 -7
  24. data/test/reports/TEST-Fields-Field-display-blank-is-allowed.xml +0 -11
  25. data/test/reports/TEST-Fields-Field-display-blank-is-not-allowed.xml +0 -11
  26. data/test/reports/TEST-Fields-Field-hide-blank-.xml +0 -11
  27. data/test/reports/TEST-Fields-Field-parameters.xml +0 -9
  28. data/test/reports/TEST-Fields-Field.xml +0 -13
  29. data/test/reports/TEST-HammerCLI-AbstractCommand-build-options.xml +0 -15
  30. data/test/reports/TEST-HammerCLI-AbstractCommand-exception-handler.xml +0 -13
  31. data/test/reports/TEST-HammerCLI-AbstractCommand-logging.xml +0 -21
  32. data/test/reports/TEST-HammerCLI-AbstractCommand-option-builder.xml +0 -11
  33. data/test/reports/TEST-HammerCLI-AbstractCommand-options.xml +0 -11
  34. data/test/reports/TEST-HammerCLI-AbstractCommand-output.xml +0 -19
  35. data/test/reports/TEST-HammerCLI-AbstractCommand-subcommand-behavior-remove-subcommand.xml +0 -11
  36. data/test/reports/TEST-HammerCLI-AbstractCommand-subcommand-behavior-subcommand-.xml +0 -13
  37. data/test/reports/TEST-HammerCLI-AbstractCommand-subcommand-behavior-subcommand.xml +0 -11
  38. data/test/reports/TEST-HammerCLI-AbstractCommand-subcommand-behavior.xml +0 -7
  39. data/test/reports/TEST-HammerCLI-AbstractCommand.xml +0 -11
  40. data/test/reports/TEST-HammerCLI-Apipie-Command-options.xml +0 -11
  41. data/test/reports/TEST-HammerCLI-Apipie-Command-resource-defined.xml +0 -9
  42. data/test/reports/TEST-HammerCLI-Apipie-Command-setting-resources.xml +0 -19
  43. data/test/reports/TEST-HammerCLI-Apipie-Command.xml +0 -9
  44. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder-aliasing-resources.xml +0 -13
  45. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder-filtering-options.xml +0 -15
  46. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder-required-options.xml +0 -11
  47. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder-setting-correct-normalizers.xml +0 -9
  48. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder-with-hash-params.xml +0 -11
  49. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder-with-one-simple-param.xml +0 -15
  50. data/test/reports/TEST-HammerCLI-Apipie-OptionBuilder.xml +0 -7
  51. data/test/reports/TEST-HammerCLI-Completer-command-completion.xml +0 -29
  52. data/test/reports/TEST-HammerCLI-Completer-option-value-completion.xml +0 -17
  53. data/test/reports/TEST-HammerCLI-Completer-subcommand-completion.xml +0 -19
  54. data/test/reports/TEST-HammerCLI-Completer.xml +0 -7
  55. data/test/reports/TEST-HammerCLI-CompleterLine-line-complete.xml +0 -25
  56. data/test/reports/TEST-HammerCLI-CompleterLine-splitting-words.xml +0 -29
  57. data/test/reports/TEST-HammerCLI-CompleterLine.xml +0 -7
  58. data/test/reports/TEST-HammerCLI-CompleterWord-complete-.xml +0 -23
  59. data/test/reports/TEST-HammerCLI-CompleterWord-quote.xml +0 -15
  60. data/test/reports/TEST-HammerCLI-CompleterWord-quoted-.xml +0 -13
  61. data/test/reports/TEST-HammerCLI-CompleterWord.xml +0 -7
  62. data/test/reports/TEST-HammerCLI-Connection.xml +0 -21
  63. data/test/reports/TEST-HammerCLI-ExceptionHandler.xml +0 -21
  64. data/test/reports/TEST-HammerCLI-I18n.xml +0 -11
  65. data/test/reports/TEST-HammerCLI-MainCommand-loading-context-password.xml +0 -11
  66. data/test/reports/TEST-HammerCLI-MainCommand-loading-context-username.xml +0 -11
  67. data/test/reports/TEST-HammerCLI-MainCommand-loading-context-verbose.xml +0 -9
  68. data/test/reports/TEST-HammerCLI-MainCommand-loading-context.xml +0 -7
  69. data/test/reports/TEST-HammerCLI-MainCommand.xml +0 -7
  70. data/test/reports/TEST-HammerCLI-Modules-find-by-name.xml +0 -13
  71. data/test/reports/TEST-HammerCLI-Modules-load-a-module-module-not-found.xml +0 -13
  72. data/test/reports/TEST-HammerCLI-Modules-load-a-module-module-runtime-exception.xml +0 -13
  73. data/test/reports/TEST-HammerCLI-Modules-load-a-module-success.xml +0 -15
  74. data/test/reports/TEST-HammerCLI-Modules-load-a-module.xml +0 -7
  75. data/test/reports/TEST-HammerCLI-Modules-load-all-modules.xml +0 -9
  76. data/test/reports/TEST-HammerCLI-Modules-names.xml +0 -13
  77. data/test/reports/TEST-HammerCLI-Modules.xml +0 -7
  78. data/test/reports/TEST-HammerCLI-OptionBuilderContainer.0.xml +0 -7
  79. data/test/reports/TEST-HammerCLI-OptionBuilderContainer.xml +0 -11
  80. data/test/reports/TEST-HammerCLI-Options-Normalizers-abstract.xml +0 -9
  81. data/test/reports/TEST-HammerCLI-Options-Normalizers-bool.xml +0 -31
  82. data/test/reports/TEST-HammerCLI-Options-Normalizers-datetime.xml +0 -17
  83. data/test/reports/TEST-HammerCLI-Options-Normalizers-enum.xml +0 -15
  84. data/test/reports/TEST-HammerCLI-Options-Normalizers-enumlist.xml +0 -21
  85. data/test/reports/TEST-HammerCLI-Options-Normalizers-json-input.xml +0 -15
  86. data/test/reports/TEST-HammerCLI-Options-Normalizers-key-value-list.xml +0 -17
  87. data/test/reports/TEST-HammerCLI-Options-Normalizers-list.xml +0 -15
  88. data/test/reports/TEST-HammerCLI-Options-Normalizers.xml +0 -7
  89. data/test/reports/TEST-HammerCLI-Options-OptionDefinition-context.xml +0 -9
  90. data/test/reports/TEST-HammerCLI-Options-OptionDefinition-formatters.xml +0 -11
  91. data/test/reports/TEST-HammerCLI-Options-OptionDefinition.xml +0 -7
  92. data/test/reports/TEST-HammerCLI-Output-Adapter-Abstract-error-messages.xml +0 -15
  93. data/test/reports/TEST-HammerCLI-Output-Adapter-Abstract-messages.xml +0 -11
  94. data/test/reports/TEST-HammerCLI-Output-Adapter-Abstract-test-data-for-field.xml +0 -15
  95. data/test/reports/TEST-HammerCLI-Output-Adapter-Abstract.xml +0 -17
  96. data/test/reports/TEST-HammerCLI-Output-Adapter-Base-print-collection-show-ids.xml +0 -9
  97. data/test/reports/TEST-HammerCLI-Output-Adapter-Base-print-collection.xml +0 -27
  98. data/test/reports/TEST-HammerCLI-Output-Adapter-Base.xml +0 -7
  99. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues-print-collection-formatters.xml +0 -11
  100. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues-print-collection-handle-fields-with-collections.xml +0 -13
  101. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues-print-collection-handle-fields-with-containers.xml +0 -11
  102. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues-print-collection-handle-ids.xml +0 -11
  103. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues-print-collection.xml +0 -11
  104. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues-print-message.xml +0 -11
  105. data/test/reports/TEST-HammerCLI-Output-Adapter-CSValues.xml +0 -7
  106. data/test/reports/TEST-HammerCLI-Output-Adapter-Table-print-collection-column-width.xml +0 -15
  107. data/test/reports/TEST-HammerCLI-Output-Adapter-Table-print-collection-formatters.xml +0 -11
  108. data/test/reports/TEST-HammerCLI-Output-Adapter-Table-print-collection-handle-ids.xml +0 -11
  109. data/test/reports/TEST-HammerCLI-Output-Adapter-Table-print-collection-sort-columns.xml +0 -9
  110. data/test/reports/TEST-HammerCLI-Output-Adapter-Table-print-collection.xml +0 -11
  111. data/test/reports/TEST-HammerCLI-Output-Adapter-Table.xml +0 -7
  112. data/test/reports/TEST-HammerCLI-Output-Definition-empty-.xml +0 -11
  113. data/test/reports/TEST-HammerCLI-Output-Definition.xml +0 -11
  114. data/test/reports/TEST-HammerCLI-Output-Dsl-collection.xml +0 -13
  115. data/test/reports/TEST-HammerCLI-Output-Dsl-custom-fields.xml +0 -11
  116. data/test/reports/TEST-HammerCLI-Output-Dsl-fields.xml +0 -15
  117. data/test/reports/TEST-HammerCLI-Output-Dsl-label.xml +0 -13
  118. data/test/reports/TEST-HammerCLI-Output-Dsl-path-definition.xml +0 -13
  119. data/test/reports/TEST-HammerCLI-Output-Dsl.xml +0 -9
  120. data/test/reports/TEST-HammerCLI-Output-FieldFilter.xml +0 -13
  121. data/test/reports/TEST-HammerCLI-Output-Formatters-BooleanFormatter.xml +0 -11
  122. data/test/reports/TEST-HammerCLI-Output-Formatters-ColorFormatter.xml +0 -9
  123. data/test/reports/TEST-HammerCLI-Output-Formatters-DateFormatter.xml +0 -11
  124. data/test/reports/TEST-HammerCLI-Output-Formatters-FieldFormatter.xml +0 -13
  125. data/test/reports/TEST-HammerCLI-Output-Formatters-FormatterContainer.xml +0 -13
  126. data/test/reports/TEST-HammerCLI-Output-Formatters-FormatterLibrary.xml +0 -11
  127. data/test/reports/TEST-HammerCLI-Output-Formatters-KeyValueFormatter.xml +0 -13
  128. data/test/reports/TEST-HammerCLI-Output-Formatters-ListFormatter.xml +0 -13
  129. data/test/reports/TEST-HammerCLI-Output-Formatters-LongTextFormatter.xml +0 -13
  130. data/test/reports/TEST-HammerCLI-Output-Output-adapters.xml +0 -17
  131. data/test/reports/TEST-HammerCLI-Output-Output-data.xml +0 -15
  132. data/test/reports/TEST-HammerCLI-Output-Output-formatters.xml +0 -9
  133. data/test/reports/TEST-HammerCLI-Output-Output-messages.xml +0 -19
  134. data/test/reports/TEST-HammerCLI-Output-Output.xml +0 -7
  135. data/test/reports/TEST-HammerCLI-Output-RecordCollection.xml +0 -13
  136. data/test/reports/TEST-HammerCLI-Settings-load-from-paths.xml +0 -15
  137. data/test/reports/TEST-HammerCLI-Settings.xml +0 -25
  138. data/test/reports/TEST-HammerCLI-ShellHistory-loading-old-history.xml +0 -11
  139. data/test/reports/TEST-HammerCLI-ShellHistory-saving-history.xml +0 -15
  140. data/test/reports/TEST-HammerCLI-ShellHistory.xml +0 -7
  141. data/test/reports/TEST-MiniTest-Spec.xml +0 -7
  142. data/test/reports/TEST-String-camelize.xml +0 -11
  143. data/test/reports/TEST-String-formatting.xml +0 -17
  144. data/test/reports/TEST-String-indent.xml +0 -11
  145. data/test/reports/TEST-String-interactive-.xml +0 -13
  146. data/test/reports/TEST-String.xml +0 -7
  147. data/test/reports/TEST-constraints-HammerCLI-Validator-AllConstraint-exist-.xml +0 -13
  148. data/test/reports/TEST-constraints-HammerCLI-Validator-AllConstraint.xml +0 -7
  149. data/test/reports/TEST-constraints-HammerCLI-Validator-AnyConstraint-exist-.xml +0 -13
  150. data/test/reports/TEST-constraints-HammerCLI-Validator-AnyConstraint.xml +0 -7
  151. data/test/reports/TEST-constraints-HammerCLI-Validator-BaseConstraint-exist-.xml +0 -9
  152. data/test/reports/TEST-constraints-HammerCLI-Validator-BaseConstraint-rejected.xml +0 -13
  153. data/test/reports/TEST-constraints-HammerCLI-Validator-BaseConstraint-required.xml +0 -13
  154. data/test/reports/TEST-constraints-HammerCLI-Validator-BaseConstraint.xml +0 -7
  155. data/test/reports/TEST-constraints.xml +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a51bb0421b077ec5f6b58516eb36bfb36593543a
4
- data.tar.gz: e10795cc718d474a9b71e084472c2e9455bcc208
3
+ metadata.gz: 230906bc19a41759805ad05e51cee6244938cc5e
4
+ data.tar.gz: 7e4389026de508ce212c3af34823bc1606b6851e
5
5
  SHA512:
6
- metadata.gz: 177b9fe7bcfc57623a87fe32890a44ffe8ce2ed9c25bdcde061c87670fb8a1a6e5cf5993046262eabbf993984621e119fda6ec72ce880531f998893da80c6a9d
7
- data.tar.gz: 37f64d084f60e2e87fa99ac7f1b2ffe670a7577ec715e349439dce07432d9deee7e2193b16be8b35c896d75777a7dd0e55360b2c8e529b2ffbebb8adf2ac2df4
6
+ metadata.gz: 55c6841e58e1645e48eb83e147d662abc847cf99f18df1f5636b6fb9a09be79e133d99429fa626c3e6ca2456003091e7e74f2813ac43dc22ee163c0f2862b54d
7
+ data.tar.gz: ddeec7d17a560050722bddc473bbc2ff8bba5e967c3cf3f7772249fcf300ca38d03db2f5d8d75df0be5e1b10025eafeef257bf4b8ca5280d71720537ea0e6a0f
data/bin/hammer CHANGED
@@ -125,7 +125,7 @@ require 'hammer_cli'
125
125
 
126
126
  if preparser.fetch_ca_cert
127
127
  require 'hammer_cli/ca_cert_fetcher'
128
- ca_path = HammerCLI::SSLOptions.get_options[:ssl_ca_path]
128
+ ca_path = HammerCLI::SSLOptions.new.get_local_ca_store_path
129
129
  exit HammerCLI::CACertFetcher.new.fetch_ca_cert(preparser.fetch_ca_cert, ca_path)
130
130
  end
131
131
 
@@ -38,7 +38,7 @@
38
38
  #:ssl_ca_file: '/path/to/ca_certificate.pem'
39
39
 
40
40
  # Path to a direcotry with ca file
41
- #:ssl_ca_path: '~/.hammer/certs'
41
+ #:ssl_ca_path: '/path/to/ca'
42
42
 
43
43
  # Turn SSL verification on/off
44
44
  #:verify_ssl: true
@@ -51,3 +51,6 @@
51
51
 
52
52
  # Enable standard authentication in addition to client certificate authentication
53
53
  #:ssl_with_basic_auth: true
54
+
55
+ # local CA cert store path
56
+ #:local_ca_store_path: '~/.hammer/certs'
data/doc/release_notes.md CHANGED
@@ -1,6 +1,11 @@
1
1
  Release notes
2
2
  =============
3
3
 
4
+ ### 0.10.2 (2017-05-05)
5
+ * Retry command on session expiry ([#19431](http://projects.theforeman.org/issues/19431))
6
+ * Prevent fetching of non-CA certificates ([#19391](http://projects.theforeman.org/issues/19391))
7
+ * Use local ca cert store instead of ssl_ca_path ([#19390](http://projects.theforeman.org/issues/19390))
8
+
4
9
  ### 0.10.1 (2017-04-24)
5
10
  * Require apipie-bindings >= 0.2.0 ([#19083](http://projects.theforeman.org/issues/19083))
6
11
  * Install server CA cert without root access ([#19083](http://projects.theforeman.org/issues/19083))
@@ -24,11 +24,16 @@ module HammerCLI
24
24
  end
25
25
 
26
26
  def run(arguments)
27
- exit_code = super
28
- raise "exit code must be integer" unless exit_code.is_a? Integer
27
+ begin
28
+ begin
29
+ exit_code = super
30
+ raise "exit code must be integer" unless exit_code.is_a? Integer
31
+ rescue => e
32
+ exit_code = handle_exception(e)
33
+ end
34
+ logger.debug 'Retrying the command' if (exit_code == HammerCLI::EX_RETRY)
35
+ end while (exit_code == HammerCLI::EX_RETRY)
29
36
  return exit_code
30
- rescue => e
31
- handle_exception e
32
37
  end
33
38
 
34
39
  def parse(arguments)
@@ -7,7 +7,7 @@ module HammerCLI::Apipie
7
7
 
8
8
  def initialize(params, options = {})
9
9
  @logger = options[:logger]
10
- @api = ApipieBindings::API.new(params, HammerCLI::SSLOptions.get_options)
10
+ @api = ApipieBindings::API.new(params, HammerCLI::SSLOptions.new.get_options(params[:uri]))
11
11
  if options[:reload_cache]
12
12
  @api.clean_cache
13
13
  @logger.debug 'Apipie cache was cleared' unless @logger.nil?
@@ -1,13 +1,13 @@
1
1
  require 'hammer_cli/ca_cert_manager'
2
2
  module HammerCLI
3
3
  class CACertFetcher
4
- def fetch_ca_cert(service_uri, ca_path)
4
+ def fetch_ca_cert(service_uri, ca_store_path)
5
5
  begin
6
6
  uri = URI.parse(service_uri)
7
7
  raise URI::InvalidURIError.new(_("Unable to find hostname in #{service_uri}")) if uri.host.nil?
8
8
  raise URI::InvalidURIError.new(scheme_error(uri)) unless uri.scheme == 'https'
9
- ca_cert_manager = HammerCLI::CACertManager.new(ca_path)
10
- raw_cert = HammerCLI::CACertDownloader.new.download(uri)
9
+ ca_cert_manager = HammerCLI::CACertManager.new(ca_store_path)
10
+ raw_cert = HammerCLI::CertDownloader.new.download(uri)
11
11
  cert_file = ca_cert_manager.cert_file_name(uri)
12
12
  ca_cert_manager.store_ca_cert(raw_cert, cert_file)
13
13
 
@@ -17,8 +17,9 @@ module HammerCLI
17
17
  deb_update_cmd = "update-ca-certificates"
18
18
  cert_file = ca_cert_manager.cert_file_name(uri)
19
19
 
20
- puts _("CA certificate for #{service_uri} was stored to #{cert_file}")
20
+ puts _("CA certificate for %{uri} was stored to %{file}") % {:uri => service_uri, :file => cert_file}
21
21
  puts _("Now hammer can use the downloaded certificate to verify SSL connection to the server.")
22
+ puts _("It will be used automatically when ssl_ca_path and ssl_ca_file options are not set.")
22
23
  puts
23
24
  puts _("Be aware that hammer cannot verify whether the certificate is correct and you should verify its authenticity.")
24
25
  puts
@@ -44,6 +45,16 @@ module HammerCLI
44
45
  $stderr.puts _("Couldn't parse URI '%s'.") % service_uri
45
46
  $stderr.puts e.message
46
47
  return HammerCLI::EX_SOFTWARE
48
+ rescue HammerCLI::NoCACertificate => e
49
+ $stderr.puts _("The CA certificate for %{uri} couldn't be downloaded. No CA cert was found.") % {:uri => service_uri}
50
+ $stderr.puts
51
+ $stderr.puts _("Make sure your server sends cert chain including the CA.")
52
+ $stderr.puts _("To see the actual chain you can use openssl command")
53
+ $stderr.puts " $ openssl s_client -showcerts -connect #{uri.host}:#{uri.port} </dev/null"
54
+ $stderr.puts
55
+ $stderr.puts _("You can also download the certificate manually and store it as #{cert_file}")
56
+ $stderr.puts _("If you choose any other location set the ssl_ca_path or ssl_ca_file configuration options appropriately.")
57
+ return HammerCLI::EX_SOFTWARE
47
58
  rescue StandardError => e
48
59
  logger = Logging.logger['CACertFetcher']
49
60
  msg = [_('Fetching the CA certificate failed:')]
@@ -2,58 +2,43 @@ require 'fileutils'
2
2
 
3
3
  module HammerCLI
4
4
  class CACertManager
5
- attr_reader :ca_path
5
+ attr_reader :ca_store_path
6
6
 
7
- def initialize(ca_path)
8
- @ca_path = ca_path
7
+ def initialize(ca_store_path)
8
+ @ca_store_path = File.expand_path(ca_store_path)
9
9
  end
10
10
 
11
11
  def store_ca_cert(raw_cert, cert_file)
12
- ensure_ca_path_exist
12
+ raise HammerCLI::NoCACertificate.new unless is_ca_cert?(raw_cert)
13
+ ensure_ca_store_exist
13
14
  File.write(cert_file, raw_cert)
14
- hash = cert_hash(raw_cert)
15
- create_link(hash, cert_file)
16
15
  cert_file
17
16
  end
18
17
 
19
- def cert_hash(raw_cert)
20
- cert = OpenSSL::X509::Certificate.new(raw_cert)
21
- subject = OpenSSL::X509::Name.new(cert.subject)
22
- subject.hash
23
- end
24
-
25
- def create_link(hash, cert_file)
26
- ensure_ca_path_exist
27
- cert_link = File.join(ca_path, "#{hash.to_s(16)}.%s")
28
- count = 0
29
- # increase hash index if file or link to different target already exist
30
- while plain_file?(cert_link % count) || link_to_different_target?(cert_link % count, cert_file) do
31
- count += 1
32
- end
33
- File.symlink(cert_file, cert_link % count) unless File.symlink?(cert_link % count)
18
+ def cert_file_name(uri)
19
+ File.join(ca_store_path, "#{uri.host}_#{uri.port}.pem")
34
20
  end
35
21
 
36
- def cert_file_name(uri)
37
- File.join(ca_path, "#{uri.host}.pem")
22
+ def cert_exist?(uri)
23
+ File.exist?(cert_file_name(uri))
38
24
  end
39
25
 
40
26
  protected
41
27
 
42
-
43
- def ensure_ca_path_exist
44
- FileUtils.mkpath(ca_path) unless File.directory?(ca_path)
45
- end
46
-
47
- def plain_file?(path)
48
- File.exist?(path) && !File.symlink?(path)
28
+ def ensure_ca_store_exist
29
+ FileUtils.mkpath(ca_store_path) unless File.directory?(ca_store_path)
49
30
  end
50
31
 
51
- def link_to_different_target?(path, target)
52
- File.symlink?(path) && File.expand_path(File.readlink(path)) != File.expand_path(target)
32
+ def is_ca_cert?(cert)
33
+ cert = OpenSSL::X509::Certificate.new(cert) if cert.is_a? String
34
+ cert.extensions.any? do |ex|
35
+ (ex.oid == 'basicConstraints' && ex.value.upcase == 'CA:TRUE') ||
36
+ (ex.oid == 'keyUsage' && ex.value =~ /Cert(ificate )?Sign/i)
37
+ end
53
38
  end
54
39
  end
55
40
 
56
- class CACertDownloader
41
+ class CertDownloader
57
42
  def download(uri)
58
43
  noverify_ssl_connection = OpenSSL::SSL::SSLSocket.new(TCPSocket.new(uri.host, uri.port), noverify_ssl_context)
59
44
  noverify_ssl_connection.connect
@@ -4,5 +4,6 @@ module HammerCLI
4
4
  class OperationNotSupportedError < StandardError; end
5
5
  class ModuleLoadingError < StandardError; end
6
6
  class ModuleDisabledButRequired < StandardError; end
7
+ class NoCACertificate < StandardError; end
7
8
 
8
9
  end
@@ -4,7 +4,7 @@ module HammerCLI
4
4
  EX_USAGE = 64 # command line usage error
5
5
  EX_DATAERR = 65 # data format error
6
6
  EX_NOINPUT = 66 # cannot open input
7
- EX_NOUSER = 67 # addressee unknown
7
+ EX_NOUSER = 67 # addressee unknown
8
8
  EX_NOHOST = 68 # host name unknown
9
9
  EX_UNAVAILABLE = 69 # service unavailable
10
10
  EX_SOFTWARE = 70 # internal software error
@@ -20,4 +20,5 @@ module HammerCLI
20
20
  # non POSIX codes
21
21
  EX_NOT_FOUND = 128 # resource was not found
22
22
  EX_UNAUTHORIZED = 129 # authorization failed
23
+ EX_RETRY = 666 # retry the command
23
24
  end
@@ -1,17 +1,18 @@
1
1
  module HammerCLI
2
2
  class SSLOptions
3
- DEFAULT_SSL_CA_PATH = "~/.hammer/certs"
3
+ DEFAULT_LOCAL_CA_STORE_PATH = "~/.hammer/certs"
4
4
 
5
- def initialize(settings = HammerCLI::Settings, logger = Logging.logger['SSLoptions'])
6
- @settings = settings
7
- @logger = logger
5
+ def initialize(options={})
6
+ @settings = options.fetch(:settings, HammerCLI::Settings)
7
+ @logger = options.fetch(:logger, Logging.logger['SSLoptions'])
8
+ @ca_manager = options.fetch(:ca_manager, HammerCLI::CACertManager.new(DEFAULT_LOCAL_CA_STORE_PATH))
8
9
  end
9
10
 
10
- def self.get_options(settings = HammerCLI::Settings, logger = Logging.logger['SSLoptions'])
11
- self.new(settings, logger).get_options
11
+ def get_local_ca_store_path
12
+ @settings.get(:ssl, :local_ca_store_path) || DEFAULT_LOCAL_CA_STORE_PATH
12
13
  end
13
14
 
14
- def get_options
15
+ def get_options(uri = nil)
15
16
  ssl_options = {}
16
17
  for sslopt in [:ssl_ca_file, :ssl_ca_path, :verify_ssl] do
17
18
  ssloptval = read_ssl_option(sslopt)
@@ -19,13 +20,21 @@ module HammerCLI
19
20
  end
20
21
  ssl_options.merge!(cert_key_options)
21
22
 
22
- ssl_options[:ssl_ca_path] = DEFAULT_SSL_CA_PATH if ssl_options[:ssl_ca_path].nil?
23
+ # enable ssl verification
24
+ ssl_options[:verify_ssl] = true if ssl_options[:verify_ssl].nil?
25
+
26
+ if ssl_options[:verify_ssl] && uri && !ssl_options[:ssl_ca_file] && !ssl_options[:ssl_ca_path]
27
+ uri = URI.parse(uri) if uri.is_a?(String)
28
+ if @ca_manager.cert_exist?(uri)
29
+ ssl_options[:ssl_ca_file] = @ca_manager.cert_file_name(uri)
30
+ @logger.info("Matching CA cert was found in local CA store #{ssl_options[:ssl_ca_file]}")
31
+ end
32
+ end
33
+
23
34
  [:ssl_ca_file, :ssl_ca_path].each do |opt|
24
35
  ssl_options[opt] = File.expand_path(ssl_options[opt]) unless ssl_options[opt].nil?
25
36
  end
26
37
 
27
- # enable ssl verification
28
- ssl_options[:verify_ssl] = true if ssl_options[:verify_ssl].nil?
29
38
  @logger.debug("SSL options: #{ApipieBindings::Utils::inspect_data(ssl_options)}")
30
39
  ssl_options
31
40
  end
@@ -1,5 +1,5 @@
1
1
  module HammerCLI
2
2
  def self.version
3
- @version ||= Gem::Version.new '0.10.1'
3
+ @version ||= Gem::Version.new '0.10.2'
4
4
  end
5
5
  end
data/lib/hammer_cli.rb CHANGED
@@ -5,6 +5,7 @@ require 'hammer_cli/version'
5
5
  require 'hammer_cli/modules'
6
6
  require 'hammer_cli/exit_codes'
7
7
  require 'hammer_cli/settings'
8
+ require 'hammer_cli/ca_cert_manager'
8
9
  require 'hammer_cli/connection'
9
10
  require 'hammer_cli/validator'
10
11
  require 'hammer_cli/output'
data/man/hammer.1.gz CHANGED
Binary file
@@ -10,7 +10,6 @@ describe HammerCLI::Apipie::ApiConnection do
10
10
  def api_stub(params = {}, options = {})
11
11
  api_stub = stub()
12
12
  options[:verify_ssl] = true if options[:verify_ssl].nil?
13
- options[:ssl_ca_path] = File.expand_path(HammerCLI::SSLOptions::DEFAULT_SSL_CA_PATH) if options[:ssl_ca_path].nil?
14
13
  ApipieBindings::API.expects(:new).with(params, options).returns(api_stub)
15
14
  api_stub
16
15
  end
@@ -6,59 +6,49 @@ require 'hammer_cli/ca_cert_manager'
6
6
  describe HammerCLI::CACertManager do
7
7
 
8
8
  before(:all) do
9
- @ca_path = Dir.mktmpdir('ca_cert_manager')
9
+ @ca_store_path = Dir.mktmpdir('ca_cert_manager')
10
10
  end
11
11
 
12
12
  after(:all) do
13
- FileUtils.rm_rf(@ca_path) if File.exist?(@ca_path)
13
+ FileUtils.rm_rf(@ca_store_path) if File.exist?(@ca_store_path)
14
14
  end
15
15
 
16
16
  let(:service_uri) { URI.parse("https://test.host.com") }
17
- let(:ca_cert_manager) { HammerCLI::CACertManager.new(@ca_path) }
17
+ let(:ca_cert_manager) { HammerCLI::CACertManager.new(@ca_store_path) }
18
18
  let(:cert_file) { ca_cert_manager.cert_file_name(service_uri) }
19
+ let(:ca_cert_fixture) { File.join(File.dirname(__FILE__), '/fixtures/certs/ca_cert.pem') }
20
+ let(:non_ca_cert_fixture) { File.join(File.dirname(__FILE__), '/fixtures/certs/non_ca_cert.pem') }
19
21
 
20
22
  describe '#store_ca_cert' do
21
- let(:cert_fixture) { File.join(File.dirname(__FILE__), '/fixtures/certs/ca_cert.pem') }
22
-
23
23
  it 'stores ca cert' do
24
- new_cert_file = ca_cert_manager.store_ca_cert(File.read(cert_fixture), cert_file)
25
- assert File.exist?(File.join(@ca_path, "2c543cd1.0"))
24
+ new_cert_file = ca_cert_manager.store_ca_cert(File.read(ca_cert_fixture), cert_file)
26
25
  assert File.exist?(cert_file)
27
26
  assert_equal cert_file, new_cert_file
28
27
  end
29
- end
30
-
31
- describe '#create_link' do
32
- let(:hash) { 123456789 }
33
- let(:hash_file) { hash.to_s(16) }
34
28
 
35
- it "creates link to cert" do
36
- FileUtils.touch(cert_file)
37
- ca_cert_manager.create_link(hash, cert_file)
38
- assert File.exist?(File.join(@ca_path, "#{hash_file}.0"))
29
+ it 'refuses non-CA cert' do
30
+ assert_raises HammerCLI::NoCACertificate do
31
+ ca_cert_manager.store_ca_cert(File.read(non_ca_cert_fixture), cert_file)
32
+ end
39
33
  end
34
+ end
40
35
 
41
- it "creates ca path if missing" do
42
- FileUtils.rm_rf(@ca_path) if File.exist?(@ca_path)
43
- ca_cert_manager.create_link(hash, cert_file)
44
- FileUtils.touch(cert_file)
45
- assert File.exist?(File.join(@ca_path, "#{hash_file}.0"))
36
+ describe '#cert_exist?' do
37
+ it 'return true if the cert exist' do
38
+ ca_cert_manager.store_ca_cert(File.read(ca_cert_fixture), cert_file)
39
+ assert ca_cert_manager.cert_exist?(service_uri)
46
40
  end
47
41
 
48
- it "does not create new link if it already exist" do
49
- FileUtils.touch(cert_file)
50
- File.symlink(cert_file, File.join(@ca_path, "#{hash_file}.0"))
51
- ca_cert_manager.create_link(hash, cert_file)
52
- assert File.exist?(File.join(@ca_path, "#{hash_file}.0"))
53
- refute File.exist?(File.join(@ca_path, "#{hash_file}.1"))
42
+ it 'return false if the cert does not exist' do
43
+ refute ca_cert_manager.cert_exist?(service_uri)
54
44
  end
45
+ end
55
46
 
56
- it "does not override existing link if it has different target" do
57
- FileUtils.touch(cert_file)
58
- FileUtils.touch(File.join(@ca_path, "#{hash_file}.0"))
59
- ca_cert_manager.create_link(hash, cert_file)
60
- assert File.exist?(File.join(@ca_path, "#{hash_file}.0"))
61
- assert File.exist?(File.join(@ca_path, "#{hash_file}.1"))
47
+ describe '#cert_file_name' do
48
+ it 'make file name from host uri' do
49
+ uri = URI.parse("https://test.example.com:1111")
50
+ filename = ca_cert_manager.cert_file_name(uri)
51
+ assert_equal File.join(ca_cert_manager.ca_store_path,'test.example.com_1111.pem'), filename
62
52
  end
63
53
  end
64
54
  end
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEgDCCA2igAwIBAgIIdKMhNG6e8c4wDQYJKoZIhvcNAQELBQAwSTELMAkGA1UE
3
+ BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
4
+ cm5ldCBBdXRob3JpdHkgRzIwHhcNMTcwNDEyMTMyODAwWhcNMTcwNzA1MTMyODAw
5
+ WjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
6
+ TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UEAwwOd3d3
7
+ Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDI5ZXl
8
+ lffcMQiwJGs2CxZToWbi16Q7LL6H8y3xuRwDn//gp8qX8Jcv4x7KG4aEipB8wOUm
9
+ jrZs2Q1BT8VIqfQLLqhSrP61LZfuAzv/v1WAw7CmtvqCH91qKM/SkOXyCJK1Cf2/
10
+ +i5oLoY2NnxV7iM7+tidPSF4ZPMjtb13ABVcdr49lDlSZce8YXDoWtlr9hjfAPEF
11
+ mzxlSsA5gPv/BNUhgW8Rna3UWwl4jgKAFOgLybKTharhnEl14cYUeDJ/HnGLc8L1
12
+ 6es05HjHsz6J3jpXi9x0X1b5lQfeEFYLBPQ3nPjGgOZvECLLl8esBkpAWy+BLGd0
13
+ vPYRWZnnnOU3+i33AgMBAAGjggFLMIIBRzAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
14
+ KwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYBBQUHAQEE
15
+ XDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3J0
16
+ MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9vY3NwMB0G
17
+ A1UdDgQWBBRfEHz1zg9lsVjVptJ9ncVQH7wzIzAMBgNVHRMBAf8EAjAAMB8GA1Ud
18
+ IwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMCEGA1UdIAQaMBgwDAYKKwYBBAHW
19
+ eQIFATAIBgZngQwBAgIwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29n
20
+ bGUuY29tL0dJQUcyLmNybDANBgkqhkiG9w0BAQsFAAOCAQEAgo6rk/60YN5JYhPO
21
+ oBm3nTawx+0FW/rFh6tEsxUl2x6d6eYN/W95Wx2ofJFTe0blC5W40GHyFjuGoEGb
22
+ 7soS+gUMsMNb1oiz8osD/OOce70afUcGMSB1qEXC2Q7plCqnJSTDIEsO1r7O0Y6n
23
+ JUqvdrB/LqihaKYnYKsSM6ya+i3fX55KfamkqeRmDIVOspDNcy8zl+EbAX3sJk3j
24
+ k2bitWNktp37t+suGhimvmOVaP/EVRMl8z5RUrNTi8q2M1BzDOZD5k/Hhxy1UFpr
25
+ QwHC6MuyOpgKBIHQguLvzD2I3dyK1GzzsfiyYx3eCJVq4f5iAfSkaxf8spWIhIRo
26
+ YgQaig==
27
+ -----END CERTIFICATE-----