From 86de5fe13bfd0dc66c3b4675ca2d83f2e124f4cc Mon Sep 17 00:00:00 2001 From: Todd Derr Date: Mon, 29 Jun 2015 16:35:45 -0400 Subject: [PATCH 1/3] Add support for a system default credentials file. The path is: - windows: %ProgramData%\Google\Auth\application_default_credentials.json - other: /etc/google/auth/application_default_credentials.json --- googleauth.gemspec | 1 + lib/googleauth.rb | 3 +- lib/googleauth/credentials_loader.rb | 24 ++++++++++- .../get_application_default_spec.rb | 14 +++++++ spec/googleauth/service_account_spec.rb | 27 ++++++++++++ spec/googleauth/user_refresh_spec.rb | 41 +++++++++++++++++++ 6 files changed, 108 insertions(+), 2 deletions(-) diff --git a/googleauth.gemspec b/googleauth.gemspec index 35c9b70..813e419 100755 --- a/googleauth.gemspec +++ b/googleauth.gemspec @@ -35,6 +35,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'bundler', '~> 1.9' s.add_development_dependency 'simplecov', '~> 0.9' s.add_development_dependency 'coveralls', '~> 0.7' + s.add_development_dependency 'fakefs', '~> 0.6' s.add_development_dependency 'rake', '~> 10.0' s.add_development_dependency 'rubocop', '~> 0.30' s.add_development_dependency 'rspec', '~> 3.0' diff --git a/lib/googleauth.rb b/lib/googleauth.rb index 520bbf4..cacf94c 100644 --- a/lib/googleauth.rb +++ b/lib/googleauth.rb @@ -110,7 +110,8 @@ END # @param options [hash] allows override of the connection being used def get_application_default(scope = nil, options = {}) creds = DefaultCredentials.from_env(scope) || - DefaultCredentials.from_well_known_path(scope) + DefaultCredentials.from_well_known_path(scope) || + DefaultCredentials.from_system_default_path(scope) return creds unless creds.nil? fail NOT_FOUND_ERROR unless GCECredentials.on_gce?(options) GCECredentials.new diff --git a/lib/googleauth/credentials_loader.rb b/lib/googleauth/credentials_loader.rb index d3cbf0c..04d5fc0 100644 --- a/lib/googleauth/credentials_loader.rb +++ b/lib/googleauth/credentials_loader.rb @@ -47,11 +47,14 @@ module Google REFRESH_TOKEN_VAR = 'GOOGLE_REFRESH_TOKEN' ACCOUNT_TYPE_VAR = 'GOOGLE_ACCOUNT_TYPE' + CREDENTIALS_FILE_NAME = 'application_default_credentials.json' NOT_FOUND_ERROR = "Unable to read the credential file specified by #{ENV_VAR}" - WELL_KNOWN_PATH = 'gcloud/application_default_credentials.json' + WELL_KNOWN_PATH = "gcloud/#{CREDENTIALS_FILE_NAME}" WELL_KNOWN_ERROR = 'Unable to read the default credential file' + SYSTEM_DEFAULT_ERROR = 'Unable to read the system default credential file' + # determines if the current OS is windows def windows? RbConfig::CONFIG['host_os'] =~ /Windows|mswin/ @@ -100,6 +103,25 @@ module Google raise "#{WELL_KNOWN_ERROR}: #{e}" end + # Creates an instance from the system default path + # + # @param scope [string|array|nil] the scope(s) to access + def from_system_default_path(scope = nil) + if windows? + return nil unless ENV['ProgramData'] + 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(json_key_io: f, scope: scope) + end + rescue StandardError => e + raise "#{SYSTEM_DEFAULT_ERROR}: #{e}" + end + private def service_account_env_vars? diff --git a/spec/googleauth/get_application_default_spec.rb b/spec/googleauth/get_application_default_spec.rb index e143b0b..981a31b 100644 --- a/spec/googleauth/get_application_default_spec.rb +++ b/spec/googleauth/get_application_default_spec.rb @@ -32,6 +32,7 @@ $LOAD_PATH.unshift(spec_dir) $LOAD_PATH.uniq! require 'faraday' +require 'fakefs/safe' require 'googleauth' require 'spec_helper' @@ -140,6 +141,19 @@ describe '#get_application_default' do stubs.verify_stubbed_calls end + it 'succeeds with system default file' do + ENV.delete(@var_name) unless ENV[@var_name].nil? + FakeFS do + Dir.mktmpdir do |dir| + key_path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME) + FileUtils.mkdir_p(File.dirname(key_path)) + File.write(key_path, cred_json_text) + expect(Google::Auth.get_application_default(@scope)).to_not be_nil + File.delete(key_path) + end + end + end + 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] diff --git a/spec/googleauth/service_account_spec.rb b/spec/googleauth/service_account_spec.rb index 7c3209f..2938f0f 100644 --- a/spec/googleauth/service_account_spec.rb +++ b/spec/googleauth/service_account_spec.rb @@ -32,6 +32,7 @@ $LOAD_PATH.unshift(spec_dir) $LOAD_PATH.uniq! require 'apply_auth_examples' +require 'fakefs/safe' require 'fileutils' require 'googleauth/service_account' require 'jwt' @@ -231,6 +232,32 @@ describe Google::Auth::ServiceAccountCredentials do end end end + + describe '#from_system_default_path' do + before(:example) do + @scope = 'https://www.googleapis.com/auth/userinfo.profile' + @path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME) + @clz = ServiceAccountCredentials + end + + 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 + FakeFS do + Dir.mktmpdir do |dir| + 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) + end + end + end + end end describe Google::Auth::ServiceAccountJwtHeaderCredentials do diff --git a/spec/googleauth/user_refresh_spec.rb b/spec/googleauth/user_refresh_spec.rb index 64047ec..f350ea1 100644 --- a/spec/googleauth/user_refresh_spec.rb +++ b/spec/googleauth/user_refresh_spec.rb @@ -32,6 +32,7 @@ $LOAD_PATH.unshift(spec_dir) $LOAD_PATH.uniq! require 'apply_auth_examples' +require 'fakefs/safe' require 'fileutils' require 'googleauth/user_refresh' require 'jwt' @@ -185,4 +186,44 @@ describe Google::Auth::UserRefreshCredentials do end end end + + describe '#from_system_default_path' do + before(:example) do + @scope = 'https://www.googleapis.com/auth/userinfo.profile' + @path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME) + @clz = UserRefreshCredentials + end + + 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) + needed.each do |missing| + FakeFS do + Dir.mktmpdir do |dir| + FileUtils.mkdir_p(File.dirname(@path)) + File.write(@path, cred_json_text(missing)) + expect { @clz.from_system_default_path(@scope) }.to raise_error + File.delete(@path) + end + end + end + end + + it 'successfully loads the file when it is present' do + FakeFS do + Dir.mktmpdir do |dir| + 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) + end + end + end + end end From c04e91569dc6c8183934b7b3e7846a35fda86c3d Mon Sep 17 00:00:00 2001 From: Todd Derr Date: Mon, 29 Jun 2015 16:56:42 -0400 Subject: [PATCH 2/3] Eliminate a new 'raise_error' WARNING. I have another outstanding PR to fix these; so avoid introducing another one here. --- spec/googleauth/user_refresh_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/googleauth/user_refresh_spec.rb b/spec/googleauth/user_refresh_spec.rb index f350ea1..4462a6e 100644 --- a/spec/googleauth/user_refresh_spec.rb +++ b/spec/googleauth/user_refresh_spec.rb @@ -208,7 +208,8 @@ describe Google::Auth::UserRefreshCredentials do Dir.mktmpdir do |dir| FileUtils.mkdir_p(File.dirname(@path)) File.write(@path, cred_json_text(missing)) - expect { @clz.from_system_default_path(@scope) }.to raise_error + expect { @clz.from_system_default_path(@scope) }. + to raise_error RuntimeError File.delete(@path) end end From f0de3295fcb7f26bcf9d41cc31080509f30fc0c7 Mon Sep 17 00:00:00 2001 From: Todd Derr Date: Tue, 30 Jun 2015 17:42:30 -0400 Subject: [PATCH 3/3] Fix some issues discovered by rubocop. fix failures due to: - misformatting of wrapped lines - a superflous block (and temp dir creation) due to overzealous cut & paste. --- .../get_application_default_spec.rb | 12 ++++----- spec/googleauth/service_account_spec.rb | 14 +++++----- spec/googleauth/user_refresh_spec.rb | 26 ++++++++----------- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/spec/googleauth/get_application_default_spec.rb b/spec/googleauth/get_application_default_spec.rb index 981a31b..05128c0 100644 --- a/spec/googleauth/get_application_default_spec.rb +++ b/spec/googleauth/get_application_default_spec.rb @@ -144,13 +144,11 @@ describe '#get_application_default' do it 'succeeds with system default file' do ENV.delete(@var_name) unless ENV[@var_name].nil? FakeFS do - Dir.mktmpdir do |dir| - key_path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME) - FileUtils.mkdir_p(File.dirname(key_path)) - File.write(key_path, cred_json_text) - expect(Google::Auth.get_application_default(@scope)).to_not be_nil - File.delete(key_path) - end + key_path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME) + FileUtils.mkdir_p(File.dirname(key_path)) + File.write(key_path, cred_json_text) + expect(Google::Auth.get_application_default(@scope)).to_not be_nil + File.delete(key_path) end end diff --git a/spec/googleauth/service_account_spec.rb b/spec/googleauth/service_account_spec.rb index 2938f0f..24f597b 100644 --- a/spec/googleauth/service_account_spec.rb +++ b/spec/googleauth/service_account_spec.rb @@ -242,19 +242,17 @@ describe Google::Auth::ServiceAccountCredentials do it 'is nil if no file exists' do FakeFS do - expect(ServiceAccountCredentials.from_system_default_path(@scope)). - to be_nil + expect(ServiceAccountCredentials.from_system_default_path(@scope)) + .to be_nil end end it 'successfully loads the file when it is present' do FakeFS do - Dir.mktmpdir do |dir| - 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) - end + 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) end end end diff --git a/spec/googleauth/user_refresh_spec.rb b/spec/googleauth/user_refresh_spec.rb index 4462a6e..394edbd 100644 --- a/spec/googleauth/user_refresh_spec.rb +++ b/spec/googleauth/user_refresh_spec.rb @@ -196,8 +196,8 @@ describe Google::Auth::UserRefreshCredentials do it 'is nil if no file exists' do FakeFS do - expect(UserRefreshCredentials.from_system_default_path(@scope)). - to be_nil + expect(UserRefreshCredentials.from_system_default_path(@scope)) + .to be_nil end end @@ -205,25 +205,21 @@ describe Google::Auth::UserRefreshCredentials do needed = %w(client_id client_secret refresh_token) needed.each do |missing| FakeFS do - Dir.mktmpdir do |dir| - 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) - end + 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) end end end it 'successfully loads the file when it is present' do FakeFS do - Dir.mktmpdir do |dir| - 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) - end + 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) end end end