Support configuration of the connection object used to fetch tokens (#185)
This commit is contained in:
parent
dc8b127549
commit
f0b95e72f9
|
@ -11,6 +11,8 @@ Metrics/CyclomaticComplexity:
|
||||||
Max: 8
|
Max: 8
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
Max: 20
|
Max: 20
|
||||||
|
Metrics/ModuleLength:
|
||||||
|
Max: 150
|
||||||
Metrics/ClassLength:
|
Metrics/ClassLength:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
Layout/IndentHeredoc:
|
Layout/IndentHeredoc:
|
||||||
|
|
|
@ -52,11 +52,21 @@ ERROR_MESSAGE
|
||||||
# scope is ignored.
|
# scope is ignored.
|
||||||
#
|
#
|
||||||
# @param scope [string|array|nil] the scope(s) to access
|
# @param scope [string|array|nil] the scope(s) to access
|
||||||
# @param options [hash] allows override of the connection being used
|
# @param options [Hash] Connection options. These may be used to configure
|
||||||
|
# the `Faraday::Connection` used for outgoing HTTP requests. For
|
||||||
|
# example, if a connection proxy must be used in the current network,
|
||||||
|
# you may provide a connection with with the needed proxy options.
|
||||||
|
# The following keys are recognized:
|
||||||
|
# * `:default_connection` The connection object to use for token
|
||||||
|
# refresh requests.
|
||||||
|
# * `:connection_builder` A `Proc` that creates and returns a
|
||||||
|
# connection to use for token refresh requests.
|
||||||
|
# * `:connection` The connection to use to determine whether GCE
|
||||||
|
# metadata credentials are available.
|
||||||
def get_application_default(scope = nil, options = {})
|
def get_application_default(scope = nil, options = {})
|
||||||
creds = DefaultCredentials.from_env(scope) ||
|
creds = DefaultCredentials.from_env(scope, options) ||
|
||||||
DefaultCredentials.from_well_known_path(scope) ||
|
DefaultCredentials.from_well_known_path(scope, options) ||
|
||||||
DefaultCredentials.from_system_default_path(scope)
|
DefaultCredentials.from_system_default_path(scope, options)
|
||||||
return creds unless creds.nil?
|
return creds unless creds.nil?
|
||||||
unless GCECredentials.on_gce?(options)
|
unless GCECredentials.on_gce?(options)
|
||||||
# Clear cache of the result of GCECredentials.on_gce?
|
# Clear cache of the result of GCECredentials.on_gce?
|
||||||
|
|
|
@ -87,10 +87,9 @@ ERROR
|
||||||
# fetched.
|
# fetched.
|
||||||
def fetch_access_token(options = {})
|
def fetch_access_token(options = {})
|
||||||
c = options[:connection] || Faraday.default_connection
|
c = options[:connection] || Faraday.default_connection
|
||||||
c.headers = { 'Metadata-Flavor' => 'Google' }
|
|
||||||
|
|
||||||
retry_with_error do
|
retry_with_error do
|
||||||
resp = c.get(COMPUTE_AUTH_TOKEN_URI)
|
headers = { 'Metadata-Flavor' => 'Google' }
|
||||||
|
resp = c.get(COMPUTE_AUTH_TOKEN_URI, nil, headers)
|
||||||
case resp.status
|
case resp.status
|
||||||
when 200
|
when 200
|
||||||
Signet::OAuth2.parse_credentials(resp.body,
|
Signet::OAuth2.parse_credentials(resp.body,
|
||||||
|
|
|
@ -66,14 +66,14 @@ module Google
|
||||||
elsif keyfile.is_a? Hash
|
elsif keyfile.is_a? Hash
|
||||||
hash = stringify_hash_keys keyfile
|
hash = stringify_hash_keys keyfile
|
||||||
hash['scope'] ||= scope
|
hash['scope'] ||= scope
|
||||||
@client = init_client hash
|
@client = init_client hash, options
|
||||||
@project_id ||= (hash['project_id'] || hash['project'])
|
@project_id ||= (hash['project_id'] || hash['project'])
|
||||||
else
|
else
|
||||||
verify_keyfile_exists! keyfile
|
verify_keyfile_exists! keyfile
|
||||||
json = JSON.parse ::File.read(keyfile)
|
json = JSON.parse ::File.read(keyfile)
|
||||||
json['scope'] ||= scope
|
json['scope'] ||= scope
|
||||||
@project_id ||= (json['project_id'] || json['project'])
|
@project_id ||= (json['project_id'] || json['project'])
|
||||||
@client = init_client json
|
@client = init_client json, options
|
||||||
end
|
end
|
||||||
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
|
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
|
||||||
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
||||||
|
@ -85,33 +85,32 @@ module Google
|
||||||
# previously stated locations do not contain keyfile information,
|
# previously stated locations do not contain keyfile information,
|
||||||
# this method defaults to use the application default.
|
# this method defaults to use the application default.
|
||||||
def self.default(options = {})
|
def self.default(options = {})
|
||||||
scope = options[:scope]
|
|
||||||
# First try to find keyfile file from environment variables.
|
# First try to find keyfile file from environment variables.
|
||||||
client = from_path_vars scope
|
client = from_path_vars options
|
||||||
|
|
||||||
# Second try to find keyfile json from environment variables.
|
# Second try to find keyfile json from environment variables.
|
||||||
client ||= from_json_vars scope
|
client ||= from_json_vars options
|
||||||
|
|
||||||
# Third try to find keyfile file from known file paths.
|
# Third try to find keyfile file from known file paths.
|
||||||
client ||= from_default_paths scope
|
client ||= from_default_paths options
|
||||||
|
|
||||||
# Finally get instantiated client from Google::Auth
|
# Finally get instantiated client from Google::Auth
|
||||||
client ||= from_application_default scope
|
client ||= from_application_default options
|
||||||
client
|
client
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.from_path_vars(scope)
|
def self.from_path_vars(options)
|
||||||
self::PATH_ENV_VARS
|
self::PATH_ENV_VARS
|
||||||
.map { |v| ENV[v] }
|
.map { |v| ENV[v] }
|
||||||
.compact
|
.compact
|
||||||
.select { |p| ::File.file? p }
|
.select { |p| ::File.file? p }
|
||||||
.each do |file|
|
.each do |file|
|
||||||
return new file, scope: scope
|
return new file, options
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.from_json_vars(scope)
|
def self.from_json_vars(options)
|
||||||
json = lambda do |v|
|
json = lambda do |v|
|
||||||
unless ENV[v].nil?
|
unless ENV[v].nil?
|
||||||
begin
|
begin
|
||||||
|
@ -122,24 +121,24 @@ module Google
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self::JSON_ENV_VARS.map(&json).compact.each do |hash|
|
self::JSON_ENV_VARS.map(&json).compact.each do |hash|
|
||||||
return new hash, scope: scope
|
return new hash, options
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.from_default_paths(scope)
|
def self.from_default_paths(options)
|
||||||
self::DEFAULT_PATHS
|
self::DEFAULT_PATHS
|
||||||
.select { |p| ::File.file? p }
|
.select { |p| ::File.file? p }
|
||||||
.each do |file|
|
.each do |file|
|
||||||
return new file, scope: scope
|
return new file, options
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.from_application_default(scope)
|
def self.from_application_default(options)
|
||||||
scope ||= self::SCOPE
|
scope = options[:scope] || self::SCOPE
|
||||||
client = Google::Auth.get_application_default scope
|
client = Google::Auth.get_application_default scope
|
||||||
new client
|
new client, options
|
||||||
end
|
end
|
||||||
private_class_method :from_path_vars,
|
private_class_method :from_path_vars,
|
||||||
:from_json_vars,
|
:from_json_vars,
|
||||||
|
@ -161,9 +160,10 @@ module Google
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initializes the Signet client.
|
# Initializes the Signet client.
|
||||||
def init_client(keyfile)
|
def init_client(keyfile, connection_options = {})
|
||||||
client_opts = client_options keyfile
|
client_opts = client_options keyfile
|
||||||
Signet::OAuth2::Client.new client_opts
|
Signet::OAuth2::Client.new(client_opts)
|
||||||
|
.configure_connection(connection_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# returns a new Hash with string keys instead of symbol keys.
|
# returns a new Hash with string keys instead of symbol keys.
|
||||||
|
|
|
@ -76,22 +76,35 @@ module Google
|
||||||
# By default, it calls #new on the current class, but this behaviour can
|
# By default, it calls #new on the current class, but this behaviour can
|
||||||
# be modified, allowing different instances to be created.
|
# be modified, allowing different instances to be created.
|
||||||
def make_creds(*args)
|
def make_creds(*args)
|
||||||
new(*args)
|
creds = new(*args)
|
||||||
|
if creds.respond_to?(:configure_connection) && args.size == 1
|
||||||
|
creds = creds.configure_connection(args[0])
|
||||||
|
end
|
||||||
|
creds
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates an instance from the path specified in an environment
|
# Creates an instance from the path specified in an environment
|
||||||
# variable.
|
# variable.
|
||||||
#
|
#
|
||||||
# @param scope [string|array|nil] the scope(s) to access
|
# @param scope [string|array|nil] the scope(s) to access
|
||||||
def from_env(scope = nil)
|
# @param options [Hash] Connection options. These may be used to configure
|
||||||
|
# how OAuth tokens are retrieved, by providing a suitable
|
||||||
|
# `Faraday::Connection`. For example, if a connection proxy must be
|
||||||
|
# used in the current network, you may provide a connection with
|
||||||
|
# with the needed proxy options.
|
||||||
|
# The following keys are recognized:
|
||||||
|
# * `:default_connection` The connection object to use.
|
||||||
|
# * `:connection_builder` A `Proc` that returns a connection.
|
||||||
|
def from_env(scope = nil, options = {})
|
||||||
|
options = interpret_options scope, options
|
||||||
if ENV.key?(ENV_VAR)
|
if ENV.key?(ENV_VAR)
|
||||||
path = ENV[ENV_VAR]
|
path = ENV[ENV_VAR]
|
||||||
raise "file #{path} does not exist" unless File.exist?(path)
|
raise "file #{path} does not exist" unless File.exist?(path)
|
||||||
File.open(path) do |f|
|
File.open(path) do |f|
|
||||||
return make_creds(json_key_io: f, scope: scope)
|
return make_creds(options.merge(json_key_io: f))
|
||||||
end
|
end
|
||||||
elsif service_account_env_vars? || authorized_user_env_vars?
|
elsif service_account_env_vars? || authorized_user_env_vars?
|
||||||
return make_creds(scope: scope)
|
return make_creds(options)
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
raise "#{NOT_FOUND_ERROR}: #{e}"
|
raise "#{NOT_FOUND_ERROR}: #{e}"
|
||||||
|
@ -100,7 +113,16 @@ module Google
|
||||||
# Creates an instance from a well known path.
|
# Creates an instance from a well known path.
|
||||||
#
|
#
|
||||||
# @param scope [string|array|nil] the scope(s) to access
|
# @param scope [string|array|nil] the scope(s) to access
|
||||||
def from_well_known_path(scope = nil)
|
# @param options [Hash] Connection options. These may be used to configure
|
||||||
|
# how OAuth tokens are retrieved, by providing a suitable
|
||||||
|
# `Faraday::Connection`. For example, if a connection proxy must be
|
||||||
|
# used in the current network, you may provide a connection with
|
||||||
|
# with the needed proxy options.
|
||||||
|
# The following keys are recognized:
|
||||||
|
# * `:default_connection` The connection object to use.
|
||||||
|
# * `:connection_builder` A `Proc` that returns a connection.
|
||||||
|
def from_well_known_path(scope = nil, options = {})
|
||||||
|
options = interpret_options scope, options
|
||||||
home_var = OS.windows? ? 'APPDATA' : 'HOME'
|
home_var = OS.windows? ? 'APPDATA' : 'HOME'
|
||||||
base = WELL_KNOWN_PATH
|
base = WELL_KNOWN_PATH
|
||||||
root = ENV[home_var].nil? ? '' : ENV[home_var]
|
root = ENV[home_var].nil? ? '' : ENV[home_var]
|
||||||
|
@ -108,7 +130,7 @@ module Google
|
||||||
path = File.join(root, base)
|
path = File.join(root, base)
|
||||||
return nil unless File.exist?(path)
|
return nil unless File.exist?(path)
|
||||||
File.open(path) do |f|
|
File.open(path) do |f|
|
||||||
return make_creds(json_key_io: f, scope: scope)
|
return make_creds(options.merge(json_key_io: f))
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
raise "#{WELL_KNOWN_ERROR}: #{e}"
|
raise "#{WELL_KNOWN_ERROR}: #{e}"
|
||||||
|
@ -117,7 +139,16 @@ module Google
|
||||||
# Creates an instance from the system default path
|
# Creates an instance from the system default path
|
||||||
#
|
#
|
||||||
# @param scope [string|array|nil] the scope(s) to access
|
# @param scope [string|array|nil] the scope(s) to access
|
||||||
def from_system_default_path(scope = nil)
|
# @param options [Hash] Connection options. These may be used to configure
|
||||||
|
# how OAuth tokens are retrieved, by providing a suitable
|
||||||
|
# `Faraday::Connection`. For example, if a connection proxy must be
|
||||||
|
# used in the current network, you may provide a connection with
|
||||||
|
# with the needed proxy options.
|
||||||
|
# The following keys are recognized:
|
||||||
|
# * `:default_connection` The connection object to use.
|
||||||
|
# * `:connection_builder` A `Proc` that returns a connection.
|
||||||
|
def from_system_default_path(scope = nil, options = {})
|
||||||
|
options = interpret_options scope, options
|
||||||
if OS.windows?
|
if OS.windows?
|
||||||
return nil unless ENV['ProgramData']
|
return nil unless ENV['ProgramData']
|
||||||
prefix = File.join(ENV['ProgramData'], 'Google/Auth')
|
prefix = File.join(ENV['ProgramData'], 'Google/Auth')
|
||||||
|
@ -127,7 +158,7 @@ module Google
|
||||||
path = File.join(prefix, CREDENTIALS_FILE_NAME)
|
path = File.join(prefix, CREDENTIALS_FILE_NAME)
|
||||||
return nil unless File.exist?(path)
|
return nil unless File.exist?(path)
|
||||||
File.open(path) do |f|
|
File.open(path) do |f|
|
||||||
return make_creds(json_key_io: f, scope: scope)
|
return make_creds(options.merge(json_key_io: f))
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
|
raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
|
||||||
|
@ -152,6 +183,18 @@ module Google
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def interpret_options(scope, options)
|
||||||
|
if scope.is_a? Hash
|
||||||
|
options = scope
|
||||||
|
scope = nil
|
||||||
|
end
|
||||||
|
if scope && !options[:scope]
|
||||||
|
options.merge(scope: scope)
|
||||||
|
else
|
||||||
|
options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def service_account_env_vars?
|
def service_account_env_vars?
|
||||||
([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty?
|
([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty?
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,16 +46,16 @@ module Google
|
||||||
# override CredentialsLoader#make_creds to use the class determined by
|
# override CredentialsLoader#make_creds to use the class determined by
|
||||||
# loading the json.
|
# loading the json.
|
||||||
def self.make_creds(options = {})
|
def self.make_creds(options = {})
|
||||||
json_key_io, scope = options.values_at(:json_key_io, :scope)
|
json_key_io = options[:json_key_io]
|
||||||
if json_key_io
|
if json_key_io
|
||||||
json_key, clz = determine_creds_class(json_key_io)
|
json_key, clz = determine_creds_class(json_key_io)
|
||||||
warn_if_cloud_sdk_credentials json_key['client_id']
|
warn_if_cloud_sdk_credentials json_key['client_id']
|
||||||
clz.make_creds(json_key_io: StringIO.new(MultiJson.dump(json_key)),
|
io = StringIO.new(MultiJson.dump(json_key))
|
||||||
scope: scope)
|
clz.make_creds(options.merge(json_key_io: io))
|
||||||
else
|
else
|
||||||
warn_if_cloud_sdk_credentials ENV[CredentialsLoader::CLIENT_ID_VAR]
|
warn_if_cloud_sdk_credentials ENV[CredentialsLoader::CLIENT_ID_VAR]
|
||||||
clz = read_creds
|
clz = read_creds
|
||||||
clz.make_creds(scope: scope)
|
clz.make_creds(options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ module Google
|
||||||
issuer: client_email,
|
issuer: client_email,
|
||||||
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
||||||
project_id: project_id)
|
project_id: project_id)
|
||||||
|
.configure_connection(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Handles certain escape sequences that sometimes appear in input.
|
# Handles certain escape sequences that sometimes appear in input.
|
||||||
|
|
|
@ -38,6 +38,12 @@ module Signet
|
||||||
# This reopens Client to add #apply and #apply! methods which update a
|
# This reopens Client to add #apply and #apply! methods which update a
|
||||||
# hash with the fetched authentication token.
|
# hash with the fetched authentication token.
|
||||||
class Client
|
class Client
|
||||||
|
def configure_connection(options)
|
||||||
|
@connection_info =
|
||||||
|
options[:connection_builder] || options[:default_connection]
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
# Updates a_hash updated with the authentication token
|
# Updates a_hash updated with the authentication token
|
||||||
def apply!(a_hash, opts = {})
|
def apply!(a_hash, opts = {})
|
||||||
# fetch the access token there is currently not one, or if the client
|
# fetch the access token there is currently not one, or if the client
|
||||||
|
@ -66,6 +72,10 @@ module Signet
|
||||||
|
|
||||||
alias orig_fetch_access_token! fetch_access_token!
|
alias orig_fetch_access_token! fetch_access_token!
|
||||||
def fetch_access_token!(options = {})
|
def fetch_access_token!(options = {})
|
||||||
|
unless options[:connection]
|
||||||
|
connection = build_default_connection
|
||||||
|
options = options.merge(connection: connection) if connection
|
||||||
|
end
|
||||||
info = orig_fetch_access_token!(options)
|
info = orig_fetch_access_token!(options)
|
||||||
notify_refresh_listeners
|
notify_refresh_listeners
|
||||||
info
|
info
|
||||||
|
@ -78,6 +88,16 @@ module Signet
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_default_connection
|
||||||
|
if !defined?(@connection_info)
|
||||||
|
nil
|
||||||
|
elsif @connection_info.respond_to? :call
|
||||||
|
@connection_info.call
|
||||||
|
else
|
||||||
|
@connection_info
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def retry_with_error(max_retry_count = 5)
|
def retry_with_error(max_retry_count = 5)
|
||||||
retry_count = 0
|
retry_count = 0
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ module Google
|
||||||
refresh_token: user_creds['refresh_token'],
|
refresh_token: user_creds['refresh_token'],
|
||||||
project_id: user_creds['project_id'],
|
project_id: user_creds['project_id'],
|
||||||
scope: scope)
|
scope: scope)
|
||||||
|
.configure_connection(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reads the client_id, client_secret and refresh_token fields from the
|
# Reads the client_id, client_secret and refresh_token fields from the
|
||||||
|
|
|
@ -47,6 +47,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
|
|
||||||
it 'uses a default scope' do
|
it 'uses a default scope' do
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
|
@ -64,6 +65,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
|
|
||||||
it 'uses a custom scope' do
|
it 'uses a custom scope' do
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
|
@ -96,6 +98,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
allow(::File).to receive(:file?).with(TEST_PATH_ENV_VAL) { false }
|
allow(::File).to receive(:file?).with(TEST_PATH_ENV_VAL) { false }
|
||||||
|
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
|
@ -129,6 +132,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
allow(::File).to receive(:read).with('/unknown/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
|
allow(::File).to receive(:read).with('/unknown/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
|
||||||
|
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
|
@ -161,6 +165,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
allow(::ENV).to receive(:[]).with('JSON_ENV_TEST') { JSON.generate(default_keyfile_hash) }
|
allow(::ENV).to receive(:[]).with('JSON_ENV_TEST') { JSON.generate(default_keyfile_hash) }
|
||||||
|
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
|
@ -194,6 +199,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
allow(::File).to receive(:read).with('~/default/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
|
allow(::File).to receive(:read).with('~/default/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
|
||||||
|
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
|
@ -226,6 +232,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
allow(::File).to receive(:file?).with('~/default/path/to/file.txt') { false }
|
allow(::File).to receive(:file?).with('~/default/path/to/file.txt') { false }
|
||||||
|
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(mocked_signet).to receive(:client_id)
|
allow(mocked_signet).to receive(:client_id)
|
||||||
allow(Google::Auth).to receive(:get_application_default) do |scope|
|
allow(Google::Auth).to receive(:get_application_default) do |scope|
|
||||||
|
@ -253,6 +260,7 @@ describe Google::Auth::Credentials, :private do
|
||||||
|
|
||||||
it 'warns when cloud sdk credentials are used' do
|
it 'warns when cloud sdk credentials are used' do
|
||||||
mocked_signet = double('Signet::OAuth2::Client')
|
mocked_signet = double('Signet::OAuth2::Client')
|
||||||
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
||||||
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
||||||
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
||||||
mocked_signet
|
mocked_signet
|
||||||
|
|
|
@ -100,6 +100,19 @@ describe '#get_application_default' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "propagates default_connection option" do
|
||||||
|
Dir.mktmpdir do |dir|
|
||||||
|
key_path = File.join(dir, 'my_cert_file')
|
||||||
|
FileUtils.mkdir_p(File.dirname(key_path))
|
||||||
|
File.write(key_path, cred_json_text)
|
||||||
|
ENV[@var_name] = key_path
|
||||||
|
connection = Faraday.new(headers: {"User-Agent" => "hello"})
|
||||||
|
opts = options.merge(default_connection: connection)
|
||||||
|
creds = Google::Auth.get_application_default(@scope, opts)
|
||||||
|
expect(creds.build_default_connection).to be connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'succeeds with default file without GOOGLE_APPLICATION_CREDENTIALS' do
|
it 'succeeds with default file without GOOGLE_APPLICATION_CREDENTIALS' do
|
||||||
ENV.delete(@var_name) unless ENV[@var_name].nil?
|
ENV.delete(@var_name) unless ENV[@var_name].nil?
|
||||||
Dir.mktmpdir do |dir|
|
Dir.mktmpdir do |dir|
|
||||||
|
|
|
@ -229,6 +229,14 @@ describe Google::Auth::ServiceAccountCredentials do
|
||||||
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
|
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
|
||||||
expect(@clz.from_env(@scope)).to_not be_nil
|
expect(@clz.from_env(@scope)).to_not be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "propagates default_connection option" do
|
||||||
|
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
|
||||||
|
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
|
||||||
|
connection = Faraday.new(headers: {"User-Agent" => "hello"})
|
||||||
|
creds = @clz.from_env(@scope, default_connection: connection)
|
||||||
|
expect(creds.build_default_connection).to be connection
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#from_well_known_path' do
|
describe '#from_well_known_path' do
|
||||||
|
@ -274,6 +282,20 @@ describe Google::Auth::ServiceAccountCredentials do
|
||||||
expect(credentials.project_id).to eq(cred_json[:project_id])
|
expect(credentials.project_id).to eq(cred_json[:project_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "propagates default_connection option" do
|
||||||
|
Dir.mktmpdir do |dir|
|
||||||
|
key_path = File.join(dir, '.config', @known_path)
|
||||||
|
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
|
||||||
|
FileUtils.mkdir_p(File.dirname(key_path))
|
||||||
|
File.write(key_path, cred_json_text)
|
||||||
|
ENV['HOME'] = dir
|
||||||
|
ENV['APPDATA'] = dir
|
||||||
|
connection = Faraday.new(headers: {"User-Agent" => "hello"})
|
||||||
|
creds = @clz.from_well_known_path(@scope, default_connection: connection)
|
||||||
|
expect(creds.build_default_connection).to be connection
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#from_system_default_path' do
|
describe '#from_system_default_path' do
|
||||||
|
@ -305,6 +327,18 @@ describe Google::Auth::ServiceAccountCredentials do
|
||||||
File.delete(@path)
|
File.delete(@path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "propagates default_connection option" do
|
||||||
|
FakeFS do
|
||||||
|
ENV['ProgramData'] = '/etc'
|
||||||
|
FileUtils.mkdir_p(File.dirname(@path))
|
||||||
|
File.write(@path, cred_json_text)
|
||||||
|
connection = Faraday.new(headers: {"User-Agent" => "hello"})
|
||||||
|
creds = @clz.from_system_default_path(@scope, default_connection: connection)
|
||||||
|
expect(creds.build_default_connection).to be connection
|
||||||
|
File.delete(@path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -60,14 +60,45 @@ describe Signet::OAuth2::Client do
|
||||||
@key.public_key, true,
|
@key.public_key, true,
|
||||||
algorithm: 'RS256')
|
algorithm: 'RS256')
|
||||||
end
|
end
|
||||||
|
with_params = {body: hash_including(
|
||||||
|
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer")}
|
||||||
|
if opts[:user_agent]
|
||||||
|
with_params[:headers] = {"User-Agent" => opts[:user_agent]}
|
||||||
|
end
|
||||||
stub_request(:post, 'https://oauth2.googleapis.com/token')
|
stub_request(:post, 'https://oauth2.googleapis.com/token')
|
||||||
.with(body: hash_including(
|
.with(with_params, &blk)
|
||||||
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'
|
|
||||||
), &blk)
|
|
||||||
.to_return(body: body,
|
.to_return(body: body,
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { 'Content-Type' => 'application/json' })
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'apply/apply! are OK'
|
it_behaves_like 'apply/apply! are OK'
|
||||||
|
|
||||||
|
describe "#configure_connection" do
|
||||||
|
it "honors default_connection" do
|
||||||
|
token = "1/abcdef1234567890"
|
||||||
|
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/1.0"
|
||||||
|
conn = Faraday.new headers: {"User-Agent" => "RubyRocks/1.0"}
|
||||||
|
@client.configure_connection(default_connection: conn)
|
||||||
|
md = { foo: "bar" }
|
||||||
|
@client.apply!(md)
|
||||||
|
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
|
||||||
|
Faraday.new headers: {"User-Agent" => "RubyRocks/2.0"}
|
||||||
|
end
|
||||||
|
@client.configure_connection(connection_builder: connection_builder)
|
||||||
|
md = { foo: "bar" }
|
||||||
|
@client.apply!(md)
|
||||||
|
want = { foo: "bar", authorization: "Bearer #{token}" }
|
||||||
|
expect(md).to eq(want)
|
||||||
|
expect(stub).to have_been_requested
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue