Credentials: Use methods instead of constants (#212)

Update Credentials to use methods for values that are intended to
be changed by users, replacing constants.
Add Credentials documentation.
This commit is contained in:
Mike Moore 2019-05-07 17:56:17 -06:00 committed by GitHub
parent 63381e4d9c
commit a33a640e61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 559 additions and 172 deletions

View File

@ -7,6 +7,8 @@ AllCops:
- "Rakefile" - "Rakefile"
Metrics/ClassLength: Metrics/ClassLength:
Max: 110 Max: 110
Exclude:
- "lib/googleauth/credentials.rb"
Metrics/ModuleLength: Metrics/ModuleLength:
Max: 110 Max: 110
Metrics/BlockLength: Metrics/BlockLength:

View File

@ -35,26 +35,206 @@ require "googleauth/credentials_loader"
module Google module Google
module Auth module Auth
# This class is intended to be inherited by API-specific classes ##
# which overrides the SCOPE constant. # Credentials is responsible for representing the authentication when connecting to an API. This
# class is also intended to be inherited by API-specific classes.
class Credentials class Credentials
##
# The default token credential URI to be used when none is provided during initialization.
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token".freeze TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token".freeze
##
# The default target audience ID to be used when none is provided during initialization.
AUDIENCE = "https://oauth2.googleapis.com/token".freeze AUDIENCE = "https://oauth2.googleapis.com/token".freeze
SCOPE = [].freeze
PATH_ENV_VARS = [].freeze
JSON_ENV_VARS = [].freeze
DEFAULT_PATHS = [].freeze
##
# The default token credential URI to be used when none is provided during initialization.
# The URI is the authorization server's HTTP endpoint capable of issuing tokens and
# refreshing expired tokens.
#
# @return [String]
#
def self.token_credential_uri
return @token_credential_uri unless @token_credential_uri.nil?
const_get :TOKEN_CREDENTIAL_URI if const_defined? :TOKEN_CREDENTIAL_URI
end
##
# Set the default token credential URI to be used when none is provided during initialization.
#
# @param [String] new_token_credential_uri
# @return [String]
#
def self.token_credential_uri= new_token_credential_uri
@token_credential_uri = new_token_credential_uri
end
##
# The default target audience ID to be used when none is provided during initialization.
# Used only by the assertion grant type.
#
# @return [String]
#
def self.audience
return @audience unless @audience.nil?
const_get :AUDIENCE if const_defined? :AUDIENCE
end
##
# Sets the default target audience ID to be used when none is provided during initialization.
#
# @param [String] new_audience
# @return [String]
#
def self.audience= new_audience
@audience = new_audience
end
##
# The default scope to be used when none is provided during initialization.
# A scope is an access range defined by the authorization server.
# The scope can be a single value or a list of values.
#
# @return [String, Array<String>]
#
def self.scope
return @scope unless @scope.nil?
tmp_scope = []
# Pull in values is the SCOPE constant exists.
tmp_scope << const_get(:SCOPE) if const_defined? :SCOPE
tmp_scope.flatten.uniq
end
##
# Sets the default scope to be used when none is provided during initialization.
#
# @param [String, Array<String>] new_scope
# @return [String, Array<String>]
#
def self.scope= new_scope
new_scope = Array new_scope unless new_scope.nil?
@scope = new_scope
end
##
# The environment variables to search for credentials. Values can either be a file path to the
# credentials file, or the JSON contents of the credentials file.
#
# @return [Array<String>]
#
def self.env_vars
return @env_vars unless @env_vars.nil?
# Pull values when PATH_ENV_VARS or JSON_ENV_VARS constants exists.
tmp_env_vars = []
tmp_env_vars << const_get(:PATH_ENV_VARS) if const_defined? :PATH_ENV_VARS
tmp_env_vars << const_get(:JSON_ENV_VARS) if const_defined? :JSON_ENV_VARS
tmp_env_vars.flatten.uniq
end
##
# Sets the environment variables to search for credentials.
#
# @param [Array<String>] new_env_vars
# @return [Array<String>]
#
def self.env_vars= new_env_vars
new_env_vars = Array new_env_vars unless new_env_vars.nil?
@env_vars = new_env_vars
end
##
# The file paths to search for credentials files.
#
# @return [Array<String>]
#
def self.paths
return @paths unless @paths.nil?
tmp_paths = []
# Pull in values is the DEFAULT_PATHS constant exists.
tmp_paths << const_get(:DEFAULT_PATHS) if const_defined? :DEFAULT_PATHS
tmp_paths.flatten.uniq
end
##
# Set the file paths to search for credentials files.
#
# @param [Array<String>] new_paths
# @return [Array<String>]
#
def self.paths= new_paths
new_paths = Array new_paths unless new_paths.nil?
@paths = new_paths
end
##
# The Signet::OAuth2::Client object the Credentials instance is using.
#
# @return [Signet::OAuth2::Client]
#
attr_accessor :client attr_accessor :client
attr_reader :project_id
# Delegate client methods to the client object. ##
# Identifier for the project the client is authenticating with.
#
# @return [String]
#
attr_reader :project_id
# @private Delegate client methods to the client object.
extend Forwardable extend Forwardable
##
# @!attribute [r] token_credential_uri
# @return [String] The token credential URI. The URI is the authorization server's HTTP
# endpoint capable of issuing tokens and refreshing expired tokens.
#
# @!attribute [r] audience
# @return [String] The target audience ID when issuing assertions. Used only by the
# assertion grant type.
#
# @!attribute [r] scope
# @return [String, Array<String>] The scope for this client. A scope is an access range
# defined by the authorization server. The scope can be a single value or a list of values.
#
# @!attribute [r] issuer
# @return [String] The issuer ID associated with this client.
#
# @!attribute [r] signing_key
# @return [String, OpenSSL::PKey] The signing key associated with this client.
#
# @!attribute [r] updater_proc
# @return [Proc] Returns a reference to the {Signet::OAuth2::Client#apply} method,
# suitable for passing as a closure.
#
def_delegators :@client, def_delegators :@client,
:token_credential_uri, :audience, :token_credential_uri, :audience,
:scope, :issuer, :signing_key, :updater_proc :scope, :issuer, :signing_key, :updater_proc
# rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/AbcSize
##
# Creates a new Credentials instance with the provided auth credentials, and with the default
# values configured on the class.
#
# @param [String, Hash, Signet::OAuth2::Client] keyfile
# The keyfile can be provided as one of the following:
#
# * The path to a JSON keyfile (as a +String+)
# * The contents of a JSON keyfile (as a +Hash+)
# * A +Signet::OAuth2::Client+ object
# @param [Hash] options
# The options for configuring the credentials instance. The following is supported:
#
# * +:scope+ - the scope for the client
# * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
# * +:connection_builder+ - the connection builder to use for the client
# * +:default_connection+ - the default connection to use for the client
#
def initialize keyfile, options = {} def initialize keyfile, options = {}
scope = options[:scope] scope = options[:scope]
verify_keyfile_provided! keyfile verify_keyfile_provided! keyfile
@ -80,10 +260,27 @@ module Google
end end
# rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/AbcSize
# Returns the default credentials checking, in this order, the path env ##
# evironment variables, json environment variables, default paths. If the # Creates a new Credentials instance with auth credentials acquired by searching the
# previously stated locations do not contain keyfile information, # environment variables and paths configured on the class, and with the default values
# this method defaults to use the application default. # configured on the class.
#
# The auth credentials are searched for in the following order:
#
# 1. configured environment variables (see {Credentials.env_vars})
# 2. configured default file paths (see {Credentials.paths})
# 3. application default (see {Google::Auth.get_application_default})
#
# @param [Hash] options
# The options for configuring the credentials instance. The following is supported:
#
# * +:scope+ - the scope for the client
# * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
# * +:connection_builder+ - the connection builder to use for the client
# * +:default_connection+ - the default connection to use for the client
#
# @return [Credentials]
#
def self.default options = {} def self.default options = {}
# First try to find keyfile file or json from environment variables. # First try to find keyfile file or json from environment variables.
client = from_env_vars options client = from_env_vars options
@ -96,8 +293,10 @@ module Google
client client
end end
##
# @private Lookup Credentials from environment variables.
def self.from_env_vars options def self.from_env_vars options
(self::PATH_ENV_VARS + self::JSON_ENV_VARS).each do |env_var| env_vars.each do |env_var|
str = ENV[env_var] str = ENV[env_var]
next if str.nil? next if str.nil?
return new str, options if ::File.file? str return new str, options if ::File.file? str
@ -106,8 +305,10 @@ module Google
nil nil
end end
##
# @private Lookup Credentials from default file paths.
def self.from_default_paths options def self.from_default_paths options
self::DEFAULT_PATHS paths
.select { |p| ::File.file? p } .select { |p| ::File.file? p }
.each do |file| .each do |file|
return new file, options return new file, options
@ -115,11 +316,14 @@ module Google
nil nil
end end
##
# @private Lookup Credentials using Google::Auth.get_application_default.
def self.from_application_default options def self.from_application_default options
scope = 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, options new client, options
end end
private_class_method :from_env_vars, private_class_method :from_env_vars,
:from_default_paths, :from_default_paths,
:from_application_default :from_application_default
@ -152,9 +356,9 @@ module Google
def client_options options def client_options options
# Keyfile options have higher priority over constructor defaults # Keyfile options have higher priority over constructor defaults
options["token_credential_uri"] ||= self.class::TOKEN_CREDENTIAL_URI options["token_credential_uri"] ||= self.class.token_credential_uri
options["audience"] ||= self.class::AUDIENCE options["audience"] ||= self.class.audience
options["scope"] ||= self.class::SCOPE options["scope"] ||= self.class.scope
# client options for initializing signet client # client options for initializing signet client
{ token_credential_uri: options["token_credential_uri"], { token_credential_uri: options["token_credential_uri"],

View File

@ -81,186 +81,367 @@ describe Google::Auth::Credentials, :private do
Google::Auth::Credentials.new default_keyfile_hash, scope: "http://example.com/scope" Google::Auth::Credentials.new default_keyfile_hash, scope: "http://example.com/scope"
end end
it 'can be subclassed to pass in other env paths' do describe "using CONSTANTS" do
TEST_PATH_ENV_VAR = 'TEST_PATH'.freeze it "can be subclassed to pass in other env paths" do
TEST_PATH_ENV_VAL = '/unknown/path/to/file.txt'.freeze test_path_env_val = "/unknown/path/to/file.txt".freeze
TEST_JSON_ENV_VAR = 'TEST_JSON_VARS'.freeze test_json_env_val = JSON.generate default_keyfile_hash
TEST_JSON_ENV_VAL = JSON.generate(default_keyfile_hash)
ENV[TEST_PATH_ENV_VAR] = TEST_PATH_ENV_VAL ENV["TEST_PATH"] = test_path_env_val
ENV[TEST_JSON_ENV_VAR] = TEST_JSON_ENV_VAL ENV["TEST_JSON_VARS"] = test_json_env_val
class TestCredentials < Google::Auth::Credentials class TestCredentials1 < Google::Auth::Credentials
SCOPE = "http://example.com/scope".freeze TOKEN_CREDENTIAL_URI = "https://example.com/token".freeze
PATH_ENV_VARS = [TEST_PATH_ENV_VAR].freeze AUDIENCE = "https://example.com/audience".freeze
JSON_ENV_VARS = [TEST_JSON_ENV_VAR].freeze SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = ["TEST_PATH"].freeze
JSON_ENV_VARS = ["TEST_JSON_VARS"].freeze
end
allow(::File).to receive(:file?).with(test_path_env_val) { false }
allow(::File).to receive(:file?).with(test_json_env_val) { false }
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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://example.com/token")
expect(options[:audience]).to eq("https://example.com/audience")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials1.default
expect(creds).to be_a_kind_of(TestCredentials1)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
allow(::File).to receive(:file?).with(TEST_PATH_ENV_VAL) { false } it "subclasses can use PATH_ENV_VARS to get keyfile path" do
allow(::File).to receive(:file?).with(TEST_JSON_ENV_VAL) { false } class TestCredentials2 < Google::Auth::Credentials
SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = %w[PATH_ENV_DUMMY PATH_ENV_TEST].freeze
JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
end
mocked_signet = double "Signet::OAuth2::Client" allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet) allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true) allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
allow(mocked_signet).to receive(:client_id) allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
allow(Signet::OAuth2::Client).to receive(:new) do |options| allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet 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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials2.default
expect(creds).to be_a_kind_of(TestCredentials2)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
creds = TestCredentials.default it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
expect(creds).to be_a_kind_of(TestCredentials) test_json_env_val = JSON.generate default_keyfile_hash
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"]) class TestCredentials3 < Google::Auth::Credentials
SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
JSON_ENV_VARS = %w[JSON_ENV_DUMMY JSON_ENV_TEST].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
end
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(::File).to receive(:file?).with(test_json_env_val) { false }
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials3.default
expect(creds).to be_a_kind_of(TestCredentials3)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it "subclasses can use DEFAULT_PATHS to get keyfile path" do
class TestCredentials4 < Google::Auth::Credentials
SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
end
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials4.default
expect(creds).to be_a_kind_of(TestCredentials4)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it "subclasses that find no matches default to Google::Auth.get_application_default" do
class TestCredentials5 < Google::Auth::Credentials
SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
end
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
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(:client_id)
allow(Google::Auth).to receive(:get_application_default) do |scope|
expect(scope).to eq([TestCredentials5::SCOPE])
# This should really be a Signet::OAuth2::Client object,
# but mocking is making that difficult, so return a valid hash instead.
default_keyfile_hash
end
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials5.default
expect(creds).to be_a_kind_of(TestCredentials5)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
end end
it "subclasses can use PATH_ENV_VARS to get keyfile path" do describe "using class methods" do
class TestCredentials < Google::Auth::Credentials it "can be subclassed to pass in other env paths" do
SCOPE = "http://example.com/scope".freeze test_path_env_val = "/unknown/path/to/file.txt".freeze
PATH_ENV_VARS = %w[PATH_ENV_DUMMY PATH_ENV_TEST].freeze test_json_env_val = JSON.generate default_keyfile_hash
JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze ENV["TEST_PATH"] = test_path_env_val
ENV["TEST_JSON_VARS"] = test_json_env_val
class TestCredentials11 < Google::Auth::Credentials
self.token_credential_uri = "https://example.com/token"
self.audience = "https://example.com/audience"
self.scope = "http://example.com/scope"
self.env_vars = ["TEST_PATH", "TEST_JSON_VARS"]
end
allow(::File).to receive(:file?).with(test_path_env_val) { false }
allow(::File).to receive(:file?).with(test_json_env_val) { false }
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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://example.com/token")
expect(options[:audience]).to eq("https://example.com/audience")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials11.default
expect(creds).to be_a_kind_of(TestCredentials11)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" } it "subclasses can use PATH_ENV_VARS to get keyfile path" do
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false } class TestCredentials12 < Google::Auth::Credentials
allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" } self.scope = "http://example.com/scope"
allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true } self.env_vars = %w[PATH_ENV_DUMMY PATH_ENV_TEST JSON_ENV_DUMMY]
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash } self.paths = ["~/default/path/to/file.txt"]
end
mocked_signet = double "Signet::OAuth2::Client" allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet) allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true) allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
allow(mocked_signet).to receive(:client_id) allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
allow(Signet::OAuth2::Client).to receive(:new) do |options| allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet 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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials12.default
expect(creds).to be_a_kind_of(TestCredentials12)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
creds = TestCredentials.default it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
expect(creds).to be_a_kind_of(TestCredentials) test_json_env_val = JSON.generate default_keyfile_hash
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it 'subclasses can use JSON_ENV_VARS to get keyfile contents' do class TestCredentials13 < Google::Auth::Credentials
TEST_JSON_ENV_VAL = JSON.generate(default_keyfile_hash) self.scope = "http://example.com/scope"
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY JSON_ENV_TEST]
self.paths = ["~/default/path/to/file.txt"]
end
class TestCredentials < Google::Auth::Credentials allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
SCOPE = "http://example.com/scope".freeze allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze allow(::File).to receive(:file?).with(test_json_env_val) { false }
JSON_ENV_VARS = %w[JSON_ENV_DUMMY JSON_ENV_TEST].freeze allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials13.default
expect(creds).to be_a_kind_of(TestCredentials13)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
allow(::ENV).to receive(:[]).with('PATH_ENV_DUMMY') { '/fake/path/to/file.txt' } it "subclasses can use DEFAULT_PATHS to get keyfile path" do
allow(::File).to receive(:file?).with('/fake/path/to/file.txt') { false } class TestCredentials14 < Google::Auth::Credentials
allow(::File).to receive(:file?).with(TEST_JSON_ENV_VAL) { false } self.scope = "http://example.com/scope"
allow(::ENV).to receive(:[]).with('JSON_ENV_DUMMY') { nil } self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
allow(::ENV).to receive(:[]).with('JSON_ENV_TEST') { TEST_JSON_ENV_VAL } self.paths = ["~/default/path/to/file.txt"]
end
mocked_signet = double "Signet::OAuth2::Client" allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet) allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true) allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
allow(mocked_signet).to receive(:client_id) allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
allow(Signet::OAuth2::Client).to receive(:new) do |options| allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet 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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials14.default
expect(creds).to be_a_kind_of(TestCredentials14)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
creds = TestCredentials.default it "subclasses that find no matches default to Google::Auth.get_application_default" do
expect(creds).to be_a_kind_of(TestCredentials) class TestCredentials15 < Google::Auth::Credentials
expect(creds.client).to eq(mocked_signet) self.scope = "http://example.com/scope"
expect(creds.project_id).to eq(default_keyfile_hash["project_id"]) self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
end self.paths = ["~/default/path/to/file.txt"]
end
it "subclasses can use DEFAULT_PATHS to get keyfile path" do allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
class TestCredentials < Google::Auth::Credentials allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
SCOPE = "http://example.com/scope".freeze allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze 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(:client_id)
allow(Google::Auth).to receive(:get_application_default) do |scope|
expect(scope).to eq(TestCredentials15.scope)
# This should really be a Signet::OAuth2::Client object,
# but mocking is making that difficult, so return a valid hash instead.
default_keyfile_hash
end
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials15.default
expect(creds).to be_a_kind_of(TestCredentials15)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
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(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it "subclasses that find no matches default to Google::Auth.get_application_default" do
class TestCredentials < Google::Auth::Credentials
SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
end
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
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(:client_id)
allow(Google::Auth).to receive(:get_application_default) do |scope|
expect(scope).to eq(TestCredentials::SCOPE)
# This should really be a Signet::OAuth2::Client object,
# but mocking is making that difficult, so return a valid hash instead.
default_keyfile_hash
end
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
expect(options[:scope]).to eq(["http://example.com/scope"])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end end
it "warns when cloud sdk credentials are used" do it "warns when cloud sdk credentials are used" do