[前][次][番号順一覧][スレッド一覧]

ruby-changes:71645

From: Ashley <ko1@a...>
Date: Wed, 6 Apr 2022 08:55:16 +0900 (JST)
Subject: [ruby-changes:71645] b3f1b3ccef (master): [rubygems/rubygems] Enable mfa on specific keys during gem signin

https://git.ruby-lang.org/ruby.git/commit/?id=b3f1b3ccef

From b3f1b3ccef6f61b95685690e5a8faaa3f009c25f Mon Sep 17 00:00:00 2001
From: Ashley Ellis Pierce <anellis12@g...>
Date: Mon, 24 Jan 2022 15:25:28 -0500
Subject: [rubygems/rubygems] Enable mfa on specific keys during gem signin

https://github.com/rubygems/rubygems/commit/e787f7f655
---
 lib/rubygems/gemcutter_utilities.rb               | 25 +++++++++-
 test/rubygems/test_gem_commands_push_command.rb   |  5 ++
 test/rubygems/test_gem_commands_signin_command.rb | 61 +++++++++++++++++++++--
 test/rubygems/test_gem_gemcutter_utilities.rb     |  2 +
 4 files changed, 88 insertions(+), 5 deletions(-)

diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index 0968e1a6f9..adf85d1b6c 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -1,6 +1,7 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L1
 # frozen_string_literal: true
 require_relative 'remote_fetcher'
 require_relative 'text'
+require 'json'
 
 ##
 # Utility methods for using the RubyGems API.
@@ -163,12 +164,13 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L164
 
     key_name     = get_key_name(scope)
     scope_params = get_scope_params(scope)
+    mfa_params   = get_mfa_params(email, password)
 
     response = rubygems_api_request(:post, "api/v1/api_key",
                                     sign_in_host, scope: scope) do |request|
       request.basic_auth email, password
       request["OTP"] = otp if otp
-      request.body = URI.encode_www_form({ name: key_name }.merge(scope_params))
+      request.body = URI.encode_www_form({ name: key_name }.merge(scope_params, mfa_params))
     end
 
     with_response response do |resp|
@@ -267,6 +269,27 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L269
     scope_params
   end
 
+  def get_mfa_params(email, password)
+    mfa_level = get_user_mfa_level(email, password)
+    params = {}
+    if mfa_level == "ui_only" || mfa_level == "ui_and_gem_sign"
+      selected = ask "Would you like to enable MFA for this key? [y/N]"
+      params["mfa"] = true if selected =~ /^[yY](es)?$/
+    elsif mfa_level == "ui_and_api"
+      params["mfa"] = true
+    end
+    params
+  end
+
+  def get_user_mfa_level(email, password)
+    response = rubygems_api_request(:get, "api/v1/profile") do |request|
+      request.basic_auth email, password
+    end
+    with_response response do |resp|
+      JSON.parse(resp.body)["mfa"]
+    end
+  end
+
   def get_key_name(scope)
     hostname = Socket.gethostname || "unknown-host"
     user = ENV["USER"] || ENV["USERNAME"] || "unknown-user"
diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb
index fa3968ffce..e219ae170e 100644
--- a/test/rubygems/test_gem_commands_push_command.rb
+++ b/test/rubygems/test_gem_commands_push_command.rb
@@ -435,6 +435,7 @@ class TestGemCommandsPushCommand < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_push_command.rb#L435
 
     response_mfa_enabled = "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)'
+    response_profile     = {"mfa" => "disabled"}.to_json
 
     @fetcher.data["#{@host}/api/v1/gems"] = [
       [response_success, 200, "OK"],
@@ -445,6 +446,10 @@ class TestGemCommandsPushCommand < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_push_command.rb#L446
       ["", 200, "OK"],
     ]
 
+    @fetcher.data["#{@host}/api/v1/profile"] = [
+      [response_profile, 200, "OK"],
+    ]
+
     @cmd.instance_variable_set :@scope, :push_rubygem
     @cmd.options[:args] = [@path]
     @cmd.options[:host] = @host
diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb
index 0f856a53ba..b1493fb137 100644
--- a/test/rubygems/test_gem_commands_signin_command.rb
+++ b/test/rubygems/test_gem_commands_signin_command.rb
@@ -105,18 +105,71 @@ class TestGemCommandsSigninCommand < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_signin_command.rb#L105
     assert_equal api_key, credentials[:rubygems_api_key]
   end
 
+  def test_execute_with_key_name_scope_and_mfa
+    email     = 'you@e...'
+    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\ny"
+    util_capture_with_mfa_enabled(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 "index_rubygems [y/N]", key_name_ui.output
+    assert_match "push_rubygem [y/N]", key_name_ui.output
+    assert_match "yank_rubygem [y/N]", key_name_ui.output
+    assert_match "add_owner [y/N]", key_name_ui.output
+    assert_match "remove_owner [y/N]", key_name_ui.output
+    assert_match "access_webhooks [y/N]", key_name_ui.output
+    assert_match "show_dashboard [y/N]", key_name_ui.output
+    assert_match "Would you like to enable MFA for this key? [y/N]", key_name_ui.output
+    assert_equal "name=test-key&push_rubygem=true&mfa=true", fetcher.last_request.body
+
+    credentials = load_yaml_file Gem.configuration.credentials_path
+    assert_equal api_key, credentials[:rubygems_api_key]
+  end
+
   # Utility method to capture IO/UI within the block passed
 
   def util_capture(ui_stub = nil, host = nil, api_key = nil, fetcher = Gem::FakeFetcher.new)
-    api_key ||= 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
-    response  = [api_key, 200, 'OK']
-    email     = 'you@e...'
-    password  = 'secret'
+    api_key        ||= 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+    response         = [api_key, 200, 'OK']
+    profile_response =[{"mfa" => "disabled"}.to_json, 200, 'OK']
+    email            = 'you@e...'
+    password         = 'secret'
+
+    # Set the expected response for the Web-API supplied
+    ENV['RUBYGEMS_HOST']       = host || Gem::DEFAULT_HOST
+    data_key                   = "#{ENV['RUBYGEMS_HOST']}/api/v1/api_key"
+    fetcher.data[data_key]     = response
+    profile                    = "#{ENV['RUBYGEMS_HOST']}/api/v1/profile"
+    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")
+
+    use_ui sign_in_ui do
+      yield
+    end
+
+    sign_in_ui
+  end
+
+  def util_capture_with_mfa_enabled(ui_stub = nil, host = nil, api_key = nil, fetcher = Gem::FakeFetcher.new)
+    api_key        ||= 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+    response         = [api_key, 200, 'OK']
+    profile_response =[{"mfa" => "ui_only"}.to_json, 200, 'OK']
+    email            = 'you@e...'
+    password         = 'secret'
 
     # Set the expected response for the Web-API supplied
     ENV['RUBYGEMS_HOST']       = host || Gem::DEFAULT_HOST
     data_key                   = "#{ENV['RUBYGEMS_HOST']}/api/v1/api_key"
     fetcher.data[data_key]     = response
+    profile                    = "#{ENV['RUBYGEMS_HOST']}/api/v1/profile"
+    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")
diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb
index 0bcd1504e9..6eebea6ca7 100644
--- a/test/rubygems/test_gem_gemcutter_utilities.rb
+++ b/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -229,6 +229,7 @@ class TestGemGemcutterUtilities < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_gemcutter_utilities.rb#L229
   def util_sign_in(response, host = nil, args = [], extra_input = '')
     email    = 'you@e...'
     password = 'secret'
+    profile_response =[{"mfa" => "disabled"}.to_json, 200, 'OK']
 
     if host
       ENV['RUBYGEMS_HOST'] = host
@@ -238,6 +239,7 @@ class TestGemGemcutterUtilities < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_gemcutter_utilities.rb#L239
 
     @fetcher = Gem::FakeFetcher.new
     @fetcher.data["#{host}/api/v1/api_key"] = response
+    @fetcher.data["#{host}/api/v1/profile"] = profile_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


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]