Skip to content

Commit

Permalink
[rubygems/rubygems] Show better error when PAT can't authenticate to …
Browse files Browse the repository at this point in the history
…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.
```

rubygems/rubygems@2ae69c964a
  • Loading branch information
deivid-rodriguez authored and hsbt committed Aug 16, 2023
1 parent e678aff commit fe240b6
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 23 deletions.
12 changes: 11 additions & 1 deletion lib/bundler/fetcher.rb
Expand Up @@ -61,6 +61,16 @@ def initialize(remote_uri)
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,
Expand All @@ -70,7 +80,7 @@ def initialize(remote_uri)
: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
Expand Down
2 changes: 2 additions & 0 deletions lib/bundler/fetcher/downloader.rb
Expand Up @@ -41,6 +41,8 @@ def fetch(uri, headers = {}, counter = 0)
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
Expand Down
3 changes: 1 addition & 2 deletions lib/bundler/fetcher/index.rb
Expand Up @@ -15,8 +15,7 @@ def specs(_gem_names)
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
Expand Down
10 changes: 10 additions & 0 deletions spec/bundler/bundler/fetcher/downloader_spec.rb
Expand Up @@ -98,6 +98,16 @@
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") }

Expand Down
23 changes: 3 additions & 20 deletions spec/bundler/bundler/fetcher/index_spec.rb
Expand Up @@ -63,26 +63,9 @@
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

Expand Down

0 comments on commit fe240b6

Please sign in to comment.