diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 15e61440e3df..9312897d5e96 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 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]: " + say "\n" + + unless rubygem_name.nil? || rubygem_name.empty? + rubygem_scope_params[:rubygem_name] = rubygem_name + end + + rubygem_scope_params + end + + def enabled_scopes(scope_params) + scope_params.select {|_, v| v }.keys + end + def default_host? host == Gem::DEFAULT_HOST end 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 fd4ffb414a3f..aa75e07d9c48 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"] @@ -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 } + + 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 email = "you@example.com" password = "secret" @@ -207,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 @@ -228,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