Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion lib/rubygems/gemcutter_utilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 }

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/rubygems/test_gem_commands_push_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"

@jenshenny jenshenny Jul 12, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this and other flows, I'm assuming entering another newline (to enter gem scope) wouldn't be a breaking change

use_ui @ui do
@cmd.execute
end
Expand Down
95 changes: 91 additions & 4 deletions test/rubygems/test_gem_commands_signin_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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"]
Expand All @@ -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"]
Expand All @@ -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"
Expand All @@ -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
Expand All @@ -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

Expand Down