[rubygems/rubygems] Show better error when PAT can't authenticate to a private server

Before:

```
Fetching gem metadata from https://rubygems.org/........
Fetching source index from https://rubygems.pkg.github.com/my-org/

Bad username or password for https://x-access-token@rubygems.pkg.github.com/my-org/.
Please double-check your credentials and correct them.
```

After:

```
Fetching gem metadata from https://rubygems.org/........
Fetching source index from https://rubygems.pkg.github.com/my-org/

Access token could not be authenticated for https://x-access-token@rubygems.pkg.github.com/my-org/.
Make sure it's valid and has the necessary scopes configured.
```

https://github.com/rubygems/rubygems/commit/2ae69c964a
This commit is contained in:
David Rodríguez 2023-08-10 21:37:15 +02:00 committed by Hiroshi SHIBATA
parent e678affe70
commit fe240b672b
No known key found for this signature in database
GPG Key ID: F9CF13417264FAC2
5 changed files with 27 additions and 23 deletions

View File

@ -61,6 +61,16 @@ module Bundler
end
end
# This error is raised if HTTP authentication is correct, but lacks
# necessary permissions.
class AuthenticationForbiddenError < HTTPError
def initialize(remote_uri)
remote_uri = filter_uri(remote_uri)
super "Access token could not be authenticated for #{remote_uri}.\n" \
"Make sure it's valid and has the necessary scopes configured."
end
end
# Exceptions classes that should bypass retry attempts. If your password didn't work the
# first time, it's not going to the third time.
NET_ERRORS = [:HTTPBadGateway, :HTTPBadRequest, :HTTPFailedDependency,
@ -70,7 +80,7 @@ module Bundler
:HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity,
:HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze
FAIL_ERRORS = begin
fail_errors = [AuthenticationRequiredError, BadAuthenticationError, FallbackError]
fail_errors = [AuthenticationRequiredError, BadAuthenticationError, AuthenticationForbiddenError, FallbackError]
fail_errors << Gem::Requirement::BadRequirementError
fail_errors.concat(NET_ERRORS.map {|e| Net.const_get(e) })
end.freeze

View File

@ -41,6 +41,8 @@ module Bundler
when Net::HTTPUnauthorized
raise BadAuthenticationError, uri.host if uri.userinfo
raise AuthenticationRequiredError, uri.host
when Net::HTTPForbidden
raise AuthenticationForbiddenError, uri.host
when Net::HTTPNotFound
raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
else

View File

@ -15,8 +15,7 @@ module Bundler
raise BadAuthenticationError, remote_uri if remote_uri.userinfo
raise AuthenticationRequiredError, remote_uri
when /403/
raise BadAuthenticationError, remote_uri if remote_uri.userinfo
raise AuthenticationRequiredError, remote_uri
raise AuthenticationForbiddenError, remote_uri
else
raise HTTPError, "Could not fetch specs from #{display_uri} due to underlying error <#{e.message}>"
end

View File

@ -98,6 +98,16 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
context "when the request response is a Net::HTTPForbidden" do
let(:http_response) { Net::HTTPForbidden.new("1.1", 403, "Forbidden") }
let(:uri) { Bundler::URI("http://user:password@www.uri-to-fetch.com") }
it "should raise a Bundler::Fetcher::AuthenticationForbiddenError with the uri host" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
/Access token could not be authenticated for www.uri-to-fetch.com/)
end
end
context "when the request response is a Net::HTTPNotFound" do
let(:http_response) { Net::HTTPNotFound.new("1.1", 404, "Not Found") }

View File

@ -63,26 +63,9 @@ RSpec.describe Bundler::Fetcher::Index do
context "when a 403 response occurs" do
let(:error_message) { "403" }
before do
allow(remote_uri).to receive(:userinfo).and_return(userinfo)
end
context "and there was userinfo" do
let(:userinfo) { double(:userinfo) }
it "should raise a Bundler::Fetcher::BadAuthenticationError" do
expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
%r{Bad username or password for http://remote-uri.org})
end
end
context "and there was no userinfo" do
let(:userinfo) { nil }
it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
%r{Authentication is required for http://remote-uri.org})
end
it "should raise a Bundler::Fetcher::AuthenticationForbiddenError" do
expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
%r{Access token could not be authenticated for http://remote-uri.org})
end
end