add google-style and refactor (#203)

This commit is contained in:
Graham Paye 2019-03-15 12:34:54 -07:00 committed by GitHub
parent 1c64927249
commit bf822910fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1315 additions and 1349 deletions

View File

@ -1,30 +1,11 @@
inherit_gem:
google-style: google-style.yml
AllCops:
Exclude:
- "spec/**/*"
- "Rakefile"
Metrics/AbcSize:
Max: 25
Metrics/BlockLength:
Exclude:
- "googleauth.gemspec"
Metrics/CyclomaticComplexity:
Max: 8
Metrics/MethodLength:
Max: 20
Metrics/ModuleLength:
Max: 150
Metrics/ClassLength:
Enabled: false
Layout/IndentHeredoc:
Enabled: false
Style/FormatString:
Enabled: false
Style/GuardClause:
Enabled: false
Style/PercentLiteralDelimiters: # Contradicting rule
Enabled: false
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/SymbolArray: # Undefined syntax in Ruby 1.9.3
Enabled: false
Max: 110
Metrics/ModuleLength:
Max: 105

View File

@ -8,12 +8,12 @@ group :development do
gem "coveralls", "~> 0.7"
gem "fakefs", "~> 0.6"
gem "fakeredis", "~> 0.5"
gem "google-style", "~> 0.2"
gem "logging", "~> 2.0"
gem "rack-test", "~> 0.6"
gem "rake", "~> 10.0"
gem "redis", "~> 3.2"
gem "rspec", "~> 3.0"
gem "rubocop", ">= 0.41", "< 0.50"
gem "simplecov", "~> 0.9"
gem "sinatra"
gem "webmock", "~> 1.21"

View File

@ -1,20 +1,20 @@
# -*- ruby -*-
require 'rspec/core/rake_task'
require 'rubocop/rake_task'
require 'bundler/gem_tasks'
require "rspec/core/rake_task"
require "rubocop/rake_task"
require "bundler/gem_tasks"
desc 'Run Rubocop to check for style violations'
desc "Run Rubocop to check for style violations"
RuboCop::RakeTask.new
desc 'Run rake task'
RSpec::Core::RakeTask.new(:spec)
desc "Run rake task"
RSpec::Core::RakeTask.new :spec
desc 'Does rubocop lint and runs the specs'
task all: [:rubocop, :spec]
desc "Does rubocop lint and runs the specs"
task all: %i[rubocop spec]
task default: :all
task :release, :tag do |t, args|
task :release, :tag do |_t, args|
tag = args[:tag]
raise "You must provide a tag to release." if tag.nil?
@ -28,9 +28,11 @@ task :release, :tag do |t, args|
api_token = ENV["RUBYGEMS_API_TOKEN"]
require "gems"
::Gems.configure do |config|
config.key = api_token
end if api_token
if api_token
::Gems.configure do |config|
config.key = api_token
end
end
Bundler.with_clean_env do
sh "rm -rf pkg"
@ -41,9 +43,9 @@ task :release, :tag do |t, args|
path_to_be_pushed = "pkg/#{version}.gem"
if File.file? path_to_be_pushed
begin
::Gems.push(File.new path_to_be_pushed)
::Gems.push File.new(path_to_be_pushed)
puts "Successfully built and pushed googleauth for version #{version}"
rescue => e
rescue StandardError => e
puts "Error while releasing googleauth version #{version}: #{e.message}"
end
else
@ -53,10 +55,10 @@ end
namespace :kokoro do
task :load_env_vars do
service_account = "#{ENV["KOKORO_GFILE_DIR"]}/service-account.json"
service_account = "#{ENV['KOKORO_GFILE_DIR']}/service-account.json"
ENV["GOOGLE_APPLICATION_CREDENTIALS"] = service_account
filename = "#{ENV["KOKORO_GFILE_DIR"]}/env_vars.json"
env_vars = JSON.parse(File.read(filename))
filename = "#{ENV['KOKORO_GFILE_DIR']}/env_vars.json"
env_vars = JSON.parse File.read(filename)
env_vars.each { |k, v| ENV[k] = v }
end
@ -64,8 +66,8 @@ namespace :kokoro do
version = "0.1.0"
Bundler.with_clean_env do
version = `bundle exec gem list`
.split("\n").select { |line| line.include? "googleauth" }
.first.split("(").last.split(")").first || "0.1.0"
.split("\n").select { |line| line.include? "googleauth" }
.first.split("(").last.split(")").first || "0.1.0"
end
Rake::Task["kokoro:load_env_vars"].invoke
Rake::Task["release"].invoke "v/#{version}"
@ -79,4 +81,4 @@ def header str, token = "#"
puts "#{token * 3} #{str} #{token * 3}"
puts token * line_length
puts ""
end
end

View File

@ -1,7 +1,7 @@
# -*- ruby -*-
# encoding: utf-8
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
$LOAD_PATH.push File.expand_path("lib", __dir__)
require "googleauth/version"
Gem::Specification.new do |gem|
@ -18,10 +18,10 @@ Gem::Specification.new do |gem|
https://developers.google.com/accounts/docs/application-default-credentials
DESCRIPTION
gem.files = `git ls-files`.split("\n")
gem.test_files = `git ls-files -- spec/*`.split("\n")
gem.files = `git ls-files`.split "\n"
gem.test_files = `git ls-files -- spec/*`.split "\n"
gem.executables = `git ls-files -- bin/*.rb`.split("\n").map do |f|
File.basename(f)
File.basename f
end
gem.require_paths = ["lib"]
gem.platform = Gem::Platform::RUBY

View File

@ -34,11 +34,13 @@ module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
NOT_FOUND_ERROR = <<ERROR_MESSAGE.freeze
Could not load the default credentials. Browse to
https://developers.google.com/accounts/docs/application-default-credentials
for more information
ERROR_MESSAGE
NOT_FOUND_ERROR = <<~ERROR_MESSAGE.freeze
Could not load the default credentials. Browse to
https://developers.google.com/accounts/docs/application-default-credentials
for more information
ERROR_MESSAGE
module_function
# Obtains the default credentials implementation to use in this
# environment.
@ -63,19 +65,17 @@ ERROR_MESSAGE
# 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, options) ||
DefaultCredentials.from_well_known_path(scope, options) ||
DefaultCredentials.from_system_default_path(scope, options)
return creds unless creds.nil?
unless GCECredentials.on_gce?(options)
unless GCECredentials.on_gce? options
# Clear cache of the result of GCECredentials.on_gce?
GCECredentials.unmemoize_all
raise NOT_FOUND_ERROR
end
GCECredentials.new
end
module_function :get_application_default
end
end

View File

@ -63,7 +63,7 @@ module Google
# @note Direction instantion is discouraged to avoid embedding IDs
# & secrets in source. See {#from_file} to load from
# `client_secrets.json` files.
def initialize(id, secret)
def initialize id, secret
CredentialsLoader.warn_if_cloud_sdk_credentials id
raise "Client id can not be nil" if id.nil?
raise "Client secret can not be nil" if secret.nil?
@ -77,12 +77,12 @@ module Google
# @param [String, File] file
# Path of file to read from
# @return [Google::Auth::ClientID]
def self.from_file(file)
def self.from_file file
raise "File can not be nil." if file.nil?
File.open(file.to_s) do |f|
File.open file.to_s do |f|
json = f.read
config = MultiJson.load json
from_hash(config)
from_hash config
end
end
@ -93,11 +93,11 @@ module Google
# @param [hash] config
# Parsed contents of the JSON file
# @return [Google::Auth::ClientID]
def self.from_hash(config)
def self.from_hash config
raise "Hash can not be nil." if config.nil?
raw_detail = config[INSTALLED_APP] || config[WEB_APP]
raise MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
ClientId.new(raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET])
ClientId.new raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET]
end
end
end

View File

@ -35,16 +35,16 @@ module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
NO_METADATA_SERVER_ERROR = <<ERROR.freeze
Error code 404 trying to get security access token
from Compute Engine metadata for the default service account. This
may be because the virtual machine instance does not have permission
scopes specified.
ERROR
UNEXPECTED_ERROR_SUFFIX = <<ERROR.freeze
trying to get security access token from Compute Engine metadata for
the default service account
ERROR
NO_METADATA_SERVER_ERROR = <<~ERROR.freeze
Error code 404 trying to get security access token
from Compute Engine metadata for the default service account. This
may be because the virtual machine instance does not have permission
scopes specified.
ERROR
UNEXPECTED_ERROR_SUFFIX = <<~ERROR.freeze
trying to get security access token from Compute Engine metadata for
the default service account
ERROR
# Extends Signet::OAuth2::Client so that the auth token is obtained from
# the GCE metadata server.
@ -60,9 +60,9 @@ ERROR
# Detect if this appear to be a GCE instance, by checking if metadata
# is available
def on_gce?(options = {})
def on_gce? options = {}
c = options[:connection] || Faraday.default_connection
resp = c.get(COMPUTE_CHECK_URI) do |req|
resp = c.get COMPUTE_CHECK_URI do |req|
# Comment from: oauth2client/client.py
#
# Note: the explicit `timeout` below is a workaround. The underlying
@ -74,10 +74,10 @@ ERROR
req.options.timeout = 0.1
end
return false unless resp.status == 200
return false unless resp.headers.key?("Metadata-Flavor")
return resp.headers["Metadata-Flavor"] == "Google"
return false unless resp.headers.key? "Metadata-Flavor"
resp.headers["Metadata-Flavor"] == "Google"
rescue Faraday::TimeoutError, Faraday::ConnectionFailed
return false
false
end
memoize :on_gce?
@ -85,21 +85,21 @@ ERROR
# Overrides the super class method to change how access tokens are
# fetched.
def fetch_access_token(options = {})
def fetch_access_token options = {}
c = options[:connection] || Faraday.default_connection
retry_with_error do
headers = { "Metadata-Flavor" => "Google" }
resp = c.get(COMPUTE_AUTH_TOKEN_URI, nil, headers)
resp = c.get COMPUTE_AUTH_TOKEN_URI, nil, headers
case resp.status
when 200
Signet::OAuth2.parse_credentials(resp.body,
resp.headers["content-type"])
when 404
raise(Signet::AuthorizationError, NO_METADATA_SERVER_ERROR)
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
else
msg = "Unexpected error code #{resp.status}" \
"#{UNEXPECTED_ERROR_SUFFIX}"
raise(Signet::AuthorizationError, msg)
raise Signet::AuthorizationError, msg
end
end
end

View File

@ -27,8 +27,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, MethodLength
require "forwardable"
require "json"
require "signet/oauth_2/client"
@ -56,7 +54,8 @@ module Google
:token_credential_uri, :audience,
:scope, :issuer, :signing_key, :updater_proc
def initialize(keyfile, options = {})
# rubocop:disable Metrics/AbcSize
def initialize keyfile, options = {}
scope = options[:scope]
verify_keyfile_provided! keyfile
@project_id = options["project_id"] || options["project"]
@ -79,12 +78,13 @@ module Google
@project_id ||= CredentialsLoader.load_gcloud_project_id
@client.fetch_access_token!
end
# rubocop:enable Metrics/AbcSize
# Returns the default credentials checking, in this order, the path env
# evironment variables, json environment variables, default paths. If the
# previously stated locations do not contain keyfile information,
# this method defaults to use the application default.
def self.default(options = {})
def self.default options = {}
# First try to find keyfile file from environment variables.
client = from_path_vars options
@ -99,7 +99,7 @@ module Google
client
end
def self.from_path_vars(options)
def self.from_path_vars options
self::PATH_ENV_VARS
.map { |v| ENV[v] }
.compact
@ -110,23 +110,21 @@ module Google
nil
end
def self.from_json_vars(options)
def self.from_json_vars options
json = lambda do |v|
unless ENV[v].nil?
begin
JSON.parse ENV[v]
rescue
rescue StandardError
nil
end
end
end
self::JSON_ENV_VARS.map(&json).compact.each do |hash|
return new hash, options
end
self::JSON_ENV_VARS.map(&json).compact.each { |hash| return new hash, options }
nil
end
def self.from_default_paths(options)
def self.from_default_paths options
self::DEFAULT_PATHS
.select { |p| ::File.file? p }
.each do |file|
@ -135,7 +133,7 @@ module Google
nil
end
def self.from_application_default(options)
def self.from_application_default options
scope = options[:scope] || self::SCOPE
client = Google::Auth.get_application_default scope
new client, options
@ -148,30 +146,30 @@ module Google
protected
# Verify that the keyfile argument is provided.
def verify_keyfile_provided!(keyfile)
def verify_keyfile_provided! keyfile
return unless keyfile.nil?
raise "The keyfile passed to Google::Auth::Credentials.new was nil."
end
# Verify that the keyfile argument is a file.
def verify_keyfile_exists!(keyfile)
def verify_keyfile_exists! keyfile
exists = ::File.file? keyfile
raise "The keyfile '#{keyfile}' is not a valid file." unless exists
end
# Initializes the Signet client.
def init_client(keyfile, connection_options = {})
def init_client keyfile, connection_options = {}
client_opts = client_options keyfile
Signet::OAuth2::Client.new(client_opts)
.configure_connection(connection_options)
end
# returns a new Hash with string keys instead of symbol keys.
def stringify_hash_keys(hash)
def stringify_hash_keys hash
Hash[hash.map { |k, v| [k.to_s, v] }]
end
def client_options(options)
def client_options options
# Keyfile options have higher priority over constructor defaults
options["token_credential_uri"] ||= self.class::TOKEN_CREDENTIAL_URI
options["audience"] ||= self.class::AUDIENCE
@ -179,10 +177,10 @@ module Google
# client options for initializing signet client
{ token_credential_uri: options["token_credential_uri"],
audience: options["audience"],
scope: Array(options["scope"]),
issuer: options["client_email"],
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
audience: options["audience"],
scope: Array(options["scope"]),
issuer: options["client_email"],
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
end
end
end

View File

@ -75,11 +75,9 @@ module Google
#
# By default, it calls #new on the current class, but this behaviour can
# be modified, allowing different instances to be created.
def make_creds(*args)
def make_creds *args
creds = new(*args)
if creds.respond_to?(:configure_connection) && args.size == 1
creds = creds.configure_connection(args[0])
end
creds = creds.configure_connection args[0] if creds.respond_to?(:configure_connection) && args.size == 1
creds
end
@ -95,16 +93,16 @@ module Google
# 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 = {})
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]
raise "file #{path} does not exist" unless File.exist?(path)
File.open(path) do |f|
return make_creds(options.merge(json_key_io: f))
raise "file #{path} does not exist" unless File.exist? path
File.open path do |f|
return make_creds options.merge(json_key_io: f)
end
elsif service_account_env_vars? || authorized_user_env_vars?
return make_creds(options)
return make_creds options
end
rescue StandardError => e
raise "#{NOT_FOUND_ERROR}: #{e}"
@ -121,16 +119,16 @@ module Google
# 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 = {})
def from_well_known_path scope = nil, options = {}
options = interpret_options scope, options
home_var = OS.windows? ? "APPDATA" : "HOME"
base = WELL_KNOWN_PATH
root = ENV[home_var].nil? ? "" : ENV[home_var]
base = File.join(".config", base) unless OS.windows?
path = File.join(root, base)
return nil unless File.exist?(path)
File.open(path) do |f|
return make_creds(options.merge(json_key_io: f))
base = File.join ".config", base unless OS.windows?
path = File.join root, base
return nil unless File.exist? path
File.open path do |f|
return make_creds options.merge(json_key_io: f)
end
rescue StandardError => e
raise "#{WELL_KNOWN_ERROR}: #{e}"
@ -147,28 +145,29 @@ module Google
# 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 = {})
def from_system_default_path scope = nil, options = {}
options = interpret_options scope, options
if OS.windows?
return nil unless ENV["ProgramData"]
prefix = File.join(ENV["ProgramData"], "Google/Auth")
prefix = File.join ENV["ProgramData"], "Google/Auth"
else
prefix = "/etc/google/auth/"
end
path = File.join(prefix, CREDENTIALS_FILE_NAME)
return nil unless File.exist?(path)
File.open(path) do |f|
return make_creds(options.merge(json_key_io: f))
path = File.join prefix, CREDENTIALS_FILE_NAME
return nil unless File.exist? path
File.open path do |f|
return make_creds options.merge(json_key_io: f)
end
rescue StandardError => e
raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
end
module_function
# Issues warning if cloud sdk client id is used
def warn_if_cloud_sdk_credentials(client_id)
def warn_if_cloud_sdk_credentials client_id
warn CLOUD_SDK_CREDENTIALS_WARNING if client_id == CLOUD_SDK_CLIENT_ID
end
module_function :warn_if_cloud_sdk_credentials
# Finds project_id from gcloud CLI configuration
def load_gcloud_project_id
@ -177,23 +176,19 @@ module Google
gcloud_json = IO.popen("#{gcloud} #{GCLOUD_CONFIG_COMMAND}", &:read)
config = MultiJson.load gcloud_json
config["configuration"]["properties"]["core"]["project"]
rescue
rescue StandardError
nil
end
module_function :load_gcloud_project_id
private
def interpret_options(scope, options)
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
return options.merge scope: scope if scope && !options[:scope]
options
end
def service_account_env_vars?
@ -201,8 +196,7 @@ module Google
end
def authorized_user_env_vars?
([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] -
ENV.keys).empty?
([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] - ENV.keys).empty?
end
end
end

View File

@ -45,17 +45,17 @@ module Google
# override CredentialsLoader#make_creds to use the class determined by
# loading the json.
def self.make_creds(options = {})
def self.make_creds options = {}
json_key_io = options[: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"]
io = StringIO.new(MultiJson.dump(json_key))
clz.make_creds(options.merge(json_key_io: io))
io = StringIO.new MultiJson.dump(json_key)
clz.make_creds options.merge(json_key_io: io)
else
warn_if_cloud_sdk_credentials ENV[CredentialsLoader::CLIENT_ID_VAR]
clz = read_creds
clz.make_creds(options)
clz.make_creds options
end
end
@ -74,10 +74,10 @@ module Google
end
# Reads the input json and determines which creds class to use.
def self.determine_creds_class(json_key_io)
def self.determine_creds_class json_key_io
json_key = MultiJson.load json_key_io.read
key = "type"
raise "the json is missing the '#{key}' field" unless json_key.key?(key)
raise "the json is missing the '#{key}' field" unless json_key.key? key
type = json_key[key]
case type
when "service_account"

View File

@ -44,7 +44,7 @@ module Google
#
# @param selector the IAM selector.
# @param token the IAM token.
def initialize(selector, token)
def initialize selector, token
raise TypeError unless selector.is_a? String
raise TypeError unless token.is_a? String
@selector = selector
@ -52,16 +52,16 @@ module Google
end
# Adds the credential fields to the hash.
def apply!(a_hash)
def apply! a_hash
a_hash[SELECTOR_KEY] = @selector
a_hash[TOKEN_KEY] = @token
a_hash
end
# Returns a clone of a_hash updated with the authoriation header
def apply(a_hash)
def apply a_hash
a_copy = a_hash.clone
apply!(a_copy)
apply! a_copy
a_copy
end

View File

@ -34,10 +34,10 @@ module Google
# JsonKeyReader contains the behaviour used to read private key and
# client email fields from the service account
module JsonKeyReader
def read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read)
raise "missing client_email" unless json_key.key?("client_email")
raise "missing private_key" unless json_key.key?("private_key")
def read_json_key json_key_io
json_key = MultiJson.load json_key_io.read
raise "missing client_email" unless json_key.key? "client_email"
raise "missing private_key" unless json_key.key? "private_key"
project_id = json_key["project_id"]
[json_key["private_key"], json_key["client_email"], project_id]
end

View File

@ -36,22 +36,22 @@ module Google
# Small utility for normalizing scopes into canonical form
module ScopeUtil
ALIASES = {
"email" => "https://www.googleapis.com/auth/userinfo.email",
"email" => "https://www.googleapis.com/auth/userinfo.email",
"profile" => "https://www.googleapis.com/auth/userinfo.profile",
"openid" => "https://www.googleapis.com/auth/plus.me"
"openid" => "https://www.googleapis.com/auth/plus.me"
}.freeze
def self.normalize(scope)
list = as_array(scope)
def self.normalize scope
list = as_array scope
list.map { |item| ALIASES[item] || item }
end
def self.as_array(scope)
def self.as_array scope
case scope
when Array
scope
when String
scope.split(" ")
scope.split " "
else
raise "Invalid scope value. Must be string or array"
end

View File

@ -56,10 +56,10 @@ module Google
#
# @param json_key_io [IO] an IO from which the JSON key can be read
# @param scope [string|array|nil] the scope(s) to access
def self.make_creds(options = {})
json_key_io, scope = options.values_at(:json_key_io, :scope)
def self.make_creds options = {}
json_key_io, scope = options.values_at :json_key_io, :scope
if json_key_io
private_key, client_email, project_id = read_json_key(json_key_io)
private_key, client_email, project_id = read_json_key json_key_io
else
private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
@ -68,26 +68,26 @@ module Google
project_id ||= CredentialsLoader.load_gcloud_project_id
new(token_credential_uri: TOKEN_CRED_URI,
audience: TOKEN_CRED_URI,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key),
project_id: project_id)
audience: TOKEN_CRED_URI,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key),
project_id: project_id)
.configure_connection(options)
end
# Handles certain escape sequences that sometimes appear in input.
# Specifically, interprets the "\n" sequence for newline, and removes
# enclosing quotes.
def self.unescape(str)
def self.unescape str
str = str.gsub '\n', "\n"
str = str[1..-2] if str.start_with?('"') && str.end_with?('"')
str
end
def initialize(options = {})
def initialize options = {}
@project_id = options[:project_id]
super(options)
super options
end
# Extends the base class.
@ -95,7 +95,7 @@ module Google
# If scope(s) is not set, it creates a transient
# ServiceAccountJwtHeaderCredentials instance and uses that to
# authenticate instead.
def apply!(a_hash, opts = {})
def apply! a_hash, opts = {}
# Use the base implementation if scopes are set
unless scope.nil?
super
@ -105,13 +105,13 @@ module Google
# Use the ServiceAccountJwtHeaderCredentials using the same cred values
# if no scopes are set.
cred_json = {
private_key: @signing_key.to_s,
private_key: @signing_key.to_s,
client_email: @issuer
}
alt_clz = ServiceAccountJwtHeaderCredentials
key_io = StringIO.new(MultiJson.dump(cred_json))
alt = alt_clz.make_creds(json_key_io: key_io)
alt.apply!(a_hash)
key_io = StringIO.new MultiJson.dump(cred_json)
alt = alt_clz.make_creds json_key_io: key_io
alt.apply! a_hash
end
end
@ -141,43 +141,43 @@ module Google
# By default, it calls #new with 2 args, the second one being an
# optional scope. Here's the constructor only has one param, so
# we modify make_creds to reflect this.
def self.make_creds(*args)
new(json_key_io: args[0][:json_key_io])
def self.make_creds *args
new json_key_io: args[0][:json_key_io]
end
# Initializes a ServiceAccountJwtHeaderCredentials.
#
# @param json_key_io [IO] an IO from which the JSON key can be read
def initialize(options = {})
def initialize options = {}
json_key_io = options[:json_key_io]
if json_key_io
@private_key, @issuer, @project_id =
self.class.read_json_key(json_key_io)
self.class.read_json_key json_key_io
else
@private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
@issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
@project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
end
@project_id ||= CredentialsLoader.load_gcloud_project_id
@signing_key = OpenSSL::PKey::RSA.new(@private_key)
@signing_key = OpenSSL::PKey::RSA.new @private_key
end
# Construct a jwt token if the JWT_AUD_URI key is present in the input
# hash.
#
# The jwt token is used as the value of a 'Bearer '.
def apply!(a_hash, opts = {})
jwt_aud_uri = a_hash.delete(JWT_AUD_URI_KEY)
def apply! a_hash, opts = {}
jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
return a_hash if jwt_aud_uri.nil?
jwt_token = new_jwt_token(jwt_aud_uri, opts)
jwt_token = new_jwt_token jwt_aud_uri, opts
a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
a_hash
end
# Returns a clone of a_hash updated with the authoriation header
def apply(a_hash, opts = {})
def apply a_hash, opts = {}
a_copy = a_hash.clone
apply!(a_copy, opts)
apply! a_copy, opts
a_copy
end
@ -190,7 +190,7 @@ module Google
protected
# Creates a jwt uri token.
def new_jwt_token(jwt_aud_uri, options = {})
def new_jwt_token jwt_aud_uri, options = {}
now = Time.new
skew = options[:skew] || 60
assertion = {
@ -200,7 +200,7 @@ module Google
"exp" => (now + EXPIRY).to_i,
"iat" => (now - skew).to_i
}
JWT.encode(assertion, @signing_key, SIGNING_ALGORITHM)
JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
end
end
end

View File

@ -38,24 +38,24 @@ module Signet
# This reopens Client to add #apply and #apply! methods which update a
# hash with the fetched authentication token.
class Client
def configure_connection(options)
def configure_connection options
@connection_info =
options[:connection_builder] || options[:default_connection]
self
end
# 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
# has expired
fetch_access_token!(opts) if access_token.nil? || expires_within?(60)
fetch_access_token! opts if access_token.nil? || expires_within?(60)
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
end
# Returns a clone of a_hash updated with the authentication token
def apply(a_hash, opts = {})
def apply a_hash, opts = {}
a_copy = a_hash.clone
apply!(a_copy, opts)
apply! a_copy, opts
a_copy
end
@ -65,18 +65,18 @@ module Signet
lambda(&method(:apply))
end
def on_refresh(&block)
def on_refresh &block
@refresh_listeners ||= []
@refresh_listeners << block
end
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
options = options.merge connection: connection if connection
end
info = orig_fetch_access_token!(options)
info = orig_fetch_access_token! options
notify_refresh_listeners
info
end
@ -84,7 +84,7 @@ module Signet
def notify_refresh_listeners
listeners = @refresh_listeners || []
listeners.each do |block|
block.call(self)
block.call self
end
end
@ -98,15 +98,13 @@ module Signet
end
end
def retry_with_error(max_retry_count = 5)
def retry_with_error max_retry_count = 5
retry_count = 0
begin
yield
rescue => e
if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
raise e
end
rescue StandardError => e
raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
if retry_count < max_retry_count
retry_count += 1
@ -114,7 +112,7 @@ module Signet
retry
else
msg = "Unexpected error: #{e.inspect}"
raise(Signet::AuthorizationError, msg)
raise Signet::AuthorizationError, msg
end
end
end

View File

@ -39,24 +39,24 @@ module Google
#
# @param [String, File] file
# Path to storage file
def initialize(options = {})
def initialize options = {}
path = options[:file]
@store = YAML::Store.new(path)
@store = YAML::Store.new path
end
# (see Google::Auth::Stores::TokenStore#load)
def load(id)
def load id
@store.transaction { @store[id] }
end
# (see Google::Auth::Stores::TokenStore#store)
def store(id, token)
def store id, token
@store.transaction { @store[id] = token }
end
# (see Google::Auth::Stores::TokenStore#delete)
def delete(id)
@store.transaction { @store.delete(id) }
def delete id
@store.transaction { @store.delete id }
end
end
end

View File

@ -48,34 +48,34 @@ module Google
# @note If no redis instance is provided, a new one is created and
# the options passed through. You may include any other keys accepted
# by `Redis.new`
def initialize(options = {})
redis = options.delete(:redis)
prefix = options.delete(:prefix)
def initialize options = {}
redis = options.delete :redis
prefix = options.delete :prefix
@redis = case redis
when Redis
redis
else
Redis.new(options)
Redis.new options
end
@prefix = prefix || DEFAULT_KEY_PREFIX
end
# (see Google::Auth::Stores::TokenStore#load)
def load(id)
key = key_for(id)
@redis.get(key)
def load id
key = key_for id
@redis.get key
end
# (see Google::Auth::Stores::TokenStore#store)
def store(id, token)
key = key_for(id)
@redis.set(key, token)
def store id, token
key = key_for id
@redis.set key, token
end
# (see Google::Auth::Stores::TokenStore#delete)
def delete(id)
key = key_for(id)
@redis.del(key)
def delete id
key = key_for id
@redis.del key
end
private
@ -86,7 +86,7 @@ module Google
# ID of the token
# @return [String]
# Redis key
def key_for(id)
def key_for id
@prefix + id
end
end

View File

@ -43,7 +43,7 @@ module Google
# ID of token data to load.
# @return [String]
# The loaded token data.
def load(_id)
def load _id
raise "Not implemented"
end
@ -53,7 +53,7 @@ module Google
# ID of token data to store.
# @param [String] token
# The token data to store.
def store(_id, _token)
def store _id, _token
raise "Not implemented"
end
@ -61,7 +61,7 @@ module Google
#
# @param [String] id
# ID of the token data to delete
def delete(_id)
def delete _id
raise "Not implemented"
end
end

View File

@ -72,7 +72,7 @@ module Google
# @param [String] callback_uri
# URL (either absolute or relative) of the auth callback.
# Defaults to '/oauth2callback'
def initialize(client_id, scope, token_store, callback_uri = nil)
def initialize client_id, scope, token_store, callback_uri = nil
raise NIL_CLIENT_ID_ERROR if client_id.nil?
raise NIL_SCOPE_ERROR if scope.nil?
@ -97,20 +97,20 @@ module Google
# nil.
# @return [String]
# Authorization url
def get_authorization_url(options = {})
def get_authorization_url options = {}
scope = options[:scope] || @scope
credentials = UserRefreshCredentials.new(
client_id: @client_id.id,
client_id: @client_id.id,
client_secret: @client_id.secret,
scope: scope
scope: scope
)
redirect_uri = redirect_uri_for(options[:base_url])
url = credentials.authorization_uri(access_type: "offline",
redirect_uri: redirect_uri,
approval_prompt: "force",
state: options[:state],
redirect_uri = redirect_uri_for options[:base_url]
url = credentials.authorization_uri(access_type: "offline",
redirect_uri: redirect_uri,
approval_prompt: "force",
state: options[:state],
include_granted_scopes: true,
login_hint: options[:login_hint])
login_hint: options[:login_hint])
url.to_s
end
@ -123,28 +123,26 @@ module Google
# the requested scopes
# @return [Google::Auth::UserRefreshCredentials]
# Stored credentials, nil if none present
def get_credentials(user_id, scope = nil)
saved_token = stored_token(user_id)
def get_credentials user_id, scope = nil
saved_token = stored_token user_id
return nil if saved_token.nil?
data = MultiJson.load(saved_token)
data = MultiJson.load saved_token
if data.fetch("client_id", @client_id.id) != @client_id.id
raise sprintf(MISMATCHED_CLIENT_ID_ERROR,
data["client_id"], @client_id.id)
raise format(MISMATCHED_CLIENT_ID_ERROR,
data["client_id"], @client_id.id)
end
credentials = UserRefreshCredentials.new(
client_id: @client_id.id,
client_id: @client_id.id,
client_secret: @client_id.secret,
scope: data["scope"] || @scope,
access_token: data["access_token"],
scope: data["scope"] || @scope,
access_token: data["access_token"],
refresh_token: data["refresh_token"],
expires_at: data.fetch("expiration_time_millis", 0) / 1000
expires_at: data.fetch("expiration_time_millis", 0) / 1000
)
scope ||= @scope
if credentials.includes_scope?(scope)
return monitor_credentials(user_id, credentials)
end
return monitor_credentials user_id, credentials if credentials.includes_scope? scope
nil
end
@ -163,20 +161,20 @@ module Google
# callback uri is a relative.
# @return [Google::Auth::UserRefreshCredentials]
# Credentials if exchange is successful
def get_credentials_from_code(options = {})
def get_credentials_from_code options = {}
user_id = options[:user_id]
code = options[:code]
scope = options[:scope] || @scope
base_url = options[:base_url]
credentials = UserRefreshCredentials.new(
client_id: @client_id.id,
client_id: @client_id.id,
client_secret: @client_id.secret,
redirect_uri: redirect_uri_for(base_url),
scope: scope
redirect_uri: redirect_uri_for(base_url),
scope: scope
)
credentials.code = code
credentials.fetch_access_token!({})
monitor_credentials(user_id, credentials)
monitor_credentials user_id, credentials
end
# Exchanges an authorization code returned in the oauth callback.
@ -196,9 +194,9 @@ module Google
# callback uri is a relative.
# @return [Google::Auth::UserRefreshCredentials]
# Credentials if exchange is successful
def get_and_store_credentials_from_code(options = {})
credentials = get_credentials_from_code(options)
store_credentials(options[:user_id], credentials)
def get_and_store_credentials_from_code options = {}
credentials = get_credentials_from_code options
store_credentials options[:user_id], credentials
end
# Revokes a user's credentials. This both revokes the actual
@ -206,11 +204,11 @@ module Google
#
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
def revoke_authorization(user_id)
credentials = get_credentials(user_id)
def revoke_authorization user_id
credentials = get_credentials user_id
if credentials
begin
@token_store.delete(user_id)
@token_store.delete user_id
ensure
credentials.revoke!
end
@ -226,15 +224,15 @@ module Google
# Unique ID of the user for loading/storing credentials.
# @param [Google::Auth::UserRefreshCredentials] credentials
# Credentials to store.
def store_credentials(user_id, credentials)
def store_credentials user_id, credentials
json = MultiJson.dump(
client_id: credentials.client_id,
access_token: credentials.access_token,
refresh_token: credentials.refresh_token,
scope: credentials.scope,
client_id: credentials.client_id,
access_token: credentials.access_token,
refresh_token: credentials.refresh_token,
scope: credentials.scope,
expiration_time_millis: credentials.expires_at.to_i * 1000
)
@token_store.store(user_id, json)
@token_store.store user_id, json
credentials
end
@ -245,11 +243,11 @@ module Google
# @param [String] user_id
# Unique ID of the user for loading/storing credentials.
# @return [String] The saved token from @token_store
def stored_token(user_id)
def stored_token user_id
raise NIL_USER_ID_ERROR if user_id.nil?
raise NIL_TOKEN_STORE_ERROR if @token_store.nil?
@token_store.load(user_id)
@token_store.load user_id
end
# Begin watching a credential for refreshes so the access token can be
@ -259,9 +257,9 @@ module Google
# Unique ID of the user for loading/storing credentials.
# @param [Google::Auth::UserRefreshCredentials] credentials
# Credentials to store.
def monitor_credentials(user_id, credentials)
def monitor_credentials user_id, credentials
credentials.on_refresh do |cred|
store_credentials(user_id, cred)
store_credentials user_id, cred
end
credentials
end
@ -272,11 +270,9 @@ module Google
# Absolute URL to resolve the callback against if necessary.
# @return [String]
# Redirect URI
def redirect_uri_for(base_url)
def redirect_uri_for base_url
return @callback_uri unless URI(@callback_uri).scheme.nil?
if base_url.nil? || URI(base_url).scheme.nil?
raise sprintf(MISSING_ABSOLUTE_URL_ERROR, @callback_uri)
end
raise format(MISSING_ABSOLUTE_URL_ERROR, @callback_uri) if base_url.nil? || URI(base_url).scheme.nil?
URI.join(base_url, @callback_uri).to_s
end
end

View File

@ -56,9 +56,9 @@ module Google
#
# @param json_key_io [IO] an IO from which the JSON key can be read
# @param scope [string|array|nil] the scope(s) to access
def self.make_creds(options = {})
json_key_io, scope = options.values_at(:json_key_io, :scope)
user_creds = read_json_key(json_key_io) if json_key_io
def self.make_creds options = {}
json_key_io, scope = options.values_at :json_key_io, :scope
user_creds = read_json_key json_key_io if json_key_io
user_creds ||= {
"client_id" => ENV[CredentialsLoader::CLIENT_ID_VAR],
"client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
@ -67,36 +67,36 @@ module Google
}
new(token_credential_uri: TOKEN_CRED_URI,
client_id: user_creds["client_id"],
client_secret: user_creds["client_secret"],
refresh_token: user_creds["refresh_token"],
project_id: user_creds["project_id"],
scope: scope)
client_id: user_creds["client_id"],
client_secret: user_creds["client_secret"],
refresh_token: user_creds["refresh_token"],
project_id: user_creds["project_id"],
scope: scope)
.configure_connection(options)
end
# Reads the client_id, client_secret and refresh_token fields from the
# JSON key.
def self.read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read)
wanted = %w(client_id client_secret refresh_token)
def self.read_json_key json_key_io
json_key = MultiJson.load json_key_io.read
wanted = %w[client_id client_secret refresh_token]
wanted.each do |key|
raise "the json is missing the #{key} field" unless json_key.key?(key)
raise "the json is missing the #{key} field" unless json_key.key? key
end
json_key
end
def initialize(options = {})
def initialize options = {}
options ||= {}
options[:token_credential_uri] ||= TOKEN_CRED_URI
options[:authorization_uri] ||= AUTHORIZATION_URI
@project_id = options[:project_id]
@project_id ||= CredentialsLoader.load_gcloud_project_id
super(options)
super options
end
# Revokes the credential
def revoke!(options = {})
def revoke! options = {}
c = options[:connection] || Faraday.default_connection
retry_with_error do
@ -119,7 +119,7 @@ module Google
# Scope to verify
# @return [Boolean]
# True if scope is granted
def includes_scope?(required_scope)
def includes_scope? required_scope
missing_scope = Google::Auth::ScopeUtil.normalize(required_scope) -
Google::Auth::ScopeUtil.normalize(scope)
missing_scope.empty?

View File

@ -97,9 +97,9 @@ module Google
#
# @param [Rack::Request] request
# Current request
def self.handle_auth_callback_deferred(request)
callback_state, redirect_uri = extract_callback_state(request)
request.session[CALLBACK_STATE_KEY] = MultiJson.dump(callback_state)
def self.handle_auth_callback_deferred request
callback_state, redirect_uri = extract_callback_state request
request.session[CALLBACK_STATE_KEY] = MultiJson.dump callback_state
redirect_uri
end
@ -114,8 +114,8 @@ module Google
# @param [String] callback_uri
# URL (either absolute or relative) of the auth callback. Defaults
# to '/oauth2callback'
def initialize(client_id, scope, token_store, callback_uri = nil)
super(client_id, scope, token_store, callback_uri)
def initialize client_id, scope, token_store, callback_uri = nil
super client_id, scope, token_store, callback_uri
end
# Handle the result of the oauth callback. Exchanges the authorization
@ -127,15 +127,15 @@ module Google
# Current request
# @return (Google::Auth::UserRefreshCredentials, String)
# credentials & next URL to redirect to
def handle_auth_callback(user_id, request)
def handle_auth_callback user_id, request
callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state(
request
)
WebUserAuthorizer.validate_callback_state(callback_state, request)
WebUserAuthorizer.validate_callback_state callback_state, request
credentials = get_and_store_credentials_from_code(
user_id: user_id,
code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY],
user_id: user_id,
code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY],
base_url: request.url
)
[credentials, redirect_uri]
@ -156,7 +156,7 @@ module Google
# not nil.
# @return [String]
# Authorization url
def get_authorization_url(options = {})
def get_authorization_url options = {}
options = options.dup
request = options[:request]
raise NIL_REQUEST_ERROR if request.nil?
@ -165,11 +165,11 @@ module Google
redirect_to = options[:redirect_to] || request.url
request.session[XSRF_KEY] = SecureRandom.base64
options[:state] = MultiJson.dump(
SESSION_ID_KEY => request.session[XSRF_KEY],
SESSION_ID_KEY => request.session[XSRF_KEY],
CURRENT_URI_KEY => redirect_to
)
options[:base_url] = request.url
super(options)
super options
end
# Fetch stored credentials for the user.
@ -186,32 +186,32 @@ module Google
# @raise [Signet::AuthorizationError]
# May raise an error if an authorization code is present in the session
# and exchange of the code fails
def get_credentials(user_id, request, scope = nil)
if request.session.key?(CALLBACK_STATE_KEY)
def get_credentials user_id, request, scope = nil
if request.session.key? CALLBACK_STATE_KEY
# Note - in theory, no need to check required scope as this is
# expected to be called immediately after a return from authorization
state_json = request.session.delete(CALLBACK_STATE_KEY)
callback_state = MultiJson.load(state_json)
WebUserAuthorizer.validate_callback_state(callback_state, request)
state_json = request.session.delete CALLBACK_STATE_KEY
callback_state = MultiJson.load state_json
WebUserAuthorizer.validate_callback_state callback_state, request
get_and_store_credentials_from_code(
user_id: user_id,
code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY],
user_id: user_id,
code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY],
base_url: request.url
)
else
super(user_id, scope)
super user_id, scope
end
end
def self.extract_callback_state(request)
def self.extract_callback_state request
state = MultiJson.load(request[STATE_PARAM] || "{}")
redirect_uri = state[CURRENT_URI_KEY]
callback_state = {
AUTH_CODE_KEY => request[AUTH_CODE_KEY],
AUTH_CODE_KEY => request[AUTH_CODE_KEY],
ERROR_CODE_KEY => request[ERROR_CODE_KEY],
SESSION_ID_KEY => state[SESSION_ID_KEY],
SCOPE_KEY => request[SCOPE_KEY]
SCOPE_KEY => request[SCOPE_KEY]
}
[callback_state, redirect_uri]
end
@ -226,12 +226,11 @@ module Google
# Error message if failed
# @param [Rack::Request] request
# Current request
def self.validate_callback_state(state, request)
if state[AUTH_CODE_KEY].nil?
raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR
elsif state[ERROR_CODE_KEY]
def self.validate_callback_state state, request
raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR if state[AUTH_CODE_KEY].nil?
if state[ERROR_CODE_KEY]
raise Signet::AuthorizationError,
sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
format(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
raise Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
end
@ -275,9 +274,9 @@ module Google
# Rack environment
# @return [Array]
# HTTP response
def self.call(env)
request = Rack::Request.new(env)
return_url = WebUserAuthorizer.handle_auth_callback_deferred(request)
def self.call env
request = Rack::Request.new env
return_url = WebUserAuthorizer.handle_auth_callback_deferred request
if return_url
[REDIR_STATUS, { LOCATION_HEADER => return_url }, []]
else
@ -285,8 +284,8 @@ module Google
end
end
def call(env)
self.class.call(env)
def call env
self.class.call env
end
end
end

View File

@ -27,14 +27,14 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'faraday'
require 'spec_helper'
require "faraday"
require "spec_helper"
shared_examples 'apply/apply! are OK' do
shared_examples "apply/apply! are OK" do
let(:auth_key) { :authorization }
# tests that use these examples need to define
@ -43,103 +43,103 @@ shared_examples 'apply/apply! are OK' do
#
# @make_auth_stubs, which should stub out the expected http behaviour of the
# auth client
describe '#fetch_access_token' do
let(:token) { '1/abcdef1234567890' }
let(:stub) do
describe "#fetch_access_token" do
let(:token) { "1/abcdef1234567890" }
let :stub do
make_auth_stubs access_token: token
end
it 'should set access_token to the fetched value' do
it "should set access_token to the fetched value" do
stub
@client.fetch_access_token!
expect(@client.access_token).to eq(token)
expect(stub).to have_been_requested
end
it 'should notify refresh listeners after updating' do
it "should notify refresh listeners after updating" do
stub
expect do |b|
@client.on_refresh(&b)
@client.fetch_access_token!
end.to yield_with_args(have_attributes(
access_token: '1/abcdef1234567890'
))
access_token: "1/abcdef1234567890"
))
expect(stub).to have_been_requested
end
end
describe '#apply!' do
it 'should update the target hash with fetched access token' do
token = '1/abcdef1234567890'
describe "#apply!" do
it "should update the target hash with fetched access token" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
@client.apply!(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
md = { foo: "bar" }
@client.apply! md
want = { :foo => "bar", auth_key => "Bearer #{token}" }
expect(md).to eq(want)
expect(stub).to have_been_requested
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the access token' do
token = '1/abcdef1234567890'
describe "updater_proc" do
it "should provide a proc that updates a hash with the access token" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
md = { foo: "bar" }
the_proc = @client.updater_proc
got = the_proc.call(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
got = the_proc.call md
want = { :foo => "bar", auth_key => "Bearer #{token}" }
expect(got).to eq(want)
expect(stub).to have_been_requested
end
end
describe '#apply' do
it 'should not update the original hash with the access token' do
token = '1/abcdef1234567890'
describe "#apply" do
it "should not update the original hash with the access token" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
@client.apply(md)
want = { foo: 'bar' }
md = { foo: "bar" }
@client.apply md
want = { foo: "bar" }
expect(md).to eq(want)
expect(stub).to have_been_requested
end
it 'should add the token to the returned hash' do
token = '1/abcdef1234567890'
it "should add the token to the returned hash" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
md = { foo: "bar" }
got = @client.apply md
want = { :foo => "bar", auth_key => "Bearer #{token}" }
expect(got).to eq(want)
expect(stub).to have_been_requested
end
it 'should not fetch a new token if the current is not expired' do
token = '1/abcdef1234567890'
it "should not fetch a new token if the current is not expired" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token
n = 5 # arbitrary
n.times do |_t|
md = { foo: 'bar' }
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
md = { foo: "bar" }
got = @client.apply md
want = { :foo => "bar", auth_key => "Bearer #{token}" }
expect(got).to eq(want)
end
expect(stub).to have_been_requested
end
it 'should fetch a new token if the current one is expired' do
token1 = '1/abcdef1234567890'
token2 = '2/abcdef1234567891'
it "should fetch a new token if the current one is expired" do
token1 = "1/abcdef1234567890"
token2 = "2/abcdef1234567891"
[token1, token2].each do |t|
make_auth_stubs access_token: t
md = { foo: 'bar' }
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{t}" }
md = { foo: "bar" }
got = @client.apply md
want = { :foo => "bar", auth_key => "Bearer #{t}" }
expect(got).to eq(want)
@client.expires_at -= 3601 # default is to expire in 1hr
end

View File

@ -27,131 +27,131 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'spec_helper'
require 'fakefs/safe'
require 'googleauth'
require "spec_helper"
require "fakefs/safe"
require "googleauth"
describe Google::Auth::ClientId do
shared_examples 'it has a valid config' do
it 'should include a valid id' do
expect(client_id.id).to eql 'abc@example.com'
shared_examples "it has a valid config" do
it "should include a valid id" do
expect(client_id.id).to eql "abc@example.com"
end
it 'should include a valid secret' do
expect(client_id.secret).to eql 'notasecret'
it "should include a valid secret" do
expect(client_id.secret).to eql "notasecret"
end
end
shared_examples 'it can successfully load client_id' do
context 'loaded from hash' do
shared_examples "it can successfully load client_id" do
context "loaded from hash" do
let(:client_id) { Google::Auth::ClientId.from_hash config }
it_behaves_like 'it has a valid config'
it_behaves_like "it has a valid config"
end
context 'loaded from file' do
file_path = '/client_secrets.json'
context "loaded from file" do
file_path = "/client_secrets.json"
let(:client_id) do
let :client_id do
FakeFS do
content = MultiJson.dump(config)
File.write(file_path, content)
Google::Auth::ClientId.from_file(file_path)
content = MultiJson.dump config
File.write file_path, content
Google::Auth::ClientId.from_file file_path
end
end
it_behaves_like 'it has a valid config'
it_behaves_like "it has a valid config"
end
end
describe 'with web config' do
let(:config) do
describe "with web config" do
let :config do
{
'web' => {
'client_id' => 'abc@example.com',
'client_secret' => 'notasecret'
"web" => {
"client_id" => "abc@example.com",
"client_secret" => "notasecret"
}
}
end
it_behaves_like 'it can successfully load client_id'
it_behaves_like "it can successfully load client_id"
end
describe 'with installed app config' do
let(:config) do
describe "with installed app config" do
let :config do
{
'installed' => {
'client_id' => 'abc@example.com',
'client_secret' => 'notasecret'
"installed" => {
"client_id" => "abc@example.com",
"client_secret" => "notasecret"
}
}
end
it_behaves_like 'it can successfully load client_id'
it_behaves_like "it can successfully load client_id"
end
context 'with missing top level property' do
let(:config) do
context "with missing top level property" do
let :config do
{
'notvalid' => {
'client_id' => 'abc@example.com',
'client_secret' => 'notasecret'
"notvalid" => {
"client_id" => "abc@example.com",
"client_secret" => "notasecret"
}
}
end
it 'should raise error' do
it "should raise error" do
expect { Google::Auth::ClientId.from_hash config }.to raise_error(
/Expected top level property/
)
end
end
context 'with missing client id' do
let(:config) do
context "with missing client id" do
let :config do
{
'web' => {
'client_secret' => 'notasecret'
"web" => {
"client_secret" => "notasecret"
}
}
end
it 'should raise error' do
it "should raise error" do
expect { Google::Auth::ClientId.from_hash config }.to raise_error(
/Client id can not be nil/
)
end
end
context 'with missing client secret' do
let(:config) do
context "with missing client secret" do
let :config do
{
'web' => {
'client_id' => 'abc@example.com'
"web" => {
"client_id" => "abc@example.com"
}
}
end
it 'should raise error' do
it "should raise error" do
expect { Google::Auth::ClientId.from_hash config }.to raise_error(
/Client secret can not be nil/
)
end
end
context 'with cloud sdk credentials' do
let(:config) do
context "with cloud sdk credentials" do
let :config do
{
'web' => {
'client_id' => Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID,
'client_secret' => 'notasecret'
"web" => {
"client_id" => Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID,
"client_secret" => "notasecret"
}
}
end
it 'should raise warning' do
it "should raise warning" do
expect { Google::Auth::ClientId.from_hash config }.to output(
Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
).to_stderr

View File

@ -27,65 +27,65 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'faraday'
require 'googleauth/compute_engine'
require 'spec_helper'
require "apply_auth_examples"
require "faraday"
require "googleauth/compute_engine"
require "spec_helper"
describe Google::Auth::GCECredentials do
MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'.freeze
MD_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
GCECredentials = Google::Auth::GCECredentials
before(:example) do
before :example do
@client = GCECredentials.new
end
def make_auth_stubs(opts = {})
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
def make_auth_stubs opts = {}
access_token = opts[:access_token] || ""
body = MultiJson.dump("access_token" => access_token,
"token_type" => "Bearer",
"expires_in" => 3600)
stub_request(:get, MD_URI)
.with(headers: { 'Metadata-Flavor' => 'Google' })
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
.with(headers: { "Metadata-Flavor" => "Google" })
.to_return(body: body,
status: 200,
headers: { "Content-Type" => "application/json" })
end
it_behaves_like 'apply/apply! are OK'
it_behaves_like "apply/apply! are OK"
context 'metadata is unavailable' do
describe '#fetch_access_token' do
it 'should fail if the metadata request returns a 404' do
context "metadata is unavailable" do
describe "#fetch_access_token" do
it "should fail if the metadata request returns a 404" do
stub = stub_request(:get, MD_URI)
.to_return(status: 404,
headers: { 'Metadata-Flavor' => 'Google' })
.to_return(status: 404,
headers: { "Metadata-Flavor" => "Google" })
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
expect(stub).to have_been_requested
end
it 'should fail if the metadata request returns an unexpected code' do
it "should fail if the metadata request returns an unexpected code" do
stub = stub_request(:get, MD_URI)
.to_return(status: 503,
headers: { 'Metadata-Flavor' => 'Google' })
.to_return(status: 503,
headers: { "Metadata-Flavor" => "Google" })
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
expect(stub).to have_been_requested
end
it 'should fail with Signet::AuthorizationError if request times out' do
it "should fail with Signet::AuthorizationError if request times out" do
allow_any_instance_of(Faraday::Connection).to receive(:get)
.and_raise(Faraday::TimeoutError)
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
end
it 'should fail with Signet::AuthorizationError if request fails' do
it "should fail with Signet::AuthorizationError if request fails" do
allow_any_instance_of(Faraday::Connection).to receive(:get)
.and_raise(Faraday::ConnectionFailed, nil)
expect { @client.fetch_access_token! }
@ -94,27 +94,27 @@ describe Google::Auth::GCECredentials do
end
end
describe '#on_gce?' do
it 'should be true when Metadata-Flavor is Google' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 200,
headers: { 'Metadata-Flavor' => 'Google' })
describe "#on_gce?" do
it "should be true when Metadata-Flavor is Google" do
stub = stub_request(:get, "http://169.254.169.254")
.to_return(status: 200,
headers: { "Metadata-Flavor" => "Google" })
expect(GCECredentials.on_gce?({}, true)).to eq(true)
expect(stub).to have_been_requested
end
it 'should be false when Metadata-Flavor is not Google' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 200,
headers: { 'Metadata-Flavor' => 'NotGoogle' })
it "should be false when Metadata-Flavor is not Google" do
stub = stub_request(:get, "http://169.254.169.254")
.to_return(status: 200,
headers: { "Metadata-Flavor" => "NotGoogle" })
expect(GCECredentials.on_gce?({}, true)).to eq(false)
expect(stub).to have_been_requested
end
it 'should be false if the response is not 200' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 404,
headers: { 'Metadata-Flavor' => 'NotGoogle' })
it "should be false if the response is not 200" do
stub = stub_request(:get, "http://169.254.169.254")
.to_return(status: 404,
headers: { "Metadata-Flavor" => "NotGoogle" })
expect(GCECredentials.on_gce?({}, true)).to eq(false)
expect(stub).to have_been_requested
end

View File

@ -27,34 +27,34 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'googleauth'
require "googleauth"
# This test is testing the private class Google::Auth::Credentials. We want to
# make sure that the passed in scope propogates to the Signet object. This means
# testing the private API, which is generally frowned on.
describe Google::Auth::Credentials, :private do
let(:default_keyfile_hash) do
let :default_keyfile_hash do
{
'private_key_id' => 'testabc1234567890xyz',
'private_key' => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
'client_email' => 'credz-testabc1234567890xyz@developer.gserviceaccount.com',
'client_id' => 'credz-testabc1234567890xyz.apps.googleusercontent.com',
'type' => 'service_account',
'project_id' => 'a_project_id'
"private_key_id" => "testabc1234567890xyz",
"private_key" => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
"client_email" => "credz-testabc1234567890xyz@developer.gserviceaccount.com",
"client_id" => "credz-testabc1234567890xyz.apps.googleusercontent.com",
"type" => "service_account",
"project_id" => "a_project_id"
}
end
it 'uses a default scope' do
mocked_signet = double('Signet::OAuth2::Client')
it "uses a default scope" do
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[: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([])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
@ -63,49 +63,49 @@ describe Google::Auth::Credentials, :private do
Google::Auth::Credentials.new default_keyfile_hash
end
it 'uses a custom scope' do
mocked_signet = double('Signet::OAuth2::Client')
it "uses a custom scope" do
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[: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
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
it 'can be subclassed to pass in other env paths' do
TEST_PATH_ENV_VAR = 'TEST_PATH'.freeze
TEST_PATH_ENV_VAL = '/unknown/path/to/file.txt'.freeze
TEST_JSON_ENV_VAR = 'TEST_JSON_VARS'.freeze
it "can be subclassed to pass in other env paths" do
TEST_PATH_ENV_VAR = "TEST_PATH".freeze
TEST_PATH_ENV_VAL = "/unknown/path/to/file.txt".freeze
TEST_JSON_ENV_VAR = "TEST_JSON_VARS".freeze
ENV[TEST_PATH_ENV_VAR] = TEST_PATH_ENV_VAL
ENV[TEST_JSON_ENV_VAR] = JSON.generate(default_keyfile_hash)
ENV[TEST_JSON_ENV_VAR] = JSON.generate default_keyfile_hash
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
SCOPE = "http://example.com/scope".freeze
PATH_ENV_VARS = [TEST_PATH_ENV_VAR].freeze
JSON_ENV_VARS = [TEST_JSON_ENV_VAR].freeze
end
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(: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[: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
@ -114,32 +114,32 @@ describe Google::Auth::Credentials, :private do
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'])
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it 'subclasses can use PATH_ENV_VARS to get keyfile path' do
it "subclasses can use PATH_ENV_VARS to get keyfile path" do
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
PATH_ENV_VARS = ['PATH_ENV_DUMMY', 'PATH_ENV_TEST'].freeze
JSON_ENV_VARS = ['JSON_ENV_DUMMY'].freeze
DEFAULT_PATHS = ['~/default/path/to/file.txt'].freeze
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
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('PATH_ENV_TEST') { '/unknown/path/to/file.txt' }
allow(::File).to receive(:file?).with('/unknown/path/to/file.txt') { true }
allow(::File).to receive(:read).with('/unknown/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
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("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
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(: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[: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
@ -148,31 +148,31 @@ describe Google::Auth::Credentials, :private do
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'])
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it 'subclasses can use JSON_ENV_VARS to get keyfile contents' do
it "subclasses can use JSON_ENV_VARS to get keyfile contents" 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', 'JSON_ENV_TEST'].freeze
DEFAULT_PATHS = ['~/default/path/to/file.txt'].freeze
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(::ENV).to receive(:[]).with('JSON_ENV_DUMMY') { nil }
allow(::ENV).to receive(:[]).with('JSON_ENV_TEST') { JSON.generate(default_keyfile_hash) }
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(::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(: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[: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
@ -181,32 +181,32 @@ describe Google::Auth::Credentials, :private do
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'])
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it 'subclasses can use DEFAULT_PATHS to get keyfile path' do
it "subclasses can use DEFAULT_PATHS to get keyfile path" 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
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) }
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')
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[: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
@ -215,23 +215,23 @@ describe Google::Auth::Credentials, :private do
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'])
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
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
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 }
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')
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)
@ -243,10 +243,10 @@ describe Google::Auth::Credentials, :private do
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[: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
@ -255,14 +255,14 @@ describe Google::Auth::Credentials, :private do
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'])
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
end
it 'warns when cloud sdk credentials are used' do
mocked_signet = double('Signet::OAuth2::Client')
it "warns when cloud sdk credentials are used" do
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(Signet::OAuth2::Client).to receive(:new) do |options|
allow(Signet::OAuth2::Client).to receive(:new) do |_options|
mocked_signet
end
allow(mocked_signet).to receive(:client_id).and_return(Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID)

View File

@ -27,22 +27,22 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'faraday'
require 'fakefs/safe'
require 'googleauth'
require 'spec_helper'
require 'os'
require "faraday"
require "fakefs/safe"
require "googleauth"
require "spec_helper"
require "os"
describe '#get_application_default' do
describe "#get_application_default" do
# Pass unique options each time to bypass memoization
let(:options) { |example| { dememoize: example } }
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
before :example do
@key = OpenSSL::PKey::RSA.new 2048
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, CLIENT_ID_VAR,
@ -50,36 +50,36 @@ describe '#get_application_default' do
]
@original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
@home = ENV['HOME']
@app_data = ENV['APPDATA']
@program_data = ENV['ProgramData']
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@home = ENV["HOME"]
@app_data = ENV["APPDATA"]
@program_data = ENV["ProgramData"]
@scope = "https://www.googleapis.com/auth/userinfo.profile"
end
after(:example) do
after :example do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
ENV['ProgramData'] = @program_data unless @program_data == ENV['ProgramData']
ENV["HOME"] = @home unless @home == ENV["HOME"]
ENV["APPDATA"] = @app_data unless @app_data == ENV["APPDATA"]
ENV["ProgramData"] = @program_data unless @program_data == ENV["ProgramData"]
end
shared_examples 'it cannot load misconfigured credentials' do
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
shared_examples "it cannot load misconfigured credentials" do
it "fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
key_path = File.join dir, "does-not-exist"
ENV[@var_name] = key_path
expect { Google::Auth.get_application_default @scope, options }
.to raise_error RuntimeError
end
end
it 'fails without default file or env if not on compute engine' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 404,
headers: { 'Metadata-Flavor' => 'NotGoogle' })
it "fails without default file or env if not on compute engine" do
stub = stub_request(:get, "http://169.254.169.254")
.to_return(status: 404,
headers: { "Metadata-Flavor" => "NotGoogle" })
Dir.mktmpdir do |dir|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV['HOME'] = dir # no config present in this tmp dir
ENV.delete @var_name unless ENV[@var_name].nil? # no env var
ENV["HOME"] = dir # no config present in this tmp dir
expect do
Google::Auth.get_application_default @scope, options
end.to raise_error RuntimeError
@ -88,12 +88,12 @@ describe '#get_application_default' do
end
end
shared_examples 'it can successfully load credentials' do
it 'succeeds if the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
shared_examples "it can successfully load credentials" do
it "succeeds if the GOOGLE_APPLICATION_CREDENTIALS file is valid" 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)
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
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
@ -102,73 +102,73 @@ describe '#get_application_default' do
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)
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)
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
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "succeeds with default file without GOOGLE_APPLICATION_CREDENTIALS" do
ENV.delete @var_name unless ENV[@var_name].nil?
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_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
key_path = File.join dir, ".config", WELL_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
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
end
end
it 'succeeds with default file without a scope' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "succeeds with default file without a scope" do
ENV.delete @var_name unless ENV[@var_name].nil?
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_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
key_path = File.join dir, ".config", WELL_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
expect(Google::Auth.get_application_default(nil, options)).to_not be_nil
end
end
it 'succeeds without default file or env if on compute engine' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 200,
headers: { 'Metadata-Flavor' => 'Google' })
it "succeeds without default file or env if on compute engine" do
stub = stub_request(:get, "http://169.254.169.254")
.to_return(status: 200,
headers: { "Metadata-Flavor" => "Google" })
Dir.mktmpdir do |dir|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV['HOME'] = dir # no config present in this tmp dir
ENV.delete @var_name unless ENV[@var_name].nil? # no env var
ENV["HOME"] = dir # no config present in this tmp dir
creds = Google::Auth.get_application_default @scope, options
expect(creds).to_not be_nil
end
expect(stub).to have_been_requested
end
it 'succeeds with system default file' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "succeeds with system default file" do
ENV.delete @var_name unless ENV[@var_name].nil?
FakeFS do
ENV['ProgramData'] = '/etc'
prefix = OS.windows? ? '/etc/Google/Auth/' : '/etc/google/auth/'
key_path = File.join(prefix, CREDENTIALS_FILE_NAME)
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV["ProgramData"] = "/etc"
prefix = OS.windows? ? "/etc/Google/Auth/" : "/etc/google/auth/"
key_path = File.join prefix, CREDENTIALS_FILE_NAME
FileUtils.mkdir_p File.dirname(key_path)
File.write key_path, cred_json_text
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
File.delete(key_path)
File.delete key_path
end
end
it 'succeeds if environment vars are valid' do
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
it "succeeds if environment vars are valid" do
ENV.delete @var_name unless ENV[@var_name].nil? # no env var
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[CLIENT_ID_VAR] = cred_json[:client_id]
@ -179,79 +179,79 @@ describe '#get_application_default' do
.to_not be_nil
end
it 'warns when using cloud sdk credentials' do
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
it "warns when using cloud sdk credentials" do
ENV.delete @var_name unless ENV[@var_name].nil? # no env var
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[CLIENT_ID_VAR] = Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
ENV[PROJECT_ID_VAR] = 'a_project_id'
ENV[PROJECT_ID_VAR] = "a_project_id"
expect { Google::Auth.get_application_default @scope, options }.to output(
Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
).to_stderr
end
end
describe 'when credential type is service account' do
let(:cred_json) do
describe "when credential type is service account" do
let :cred_json do
{
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: 'app@developer.gserviceaccount.com',
client_id: 'app.apps.googleusercontent.com',
type: 'service_account'
private_key_id: "a_private_key_id",
private_key: @key.to_pem,
client_email: "app@developer.gserviceaccount.com",
client_id: "app.apps.googleusercontent.com",
type: "service_account"
}
end
def cred_json_text
MultiJson.dump(cred_json)
MultiJson.dump cred_json
end
it_behaves_like 'it can successfully load credentials'
it_behaves_like 'it cannot load misconfigured credentials'
it_behaves_like "it can successfully load credentials"
it_behaves_like "it cannot load misconfigured credentials"
end
describe 'when credential type is authorized_user' do
let(:cred_json) do
describe "when credential type is authorized_user" do
let :cred_json do
{
client_secret: 'privatekey',
refresh_token: 'refreshtoken',
client_id: 'app.apps.googleusercontent.com',
type: 'authorized_user'
client_secret: "privatekey",
refresh_token: "refreshtoken",
client_id: "app.apps.googleusercontent.com",
type: "authorized_user"
}
end
def cred_json_text
MultiJson.dump(cred_json)
MultiJson.dump cred_json
end
it_behaves_like 'it can successfully load credentials'
it_behaves_like 'it cannot load misconfigured credentials'
it_behaves_like "it can successfully load credentials"
it_behaves_like "it cannot load misconfigured credentials"
end
describe 'when credential type is unknown' do
let(:cred_json) do
describe "when credential type is unknown" do
let :cred_json do
{
client_secret: 'privatekey',
refresh_token: 'refreshtoken',
client_id: 'app.apps.googleusercontent.com',
private_key: @key.to_pem,
client_email: 'app@developer.gserviceaccount.com',
type: 'not_known_type'
client_secret: "privatekey",
refresh_token: "refreshtoken",
client_id: "app.apps.googleusercontent.com",
private_key: @key.to_pem,
client_email: "app@developer.gserviceaccount.com",
type: "not_known_type"
}
end
def cred_json_text
MultiJson.dump(cred_json)
MultiJson.dump cred_json
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS file contains the creds' do
it "fails if the GOOGLE_APPLICATION_CREDENTIALS file contains the creds" 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)
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
expect do
Google::Auth.get_application_default @scope, options
@ -259,22 +259,22 @@ describe '#get_application_default' do
end
end
it 'fails if the well known file contains the creds' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "fails if the well known file contains the creds" do
ENV.delete @var_name unless ENV[@var_name].nil?
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_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
key_path = File.join dir, ".config", WELL_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
expect do
Google::Auth.get_application_default @scope, options
end.to raise_error RuntimeError
end
end
it 'fails if env vars are set' do
it "fails if env vars are set" do
ENV[ENV_VAR] = nil
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]

View File

@ -27,54 +27,54 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'googleauth/iam'
require "googleauth/iam"
describe Google::Auth::IAMCredentials do
IAMCredentials = Google::Auth::IAMCredentials
let(:test_selector) { 'the-test-selector' }
let(:test_token) { 'the-test-token' }
let(:test_creds) { IAMCredentials.new(test_selector, test_token) }
let(:test_selector) { "the-test-selector" }
let(:test_token) { "the-test-token" }
let(:test_creds) { IAMCredentials.new test_selector, test_token }
describe '#apply!' do
it 'should update the target hash with the iam values' do
md = { foo: 'bar' }
test_creds.apply!(md)
describe "#apply!" do
it "should update the target hash with the iam values" do
md = { foo: "bar" }
test_creds.apply! md
expect(md[IAMCredentials::SELECTOR_KEY]).to eq test_selector
expect(md[IAMCredentials::TOKEN_KEY]).to eq test_token
expect(md[:foo]).to eq 'bar'
expect(md[:foo]).to eq "bar"
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the iam values' do
md = { foo: 'bar' }
describe "updater_proc" do
it "should provide a proc that updates a hash with the iam values" do
md = { foo: "bar" }
the_proc = test_creds.updater_proc
got = the_proc.call(md)
got = the_proc.call md
expect(got[IAMCredentials::SELECTOR_KEY]).to eq test_selector
expect(got[IAMCredentials::TOKEN_KEY]).to eq test_token
expect(got[:foo]).to eq 'bar'
expect(got[:foo]).to eq "bar"
end
end
describe '#apply' do
it 'should not update the original hash with the iam values' do
md = { foo: 'bar' }
test_creds.apply(md)
describe "#apply" do
it "should not update the original hash with the iam values" do
md = { foo: "bar" }
test_creds.apply md
expect(md[IAMCredentials::SELECTOR_KEY]).to be_nil
expect(md[IAMCredentials::TOKEN_KEY]).to be_nil
expect(md[:foo]).to eq 'bar'
expect(md[:foo]).to eq "bar"
end
it 'should return a with the iam values' do
md = { foo: 'bar' }
got = test_creds.apply(md)
it "should return a with the iam values" do
md = { foo: "bar" }
got = test_creds.apply md
expect(got[IAMCredentials::SELECTOR_KEY]).to eq test_selector
expect(got[IAMCredentials::TOKEN_KEY]).to eq test_token
expect(got[:foo]).to eq 'bar'
expect(got[:foo]).to eq "bar"
end
end
end

View File

@ -27,51 +27,51 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'googleauth/scope_util'
require "googleauth/scope_util"
describe Google::Auth::ScopeUtil do
shared_examples 'normalizes scopes' do
let(:normalized) { Google::Auth::ScopeUtil.normalize(source) }
shared_examples "normalizes scopes" do
let(:normalized) { Google::Auth::ScopeUtil.normalize source }
it 'normalizes the email scope' do
it "normalizes the email scope" do
expect(normalized).to include(
'https://www.googleapis.com/auth/userinfo.email'
"https://www.googleapis.com/auth/userinfo.email"
)
expect(normalized).to_not include 'email'
expect(normalized).to_not include "email"
end
it 'normalizes the profile scope' do
it "normalizes the profile scope" do
expect(normalized).to include(
'https://www.googleapis.com/auth/userinfo.profile'
"https://www.googleapis.com/auth/userinfo.profile"
)
expect(normalized).to_not include 'profile'
expect(normalized).to_not include "profile"
end
it 'normalizes the openid scope' do
expect(normalized).to include 'https://www.googleapis.com/auth/plus.me'
expect(normalized).to_not include 'openid'
it "normalizes the openid scope" do
expect(normalized).to include "https://www.googleapis.com/auth/plus.me"
expect(normalized).to_not include "openid"
end
it 'leaves other other scopes as-is' do
expect(normalized).to include 'https://www.googleapis.com/auth/drive'
it "leaves other other scopes as-is" do
expect(normalized).to include "https://www.googleapis.com/auth/drive"
end
end
context 'with scope as string' do
let(:source) do
'email profile openid https://www.googleapis.com/auth/drive'
context "with scope as string" do
let :source do
"email profile openid https://www.googleapis.com/auth/drive"
end
it_behaves_like 'normalizes scopes'
it_behaves_like "normalizes scopes"
end
context 'with scope as Array' do
let(:source) do
%w(email profile openid https://www.googleapis.com/auth/drive)
context "with scope as Array" do
let :source do
%w[email profile openid https://www.googleapis.com/auth/drive]
end
it_behaves_like 'normalizes scopes'
it_behaves_like "normalizes scopes"
end
end

View File

@ -27,81 +27,81 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'fakefs/safe'
require 'fileutils'
require 'googleauth/service_account'
require 'jwt'
require 'multi_json'
require 'openssl'
require 'spec_helper'
require 'tmpdir'
require 'os'
require "apply_auth_examples"
require "fakefs/safe"
require "fileutils"
require "googleauth/service_account"
require "jwt"
require "multi_json"
require "openssl"
require "spec_helper"
require "tmpdir"
require "os"
include Google::Auth::CredentialsLoader
shared_examples 'jwt header auth' do
context 'when jwt_aud_uri is present' do
let(:test_uri) { 'https://www.googleapis.com/myservice' }
let(:auth_prefix) { 'Bearer ' }
shared_examples "jwt header auth" do
context "when jwt_aud_uri is present" do
let(:test_uri) { "https://www.googleapis.com/myservice" }
let(:auth_prefix) { "Bearer " }
let(:auth_key) { ServiceAccountJwtHeaderCredentials::AUTH_METADATA_KEY }
let(:jwt_uri_key) { ServiceAccountJwtHeaderCredentials::JWT_AUD_URI_KEY }
def expect_is_encoded_jwt(hdr)
def expect_is_encoded_jwt hdr
expect(hdr).to_not be_nil
expect(hdr.start_with?(auth_prefix)).to be true
authorization = hdr[auth_prefix.length..-1]
payload, = JWT.decode(authorization, @key.public_key, true, algorithm: 'RS256')
expect(payload['aud']).to eq(test_uri)
expect(payload['iss']).to eq(client_email)
payload, = JWT.decode authorization, @key.public_key, true, algorithm: "RS256"
expect(payload["aud"]).to eq(test_uri)
expect(payload["iss"]).to eq(client_email)
end
describe '#apply!' do
it 'should update the target hash with a jwt token' do
md = { foo: 'bar' }
describe "#apply!" do
it "should update the target hash with a jwt token" do
md = { foo: "bar" }
md[jwt_uri_key] = test_uri
@client.apply!(md)
@client.apply! md
auth_header = md[auth_key]
expect_is_encoded_jwt(auth_header)
expect_is_encoded_jwt auth_header
expect(md[jwt_uri_key]).to be_nil
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with a jwt token' do
md = { foo: 'bar' }
describe "updater_proc" do
it "should provide a proc that updates a hash with a jwt token" do
md = { foo: "bar" }
md[jwt_uri_key] = test_uri
the_proc = @client.updater_proc
got = the_proc.call(md)
got = the_proc.call md
auth_header = got[auth_key]
expect_is_encoded_jwt(auth_header)
expect_is_encoded_jwt auth_header
expect(got[jwt_uri_key]).to be_nil
expect(md[jwt_uri_key]).to_not be_nil
end
end
describe '#apply' do
it 'should not update the original hash with a jwt token' do
md = { foo: 'bar' }
describe "#apply" do
it "should not update the original hash with a jwt token" do
md = { foo: "bar" }
md[jwt_uri_key] = test_uri
the_proc = @client.updater_proc
got = the_proc.call(md)
got = the_proc.call md
auth_header = md[auth_key]
expect(auth_header).to be_nil
expect(got[jwt_uri_key]).to be_nil
expect(md[jwt_uri_key]).to_not be_nil
end
it 'should add a jwt token to the returned hash' do
md = { foo: 'bar' }
it "should add a jwt token to the returned hash" do
md = { foo: "bar" }
md[jwt_uri_key] = test_uri
got = @client.apply(md)
got = @client.apply md
auth_header = got[auth_key]
expect_is_encoded_jwt(auth_header)
expect_is_encoded_jwt auth_header
end
end
end
@ -109,63 +109,63 @@ end
describe Google::Auth::ServiceAccountCredentials do
ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
let(:client_email) { 'app@developer.gserviceaccount.com' }
let(:cred_json) do
let(:client_email) { "app@developer.gserviceaccount.com" }
let :cred_json do
{
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: client_email,
client_id: 'app.apps.googleusercontent.com',
type: 'service_account',
project_id: 'a_project_id'
private_key_id: "a_private_key_id",
private_key: @key.to_pem,
client_email: client_email,
client_id: "app.apps.googleusercontent.com",
type: "service_account",
project_id: "a_project_id"
}
end
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
before :example do
@key = OpenSSL::PKey::RSA.new 2048
@client = ServiceAccountCredentials.make_creds(
json_key_io: StringIO.new(cred_json_text),
scope: 'https://www.googleapis.com/auth/userinfo.profile'
scope: "https://www.googleapis.com/auth/userinfo.profile"
)
end
def make_auth_stubs(opts = {})
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
def make_auth_stubs opts = {}
access_token = opts[:access_token] || ""
body = MultiJson.dump("access_token" => access_token,
"token_type" => "Bearer",
"expires_in" => 3600)
blk = proc do |request|
params = Addressable::URI.form_unencode(request.body)
_claim, _header = JWT.decode(params.assoc('assertion').last,
params = Addressable::URI.form_unencode request.body
_claim, _header = JWT.decode(params.assoc("assertion").last,
@key.public_key, true,
algorithm: 'RS256')
algorithm: "RS256")
end
stub_request(:post, 'https://www.googleapis.com/oauth2/v4/token')
stub_request(:post, "https://www.googleapis.com/oauth2/v4/token")
.with(body: hash_including(
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
),
&blk)
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
.to_return(body: body,
status: 200,
headers: { "Content-Type" => "application/json" })
end
def cred_json_text
MultiJson.dump(cred_json)
MultiJson.dump cred_json
end
it_behaves_like 'apply/apply! are OK'
it_behaves_like "apply/apply! are OK"
context 'when scope is nil' do
before(:example) do
context "when scope is nil" do
before :example do
@client.scope = nil
end
it_behaves_like 'jwt header auth'
it_behaves_like "jwt header auth"
end
describe '#from_env' do
before(:example) do
describe "#from_env" do
before :example do
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
@ -174,56 +174,56 @@ describe Google::Auth::ServiceAccountCredentials do
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@scope = "https://www.googleapis.com/auth/userinfo.profile"
@clz = ServiceAccountCredentials
end
after(:example) do
after :example do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
end
it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset" do
ENV.delete @var_name unless ENV[@var_name].nil?
expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist" do
ENV.delete @var_name unless ENV[@var_name].nil?
expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
key_path = File.join dir, "does-not-exist"
ENV[@var_name] = key_path
expect { @clz.from_env(@scope) }.to raise_error RuntimeError
expect { @clz.from_env @scope }.to raise_error RuntimeError
end
end
it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
it "succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid" 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)
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
expect(@clz.from_env(@scope)).to_not be_nil
end
end
it 'succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are'\
' valid' do
it "succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are"\
" valid" do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
expect(@clz.from_env(@scope)).to_not be_nil
end
it 'sets project_id when the PROJECT_ID_VAR env var is set' do
it "sets project_id when the PROJECT_ID_VAR env var is set" do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[PROJECT_ID_VAR] = cred_json[:project_id]
ENV[ENV_VAR] = nil
credentials = @clz.from_env(@scope)
credentials = @clz.from_env @scope
expect(credentials.project_id).to eq(cred_json[:project_id])
end
it 'succeeds when GOOGLE_PRIVATE_KEY is escaped' do
it "succeeds when GOOGLE_PRIVATE_KEY is escaped" do
escaped_key = cred_json[:private_key].gsub "\n", '\n'
ENV[PRIVATE_KEY_VAR] = "\"#{escaped_key}\""
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
@ -233,110 +233,110 @@ describe Google::Auth::ServiceAccountCredentials do
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)
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
describe '#from_well_known_path' do
before(:example) do
@home = ENV['HOME']
@app_data = ENV['APPDATA']
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
describe "#from_well_known_path" do
before :example do
@home = ENV["HOME"]
@app_data = ENV["APPDATA"]
@scope = "https://www.googleapis.com/auth/userinfo.profile"
@known_path = WELL_KNOWN_PATH
@clz = ServiceAccountCredentials
end
after(:example) do
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
after :example do
ENV["HOME"] = @home unless @home == ENV["HOME"]
ENV["APPDATA"] = @app_data unless @app_data == ENV["APPDATA"]
end
it 'is nil if no file exists' do
ENV['HOME'] = File.dirname(__FILE__)
it "is nil if no file exists" do
ENV["HOME"] = File.dirname __FILE__
expect(ServiceAccountCredentials.from_well_known_path(@scope)).to be_nil
end
it 'successfully loads the file when it is present' do
it "successfully loads the file when it is present" 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
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
expect(@clz.from_well_known_path(@scope)).to_not be_nil
end
end
it 'successfully sets project_id when file is present' do
it "successfully sets project_id when file is present" 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
credentials = @clz.from_well_known_path(@scope)
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
credentials = @clz.from_well_known_path @scope
expect(credentials.project_id).to eq(cred_json[:project_id])
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)
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
describe '#from_system_default_path' do
before(:example) do
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@program_data = ENV['ProgramData']
@prefix = OS.windows? ? '/etc/Google/Auth/' : '/etc/google/auth/'
@path = File.join(@prefix, CREDENTIALS_FILE_NAME)
describe "#from_system_default_path" do
before :example do
@scope = "https://www.googleapis.com/auth/userinfo.profile"
@program_data = ENV["ProgramData"]
@prefix = OS.windows? ? "/etc/Google/Auth/" : "/etc/google/auth/"
@path = File.join @prefix, CREDENTIALS_FILE_NAME
@clz = ServiceAccountCredentials
end
after(:example) do
ENV['ProgramData'] = @program_data
after :example do
ENV["ProgramData"] = @program_data
end
it 'is nil if no file exists' do
it "is nil if no file exists" do
FakeFS do
expect(ServiceAccountCredentials.from_system_default_path(@scope))
.to be_nil
end
end
it 'successfully loads the file when it is present' do
it "successfully loads the file when it is present" do
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text)
ENV["ProgramData"] = "/etc"
FileUtils.mkdir_p File.dirname(@path)
File.write @path, cred_json_text
expect(@clz.from_system_default_path(@scope)).to_not be_nil
File.delete(@path)
File.delete @path
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)
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)
File.delete @path
end
end
end
@ -346,32 +346,32 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
ServiceAccountJwtHeaderCredentials =
Google::Auth::ServiceAccountJwtHeaderCredentials
let(:client_email) { 'app@developer.gserviceaccount.com' }
let(:client_email) { "app@developer.gserviceaccount.com" }
let(:clz) { Google::Auth::ServiceAccountJwtHeaderCredentials }
let(:cred_json) do
let :cred_json do
{
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: client_email,
client_id: 'app.apps.googleusercontent.com',
type: 'service_account',
project_id: 'a_project_id'
private_key_id: "a_private_key_id",
private_key: @key.to_pem,
client_email: client_email,
client_id: "app.apps.googleusercontent.com",
type: "service_account",
project_id: "a_project_id"
}
end
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@client = clz.make_creds(json_key_io: StringIO.new(cred_json_text))
before :example do
@key = OpenSSL::PKey::RSA.new 2048
@client = clz.make_creds json_key_io: StringIO.new(cred_json_text)
end
def cred_json_text
MultiJson.dump(cred_json)
MultiJson.dump cred_json
end
it_behaves_like 'jwt header auth'
it_behaves_like "jwt header auth"
describe '#from_env' do
before(:example) do
describe "#from_env" do
before :example do
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
@ -381,90 +381,90 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
end
after(:example) do
after :example do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
end
it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset" do
ENV.delete @var_name unless ENV[@var_name].nil?
expect(clz.from_env).to be_nil
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist" do
ENV.delete @var_name unless ENV[@var_name].nil?
expect(clz.from_env).to be_nil
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
key_path = File.join dir, "does-not-exist"
ENV[@var_name] = key_path
expect { clz.from_env }.to raise_error RuntimeError
end
end
it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
it "succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid" 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)
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
expect(clz.from_env).to_not be_nil
end
end
it 'succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are'\
' valid' do
it "succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are"\
" valid" do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
expect(clz.from_env(@scope)).to_not be_nil
end
it 'sets project_id when the PROJECT_ID_VAR env var is set' do
it "sets project_id when the PROJECT_ID_VAR env var is set" do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[PROJECT_ID_VAR] = cred_json[:project_id]
ENV[ENV_VAR] = nil
credentials = clz.from_env(@scope)
credentials = clz.from_env @scope
expect(credentials).to_not be_nil
expect(credentials.project_id).to eq(cred_json[:project_id])
end
end
describe '#from_well_known_path' do
before(:example) do
@home = ENV['HOME']
@app_data = ENV['APPDATA']
describe "#from_well_known_path" do
before :example do
@home = ENV["HOME"]
@app_data = ENV["APPDATA"]
end
after(:example) do
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
after :example do
ENV["HOME"] = @home unless @home == ENV["HOME"]
ENV["APPDATA"] = @app_data unless @app_data == ENV["APPDATA"]
end
it 'is nil if no file exists' do
ENV['HOME'] = File.dirname(__FILE__)
it "is nil if no file exists" do
ENV["HOME"] = File.dirname __FILE__
expect(clz.from_well_known_path).to be_nil
end
it 'successfully loads the file when it is present' do
it "successfully loads the file when it is present" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_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
key_path = File.join dir, ".config", WELL_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
expect(clz.from_well_known_path).to_not be_nil
end
end
it 'successfully sets project_id when file is present' do
it "successfully sets project_id when file is present" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_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
credentials = clz.from_well_known_path(@scope)
key_path = File.join dir, ".config", WELL_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
credentials = clz.from_well_known_path @scope
expect(credentials.project_id).to eq(cred_json[:project_id])
end
end

View File

@ -27,61 +27,60 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'googleauth/signet'
require 'jwt'
require 'openssl'
require 'spec_helper'
require "apply_auth_examples"
require "googleauth/signet"
require "jwt"
require "openssl"
require "spec_helper"
describe Signet::OAuth2::Client do
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
before :example do
@key = OpenSSL::PKey::RSA.new 2048
@client = Signet::OAuth2::Client.new(
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
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
)
end
def make_auth_stubs(opts)
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
def make_auth_stubs opts
access_token = opts[:access_token] || ""
body = MultiJson.dump("access_token" => access_token,
"token_type" => "Bearer",
"expires_in" => 3600)
blk = proc do |request|
params = Addressable::URI.form_unencode(request.body)
_claim, _header = JWT.decode(params.assoc('assertion').last,
params = Addressable::URI.form_unencode request.body
_claim, _header = JWT.decode(params.assoc("assertion").last,
@key.public_key, true,
algorithm: 'RS256')
algorithm: "RS256")
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')
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")
.with(with_params, &blk)
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
.to_return(body: body,
status: 200,
headers: { "Content-Type" => "application/json" })
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)
conn = Faraday.new headers: { "User-Agent" => "RubyRocks/1.0" }
@client.configure_connection default_connection: conn
md = { foo: "bar" }
@client.apply!(md)
@client.apply! md
want = { foo: "bar", authorization: "Bearer #{token}" }
expect(md).to eq(want)
expect(stub).to have_been_requested
@ -91,11 +90,11 @@ describe Signet::OAuth2::Client 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"}
Faraday.new headers: { "User-Agent" => "RubyRocks/2.0" }
end
@client.configure_connection(connection_builder: connection_builder)
@client.configure_connection connection_builder: connection_builder
md = { foo: "bar" }
@client.apply!(md)
@client.apply! md
want = { foo: "bar", authorization: "Bearer #{token}" }
expect(md).to eq(want)
expect(stub).to have_been_requested

View File

@ -27,31 +27,31 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'spec_helper'
require 'fakefs/safe'
require 'fakefs/spec_helpers'
require 'googleauth/stores/store_examples'
require "googleauth"
require "googleauth/stores/file_token_store"
require "spec_helper"
require "fakefs/safe"
require "fakefs/spec_helpers"
require "googleauth/stores/store_examples"
module FakeFS
class File
# FakeFS doesn't implement. And since we don't need to actually lock,
# just stub out...
def flock(*); end
def flock *; end
end
end
describe Google::Auth::Stores::FileTokenStore do
include FakeFS::SpecHelpers
let(:store) do
Google::Auth::Stores::FileTokenStore.new(file: '/tokens.yaml')
let :store do
Google::Auth::Stores::FileTokenStore.new file: "/tokens.yaml"
end
it_behaves_like 'token store'
it_behaves_like "token store"
end

View File

@ -27,24 +27,24 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/stores/redis_token_store'
require 'spec_helper'
require 'fakeredis/rspec'
require 'googleauth/stores/store_examples'
require "googleauth"
require "googleauth/stores/redis_token_store"
require "spec_helper"
require "fakeredis/rspec"
require "googleauth/stores/store_examples"
describe Google::Auth::Stores::RedisTokenStore do
let(:redis) do
let :redis do
Redis.new
end
let(:store) do
Google::Auth::Stores::RedisTokenStore.new(redis: redis)
let :store do
Google::Auth::Stores::RedisTokenStore.new redis: redis
end
it_behaves_like 'token store'
it_behaves_like "token store"
end

View File

@ -27,32 +27,32 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'spec_helper'
require "spec_helper"
shared_examples 'token store' do
before(:each) do
store.store('default', 'test')
shared_examples "token store" do
before :each do
store.store "default", "test"
end
it 'should return a stored value' do
expect(store.load('default')).to eq 'test'
it "should return a stored value" do
expect(store.load("default")).to eq "test"
end
it 'should return nil for missing tokens' do
expect(store.load('notavalidkey')).to be_nil
it "should return nil for missing tokens" do
expect(store.load("notavalidkey")).to be_nil
end
it 'should return nil for deleted tokens' do
store.delete('default')
expect(store.load('default')).to be_nil
it "should return nil for deleted tokens" do
store.delete "default"
expect(store.load("default")).to be_nil
end
it 'should save overwrite values on store' do
store.store('default', 'test2')
expect(store.load('default')).to eq 'test2'
it "should save overwrite values on store" do
store.store "default", "test2"
expect(store.load("default")).to eq "test2"
end
end

View File

@ -27,295 +27,294 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/user_authorizer'
require 'uri'
require 'multi_json'
require 'spec_helper'
require "googleauth"
require "googleauth/user_authorizer"
require "uri"
require "multi_json"
require "spec_helper"
describe Google::Auth::UserAuthorizer do
include TestHelpers
let(:client_id) { Google::Auth::ClientId.new('testclient', 'notasecret') }
let(:scope) { %w(email profile) }
let(:client_id) { Google::Auth::ClientId.new "testclient", "notasecret" }
let(:scope) { %w[email profile] }
let(:token_store) { DummyTokenStore.new }
let(:callback_uri) { 'https://www.example.com/oauth/callback' }
let(:authorizer) do
let(:callback_uri) { "https://www.example.com/oauth/callback" }
let :authorizer do
Google::Auth::UserAuthorizer.new(client_id,
scope,
token_store,
callback_uri)
end
shared_examples 'valid authorization url' do
it 'should have a valid base URI' do
shared_examples "valid authorization url" do
it "should have a valid base URI" do
expect(uri).to match %r{https://accounts.google.com/o/oauth2/auth}
end
it 'should request offline access' do
it "should request offline access" do
expect(URI(uri).query).to match(/access_type=offline/)
end
it 'should request response type code' do
it "should request response type code" do
expect(URI(uri).query).to match(/response_type=code/)
end
it 'should force approval' do
it "should force approval" do
expect(URI(uri).query).to match(/approval_prompt=force/)
end
it 'should include granted scopes' do
it "should include granted scopes" do
expect(URI(uri).query).to match(/include_granted_scopes=true/)
end
it 'should include the correct client id' do
it "should include the correct client id" do
expect(URI(uri).query).to match(/client_id=testclient/)
end
it 'should not include a client secret' do
it "should not include a client secret" do
expect(URI(uri).query).to_not match(/client_secret/)
end
it 'should include the callback uri' do
it "should include the callback uri" do
expect(URI(uri).query).to match(
%r{redirect_uri=https://www.example.com/oauth/callback}
)
end
it 'should include the scope' do
it "should include the scope" do
expect(URI(uri).query).to match(/scope=email%20profile/)
end
end
context 'when generating authorization URLs with user ID & state' do
let(:uri) do
authorizer.get_authorization_url(login_hint: 'user1', state: 'mystate')
context "when generating authorization URLs with user ID & state" do
let :uri do
authorizer.get_authorization_url login_hint: "user1", state: "mystate"
end
it_behaves_like 'valid authorization url'
it_behaves_like "valid authorization url"
it 'includes a login hint' do
it "includes a login hint" do
expect(URI(uri).query).to match(/login_hint=user1/)
end
it 'includes the app state' do
it "includes the app state" do
expect(URI(uri).query).to match(/state=mystate/)
end
end
context 'when generating authorization URLs with user ID and no state' do
let(:uri) { authorizer.get_authorization_url(login_hint: 'user1') }
context "when generating authorization URLs with user ID and no state" do
let(:uri) { authorizer.get_authorization_url login_hint: "user1" }
it_behaves_like 'valid authorization url'
it_behaves_like "valid authorization url"
it 'includes a login hint' do
it "includes a login hint" do
expect(URI(uri).query).to match(/login_hint=user1/)
end
it 'does not include the state parameter' do
it "does not include the state parameter" do
expect(URI(uri).query).to_not match(/state/)
end
end
context 'when generating authorization URLs with no user ID and no state' do
context "when generating authorization URLs with no user ID and no state" do
let(:uri) { authorizer.get_authorization_url }
it_behaves_like 'valid authorization url'
it_behaves_like "valid authorization url"
it 'does not include the login hint parameter' do
it "does not include the login hint parameter" do
expect(URI(uri).query).to_not match(/login_hint/)
end
it 'does not include the state parameter' do
it "does not include the state parameter" do
expect(URI(uri).query).to_not match(/state/)
end
end
context 'when retrieving tokens' do
let(:token_json) do
context "when retrieving tokens" do
let :token_json do
MultiJson.dump(
access_token: 'accesstoken',
refresh_token: 'refreshtoken',
access_token: "accesstoken",
refresh_token: "refreshtoken",
expiration_time_millis: 1_441_234_742_000
)
end
context 'with a valid user id' do
let(:credentials) do
token_store.store('user1', token_json)
authorizer.get_credentials('user1')
context "with a valid user id" do
let :credentials do
token_store.store "user1", token_json
authorizer.get_credentials "user1"
end
it 'should return an instance of UserRefreshCredentials' do
it "should return an instance of UserRefreshCredentials" do
expect(credentials).to be_instance_of(
Google::Auth::UserRefreshCredentials
)
end
it 'should return credentials with a valid refresh token' do
expect(credentials.refresh_token).to eq 'refreshtoken'
it "should return credentials with a valid refresh token" do
expect(credentials.refresh_token).to eq "refreshtoken"
end
it 'should return credentials with a valid access token' do
expect(credentials.access_token).to eq 'accesstoken'
it "should return credentials with a valid access token" do
expect(credentials.access_token).to eq "accesstoken"
end
it 'should return credentials with a valid client ID' do
expect(credentials.client_id).to eq 'testclient'
it "should return credentials with a valid client ID" do
expect(credentials.client_id).to eq "testclient"
end
it 'should return credentials with a valid client secret' do
expect(credentials.client_secret).to eq 'notasecret'
it "should return credentials with a valid client secret" do
expect(credentials.client_secret).to eq "notasecret"
end
it 'should return credentials with a valid scope' do
expect(credentials.scope).to eq %w(email profile)
it "should return credentials with a valid scope" do
expect(credentials.scope).to eq %w[email profile]
end
it 'should return credentials with a valid expiration time' do
it "should return credentials with a valid expiration time" do
expect(credentials.expires_at).to eq Time.at(1_441_234_742)
end
end
context 'with an invalid user id' do
it 'should return nil' do
expect(authorizer.get_credentials('notauser')).to be_nil
context "with an invalid user id" do
it "should return nil" do
expect(authorizer.get_credentials("notauser")).to be_nil
end
end
end
context 'when saving tokens' do
context "when saving tokens" do
let(:expiry) { Time.now.to_i }
let(:credentials) do
let :credentials do
Google::Auth::UserRefreshCredentials.new(
client_id: client_id.id,
client_id: client_id.id,
client_secret: client_id.secret,
scope: scope,
refresh_token: 'refreshtoken',
access_token: 'accesstoken',
expires_at: expiry
scope: scope,
refresh_token: "refreshtoken",
access_token: "accesstoken",
expires_at: expiry
)
end
let(:token_json) do
authorizer.store_credentials('user1', credentials)
token_store.load('user1')
let :token_json do
authorizer.store_credentials "user1", credentials
token_store.load "user1"
end
it 'should persist in the token store' do
it "should persist in the token store" do
expect(token_json).to_not be_nil
end
it 'should persist the refresh token' do
expect(MultiJson.load(token_json)['refresh_token']).to eq 'refreshtoken'
it "should persist the refresh token" do
expect(MultiJson.load(token_json)["refresh_token"]).to eq "refreshtoken"
end
it 'should persist the access token' do
expect(MultiJson.load(token_json)['access_token']).to eq 'accesstoken'
it "should persist the access token" do
expect(MultiJson.load(token_json)["access_token"]).to eq "accesstoken"
end
it 'should persist the client id' do
expect(MultiJson.load(token_json)['client_id']).to eq 'testclient'
it "should persist the client id" do
expect(MultiJson.load(token_json)["client_id"]).to eq "testclient"
end
it 'should persist the scope' do
expect(MultiJson.load(token_json)['scope']).to include('email', 'profile')
it "should persist the scope" do
expect(MultiJson.load(token_json)["scope"]).to include("email", "profile")
end
it 'should persist the expiry as milliseconds' do
it "should persist the expiry as milliseconds" do
expected_expiry = expiry * 1000
expect(MultiJson.load(token_json)['expiration_time_millis']).to eql(
expect(MultiJson.load(token_json)["expiration_time_millis"]).to eql(
expected_expiry
)
end
end
context 'with valid authorization code' do
let(:token_json) do
MultiJson.dump('access_token' => '1/abc123',
'token_type' => 'Bearer',
'expires_in' => 3600)
context "with valid authorization code" do
let :token_json do
MultiJson.dump("access_token" => "1/abc123",
"token_type" => "Bearer",
"expires_in" => 3600)
end
before(:example) do
stub_request(:post, 'https://oauth2.googleapis.com/token')
before :example do
stub_request(:post, "https://oauth2.googleapis.com/token")
.to_return(body: token_json, status: 200, headers: {
'Content-Type' => 'application/json'
"Content-Type" => "application/json"
})
end
it 'should exchange a code for credentials' do
it "should exchange a code for credentials" do
credentials = authorizer.get_credentials_from_code(
user_id: 'user1', code: 'code'
user_id: "user1", code: "code"
)
expect(credentials.access_token).to eq '1/abc123'
expect(credentials.access_token).to eq "1/abc123"
end
it 'should not store credentials when get only requested' do
authorizer.get_credentials_from_code(user_id: 'user1', code: 'code')
expect(token_store.load('user1')).to be_nil
it "should not store credentials when get only requested" do
authorizer.get_credentials_from_code user_id: "user1", code: "code"
expect(token_store.load("user1")).to be_nil
end
it 'should store credentials when requested' do
it "should store credentials when requested" do
authorizer.get_and_store_credentials_from_code(
user_id: 'user1', code: 'code'
user_id: "user1", code: "code"
)
expect(token_store.load('user1')).to_not be_nil
expect(token_store.load("user1")).to_not be_nil
end
end
context 'with invalid authorization code' do
before(:example) do
stub_request(:post, 'https://oauth2.googleapis.com/token')
context "with invalid authorization code" do
before :example do
stub_request(:post, "https://oauth2.googleapis.com/token")
.to_return(status: 400)
end
it 'should raise an authorization error' do
it "should raise an authorization error" do
expect do
authorizer.get_credentials_from_code(user_id: 'user1', code: 'badcode')
authorizer.get_credentials_from_code user_id: "user1", code: "badcode"
end.to raise_error Signet::AuthorizationError
end
it 'should not store credentials when exchange fails' do
it "should not store credentials when exchange fails" do
expect do
authorizer.get_credentials_from_code(user_id: 'user1', code: 'badcode')
authorizer.get_credentials_from_code user_id: "user1", code: "badcode"
end.to raise_error Signet::AuthorizationError
expect(token_store.load('user1')).to be_nil
expect(token_store.load("user1")).to be_nil
end
end
context 'when reovking authorization' do
let(:token_json) do
context "when reovking authorization" do
let :token_json do
MultiJson.dump(
access_token: 'accesstoken',
refresh_token: 'refreshtoken',
access_token: "accesstoken",
refresh_token: "refreshtoken",
expiration_time_millis: 1_441_234_742_000
)
end
before(:example) do
token_store.store('user1', token_json)
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'refreshtoken'))
before :example do
token_store.store "user1", token_json
stub_request(:post, "https://oauth2.googleapis.com/revoke")
.with(body: hash_including("token" => "refreshtoken"))
.to_return(status: 200)
end
it 'should revoke the grant' do
authorizer.revoke_authorization('user1')
it "should revoke the grant" do
authorizer.revoke_authorization "user1"
expect(a_request(
:post, 'https://oauth2.googleapis.com/revoke'
).with(body: hash_including('token' => 'refreshtoken'))
)
:post, "https://oauth2.googleapis.com/revoke"
).with(body: hash_including("token" => "refreshtoken")))
.to have_been_made
end
it 'should remove the token from storage' do
authorizer.revoke_authorization('user1')
expect(token_store.load('user1')).to be_nil
it "should remove the token from storage" do
authorizer.revoke_authorization "user1"
expect(token_store.load("user1")).to be_nil
end
end

View File

@ -27,64 +27,64 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'fakefs/safe'
require 'fileutils'
require 'googleauth/user_refresh'
require 'jwt'
require 'multi_json'
require 'openssl'
require 'spec_helper'
require 'tmpdir'
require 'os'
require "apply_auth_examples"
require "fakefs/safe"
require "fileutils"
require "googleauth/user_refresh"
require "jwt"
require "multi_json"
require "openssl"
require "spec_helper"
require "tmpdir"
require "os"
include Google::Auth::CredentialsLoader
describe Google::Auth::UserRefreshCredentials do
UserRefreshCredentials = Google::Auth::UserRefreshCredentials
let(:cred_json) do
let :cred_json do
{
client_secret: 'privatekey',
client_id: 'client123',
refresh_token: 'refreshtoken',
type: 'authorized_user'
client_secret: "privatekey",
client_id: "client123",
refresh_token: "refreshtoken",
type: "authorized_user"
}
end
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
before :example do
@key = OpenSSL::PKey::RSA.new 2048
@client = UserRefreshCredentials.make_creds(
json_key_io: StringIO.new(cred_json_text),
scope: 'https://www.googleapis.com/auth/userinfo.profile'
scope: "https://www.googleapis.com/auth/userinfo.profile"
)
end
def make_auth_stubs(opts = {})
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
stub_request(:post, 'https://oauth2.googleapis.com/token')
.with(body: hash_including('grant_type' => 'refresh_token'))
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
def make_auth_stubs opts = {}
access_token = opts[:access_token] || ""
body = MultiJson.dump("access_token" => access_token,
"token_type" => "Bearer",
"expires_in" => 3600)
stub_request(:post, "https://oauth2.googleapis.com/token")
.with(body: hash_including("grant_type" => "refresh_token"))
.to_return(body: body,
status: 200,
headers: { "Content-Type" => "application/json" })
end
def cred_json_text(missing = nil)
cred_json.delete(missing.to_sym) unless missing.nil?
MultiJson.dump(cred_json)
def cred_json_text missing = nil
cred_json.delete missing.to_sym unless missing.nil?
MultiJson.dump cred_json
end
it_behaves_like 'apply/apply! are OK'
it_behaves_like "apply/apply! are OK"
describe '#from_env' do
before(:example) do
describe "#from_env" do
before :example do
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR,
@ -92,243 +92,243 @@ describe Google::Auth::UserRefreshCredentials do
]
@original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@scope = "https://www.googleapis.com/auth/userinfo.profile"
@clz = UserRefreshCredentials
@project_id = 'a_project_id'
@project_id = "a_project_id"
end
after(:example) do
after :example do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
end
it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset" do
ENV.delete @var_name unless ENV[@var_name].nil?
expect(UserRefreshCredentials.from_env(@scope)).to be_nil
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
it "fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist" do
ENV.delete @var_name unless ENV[@var_name].nil?
expect(UserRefreshCredentials.from_env(@scope)).to be_nil
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
key_path = File.join dir, "does-not-exist"
ENV[@var_name] = key_path
expect { @clz.from_env(@scope) }.to raise_error RuntimeError
expect { @clz.from_env @scope }.to raise_error RuntimeError
end
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path file is invalid' do
needed = %w(client_id client_secret refresh_token)
it "fails if the GOOGLE_APPLICATION_CREDENTIALS path file is invalid" do
needed = %w[client_id client_secret refresh_token]
needed.each do |missing|
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(missing))
key_path = File.join dir, "my_cert_file"
FileUtils.mkdir_p File.dirname(key_path)
File.write key_path, cred_json_text(missing)
ENV[@var_name] = key_path
expect { @clz.from_env(@scope) }.to raise_error RuntimeError
expect { @clz.from_env @scope }.to raise_error RuntimeError
end
end
end
it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
it "succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid" 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)
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
expect(@clz.from_env(@scope)).to_not be_nil
end
end
it 'succeeds when GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and '\
'GOOGLE_REFRESH_TOKEN env vars are valid' do
it "succeeds when GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and "\
"GOOGLE_REFRESH_TOKEN env vars are valid" do
ENV[ENV_VAR] = nil
ENV[CLIENT_ID_VAR] = cred_json[:client_id]
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
creds = @clz.from_env(@scope)
creds = @clz.from_env @scope
expect(creds).to_not be_nil
expect(creds.client_id).to eq(cred_json[:client_id])
expect(creds.client_secret).to eq(cred_json[:client_secret])
expect(creds.refresh_token).to eq(cred_json[:refresh_token])
end
it 'sets project_id when the PROJECT_ID_VAR env var is set' do
it "sets project_id when the PROJECT_ID_VAR env var is set" do
ENV[ENV_VAR] = nil
ENV[CLIENT_ID_VAR] = cred_json[:client_id]
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
ENV[PROJECT_ID_VAR] = @project_id
creds = @clz.from_env(@scope)
creds = @clz.from_env @scope
expect(creds.project_id).to eq(@project_id)
end
end
describe '#from_well_known_path' do
before(:example) do
@home = ENV['HOME']
@app_data = ENV['APPDATA']
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
describe "#from_well_known_path" do
before :example do
@home = ENV["HOME"]
@app_data = ENV["APPDATA"]
@scope = "https://www.googleapis.com/auth/userinfo.profile"
@known_path = WELL_KNOWN_PATH
@clz = UserRefreshCredentials
end
after(:example) do
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
after :example do
ENV["HOME"] = @home unless @home == ENV["HOME"]
ENV["APPDATA"] = @app_data unless @app_data == ENV["APPDATA"]
end
it 'is nil if no file exists' do
ENV['HOME'] = File.dirname(__FILE__)
it "is nil if no file exists" do
ENV["HOME"] = File.dirname __FILE__
expect(UserRefreshCredentials.from_well_known_path(@scope)).to be_nil
end
it 'fails if the file is invalid' do
needed = %w(client_id client_secret refresh_token)
it "fails if the file is invalid" do
needed = %w[client_id client_secret refresh_token]
needed.each do |missing|
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, @known_path) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text(missing))
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect { @clz.from_well_known_path(@scope) }
key_path = File.join dir, ".config", @known_path
key_path = File.join dir, @known_path if OS.windows?
FileUtils.mkdir_p File.dirname(key_path)
File.write key_path, cred_json_text(missing)
ENV["HOME"] = dir
ENV["APPDATA"] = dir
expect { @clz.from_well_known_path @scope }
.to raise_error RuntimeError
end
end
end
it 'successfully loads the file when it is present' do
it "successfully loads the file when it is present" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, @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
key_path = File.join dir, ".config", @known_path
key_path = File.join dir, @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
expect(@clz.from_well_known_path(@scope)).to_not be_nil
end
end
it 'checks gcloud config for project_id if none was provided' do
it "checks gcloud config for project_id if none was provided" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, @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
key_path = File.join dir, ".config", @known_path
key_path = File.join dir, @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
ENV[PROJECT_ID_VAR] = nil
expect(Google::Auth::CredentialsLoader).to receive(:load_gcloud_project_id).with(no_args)
@clz.from_well_known_path(@scope)
@clz.from_well_known_path @scope
end
end
end
describe '#from_system_default_path' do
before(:example) do
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@prefix = OS.windows? ? '/etc/Google/Auth/' : '/etc/google/auth/'
@path = File.join(@prefix, CREDENTIALS_FILE_NAME)
@program_data = ENV['ProgramData']
describe "#from_system_default_path" do
before :example do
@scope = "https://www.googleapis.com/auth/userinfo.profile"
@prefix = OS.windows? ? "/etc/Google/Auth/" : "/etc/google/auth/"
@path = File.join @prefix, CREDENTIALS_FILE_NAME
@program_data = ENV["ProgramData"]
@clz = UserRefreshCredentials
end
after(:example) do
ENV['ProgramData'] = @program_data
after :example do
ENV["ProgramData"] = @program_data
end
it 'is nil if no file exists' do
it "is nil if no file exists" do
FakeFS do
expect(UserRefreshCredentials.from_system_default_path(@scope))
.to be_nil
end
end
it 'fails if the file is invalid' do
needed = %w(client_id client_secret refresh_token)
it "fails if the file is invalid" do
needed = %w[client_id client_secret refresh_token]
needed.each do |missing|
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text(missing))
expect { @clz.from_system_default_path(@scope) }
ENV["ProgramData"] = "/etc"
FileUtils.mkdir_p File.dirname(@path)
File.write @path, cred_json_text(missing)
expect { @clz.from_system_default_path @scope }
.to raise_error RuntimeError
File.delete(@path)
File.delete @path
end
end
end
it 'successfully loads the file when it is present' do
it "successfully loads the file when it is present" do
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text)
ENV["ProgramData"] = "/etc"
FileUtils.mkdir_p File.dirname(@path)
File.write @path, cred_json_text
expect(@clz.from_system_default_path(@scope)).to_not be_nil
File.delete(@path)
File.delete @path
end
end
end
shared_examples 'revoked token' do
it 'should nil the refresh token' do
shared_examples "revoked token" do
it "should nil the refresh token" do
expect(@client.refresh_token).to be_nil
end
it 'should nil the access token' do
it "should nil the access token" do
expect(@client.access_token).to be_nil
end
it 'should mark the token as expired' do
it "should mark the token as expired" do
expect(@client.expired?).to be_truthy
end
end
describe 'when revoking a refresh token' do
let(:stub) do
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'refreshtoken'))
.to_return(status: 200,
headers: { 'Content-Type' => 'application/json' })
describe "when revoking a refresh token" do
let :stub do
stub_request(:post, "https://oauth2.googleapis.com/revoke")
.with(body: hash_including("token" => "refreshtoken"))
.to_return(status: 200,
headers: { "Content-Type" => "application/json" })
end
before(:example) do
before :example do
stub
@client.revoke!
end
it_behaves_like 'revoked token'
it_behaves_like "revoked token"
end
describe 'when revoking an access token' do
let(:stub) do
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'accesstoken'))
.to_return(status: 200,
headers: { 'Content-Type' => 'application/json' })
describe "when revoking an access token" do
let :stub do
stub_request(:post, "https://oauth2.googleapis.com/revoke")
.with(body: hash_including("token" => "accesstoken"))
.to_return(status: 200,
headers: { "Content-Type" => "application/json" })
end
before(:example) do
before :example do
stub
@client.refresh_token = nil
@client.access_token = 'accesstoken'
@client.access_token = "accesstoken"
@client.revoke!
end
it_behaves_like 'revoked token'
it_behaves_like "revoked token"
end
describe 'when revoking an invalid token' do
let(:stub) do
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'refreshtoken'))
.to_return(status: 400,
headers: { 'Content-Type' => 'application/json' })
describe "when revoking an invalid token" do
let :stub do
stub_request(:post, "https://oauth2.googleapis.com/revoke")
.with(body: hash_including("token" => "refreshtoken"))
.to_return(status: 400,
headers: { "Content-Type" => "application/json" })
end
it 'raises an authorization error' do
it "raises an authorization error" do
stub
expect { @client.revoke! }.to raise_error(
Signet::AuthorizationError
@ -336,15 +336,15 @@ describe Google::Auth::UserRefreshCredentials do
end
end
describe 'when errors occurred with request' do
it 'should fail with Signet::AuthorizationError if request times out' do
describe "when errors occurred with request" do
it "should fail with Signet::AuthorizationError if request times out" do
allow_any_instance_of(Faraday::Connection).to receive(:post)
.and_raise(Faraday::TimeoutError)
expect { @client.revoke! }
.to raise_error Signet::AuthorizationError
end
it 'should fail with Signet::AuthorizationError if request fails' do
it "should fail with Signet::AuthorizationError if request fails" do
allow_any_instance_of(Faraday::Connection).to receive(:post)
.and_raise(Faraday::ConnectionFailed, nil)
expect { @client.revoke! }

View File

@ -27,140 +27,140 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'uri'
require 'multi_json'
require 'spec_helper'
require 'rack'
require "googleauth"
require "googleauth/web_user_authorizer"
require "uri"
require "multi_json"
require "spec_helper"
require "rack"
describe Google::Auth::WebUserAuthorizer do
include TestHelpers
let(:client_id) { Google::Auth::ClientId.new('testclient', 'notasecret') }
let(:scope) { %w(email profile) }
let(:client_id) { Google::Auth::ClientId.new "testclient", "notasecret" }
let(:scope) { %w[email profile] }
let(:token_store) { DummyTokenStore.new }
let(:authorizer) do
Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store)
let :authorizer do
Google::Auth::WebUserAuthorizer.new client_id, scope, token_store
end
describe '#get_authorization_url' do
let(:env) do
describe "#get_authorization_url" do
let :env do
Rack::MockRequest.env_for(
'http://example.com:8080/test',
'REMOTE_ADDR' => '10.10.10.10'
"http://example.com:8080/test",
"REMOTE_ADDR" => "10.10.10.10"
)
end
let(:request) { Rack::Request.new(env) }
it 'should include current url in state' do
url = authorizer.get_authorization_url(request: request)
let(:request) { Rack::Request.new env }
it "should include current url in state" do
url = authorizer.get_authorization_url request: request
expect(url).to match(
%r{%22current_uri%22:%22http://example.com:8080/test%22}
)
end
it 'should include request forgery token in state' do
expect(SecureRandom).to receive(:base64).and_return('aGVsbG8=')
url = authorizer.get_authorization_url(request: request)
it "should include request forgery token in state" do
expect(SecureRandom).to receive(:base64).and_return("aGVsbG8=")
url = authorizer.get_authorization_url request: request
expect(url).to match(/%22session_id%22:%22aGVsbG8=%22/)
end
it 'should include request forgery token in session' do
expect(SecureRandom).to receive(:base64).and_return('aGVsbG8=')
authorizer.get_authorization_url(request: request)
expect(request.session['g-xsrf-token']).to eq 'aGVsbG8='
it "should include request forgery token in session" do
expect(SecureRandom).to receive(:base64).and_return("aGVsbG8=")
authorizer.get_authorization_url request: request
expect(request.session["g-xsrf-token"]).to eq "aGVsbG8="
end
it 'should resolve callback against base URL' do
url = authorizer.get_authorization_url(request: request)
it "should resolve callback against base URL" do
url = authorizer.get_authorization_url request: request
expect(url).to match(
%r{redirect_uri=http://example.com:8080/oauth2callback}
)
end
it 'should allow overriding the current URL' do
it "should allow overriding the current URL" do
url = authorizer.get_authorization_url(
request: request,
redirect_to: '/foo'
request: request,
redirect_to: "/foo"
)
expect(url).to match %r{%22current_uri%22:%22/foo%22}
end
it 'should pass through login hint' do
it "should pass through login hint" do
url = authorizer.get_authorization_url(
request: request,
login_hint: 'user@example.com'
request: request,
login_hint: "user@example.com"
)
expect(url).to match(/login_hint=user@example.com/)
end
end
shared_examples 'handles callback' do
let(:token_json) do
MultiJson.dump('access_token' => '1/abc123',
'token_type' => 'Bearer',
'expires_in' => 3600)
shared_examples "handles callback" do
let :token_json do
MultiJson.dump("access_token" => "1/abc123",
"token_type" => "Bearer",
"expires_in" => 3600)
end
before(:example) do
stub_request(:post, 'https://oauth2.googleapis.com/token')
.to_return(body: token_json,
status: 200,
headers: { 'Content-Type' => 'application/json' })
before :example do
stub_request(:post, "https://oauth2.googleapis.com/token")
.to_return(body: token_json,
status: 200,
headers: { "Content-Type" => "application/json" })
end
let(:env) do
let :env do
Rack::MockRequest.env_for(
'http://example.com:8080/oauth2callback?code=authcode&'\
'state=%7B%22current_uri%22%3A%22%2Ffoo%22%2C%22'\
'session_id%22%3A%22abc%22%7D',
'REMOTE_ADDR' => '10.10.10.10'
"http://example.com:8080/oauth2callback?code=authcode&"\
"state=%7B%22current_uri%22%3A%22%2Ffoo%22%2C%22"\
"session_id%22%3A%22abc%22%7D",
"REMOTE_ADDR" => "10.10.10.10"
)
end
let(:request) { Rack::Request.new(env) }
let(:request) { Rack::Request.new env }
before(:example) do
request.session['g-xsrf-token'] = 'abc'
before :example do
request.session["g-xsrf-token"] = "abc"
end
it 'should return credentials when valid code present' do
it "should return credentials when valid code present" do
expect(credentials).to be_instance_of(
Google::Auth::UserRefreshCredentials
)
end
it 'should return next URL to redirect to' do
expect(next_url).to eq '/foo'
it "should return next URL to redirect to" do
expect(next_url).to eq "/foo"
end
it 'should fail if xrsf token in session and does not match request' do
request.session['g-xsrf-token'] = '123'
it "should fail if xrsf token in session and does not match request" do
request.session["g-xsrf-token"] = "123"
expect { credentials }.to raise_error(Signet::AuthorizationError)
end
end
describe '#handle_auth_callback' do
let(:result) { authorizer.handle_auth_callback('user1', request) }
describe "#handle_auth_callback" do
let(:result) { authorizer.handle_auth_callback "user1", request }
let(:credentials) { result[0] }
let(:next_url) { result[1] }
it_behaves_like 'handles callback'
it_behaves_like "handles callback"
end
describe '#handle_auth_callback_deferred and #get_credentials' do
let(:next_url) do
Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
describe "#handle_auth_callback_deferred and #get_credentials" do
let :next_url do
Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred request
end
let(:credentials) do
let :credentials do
next_url
authorizer.get_credentials('user1', request)
authorizer.get_credentials "user1", request
end
it_behaves_like 'handles callback'
it_behaves_like "handles callback"
end
end

View File

@ -27,17 +27,17 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.dirname(__FILE__))
root_dir = File.expand_path(File.join(spec_dir, '..'))
lib_dir = File.expand_path(File.join(root_dir, 'lib'))
spec_dir = __dir__
root_dir = File.expand_path File.join(spec_dir, "..")
lib_dir = File.expand_path File.join(root_dir, "lib")
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.unshift(lib_dir)
$LOAD_PATH.unshift spec_dir
$LOAD_PATH.unshift lib_dir
$LOAD_PATH.uniq!
# set up coverage
require 'simplecov'
require 'coveralls'
require "simplecov"
require "coveralls"
SimpleCov.formatters = [
Coveralls::SimpleCov::Formatter,
@ -45,18 +45,18 @@ SimpleCov.formatters = [
]
SimpleCov.start
require 'faraday'
require 'rspec'
require 'logging'
require 'rspec/logging_helper'
require 'webmock/rspec'
require 'multi_json'
require "faraday"
require "rspec"
require "logging"
require "rspec/logging_helper"
require "webmock/rspec"
require "multi_json"
# Preload adapter to work around Rubinius error with FakeFS
MultiJson.use(:json_gem)
MultiJson.use :json_gem
# Allow Faraday to support test stubs
Faraday::Adapter.load_middleware(:test)
Faraday::Adapter.load_middleware :test
# Configure RSpec to capture log messages for each test. The output from the
# logs will be stored in the @log_output variable. It is a StringIO instance.
@ -78,15 +78,15 @@ class DummyTokenStore
@tokens = {}
end
def load(id)
def load id
@tokens[id]
end
def store(id, token)
def store id, token
@tokens[id] = token
end
def delete(id)
@tokens.delete(id)
def delete id
@tokens.delete id
end
end