utils, gist-logs: improve/fix credential handling.

The API used (`Net::HTTP::Post`) does not handle basic authentication
credentials in the same way as `open` so fix both cases so they work.

Also, do some general usability tweaks to point out to people what could
be wrong with their tokens or credentials to help them debug.

Closes Homebrew/homebrew#50410.

Signed-off-by: Mike McQuaid <mike@mikemcquaid.com>
This commit is contained in:
Mike McQuaid 2016-03-25 18:35:06 +08:00 committed by Xu Cheng
parent ca2abb2be6
commit 6135da800e
2 changed files with 77 additions and 20 deletions

View File

@ -37,12 +37,12 @@ module Homebrew
if ARGV.include?("--new-issue") || ARGV.switch?("n")
auth = :AUTH_TOKEN
unless GitHub.api_credentials
if GitHub.api_credentials_type == :none
puts "You can create a personal access token: https://github.com/settings/tokens"
puts "and then set HOMEBREW_GITHUB_API_TOKEN as authentication method."
puts
auth = :AUTH_BASIC
auth = :AUTH_USER_LOGIN
end
url = new_issue(f.tap, "#{f.name} failed to build on #{MacOS.full_version}", url, auth)
@ -118,9 +118,21 @@ module Homebrew
headers = GitHub.api_headers
headers["Content-Type"] = "application/json"
request = Net::HTTP::Post.new(path, headers)
basic_auth_credentials = nil
if auth != :AUTH_USER_LOGIN
token, username = GitHub.api_credentials
case GitHub.api_credentials_type
when :keychain
basic_auth_credentials = [username, token]
when :environment
headers["Authorization"] = "token #{token}"
end
end
login(request) if auth == :AUTH_BASIC
request = Net::HTTP::Post.new(path, headers)
request.basic_auth(*basic_auth_credentials) if basic_auth_credentials
login(request) if auth == :AUTH_USER_LOGIN
request.body = Utils::JSON.dump(data)
request
@ -133,6 +145,7 @@ module Homebrew
when Net::HTTPCreated
Utils::JSON.load get_body(response)
else
GitHub.api_credentials_error_message(response)
raise "HTTP #{response.code} #{response.message} (expected 201)"
end
end

View File

@ -484,7 +484,7 @@ module GitHub
class RateLimitExceededError < Error
def initialize(reset, error)
super <<-EOS.undent
GitHub #{error}
GitHub API Error: #{error}
Try again in #{pretty_ratelimit_reset(reset)}, or create a personal access token:
#{Tty.em}https://github.com/settings/tokens/new?scopes=&description=Homebrew#{Tty.reset}
and then set the token as: export HOMEBREW_GITHUB_API_TOKEN="your_new_token"
@ -506,9 +506,12 @@ module GitHub
EOS
else
message << <<-EOS.undent
The GitHub credentials in the OS X keychain are invalid.
The GitHub credentials in the OS X keychain may be invalid.
Clear them with:
printf "protocol=https\\nhost=github.com\\n" | git credential-osxkeychain erase
Or create a personal access token:
#{Tty.em}https://github.com/settings/tokens/new?scopes=&description=Homebrew#{Tty.reset}
and then set the token as: export HOMEBREW_GITHUB_API_TOKEN="your_new_token"
EOS
end
super message
@ -536,32 +539,71 @@ module GitHub
end
end
def api_headers
@api_headers ||= begin
headers = {
"User-Agent" => HOMEBREW_USER_AGENT,
"Accept" => "application/vnd.github.v3+json"
}
token, username = api_credentials
if token && !token.empty?
if username && !username.empty?
headers[:http_basic_authentication] = [username, token]
else
headers["Authorization"] = "token #{token}"
def api_credentials_type
token, username = api_credentials
if token && !token.empty?
if username && !username.empty?
:keychain
else
:environment
end
else
:none
end
end
def api_credentials_error_message(response_headers)
@api_credentials_error_message_printed ||= begin
unauthorized = (response_headers["status"] == "401 Unauthorized")
scopes = response_headers["x-accepted-oauth-scopes"].to_s.split(", ")
if !unauthorized && scopes.empty?
credentials_scopes = response_headers["x-oauth-scopes"].to_s.split(", ")
case GitHub.api_credentials_type
when :keychain
onoe <<-EOS.undent
Your OS X keychain GitHub credentials do not have sufficient scope!
Scopes they have: #{credentials_scopes}
Create a personal access token: https://github.com/settings/tokens
and then set HOMEBREW_GITHUB_API_TOKEN as the authentication method instead.
EOS
when :environment
onoe <<-EOS.undent
Your HOMEBREW_GITHUB_API_TOKEN does not have sufficient scope!
Scopes it has: #{credentials_scopes}
Create a new personal access token: https://github.com/settings/tokens
and then set the new HOMEBREW_GITHUB_API_TOKEN as the authentication method instead.
EOS
end
end
headers
true
end
end
def api_headers
{
"User-Agent" => HOMEBREW_USER_AGENT,
"Accept" => "application/vnd.github.v3+json"
}
end
def open(url, &_block)
# This is a no-op if the user is opting out of using the GitHub API.
return if ENV["HOMEBREW_NO_GITHUB_API"]
require "net/https"
headers = api_headers
token, username = api_credentials
case api_credentials_type
when :keychain
headers[:http_basic_authentication] = [username, token]
when :environment
headers["Authorization"] = "token #{token}"
end
begin
Kernel.open(url, api_headers) { |f| yield Utils::JSON.load(f.read) }
Kernel.open(url, headers) { |f| yield Utils::JSON.load(f.read) }
rescue OpenURI::HTTPError => e
handle_api_error(e)
rescue EOFError, SocketError, OpenSSL::SSL::SSLError => e
@ -578,6 +620,8 @@ module GitHub
raise RateLimitExceededError.new(reset, error)
end
GitHub.api_credentials_error_message(e.io.meta)
case e.io.status.first
when "401", "403"
raise AuthenticationFailedError.new(e.message)