require 'rubygems/test_case' require 'rubygems/commands/cert_command' require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9" unless defined?(OpenSSL::SSL) then warn 'Skipping `gem cert` tests. openssl not found.' end class TestGemCommandsCertCommand < Gem::TestCase ALTERNATE_CERT = load_cert 'alternate' ALTERNATE_KEY_FILE = key_path 'alternate' PRIVATE_KEY_FILE = key_path 'private' PUBLIC_KEY_FILE = key_path 'public' ALTERNATE_CERT_FILE = cert_path 'alternate' CHILD_CERT_FILE = cert_path 'child' PUBLIC_CERT_FILE = cert_path 'public' def setup super @cmd = Gem::Commands::CertCommand.new @trust_dir = Gem::Security.trust_dir end def test_certificates_matching @trust_dir.trust_cert PUBLIC_CERT @trust_dir.trust_cert ALTERNATE_CERT matches = @cmd.certificates_matching '' # HACK OpenSSL::X509::Certificate#== is Object#==, so do this the hard way match = matches.next assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last match = matches.next assert_equal PUBLIC_CERT.to_pem, match.first.to_pem assert_equal @trust_dir.cert_path(PUBLIC_CERT), match.last assert_raises StopIteration do matches.next end end def test_certificates_matching_filter @trust_dir.trust_cert PUBLIC_CERT @trust_dir.trust_cert ALTERNATE_CERT matches = @cmd.certificates_matching 'alternate' match = matches.next assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last assert_raises StopIteration do matches.next end end def test_execute_add @cmd.handle_options %W[--add #{PUBLIC_CERT_FILE}] use_ui @ui do @cmd.execute end cert_path = @trust_dir.cert_path PUBLIC_CERT assert_path_exists cert_path assert_equal "Added '/CN=nobody/DC=example'\n", @ui.output assert_empty @ui.error end def test_execute_add_twice self.class.cert_path 'alternate' @cmd.handle_options %W[ --add #{PUBLIC_CERT_FILE} --add #{ALTERNATE_CERT_FILE} ] use_ui @ui do @cmd.execute end expected = <<-EXPECTED Added '/CN=nobody/DC=example' Added '/CN=alternate/DC=example' EXPECTED assert_equal expected, @ui.output assert_empty @ui.error end def test_execute_build passphrase = 'Foo bar' @cmd.handle_options %W[--build nobody@example.com] @build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase}" use_ui @build_ui do @cmd.execute end output = @build_ui.output.split "\n" assert_equal "Passphrase for your Private Key: ", output.shift assert_equal "Please repeat the passphrase for your Private Key: ", output.shift assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}", output.shift assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}", output.shift assert_equal "Don't forget to move the key file to somewhere private!", output.shift assert_empty output assert_empty @build_ui.error assert_path_exists File.join(@tempdir, 'gem-private_key.pem') assert_path_exists File.join(@tempdir, 'gem-public_cert.pem') end def test_execute_build_bad_passphrase_confirmation passphrase = 'Foo bar' passphrase_confirmation = 'Fu bar' @cmd.handle_options %W[--build nobody@example.com] @build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase_confirmation}" use_ui @build_ui do e = assert_raises Gem::CommandLineError do @cmd.execute end output = @build_ui.output.split "\n" assert_equal "Passphrase for your Private Key: ", output.shift assert_equal "Please repeat the passphrase for your Private Key: ", output.shift assert_empty output assert_equal "Passphrase and passphrase confirmation don't match", e.message end refute_path_exists File.join(@tempdir, 'gem-private_key.pem') refute_path_exists File.join(@tempdir, 'gem-public_cert.pem') end def test_execute_build_key @cmd.handle_options %W[ --build nobody@example.com --private-key #{PRIVATE_KEY_FILE} ] use_ui @ui do @cmd.execute end output = @ui.output.split "\n" assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}", output.shift assert_empty output assert_empty @ui.error assert_path_exists File.join(@tempdir, 'gem-public_cert.pem') end def test_execute_build_encrypted_key @cmd.handle_options %W[ --build nobody@example.com --private-key #{ENCRYPTED_PRIVATE_KEY_PATH} ] use_ui @ui do @cmd.execute end output = @ui.output.split "\n" assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}", output.shift assert_empty output assert_empty @ui.error assert_path_exists File.join(@tempdir, 'gem-public_cert.pem') end def test_execute_certificate use_ui @ui do @cmd.handle_options %W[--certificate #{PUBLIC_CERT_FILE}] end assert_equal '', @ui.output assert_equal '', @ui.error assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem end def test_execute_list @trust_dir.trust_cert PUBLIC_CERT @trust_dir.trust_cert ALTERNATE_CERT @cmd.handle_options %W[--list] use_ui @ui do @cmd.execute end assert_equal "/CN=alternate/DC=example\n/CN=nobody/DC=example\n", @ui.output assert_empty @ui.error end def test_execute_list_filter @trust_dir.trust_cert PUBLIC_CERT @trust_dir.trust_cert ALTERNATE_CERT @cmd.handle_options %W[--list nobody] use_ui @ui do @cmd.execute end assert_equal "/CN=nobody/DC=example\n", @ui.output assert_empty @ui.error end def test_execute_private_key use_ui @ui do @cmd.send :handle_options, %W[--private-key #{PRIVATE_KEY_FILE}] end assert_equal '', @ui.output assert_equal '', @ui.error assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem end def test_execute_encrypted_private_key use_ui @ui do @cmd.send :handle_options, %W[--private-key #{ENCRYPTED_PRIVATE_KEY_PATH}] end assert_equal '', @ui.output assert_equal '', @ui.error assert_equal ENCRYPTED_PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem end def test_execute_remove @trust_dir.trust_cert PUBLIC_CERT cert_path = @trust_dir.cert_path PUBLIC_CERT assert_path_exists cert_path @cmd.handle_options %W[--remove nobody] use_ui @ui do @cmd.execute end assert_equal "Removed '/CN=nobody/DC=example'\n", @ui.output assert_equal '', @ui.error refute_path_exists cert_path end def test_execute_remove_multiple @trust_dir.trust_cert PUBLIC_CERT @trust_dir.trust_cert ALTERNATE_CERT public_path = @trust_dir.cert_path PUBLIC_CERT alternate_path = @trust_dir.cert_path ALTERNATE_CERT assert_path_exists public_path assert_path_exists alternate_path @cmd.handle_options %W[--remove example] use_ui @ui do @cmd.execute end expected = <<-EXPECTED Removed '/CN=alternate/DC=example' Removed '/CN=nobody/DC=example' EXPECTED assert_equal expected, @ui.output assert_equal '', @ui.error refute_path_exists public_path refute_path_exists alternate_path end def test_execute_remove_twice @trust_dir.trust_cert PUBLIC_CERT @trust_dir.trust_cert ALTERNATE_CERT public_path = @trust_dir.cert_path PUBLIC_CERT alternate_path = @trust_dir.cert_path ALTERNATE_CERT assert_path_exists public_path assert_path_exists alternate_path @cmd.handle_options %W[--remove nobody --remove alternate] use_ui @ui do @cmd.execute end expected = <<-EXPECTED Removed '/CN=nobody/DC=example' Removed '/CN=alternate/DC=example' EXPECTED assert_equal expected, @ui.output assert_equal '', @ui.error refute_path_exists public_path refute_path_exists alternate_path end def test_execute_sign path = File.join @tempdir, 'cert.pem' Gem::Security.write ALTERNATE_CERT, path, 0600 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s @cmd.handle_options %W[ --private-key #{PRIVATE_KEY_FILE} --certificate #{PUBLIC_CERT_FILE} --sign #{path} ] use_ui @ui do @cmd.execute end assert_equal '', @ui.output assert_equal '', @ui.error cert = OpenSSL::X509::Certificate.new File.read path assert_equal '/CN=nobody/DC=example', cert.issuer.to_s mask = 0100600 & (~File.umask) assert_equal mask, File.stat(path).mode unless win_platform? end def test_execute_sign_encrypted_key path = File.join @tempdir, 'cert.pem' Gem::Security.write ALTERNATE_CERT, path, 0600 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s @cmd.handle_options %W[ --private-key #{ENCRYPTED_PRIVATE_KEY_PATH} --certificate #{PUBLIC_CERT_FILE} --sign #{path} ] use_ui @ui do @cmd.execute end assert_equal '', @ui.output assert_equal '', @ui.error cert = OpenSSL::X509::Certificate.new File.read path assert_equal '/CN=nobody/DC=example', cert.issuer.to_s mask = 0100600 & (~File.umask) assert_equal mask, File.stat(path).mode unless win_platform? end def test_execute_sign_default FileUtils.mkdir_p File.join Gem.user_home, '.gem' private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem' Gem::Security.write PRIVATE_KEY, private_key_path public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem' Gem::Security.write PUBLIC_CERT, public_cert_path path = File.join @tempdir, 'cert.pem' Gem::Security.write ALTERNATE_CERT, path, 0600 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s @cmd.handle_options %W[--sign #{path}] use_ui @ui do @cmd.execute end assert_equal '', @ui.output assert_equal '', @ui.error cert = OpenSSL::X509::Certificate.new File.read path assert_equal '/CN=nobody/DC=example', cert.issuer.to_s mask = 0100600 & (~File.umask) assert_equal mask, File.stat(path).mode unless win_platform? end def test_execute_sign_default_encrypted_key FileUtils.mkdir_p File.join(Gem.user_home, '.gem') private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem' Gem::Security.write ENCRYPTED_PRIVATE_KEY, private_key_path, 0600, PRIVATE_KEY_PASSPHRASE public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem' Gem::Security.write PUBLIC_CERT, public_cert_path path = File.join @tempdir, 'cert.pem' Gem::Security.write ALTERNATE_CERT, path, 0600 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s @cmd.handle_options %W[--sign #{path}] use_ui @ui do @cmd.execute end assert_equal '', @ui.output assert_equal '', @ui.error cert = OpenSSL::X509::Certificate.new File.read path assert_equal '/CN=nobody/DC=example', cert.issuer.to_s mask = 0100600 & (~File.umask) assert_equal mask, File.stat(path).mode unless win_platform? end def test_execute_sign_no_cert FileUtils.mkdir_p File.join Gem.user_home, '.gem' private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem' Gem::Security.write PRIVATE_KEY, private_key_path path = File.join @tempdir, 'cert.pem' Gem::Security.write ALTERNATE_CERT, path, 0600 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s @cmd.handle_options %W[--sign #{path}] use_ui @ui do assert_raises Gem::MockGemUi::TermError do @cmd.execute end end assert_equal '', @ui.output expected = <<-EXPECTED ERROR: --certificate not specified and ~/.gem/gem-public_cert.pem does not exist EXPECTED assert_equal expected, @ui.error end def test_execute_sign_no_key FileUtils.mkdir_p File.join Gem.user_home, '.gem' public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem' Gem::Security.write PUBLIC_CERT, public_cert_path path = File.join @tempdir, 'cert.pem' Gem::Security.write ALTERNATE_CERT, path, 0600 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s @cmd.handle_options %W[--sign #{path}] use_ui @ui do assert_raises Gem::MockGemUi::TermError do @cmd.execute end end assert_equal '', @ui.output expected = <<-EXPECTED ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exist EXPECTED assert_equal expected, @ui.error end def test_handle_options @cmd.handle_options %W[ --add #{PUBLIC_CERT_FILE} --add #{ALTERNATE_CERT_FILE} --remove nobody --remove example --list --list example --build nobody@example --build other@example ] assert_equal [PUBLIC_CERT.to_pem, ALTERNATE_CERT.to_pem], @cmd.options[:add].map { |cert| cert.to_pem } assert_equal %w[nobody example], @cmd.options[:remove] assert_equal %w[nobody@example other@example], @cmd.options[:build].map { |name| name.to_s } assert_equal ['', 'example'], @cmd.options[:list] end def test_handle_options_add_bad nonexistent = File.join @tempdir, 'nonexistent' e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--add #{nonexistent}] end assert_equal "invalid argument: --add #{nonexistent}: does not exist", e.message bad = File.join @tempdir, 'bad' FileUtils.touch bad e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--add #{bad}] end assert_equal "invalid argument: --add #{bad}: invalid X509 certificate", e.message end def test_handle_options_certificate nonexistent = File.join @tempdir, 'nonexistent' e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--certificate #{nonexistent}] end assert_equal "invalid argument: --certificate #{nonexistent}: does not exist", e.message bad = File.join @tempdir, 'bad' FileUtils.touch bad e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--certificate #{bad}] end assert_equal "invalid argument: " + "--certificate #{bad}: invalid X509 certificate", e.message end def test_handle_options_key_bad nonexistent = File.join @tempdir, 'nonexistent' e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--private-key #{nonexistent}] end assert_equal "invalid argument: " + "--private-key #{nonexistent}: does not exist", e.message bad = File.join @tempdir, 'bad' FileUtils.touch bad e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--private-key #{bad}] end assert_equal "invalid argument: --private-key #{bad}: invalid RSA key", e.message e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[--private-key #{PUBLIC_KEY_FILE}] end assert_equal "invalid argument: " + "--private-key #{PUBLIC_KEY_FILE}: private key not found", e.message end def test_handle_options_sign @cmd.handle_options %W[ --private-key #{ALTERNATE_KEY_FILE} --private-key #{PRIVATE_KEY_FILE} --certificate #{ALTERNATE_CERT_FILE} --certificate #{PUBLIC_CERT_FILE} --sign #{ALTERNATE_CERT_FILE} --sign #{CHILD_CERT_FILE} ] assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign] end def test_handle_options_sign_encrypted_key @cmd.handle_options %W[ --private-key #{ALTERNATE_KEY_FILE} --private-key #{ENCRYPTED_PRIVATE_KEY_PATH} --certificate #{ALTERNATE_CERT_FILE} --certificate #{PUBLIC_CERT_FILE} --sign #{ALTERNATE_CERT_FILE} --sign #{CHILD_CERT_FILE} ] assert_equal ENCRYPTED_PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign] end def test_handle_options_sign_nonexistent nonexistent = File.join @tempdir, 'nonexistent' e = assert_raises OptionParser::InvalidArgument do @cmd.handle_options %W[ --private-key #{ALTERNATE_KEY_FILE} --certificate #{ALTERNATE_CERT_FILE} --sign #{nonexistent} ] end assert_equal "invalid argument: --sign #{nonexistent}: does not exist", e.message end end if defined?(OpenSSL::SSL)