2015-02-12 03:23:34 +00:00
|
|
|
# Copyright 2015, Google Inc.
|
|
|
|
# All rights reserved.
|
|
|
|
#
|
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
|
|
# modification, are permitted provided that the following conditions are
|
|
|
|
# met:
|
|
|
|
#
|
|
|
|
# * Redistributions of source code must retain the above copyright
|
|
|
|
# notice, this list of conditions and the following disclaimer.
|
|
|
|
# * Redistributions in binary form must reproduce the above
|
|
|
|
# copyright notice, this list of conditions and the following disclaimer
|
|
|
|
# in the documentation and/or other materials provided with the
|
|
|
|
# distribution.
|
|
|
|
# * Neither the name of Google Inc. nor the names of its
|
|
|
|
# contributors may be used to endorse or promote products derived from
|
|
|
|
# this software without specific prior written permission.
|
|
|
|
#
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
2019-03-15 19:34:54 +00:00
|
|
|
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
|
|
|
$LOAD_PATH.unshift spec_dir
|
2015-02-12 03:23:34 +00:00
|
|
|
$LOAD_PATH.uniq!
|
|
|
|
|
2019-03-15 19:34:54 +00:00
|
|
|
require "apply_auth_examples"
|
|
|
|
require "googleauth/signet"
|
|
|
|
require "jwt"
|
|
|
|
require "openssl"
|
|
|
|
require "spec_helper"
|
2015-02-12 03:23:34 +00:00
|
|
|
|
|
|
|
describe Signet::OAuth2::Client do
|
2019-03-15 19:34:54 +00:00
|
|
|
before :example do
|
|
|
|
@key = OpenSSL::PKey::RSA.new 2048
|
2015-02-12 03:23:34 +00:00
|
|
|
@client = Signet::OAuth2::Client.new(
|
2019-03-15 19:34:54 +00:00
|
|
|
token_credential_uri: "https://oauth2.googleapis.com/token",
|
|
|
|
scope: "https://www.googleapis.com/auth/userinfo.profile",
|
|
|
|
issuer: "app@example.com",
|
|
|
|
audience: "https://oauth2.googleapis.com/token",
|
|
|
|
signing_key: @key
|
2016-07-13 22:44:16 +00:00
|
|
|
)
|
2015-02-12 03:23:34 +00:00
|
|
|
end
|
|
|
|
|
2019-03-15 19:34:54 +00:00
|
|
|
def make_auth_stubs opts
|
|
|
|
access_token = opts[:access_token] || ""
|
|
|
|
body = MultiJson.dump("access_token" => access_token,
|
|
|
|
"token_type" => "Bearer",
|
|
|
|
"expires_in" => 3600)
|
2015-12-09 23:45:22 +00:00
|
|
|
blk = proc do |request|
|
2019-03-15 19:34:54 +00:00
|
|
|
params = Addressable::URI.form_unencode request.body
|
|
|
|
_claim, _header = JWT.decode(params.assoc("assertion").last,
|
2017-09-15 13:53:38 +00:00
|
|
|
@key.public_key, true,
|
2019-03-15 19:34:54 +00:00
|
|
|
algorithm: "RS256")
|
2015-02-12 03:23:34 +00:00
|
|
|
end
|
2019-03-15 19:34:54 +00:00
|
|
|
with_params = { body: hash_including(
|
|
|
|
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
|
|
|
) }
|
|
|
|
with_params[:headers] = { "User-Agent" => opts[:user_agent] } if opts[:user_agent]
|
|
|
|
stub_request(:post, "https://oauth2.googleapis.com/token")
|
2019-01-02 22:42:42 +00:00
|
|
|
.with(with_params, &blk)
|
2019-03-15 19:34:54 +00:00
|
|
|
.to_return(body: body,
|
|
|
|
status: 200,
|
|
|
|
headers: { "Content-Type" => "application/json" })
|
2015-02-12 03:23:34 +00:00
|
|
|
end
|
|
|
|
|
2019-03-15 19:34:54 +00:00
|
|
|
it_behaves_like "apply/apply! are OK"
|
2019-01-02 22:42:42 +00:00
|
|
|
|
|
|
|
describe "#configure_connection" do
|
|
|
|
it "honors default_connection" do
|
|
|
|
token = "1/abcdef1234567890"
|
|
|
|
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/1.0"
|
2019-03-15 19:34:54 +00:00
|
|
|
conn = Faraday.new headers: { "User-Agent" => "RubyRocks/1.0" }
|
|
|
|
@client.configure_connection default_connection: conn
|
2019-01-02 22:42:42 +00:00
|
|
|
md = { foo: "bar" }
|
2019-03-15 19:34:54 +00:00
|
|
|
@client.apply! md
|
2019-01-02 22:42:42 +00:00
|
|
|
want = { foo: "bar", authorization: "Bearer #{token}" }
|
|
|
|
expect(md).to eq(want)
|
|
|
|
expect(stub).to have_been_requested
|
|
|
|
end
|
|
|
|
|
|
|
|
it "honors connection_builder" do
|
|
|
|
token = "1/abcdef1234567890"
|
|
|
|
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/2.0"
|
|
|
|
connection_builder = proc do
|
2019-03-15 19:34:54 +00:00
|
|
|
Faraday.new headers: { "User-Agent" => "RubyRocks/2.0" }
|
2019-01-02 22:42:42 +00:00
|
|
|
end
|
2019-03-15 19:34:54 +00:00
|
|
|
@client.configure_connection connection_builder: connection_builder
|
2019-01-02 22:42:42 +00:00
|
|
|
md = { foo: "bar" }
|
2019-03-15 19:34:54 +00:00
|
|
|
@client.apply! md
|
2019-01-02 22:42:42 +00:00
|
|
|
want = { foo: "bar", authorization: "Bearer #{token}" }
|
|
|
|
expect(md).to eq(want)
|
|
|
|
expect(stub).to have_been_requested
|
|
|
|
end
|
|
|
|
end
|
2019-06-10 20:52:45 +00:00
|
|
|
|
|
|
|
describe "#fetch_access_token!" do
|
|
|
|
it "retries when orig_fetch_access_token! raises Signet::RemoteServerError" do
|
|
|
|
mocked_responses = [:raise, :raise, "success"]
|
|
|
|
allow(@client).to receive(:orig_fetch_access_token!).exactly(3).times do
|
|
|
|
response = mocked_responses.shift
|
|
|
|
response == :raise ? raise(Signet::RemoteServerError) : response
|
|
|
|
end
|
|
|
|
expect(@client.fetch_access_token!).to eq("success")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises when the max retry count is exceeded" do
|
|
|
|
mocked_responses = [:raise, :raise, :raise, :raise, :raise, :raise, "success"]
|
|
|
|
allow(@client).to receive(:orig_fetch_access_token!).exactly(6).times do
|
|
|
|
response = mocked_responses.shift
|
|
|
|
response == :raise ? raise(Signet::RemoteServerError) : response
|
|
|
|
end
|
|
|
|
expect { @client.fetch_access_token! }.to raise_error Signet::AuthorizationError
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not retry and raises right away if it encounters a Signet::AuthorizationError" do
|
|
|
|
allow(@client).to receive(:orig_fetch_access_token!).at_most(:once)
|
|
|
|
.and_raise(Signet::AuthorizationError.new("Some Message"))
|
|
|
|
expect { @client.fetch_access_token! }.to raise_error Signet::AuthorizationError
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not retry and raises right away if it encounters a Signet::ParseError" do
|
|
|
|
allow(@client).to receive(:orig_fetch_access_token!).at_most(:once).and_raise(Signet::ParseError)
|
|
|
|
expect { @client.fetch_access_token! }.to raise_error Signet::ParseError
|
|
|
|
end
|
|
|
|
end
|
2015-02-12 03:23:34 +00:00
|
|
|
end
|