From 69e8a87cf0a5e44fc909c95a98520f14ea384b05 Mon Sep 17 00:00:00 2001 From: Jenny Shen Date: Mon, 11 Jul 2022 23:16:51 -0400 Subject: [PATCH 1/5] Ask for gem scope during gem signin if on default host and if the api key has rubygem push/yank and/or gem owner a/r scopes --- lib/rubygems/gemcutter_utilities.rb | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 15e61440e3df..af5d97d97e6c 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -10,6 +10,7 @@ module Gem::GemcutterUtilities ERROR_CODE = 1 API_SCOPES = [:index_rubygems, :push_rubygem, :yank_rubygem, :add_owner, :remove_owner, :access_webhooks, :show_dashboard].freeze + GEM_SCOPE_COMPATIBLE_API_SCOPES = [:push_rubygem, :yank_rubygem, :add_owner, :remove_owner].freeze include Gem::Text @@ -165,7 +166,8 @@ def sign_in(sign_in_host = nil, scope: nil) scope_params = get_scope_params(scope) profile = get_user_profile(email, password) mfa_params = get_mfa_params(profile) - all_params = scope_params.merge(mfa_params) + rubygem_scope_params = get_rubygem_scope_params(scope_params) + all_params = scope_params.merge(mfa_params).merge(rubygem_scope_params) warning = profile["warning"] credentials = { email: email, password: password } @@ -321,6 +323,25 @@ def get_scope_params(scope) scope_params end + def get_rubygem_scope_params(scope_params) + rubygem_scope_params = {} + return rubygem_scope_params unless gem_scope_compatible_api_scope_enabled?(scope_params) && default_host? + + say "Enter the name of the gem you want to scope this key to." + rubygem_name = ask "Gem name [All gems]: " + say "\n" + + unless rubygem_name.nil? || rubygem_name.empty? + rubygem_scope_params[:rubygem_name] = rubygem_name + end + + rubygem_scope_params + end + + def gem_scope_compatible_api_scope_enabled?(scope_params) + (GEM_SCOPE_COMPATIBLE_API_SCOPES & scope_params.select {|_, v| v }.keys).any? + end + def default_host? host == Gem::DEFAULT_HOST end From bc8700cf010776fb806b5e6bfedc197c7375dd72 Mon Sep 17 00:00:00 2001 From: Jenny Shen Date: Mon, 11 Jul 2022 23:19:06 -0400 Subject: [PATCH 2/5] Modify current gem signin tests for gem scope input --- test/rubygems/test_gem_commands_signin_command.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb index fd4ffb414a3f..d2e12188bfea 100644 --- a/test/rubygems/test_gem_commands_signin_command.rb +++ b/test/rubygems/test_gem_commands_signin_command.rb @@ -112,7 +112,7 @@ def test_execute_with_key_name_and_scope api_key = "1234abcd" fetcher = Gem::RemoteFetcher.fetcher - key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\n" + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\n\n" util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } user = ENV["USER"] || ENV["USERNAME"] @@ -138,7 +138,7 @@ def test_execute_with_key_name_scope_and_mfa_level_of_ui_only fetcher = Gem::RemoteFetcher.fetcher mfa_level = "ui_only" - key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny" + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny\n\n" util_capture(key_name_ui, nil, api_key, fetcher, mfa_level) { @cmd.execute } user = ENV["USER"] || ENV["USERNAME"] @@ -165,7 +165,7 @@ def test_execute_with_key_name_scope_and_mfa_level_of_gem_signin fetcher = Gem::RemoteFetcher.fetcher mfa_level = "ui_and_gem_signin" - key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny" + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny\n\n" util_capture(key_name_ui, nil, api_key, fetcher, mfa_level) { @cmd.execute } user = ENV["USER"] || ENV["USERNAME"] From c36b0130ae0e995f218702bd5c0888268b2a9f5c Mon Sep 17 00:00:00 2001 From: Jenny Shen Date: Mon, 11 Jul 2022 23:35:04 -0400 Subject: [PATCH 3/5] Add gem scope tests to gem signin command --- .../test_gem_commands_signin_command.rb | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb index d2e12188bfea..c8a0d5f475d8 100644 --- a/test/rubygems/test_gem_commands_signin_command.rb +++ b/test/rubygems/test_gem_commands_signin_command.rb @@ -185,6 +185,92 @@ def test_execute_with_key_name_scope_and_mfa_level_of_gem_signin assert_equal api_key, credentials[:rubygems_api_key] end + def test_execute_with_gem_scope_and_push_rubygem + email = "you@example.com" + password = "secret" + api_key = "1234" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ngem\n" + util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } + + assert_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + assert_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&push_rubygem=true&rubygem_name=gem", fetcher.last_request.body + end + + def test_execute_with_gem_scope_and_yank_rubygem + email = "you@example.com" + password = "secret" + api_key = "1234" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\n\ny\n\n\n\n\ngem\n" + util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } + + assert_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + assert_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&yank_rubygem=true&rubygem_name=gem", fetcher.last_request.body + end + + def test_execute_with_gem_scope_and_add_owner + email = "you@example.com" + password = "secret" + api_key = "1234" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\n\n\ny\n\n\n\ngem\n" + util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } + + assert_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + assert_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&add_owner=true&rubygem_name=gem", fetcher.last_request.body + end + + def test_execute_with_gem_scope_and_remove_owner + email = "you@example.com" + password = "secret" + api_key = "1234" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\n\n\n\ny\n\n\ngem\n" + util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } + + assert_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + assert_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&remove_owner=true&rubygem_name=gem", fetcher.last_request.body + end + + def test_execute_without_gem_scope_compatible_api_scope + email = "you@example.com" + password = "secret" + api_key = "1234" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\n\n\n\n\n\n\ngem\n" + util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } + + refute_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + refute_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&index_rubygems=true", fetcher.last_request.body + end + + def test_execute_on_gemserver_with_gem_scope + host = "http://some-gemcutter-compatible-host.org" + + email = "you@example.com" + password = "secret" + api_key = "1234" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ngem\n" + util_capture(key_name_ui, host, api_key, fetcher) { @cmd.execute } + + refute_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + refute_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body + end + def test_execute_with_warnings email = "you@example.com" password = "secret" From 937194b852c596507fc53e1de249c426ac1aba44 Mon Sep 17 00:00:00 2001 From: Jenny Shen Date: Tue, 12 Jul 2022 10:46:26 -0400 Subject: [PATCH 4/5] Ask gem scope details for all hosts --- lib/rubygems/gemcutter_utilities.rb | 2 +- test/rubygems/test_gem_commands_push_command.rb | 2 +- test/rubygems/test_gem_commands_signin_command.rb | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index af5d97d97e6c..6b730ea6bd3c 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -325,7 +325,7 @@ def get_scope_params(scope) def get_rubygem_scope_params(scope_params) rubygem_scope_params = {} - return rubygem_scope_params unless gem_scope_compatible_api_scope_enabled?(scope_params) && default_host? + return rubygem_scope_params unless gem_scope_compatible_api_scope_enabled?(scope_params) say "Enter the name of the gem you want to scope this key to." rubygem_name = ask "Gem name [All gems]: " diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb index baaf8e85c251..dec1ae8280e8 100644 --- a/test/rubygems/test_gem_commands_push_command.rb +++ b/test/rubygems/test_gem_commands_push_command.rb @@ -550,7 +550,7 @@ def test_sending_gem_with_no_local_creds @cmd.options[:args] = [@path] @cmd.options[:host] = @host - @ui = Gem::MockGemUi.new "some@mail.com\npass\n11111\n" + @ui = Gem::MockGemUi.new "some@mail.com\npass\n\n11111\n" use_ui @ui do @cmd.execute end diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb index c8a0d5f475d8..aa75e07d9c48 100644 --- a/test/rubygems/test_gem_commands_signin_command.rb +++ b/test/rubygems/test_gem_commands_signin_command.rb @@ -266,9 +266,9 @@ def test_execute_on_gemserver_with_gem_scope key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ngem\n" util_capture(key_name_ui, host, api_key, fetcher) { @cmd.execute } - refute_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output - refute_match "Gem name [All gems]:", key_name_ui.output - assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body + assert_match "Enter the name of the gem you want to scope this key to.", key_name_ui.output + assert_match "Gem name [All gems]:", key_name_ui.output + assert_equal "name=test-key&push_rubygem=true&rubygem_name=gem", fetcher.last_request.body end def test_execute_with_warnings @@ -293,7 +293,7 @@ def test_execute_on_gemserver_without_profile_me_endpoint api_key = "1234abcd" fetcher = Gem::RemoteFetcher.fetcher - key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\ny" + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\n\n" # Set the expected response for the Web-API supplied ENV["RUBYGEMS_HOST"] = host @@ -314,6 +314,7 @@ def test_execute_on_gemserver_without_profile_me_endpoint assert_match "remove_owner [yN]", key_name_ui.output assert_match "access_webhooks [yN]", key_name_ui.output assert_match "show_dashboard [yN]", key_name_ui.output + refute_match "Would you like to enable MFA for this key? (strongly recommended) [yn]", key_name_ui.output assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body end From cda86cd3a12f3dc74d4c4fe8390e4de36e1f200b Mon Sep 17 00:00:00 2001 From: Jenny Shen Date: Thu, 14 Jul 2022 10:34:56 -0400 Subject: [PATCH 5/5] Refactor check on if a gem scope compatible api scope is enabled --- lib/rubygems/gemcutter_utilities.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 6b730ea6bd3c..9312897d5e96 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -325,7 +325,7 @@ def get_scope_params(scope) def get_rubygem_scope_params(scope_params) rubygem_scope_params = {} - return rubygem_scope_params unless gem_scope_compatible_api_scope_enabled?(scope_params) + return rubygem_scope_params if (GEM_SCOPE_COMPATIBLE_API_SCOPES & enabled_scopes(scope_params)).empty? say "Enter the name of the gem you want to scope this key to." rubygem_name = ask "Gem name [All gems]: " @@ -338,8 +338,8 @@ def get_rubygem_scope_params(scope_params) rubygem_scope_params end - def gem_scope_compatible_api_scope_enabled?(scope_params) - (GEM_SCOPE_COMPATIBLE_API_SCOPES & scope_params.select {|_, v| v }.keys).any? + def enabled_scopes(scope_params) + scope_params.select {|_, v| v }.keys end def default_host?