From 6275284f025dea81f4fdca8c80534d27263b003a Mon Sep 17 00:00:00 2001 From: Ashley Ellis Pierce Date: Tue, 20 Dec 2022 16:01:08 -0500 Subject: [rubygems/rubygems] Ask user to otp at webauthn verification url https://github.com/rubygems/rubygems/commit/004eadb0c5 --- test/rubygems/test_gem_commands_owner_command.rb | 26 +++++++++++++++++++ test/rubygems/test_gem_commands_push_command.rb | 30 +++++++++++++++++++++ test/rubygems/test_gem_commands_yank_command.rb | 33 ++++++++++++++++++++++++ test/rubygems/test_gem_gemcutter_utilities.rb | 32 ++++++++++++++++++++--- 4 files changed, 117 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb index 7aaeb3a672..8774862070 100644 --- a/test/rubygems/test_gem_commands_owner_command.rb +++ b/test/rubygems/test_gem_commands_owner_command.rb @@ -331,6 +331,8 @@ EOF HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"), HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"), ] + @stub_fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @otp_ui = Gem::MockGemUi.new "111111\n" use_ui @otp_ui do @@ -346,6 +348,8 @@ EOF def test_otp_verified_failure response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = HTTPResponseFactory.create(body: response, code: 401, msg: "Unauthorized") + @stub_fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @otp_ui = Gem::MockGemUi.new "111111\n" use_ui @otp_ui do @@ -358,6 +362,28 @@ EOF assert_equal "111111", @stub_fetcher.last_request["OTP"] end + def test_webauthn_otp_verified_success + webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY" + response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." + response_success = "Owner added successfully." + + @stub_fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK") + @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [ + HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"), + HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"), + ] + + @otp_ui = Gem::MockGemUi.new "111111\n" + use_ui @otp_ui do + @cmd.add_owners("freewill", ["user-new1@example.com"]) + end + + assert_match "You have enabled multi-factor authentication. Please enter OTP code from your security device by visiting #{webauthn_verification_url}", @otp_ui.output + assert_match "Code: ", @otp_ui.output + assert_match response_success, @otp_ui.output + assert_equal "111111", @stub_fetcher.last_request["OTP"] + end + def test_remove_owners_unathorized_api_key response_forbidden = "The API key doesn't have access" response_success = "Owner removed successfully." diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb index b5458bc7ce..2a45853513 100644 --- a/test/rubygems/test_gem_commands_push_command.rb +++ b/test/rubygems/test_gem_commands_push_command.rb @@ -392,6 +392,8 @@ class TestGemCommandsPushCommand < Gem::TestCase HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"), HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"), ] + @fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @otp_ui = Gem::MockGemUi.new "111111\n" use_ui @otp_ui do @@ -407,6 +409,8 @@ class TestGemCommandsPushCommand < Gem::TestCase def test_otp_verified_failure response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." @fetcher.data["#{Gem.host}/api/v1/gems"] = HTTPResponseFactory.create(body: response, code: 401, msg: "Unauthorized") + @fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @otp_ui = Gem::MockGemUi.new "111111\n" assert_raise Gem::MockGemUi::TermError do @@ -421,6 +425,28 @@ class TestGemCommandsPushCommand < Gem::TestCase assert_equal "111111", @fetcher.last_request["OTP"] end + def test_webauthn_otp_verified_success + webauthn_verification_url = "#{Gem.host}/api/v1/webauthn_verification/odow34b93t6aPCdY" + response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." + response_success = "Successfully registered gem: freewill (1.0.0)" + + @fetcher.data["#{Gem.host}/api/v1/gems"] = [ + HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"), + HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"), + ] + @fetcher.data["#{Gem.host}/api/v1/webauthn_verification"] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK") + + @otp_ui = Gem::MockGemUi.new "111111\n" + use_ui @otp_ui do + @cmd.send_gem(@path) + end + + assert_match "You have enabled multi-factor authentication. Please enter OTP code from your security device by visiting #{webauthn_verification_url}", @otp_ui.output + assert_match "Code: ", @otp_ui.output + assert_match response_success, @otp_ui.output + assert_equal "111111", @fetcher.last_request["OTP"] + end + def test_sending_gem_unathorized_api_key_with_mfa_enabled response_mfa_enabled = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." response_forbidden = "The API key doesn't have access" @@ -431,6 +457,8 @@ class TestGemCommandsPushCommand < Gem::TestCase HTTPResponseFactory.create(body: response_forbidden, code: 403, msg: "Forbidden"), HTTPResponseFactory.create(body: response_success, code: 200, msg: "OK"), ] + @fetcher.data["#{@host}/api/v1/webauthn_verification"] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @fetcher.data["#{@host}/api/v1/api_key"] = HTTPResponseFactory.create(body: "", code: 200, msg: "OK") @cmd.instance_variable_set :@host, @host @@ -471,6 +499,8 @@ class TestGemCommandsPushCommand < Gem::TestCase @fetcher.data["#{@host}/api/v1/profile/me.yaml"] = [ HTTPResponseFactory.create(body: response_profile, code: 200, msg: "OK"), ] + @fetcher.data["#{@host}/api/v1/webauthn_verification"] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @cmd.instance_variable_set :@scope, :push_rubygem @cmd.options[:args] = [@path] diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb index 0a20617dff..1ded1146b1 100644 --- a/test/rubygems/test_gem_commands_yank_command.rb +++ b/test/rubygems/test_gem_commands_yank_command.rb @@ -73,6 +73,9 @@ class TestGemCommandsYankCommand < Gem::TestCase HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"), HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK"), ] + webauthn_uri = "http://example/api/v1/webauthn_verification" + @fetcher.data[webauthn_uri] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @cmd.options[:args] = %w[a] @cmd.options[:added_platform] = true @@ -94,6 +97,9 @@ class TestGemCommandsYankCommand < Gem::TestCase response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." yank_uri = "http://example/api/v1/gems/yank" @fetcher.data[yank_uri] = HTTPResponseFactory.create(body: response, code: 401, msg: "Unauthorized") + webauthn_uri = "http://example/api/v1/webauthn_verification" + @fetcher.data[webauthn_uri] = + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") @cmd.options[:args] = %w[a] @cmd.options[:added_platform] = true @@ -110,6 +116,33 @@ class TestGemCommandsYankCommand < Gem::TestCase assert_equal "111111", @fetcher.last_request["OTP"] end + def test_execute_with_webauthn_otp_success + webauthn_verification_url = "http://example/api/v1/webauthn_verification/odow34b93t6aPCdY" + response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." + yank_uri = "http://example/api/v1/gems/yank" + webauthn_uri = "http://example/api/v1/webauthn_verification" + @fetcher.data[webauthn_uri] = HTTPResponseFactory.create(body: webauthn_verification_url, code: 200, msg: "OK") + @fetcher.data[yank_uri] = [ + HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized"), + HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK"), + ] + + @cmd.options[:args] = %w[a] + @cmd.options[:added_platform] = true + @cmd.options[:version] = req("= 1.0") + + @otp_ui = Gem::MockGemUi.new "111111\n" + use_ui @otp_ui do + @cmd.execute + end + + assert_match "You have enabled multi-factor authentication. Please enter OTP code from your security device by visiting #{webauthn_verification_url}", @otp_ui.output + assert_match "Code: ", @otp_ui.output + assert_match %r{Yanking gem from http://example}, @otp_ui.output + assert_match %r{Successfully yanked}, @otp_ui.output + assert_equal "111111", @fetcher.last_request["OTP"] + end + def test_execute_key yank_uri = "http://example/api/v1/gems/yank" @fetcher.data[yank_uri] = HTTPResponseFactory.create(body: "Successfully yanked", code: 200, msg: "OK") diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb index 3c021fed11..1291a39fc3 100644 --- a/test/rubygems/test_gem_gemcutter_utilities.rb +++ b/test/rubygems/test_gem_gemcutter_utilities.rb @@ -231,10 +231,33 @@ class TestGemGemcutterUtilities < Gem::TestCase assert_equal "111111", @fetcher.last_request["OTP"] end - def util_sign_in(response, host = nil, args = [], extra_input = "") - email = "you@example.com" - password = "secret" - profile_response = HTTPResponseFactory.create(body: "mfa: disabled\n", code: 200, msg: "OK") + def test_sign_in_with_webauthn_otp + webauthn_verification_url = "rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY" + api_key = "a5fdbb6ba150cbb83aad2bb2fede64cf040453903" + response_fail = "You have enabled multifactor authentication" + + util_sign_in(proc do + @call_count ||= 0 + if (@call_count += 1).odd? + HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized") + else + HTTPResponseFactory.create(body: api_key, code: 200, msg: "OK") + end + end, nil, [], "111111\n", webauthn_verification_url) + + assert_match "You have enabled multi-factor authentication. Please enter OTP code from your security device by visiting #{webauthn_verification_url}", @sign_in_ui.output + end + + def util_sign_in(response, host = nil, args = [], extra_input = "", webauthn_url = nil) + email = "you@example.com" + password = "secret" + profile_response = HTTPResponseFactory.create(body: "mfa: disabled\n", code: 200, msg: "OK") + webauthn_response = + if webauthn_url + HTTPResponseFactory.create(body: webauthn_url, code: 200, msg: "OK") + else + HTTPResponseFactory.create(body: "You don't have any security devices", code: 422, msg: "Unprocessable Entity") + end if host ENV["RUBYGEMS_HOST"] = host @@ -245,6 +268,7 @@ class TestGemGemcutterUtilities < Gem::TestCase @fetcher = Gem::FakeFetcher.new @fetcher.data["#{host}/api/v1/api_key"] = response @fetcher.data["#{host}/api/v1/profile/me.yaml"] = profile_response + @fetcher.data["#{host}/api/v1/webauthn_verification"] = webauthn_response Gem::RemoteFetcher.fetcher = @fetcher @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n" + extra_input) -- cgit v1.2.1