diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 5e5c9d5a64..796938c40b 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -10,7 +10,8 @@ require_relative "gemcutter_utilities/webauthn_poller" module Gem::GemcutterUtilities ERROR_CODE = 1 - API_SCOPES = [:index_rubygems, :push_rubygem, :yank_rubygem, :add_owner, :remove_owner, :access_webhooks, :show_dashboard].freeze + API_SCOPES = [:index_rubygems, :push_rubygem, :yank_rubygem, :add_owner, :remove_owner, :access_webhooks].freeze + EXCLUSIVELY_API_SCOPES = [:show_dashboard].freeze include Gem::Text @@ -309,15 +310,31 @@ module Gem::GemcutterUtilities end def get_scope_params(scope) - scope_params = {} + scope_params = { index_rubygems: true } if scope scope_params = { scope => true } else - say "Please select scopes you want to enable for the API key (y/n)" - API_SCOPES.each do |s| - selected = ask_yes_no(s.to_s, false) - scope_params[s] = true if selected + say "The default access scope is:" + scope_params.each do |k, _v| + say " #{k}: y" + end + say "\n" + customise = ask_yes_no("Do you want to customise scopes?", false) + if customise + EXCLUSIVELY_API_SCOPES.each do |excl_scope| + selected = ask_yes_no("#{excl_scope} (exclusive scope, answering yes will not prompt for other scopes)", false) + next unless selected + + return { excl_scope => true } + end + + scope_params = {} + + API_SCOPES.each do |s| + selected = ask_yes_no(s.to_s, false) + scope_params[s] = true if selected + end end say "\n" end diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb index fd4ffb414a..29e5edceb7 100644 --- a/test/rubygems/test_gem_commands_signin_command.rb +++ b/test/rubygems/test_gem_commands_signin_command.rb @@ -85,7 +85,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase headers: { "location" => redirected_uri } ) Gem::RemoteFetcher.fetcher = fetcher - ui = Gem::MockGemUi.new("you@example.com\nsecret\n\n\n\n\n\n\n\n\n") + ui = Gem::MockGemUi.new("you@example.com\nsecret\n\n\n") assert_raise Gem::MockGemUi::TermError do use_ui ui do @@ -106,51 +106,98 @@ class TestGemCommandsSigninCommand < Gem::TestCase assert_equal api_key, credentials[:rubygems_api_key] end - def test_execute_with_key_name_and_scope + def test_execute_with_key_name_default_scope email = "you@example.com" password = "secret" 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\n" util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } user = ENV["USER"] || ENV["USERNAME"] assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output + assert_match "The default access scope is:", key_name_ui.output + assert_match "index_rubygems: y", key_name_ui.output + assert_match "Do you want to customise scopes? [yN]", key_name_ui.output + assert_equal "name=test-key&index_rubygems=true", fetcher.last_request.body + + credentials = load_yaml_file Gem.configuration.credentials_path + assert_equal api_key, credentials[:rubygems_api_key] + end + + def test_execute_with_key_name_and_custom_scope + email = "you@example.com" + password = "secret" + api_key = "1234abcd" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\n\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"] + + assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output + assert_match "The default access scope is:", key_name_ui.output + assert_match "Do you want to customise scopes? [yN]", key_name_ui.output + assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output assert_match "index_rubygems [yN]", key_name_ui.output assert_match "push_rubygem [yN]", key_name_ui.output assert_match "yank_rubygem [yN]", key_name_ui.output assert_match "add_owner [yN]", key_name_ui.output 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 assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body credentials = load_yaml_file Gem.configuration.credentials_path assert_equal api_key, credentials[:rubygems_api_key] end - def test_execute_with_key_name_scope_and_mfa_level_of_ui_only + def test_execute_with_key_name_and_exclusive_scope + email = "you@example.com" + password = "secret" + api_key = "1234abcd" + fetcher = Gem::RemoteFetcher.fetcher + + key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\ny\ny\n" + util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute } + + user = ENV["USER"] || ENV["USERNAME"] + + assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output + assert_match "The default access scope is:", key_name_ui.output + assert_match "index_rubygems: y", key_name_ui.output + assert_match "Do you want to customise scopes? [yN]", key_name_ui.output + assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output + assert_equal "name=test-key&show_dashboard=true", fetcher.last_request.body + + credentials = load_yaml_file Gem.configuration.credentials_path + assert_equal api_key, credentials[:rubygems_api_key] + end + + def test_execute_with_key_name_custom_scope_and_mfa_level_of_ui_only email = "you@example.com" password = "secret" api_key = "1234abcd" 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\ny\n\n\ny\n\n\n\n\n\n\ny" util_capture(key_name_ui, nil, api_key, fetcher, mfa_level) { @cmd.execute } user = ENV["USER"] || ENV["USERNAME"] assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output + assert_match "The default access scope is:", key_name_ui.output + assert_match "Do you want to customise scopes? [yN]", key_name_ui.output + assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output assert_match "index_rubygems [yN]", key_name_ui.output assert_match "push_rubygem [yN]", key_name_ui.output assert_match "yank_rubygem [yN]", key_name_ui.output assert_match "add_owner [yN]", key_name_ui.output 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 assert_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&mfa=true", fetcher.last_request.body @@ -158,26 +205,28 @@ class TestGemCommandsSigninCommand < Gem::TestCase assert_equal api_key, credentials[:rubygems_api_key] end - def test_execute_with_key_name_scope_and_mfa_level_of_gem_signin + def test_execute_with_key_name_custom_scope_and_mfa_level_of_gem_signin email = "you@example.com" password = "secret" api_key = "1234abcd" 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\ny\n\n\ny\n\n\n\n\n\n\ny" util_capture(key_name_ui, nil, api_key, fetcher, mfa_level) { @cmd.execute } user = ENV["USER"] || ENV["USERNAME"] assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output + assert_match "The default access scope is:", key_name_ui.output + assert_match "Do you want to customise scopes? [yN]", key_name_ui.output + assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output assert_match "index_rubygems [yN]", key_name_ui.output assert_match "push_rubygem [yN]", key_name_ui.output assert_match "yank_rubygem [yN]", key_name_ui.output assert_match "add_owner [yN]", key_name_ui.output 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 assert_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&mfa=true", fetcher.last_request.body @@ -207,7 +256,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase 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\ny\n\n\ny\n\n\n\n\n\n\ny" # Set the expected response for the Web-API supplied ENV["RUBYGEMS_HOST"] = host @@ -221,13 +270,13 @@ class TestGemCommandsSigninCommand < Gem::TestCase user = ENV["USER"] || ENV["USERNAME"] assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output + assert_match "show_dashboard (exclusive scope, answering yes will not prompt for other scopes) [yN]", key_name_ui.output assert_match "index_rubygems [yN]", key_name_ui.output assert_match "push_rubygem [yN]", key_name_ui.output assert_match "yank_rubygem [yN]", key_name_ui.output assert_match "add_owner [yN]", key_name_ui.output 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 assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body end @@ -248,7 +297,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase fetcher.data[profile] = profile_response Gem::RemoteFetcher.fetcher = fetcher - sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n") + sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n\n\n") use_ui sign_in_ui do yield diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb index 31933c9419..a3236e6276 100644 --- a/test/rubygems/test_gem_gemcutter_utilities.rb +++ b/test/rubygems/test_gem_gemcutter_utilities.rb @@ -306,7 +306,7 @@ class TestGemGemcutterUtilities < Gem::TestCase ENV["RUBYGEMS_HOST"] = @fetcher.host Gem::RemoteFetcher.fetcher = @fetcher - @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n" + extra_input) + @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n\n\n" + extra_input) use_ui @sign_in_ui do if args.length > 0