1require 'rubygems/test_case' 2require 'rubygems/commands/cert_command' 3require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9" 4 5unless defined? OpenSSL then 6 warn "`gem cert` tests are being skipped, module OpenSSL not found" 7end 8 9class TestGemCommandsCertCommand < Gem::TestCase 10 11 ALTERNATE_CERT = load_cert 'alternate' 12 13 ALTERNATE_KEY_FILE = key_path 'alternate' 14 PRIVATE_KEY_FILE = key_path 'private' 15 PUBLIC_KEY_FILE = key_path 'public' 16 17 ALTERNATE_CERT_FILE = cert_path 'alternate' 18 CHILD_CERT_FILE = cert_path 'child' 19 PUBLIC_CERT_FILE = cert_path 'public' 20 21 def setup 22 super 23 24 @cmd = Gem::Commands::CertCommand.new 25 26 @trust_dir = Gem::Security.trust_dir 27 end 28 29 def test_certificates_matching 30 @trust_dir.trust_cert PUBLIC_CERT 31 @trust_dir.trust_cert ALTERNATE_CERT 32 33 matches = @cmd.certificates_matching '' 34 35 # HACK OpenSSL::X509::Certificate#== is Object#==, so do this the hard way 36 match = matches.next 37 assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem 38 assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last 39 40 match = matches.next 41 assert_equal PUBLIC_CERT.to_pem, match.first.to_pem 42 assert_equal @trust_dir.cert_path(PUBLIC_CERT), match.last 43 44 assert_raises StopIteration do 45 matches.next 46 end 47 end 48 49 def test_certificates_matching_filter 50 @trust_dir.trust_cert PUBLIC_CERT 51 @trust_dir.trust_cert ALTERNATE_CERT 52 53 matches = @cmd.certificates_matching 'alternate' 54 55 match = matches.next 56 assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem 57 assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last 58 59 assert_raises StopIteration do 60 matches.next 61 end 62 end 63 64 def test_execute_add 65 @cmd.handle_options %W[--add #{PUBLIC_CERT_FILE}] 66 67 use_ui @ui do 68 @cmd.execute 69 end 70 71 cert_path = @trust_dir.cert_path PUBLIC_CERT 72 73 assert_path_exists cert_path 74 75 assert_equal "Added '/CN=nobody/DC=example'\n", @ui.output 76 assert_empty @ui.error 77 end 78 79 def test_execute_add_twice 80 self.class.cert_path 'alternate' 81 82 @cmd.handle_options %W[ 83 --add #{PUBLIC_CERT_FILE} 84 --add #{ALTERNATE_CERT_FILE} 85 ] 86 87 use_ui @ui do 88 @cmd.execute 89 end 90 91 expected = <<-EXPECTED 92Added '/CN=nobody/DC=example' 93Added '/CN=alternate/DC=example' 94 EXPECTED 95 96 assert_equal expected, @ui.output 97 assert_empty @ui.error 98 end 99 100 def test_execute_build 101 @cmd.handle_options %W[--build nobody@example.com] 102 103 use_ui @ui do 104 @cmd.execute 105 end 106 107 output = @ui.output.split "\n" 108 109 assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}", 110 output.shift 111 assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}", 112 output.shift 113 114 assert_equal "Don't forget to move the key file to somewhere private!", 115 output.shift 116 117 assert_empty output 118 assert_empty @ui.error 119 120 assert_path_exists File.join(@tempdir, 'gem-private_key.pem') 121 assert_path_exists File.join(@tempdir, 'gem-public_cert.pem') 122 end 123 124 def test_execute_build_key 125 @cmd.handle_options %W[ 126 --build nobody@example.com 127 --private-key #{PRIVATE_KEY_FILE} 128 ] 129 130 use_ui @ui do 131 @cmd.execute 132 end 133 134 output = @ui.output.split "\n" 135 136 assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}", 137 output.shift 138 assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}", 139 output.shift 140 141 assert_equal "Don't forget to move the key file to somewhere private!", 142 output.shift 143 144 assert_empty output 145 assert_empty @ui.error 146 147 assert_path_exists File.join(@tempdir, 'gem-public_cert.pem') 148 149 private_key_file = File.join @tempdir, 'gem-private_key.pem' 150 assert_path_exists private_key_file 151 152 assert_equal PRIVATE_KEY.to_pem, File.read(private_key_file) 153 end 154 155 def test_execute_certificate 156 use_ui @ui do 157 @cmd.handle_options %W[--certificate #{PUBLIC_CERT_FILE}] 158 end 159 160 assert_equal '', @ui.output 161 assert_equal '', @ui.error 162 163 assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem 164 end 165 166 def test_execute_list 167 @trust_dir.trust_cert PUBLIC_CERT 168 @trust_dir.trust_cert ALTERNATE_CERT 169 170 @cmd.handle_options %W[--list] 171 172 use_ui @ui do 173 @cmd.execute 174 end 175 176 assert_equal "/CN=alternate/DC=example\n/CN=nobody/DC=example\n", 177 @ui.output 178 assert_empty @ui.error 179 end 180 181 def test_execute_list_filter 182 @trust_dir.trust_cert PUBLIC_CERT 183 @trust_dir.trust_cert ALTERNATE_CERT 184 185 @cmd.handle_options %W[--list nobody] 186 187 use_ui @ui do 188 @cmd.execute 189 end 190 191 assert_equal "/CN=nobody/DC=example\n", @ui.output 192 assert_empty @ui.error 193 end 194 195 def test_execute_private_key 196 use_ui @ui do 197 @cmd.send :handle_options, %W[--private-key #{PRIVATE_KEY_FILE}] 198 end 199 200 assert_equal '', @ui.output 201 assert_equal '', @ui.error 202 203 assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem 204 end 205 206 def test_execute_remove 207 @trust_dir.trust_cert PUBLIC_CERT 208 209 cert_path = @trust_dir.cert_path PUBLIC_CERT 210 211 assert_path_exists cert_path 212 213 @cmd.handle_options %W[--remove nobody] 214 215 use_ui @ui do 216 @cmd.execute 217 end 218 219 assert_equal "Removed '/CN=nobody/DC=example'\n", @ui.output 220 assert_equal '', @ui.error 221 222 refute_path_exists cert_path 223 end 224 225 def test_execute_remove_multiple 226 @trust_dir.trust_cert PUBLIC_CERT 227 @trust_dir.trust_cert ALTERNATE_CERT 228 229 public_path = @trust_dir.cert_path PUBLIC_CERT 230 alternate_path = @trust_dir.cert_path ALTERNATE_CERT 231 232 assert_path_exists public_path 233 assert_path_exists alternate_path 234 235 @cmd.handle_options %W[--remove example] 236 237 use_ui @ui do 238 @cmd.execute 239 end 240 241 expected = <<-EXPECTED 242Removed '/CN=alternate/DC=example' 243Removed '/CN=nobody/DC=example' 244 EXPECTED 245 246 assert_equal expected, @ui.output 247 assert_equal '', @ui.error 248 249 refute_path_exists public_path 250 refute_path_exists alternate_path 251 end 252 253 def test_execute_remove_twice 254 @trust_dir.trust_cert PUBLIC_CERT 255 @trust_dir.trust_cert ALTERNATE_CERT 256 257 public_path = @trust_dir.cert_path PUBLIC_CERT 258 alternate_path = @trust_dir.cert_path ALTERNATE_CERT 259 260 assert_path_exists public_path 261 assert_path_exists alternate_path 262 263 @cmd.handle_options %W[--remove nobody --remove alternate] 264 265 use_ui @ui do 266 @cmd.execute 267 end 268 269 expected = <<-EXPECTED 270Removed '/CN=nobody/DC=example' 271Removed '/CN=alternate/DC=example' 272 EXPECTED 273 274 assert_equal expected, @ui.output 275 assert_equal '', @ui.error 276 277 refute_path_exists public_path 278 refute_path_exists alternate_path 279 end 280 281 def test_execute_sign 282 path = File.join @tempdir, 'cert.pem' 283 Gem::Security.write ALTERNATE_CERT, path, 0600 284 285 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s 286 287 @cmd.handle_options %W[ 288 --private-key #{PRIVATE_KEY_FILE} 289 --certificate #{PUBLIC_CERT_FILE} 290 291 --sign #{path} 292 ] 293 294 use_ui @ui do 295 @cmd.execute 296 end 297 298 assert_equal '', @ui.output 299 assert_equal '', @ui.error 300 301 cert = OpenSSL::X509::Certificate.new File.read path 302 303 assert_equal '/CN=nobody/DC=example', cert.issuer.to_s 304 305 mask = 0100600 & (~File.umask) 306 307 assert_equal mask, File.stat(path).mode unless win_platform? 308 end 309 310 def test_execute_sign_default 311 FileUtils.mkdir_p File.join Gem.user_home, '.gem' 312 313 private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem' 314 Gem::Security.write PRIVATE_KEY, private_key_path 315 316 public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem' 317 Gem::Security.write PUBLIC_CERT, public_cert_path 318 319 path = File.join @tempdir, 'cert.pem' 320 Gem::Security.write ALTERNATE_CERT, path, 0600 321 322 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s 323 324 @cmd.handle_options %W[--sign #{path}] 325 326 use_ui @ui do 327 @cmd.execute 328 end 329 330 assert_equal '', @ui.output 331 assert_equal '', @ui.error 332 333 cert = OpenSSL::X509::Certificate.new File.read path 334 335 assert_equal '/CN=nobody/DC=example', cert.issuer.to_s 336 337 mask = 0100600 & (~File.umask) 338 339 assert_equal mask, File.stat(path).mode unless win_platform? 340 end 341 342 def test_execute_sign_no_cert 343 FileUtils.mkdir_p File.join Gem.user_home, '.gem' 344 345 private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem' 346 Gem::Security.write PRIVATE_KEY, private_key_path 347 348 path = File.join @tempdir, 'cert.pem' 349 Gem::Security.write ALTERNATE_CERT, path, 0600 350 351 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s 352 353 @cmd.handle_options %W[--sign #{path}] 354 355 use_ui @ui do 356 assert_raises Gem::MockGemUi::TermError do 357 @cmd.execute 358 end 359 end 360 361 assert_equal '', @ui.output 362 363 expected = <<-EXPECTED 364ERROR: --certificate not specified and ~/.gem/gem-public_cert.pem does not exist 365 EXPECTED 366 367 assert_equal expected, @ui.error 368 end 369 370 def test_execute_sign_no_key 371 FileUtils.mkdir_p File.join Gem.user_home, '.gem' 372 373 public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem' 374 Gem::Security.write PUBLIC_CERT, public_cert_path 375 376 path = File.join @tempdir, 'cert.pem' 377 Gem::Security.write ALTERNATE_CERT, path, 0600 378 379 assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s 380 381 @cmd.handle_options %W[--sign #{path}] 382 383 use_ui @ui do 384 assert_raises Gem::MockGemUi::TermError do 385 @cmd.execute 386 end 387 end 388 389 assert_equal '', @ui.output 390 391 expected = <<-EXPECTED 392ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exist 393 EXPECTED 394 395 assert_equal expected, @ui.error 396 end 397 398 def test_handle_options 399 @cmd.handle_options %W[ 400 --add #{PUBLIC_CERT_FILE} 401 --add #{ALTERNATE_CERT_FILE} 402 403 --remove nobody 404 --remove example 405 406 --list 407 --list example 408 409 --build nobody@example 410 --build other@example 411 ] 412 413 assert_equal [PUBLIC_CERT.to_pem, ALTERNATE_CERT.to_pem], 414 @cmd.options[:add].map { |cert| cert.to_pem } 415 416 assert_equal %w[nobody example], @cmd.options[:remove] 417 418 assert_equal %w[nobody@example other@example], 419 @cmd.options[:build].map { |name| name.to_s } 420 421 assert_equal ['', 'example'], @cmd.options[:list] 422 end 423 424 def test_handle_options_add_bad 425 nonexistent = File.join @tempdir, 'nonexistent' 426 e = assert_raises OptionParser::InvalidArgument do 427 @cmd.handle_options %W[--add #{nonexistent}] 428 end 429 430 assert_equal "invalid argument: --add #{nonexistent}: does not exist", 431 e.message 432 433 bad = File.join @tempdir, 'bad' 434 FileUtils.touch bad 435 436 e = assert_raises OptionParser::InvalidArgument do 437 @cmd.handle_options %W[--add #{bad}] 438 end 439 440 assert_equal "invalid argument: --add #{bad}: invalid X509 certificate", 441 e.message 442 end 443 444 def test_handle_options_certificate 445 nonexistent = File.join @tempdir, 'nonexistent' 446 e = assert_raises OptionParser::InvalidArgument do 447 @cmd.handle_options %W[--certificate #{nonexistent}] 448 end 449 450 assert_equal "invalid argument: --certificate #{nonexistent}: does not exist", 451 e.message 452 453 bad = File.join @tempdir, 'bad' 454 FileUtils.touch bad 455 456 e = assert_raises OptionParser::InvalidArgument do 457 @cmd.handle_options %W[--certificate #{bad}] 458 end 459 460 assert_equal "invalid argument: " + 461 "--certificate #{bad}: invalid X509 certificate", 462 e.message 463 end 464 465 def test_handle_options_key_bad 466 nonexistent = File.join @tempdir, 'nonexistent' 467 e = assert_raises OptionParser::InvalidArgument do 468 @cmd.handle_options %W[--private-key #{nonexistent}] 469 end 470 471 assert_equal "invalid argument: " + 472 "--private-key #{nonexistent}: does not exist", 473 e.message 474 475 bad = File.join @tempdir, 'bad' 476 FileUtils.touch bad 477 478 e = assert_raises OptionParser::InvalidArgument do 479 @cmd.handle_options %W[--private-key #{bad}] 480 end 481 482 assert_equal "invalid argument: --private-key #{bad}: invalid RSA key", 483 e.message 484 485 e = assert_raises OptionParser::InvalidArgument do 486 @cmd.handle_options %W[--private-key #{PUBLIC_KEY_FILE}] 487 end 488 489 assert_equal "invalid argument: " + 490 "--private-key #{PUBLIC_KEY_FILE}: private key not found", 491 e.message 492 end 493 494 def test_handle_options_sign 495 @cmd.handle_options %W[ 496 --private-key #{ALTERNATE_KEY_FILE} 497 --private-key #{PRIVATE_KEY_FILE} 498 499 --certificate #{ALTERNATE_CERT_FILE} 500 --certificate #{PUBLIC_CERT_FILE} 501 502 --sign #{ALTERNATE_CERT_FILE} 503 --sign #{CHILD_CERT_FILE} 504 ] 505 506 assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem 507 assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem 508 509 assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign] 510 end 511 512 def test_handle_options_sign_nonexistent 513 nonexistent = File.join @tempdir, 'nonexistent' 514 e = assert_raises OptionParser::InvalidArgument do 515 @cmd.handle_options %W[ 516 --private-key #{ALTERNATE_KEY_FILE} 517 518 --certificate #{ALTERNATE_CERT_FILE} 519 520 --sign #{nonexistent} 521 ] 522 end 523 524 assert_equal "invalid argument: --sign #{nonexistent}: does not exist", 525 e.message 526 end 527 528end if defined? OpenSSL 529 530