[rubygems/rubygems] Create MockServer object to test WebAuthn logic to prevent real TCPServers from being created and be leaked into other tests

https://github.com/rubygems/rubygems/commit/96d6cb33a2
This commit is contained in:
Jenny Shen 2023-07-20 01:42:58 -04:00 committed by git
parent 3954a87d65
commit afca1a31d0
5 changed files with 78 additions and 87 deletions

View File

@ -74,3 +74,38 @@ class Gem::MultifactorAuthFetcher < Gem::FakeFetcher
"#{@webauthn_url}?port=#{port}"
end
end
##
# The MockTCPServer for use in tests or to avoid real TCPServer instances to be created
# when testing code related to the WebAuthn listener.
#
# Example:
#
# server = Gem::MockTCPServer
# port = server.addr[1].to_s
#
# # this mocks waiting for a request by calling sleep
# server.accept
#
# # this mocks the server closing
# server.close
class Gem::MockTCPServer
attr_reader :port
def initialize(port = 5678)
@port = port
end
def close
true
end
def addr
["AF_INET6", @port, "::", "::"]
end
def accept
sleep
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
require_relative "multifactor_auth_fetcher"
require_relative "multifactor_auth_utilities"
require "rubygems/commands/owner_command"
class TestGemCommandsOwnerCommand < Gem::TestCase
@ -358,8 +358,7 @@ EOF
def test_with_webauthn_enabled_success
response_success = "Owner added successfully."
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
@stub_fetcher.respond_with_webauthn_url
@ -370,11 +369,11 @@ EOF
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
end
ensure
server.close
end
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"]
assert_match response_success, @stub_ui.output
@ -382,8 +381,7 @@ EOF
def test_with_webauthn_enabled_failure
response_success = "Owner added successfully."
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
error = Gem::WebauthnVerificationError.new("Something went wrong")
@stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
@ -395,12 +393,12 @@ EOF
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
end
ensure
server.close
end
assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @stub_ui.error
refute_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
refute_match response_success, @stub_ui.output
@ -408,8 +406,7 @@ EOF
def test_with_webauthn_enabled_success_with_polling
response_success = "Owner added successfully."
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success)
@stub_fetcher.respond_with_webauthn_url
@ -419,12 +416,10 @@ EOF
use_ui @stub_ui do
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
ensure
server.close
end
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate " \
"via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
"command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output
assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"]
@ -433,8 +428,7 @@ EOF
def test_with_webauthn_enabled_failure_with_polling
response_success = "Owner added successfully."
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@stub_fetcher.respond_with_require_otp(
"#{Gem.host}/api/v1/gems/freewill/owners",
@ -447,13 +441,11 @@ EOF
use_ui @stub_ui do
@cmd.add_owners("freewill", ["user-new1@example.com"])
end
ensure
server.close
end
assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate " \
"via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
"command with the `--otp [your_code]` option.", @stub_ui.output
assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \
"or been used already.", @stub_ui.error

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
require_relative "multifactor_auth_fetcher"
require_relative "multifactor_auth_utilities"
require "rubygems/commands/push_command"
require "rubygems/config_file"
@ -423,8 +423,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
def test_with_webauthn_enabled_success
response_success = "Successfully registered gem: freewill (1.0.0)"
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
@fetcher.respond_with_webauthn_url
@ -435,11 +434,9 @@ class TestGemCommandsPushCommand < Gem::TestCase
@cmd.send_gem(@path)
end
end
ensure
server.close
end
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "You are verified with a security device. You may close the browser window.", @ui.output
@ -449,8 +446,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
def test_with_webauthn_enabled_failure
response_success = "Successfully registered gem: freewill (1.0.0)"
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
error = Gem::WebauthnVerificationError.new("Something went wrong")
@fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
@ -463,14 +459,12 @@ class TestGemCommandsPushCommand < Gem::TestCase
@cmd.send_gem(@path)
end
end
ensure
server.close
end
end
assert_equal 1, error.exit_code
assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error
@ -480,8 +474,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
def test_with_webauthn_enabled_success_with_polling
response_success = "Successfully registered gem: freewill (1.0.0)"
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
@fetcher.respond_with_webauthn_url
@ -491,11 +484,9 @@ class TestGemCommandsPushCommand < Gem::TestCase
use_ui @ui do
@cmd.send_gem(@path)
end
ensure
server.close
end
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "You are verified with a security device. You may close the browser window.", @ui.output
@ -505,8 +496,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
def test_with_webauthn_enabled_failure_with_polling
response_success = "Successfully registered gem: freewill (1.0.0)"
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success)
@fetcher.respond_with_webauthn_url
@ -517,15 +507,13 @@ class TestGemCommandsPushCommand < Gem::TestCase
use_ui @ui do
@cmd.send_gem(@path)
end
ensure
server.close
end
end
assert_equal 1, error.exit_code
assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} to authenticate " \
"via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \
"command with the `--otp [your_code]` option.", @ui.output
assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \
"or been used already.", @ui.error

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
require_relative "multifactor_auth_fetcher"
require_relative "multifactor_auth_utilities"
require "rubygems/commands/yank_command"
class TestGemCommandsYankCommand < Gem::TestCase
@ -113,8 +113,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
end
def test_with_webauthn_enabled_success
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
@fetcher.respond_with_webauthn_url
@ -129,12 +128,10 @@ class TestGemCommandsYankCommand < Gem::TestCase
@cmd.execute
end
end
ensure
server.close
end
assert_match %r{Yanking gem from http://example}, @ui.output
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "You are verified with a security device. You may close the browser window.", @ui.output
@ -143,8 +140,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
end
def test_with_webauthn_enabled_failure
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
error = Gem::WebauthnVerificationError.new("Something went wrong")
@fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
@ -161,15 +157,13 @@ class TestGemCommandsYankCommand < Gem::TestCase
@cmd.execute
end
end
ensure
server.close
end
end
assert_equal 1, error.exit_code
assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match %r{Yanking gem from http://example}, @ui.output
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error
@ -178,8 +172,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
end
def test_with_webauthn_enabled_success_with_polling
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
@fetcher.respond_with_webauthn_url
@ -193,12 +186,10 @@ class TestGemCommandsYankCommand < Gem::TestCase
use_ui @ui do
@cmd.execute
end
ensure
server.close
end
assert_match %r{Yanking gem from http://example}, @ui.output
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "You are verified with a security device. You may close the browser window.", @ui.output
@ -207,8 +198,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
end
def test_with_webauthn_enabled_failure_with_polling
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked")
@fetcher.respond_with_webauthn_url
@ -223,15 +213,13 @@ class TestGemCommandsYankCommand < Gem::TestCase
use_ui @ui do
@cmd.execute
end
ensure
server.close
end
end
assert_equal 1, error.exit_code
assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key
assert_match %r{Yanking gem from http://example}, @ui.output
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output
assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative "helper"
require_relative "multifactor_auth_fetcher"
require_relative "multifactor_auth_utilities"
require "rubygems"
require "rubygems/command"
require "rubygems/gemcutter_utilities"
@ -223,8 +223,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in_with_webauthn_enabled
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp
@fetcher.respond_with_webauthn_url
@ -232,11 +231,9 @@ class TestGemGemcutterUtilities < Gem::TestCase
Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:otp] = "Uvh6T57tkWuUnWYo" }) do
util_sign_in
end
ensure
server.close
end
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output
@ -244,8 +241,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in_with_webauthn_enabled_with_error
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
error = Gem::WebauthnVerificationError.new("Something went wrong")
@fetcher.respond_with_require_otp
@ -255,13 +251,11 @@ class TestGemGemcutterUtilities < Gem::TestCase
Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:error] = error }) do
util_sign_in
end
ensure
server.close
end
end
assert_equal 1, error.exit_code
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
assert_match "ERROR: Security device verification failed: Something went wrong", @sign_in_ui.error
@ -270,19 +264,16 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in_with_webauthn_enabled_with_polling
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp
@fetcher.respond_with_webauthn_url
@fetcher.respond_with_webauthn_polling("Uvh6T57tkWuUnWYo")
TCPServer.stub(:new, server) do
util_sign_in
ensure
server.close
end
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output
@ -290,8 +281,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_sign_in_with_webauthn_enabled_with_polling_failure
port = 5678
server = TCPServer.new(port)
server = Gem::MockTCPServer.new
@fetcher.respond_with_require_otp
@fetcher.respond_with_webauthn_url
@fetcher.respond_with_webauthn_polling_failure
@ -299,12 +289,10 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_raise Gem::MockGemUi::TermError do
TCPServer.stub(:new, server) do
util_sign_in
ensure
server.close
end
end
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \
assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \
"to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \
"you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output
assert_match "ERROR: Security device verification failed: " \