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
This commit is contained in:
Todd Derr 2015-06-29 16:35:45 -04:00
parent dc12124faf
commit 86de5fe13b
6 changed files with 108 additions and 2 deletions

View File

@ -35,6 +35,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'bundler', '~> 1.9' s.add_development_dependency 'bundler', '~> 1.9'
s.add_development_dependency 'simplecov', '~> 0.9' s.add_development_dependency 'simplecov', '~> 0.9'
s.add_development_dependency 'coveralls', '~> 0.7' 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 'rake', '~> 10.0'
s.add_development_dependency 'rubocop', '~> 0.30' s.add_development_dependency 'rubocop', '~> 0.30'
s.add_development_dependency 'rspec', '~> 3.0' s.add_development_dependency 'rspec', '~> 3.0'

View File

@ -110,7 +110,8 @@ END
# @param options [hash] allows override of the connection being used # @param options [hash] allows override of the connection being used
def get_application_default(scope = nil, options = {}) def get_application_default(scope = nil, options = {})
creds = DefaultCredentials.from_env(scope) || 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? return creds unless creds.nil?
fail NOT_FOUND_ERROR unless GCECredentials.on_gce?(options) fail NOT_FOUND_ERROR unless GCECredentials.on_gce?(options)
GCECredentials.new GCECredentials.new

View File

@ -47,11 +47,14 @@ module Google
REFRESH_TOKEN_VAR = 'GOOGLE_REFRESH_TOKEN' REFRESH_TOKEN_VAR = 'GOOGLE_REFRESH_TOKEN'
ACCOUNT_TYPE_VAR = 'GOOGLE_ACCOUNT_TYPE' ACCOUNT_TYPE_VAR = 'GOOGLE_ACCOUNT_TYPE'
CREDENTIALS_FILE_NAME = 'application_default_credentials.json'
NOT_FOUND_ERROR = NOT_FOUND_ERROR =
"Unable to read the credential file specified by #{ENV_VAR}" "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' 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 # determines if the current OS is windows
def windows? def windows?
RbConfig::CONFIG['host_os'] =~ /Windows|mswin/ RbConfig::CONFIG['host_os'] =~ /Windows|mswin/
@ -100,6 +103,25 @@ module Google
raise "#{WELL_KNOWN_ERROR}: #{e}" raise "#{WELL_KNOWN_ERROR}: #{e}"
end 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 private
def service_account_env_vars? def service_account_env_vars?

View File

@ -32,6 +32,7 @@ $LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq! $LOAD_PATH.uniq!
require 'faraday' require 'faraday'
require 'fakefs/safe'
require 'googleauth' require 'googleauth'
require 'spec_helper' require 'spec_helper'
@ -140,6 +141,19 @@ describe '#get_application_default' do
stubs.verify_stubbed_calls stubs.verify_stubbed_calls
end 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 it 'succeeds if environment vars are valid' do
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key] ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]

View File

@ -32,6 +32,7 @@ $LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq! $LOAD_PATH.uniq!
require 'apply_auth_examples' require 'apply_auth_examples'
require 'fakefs/safe'
require 'fileutils' require 'fileutils'
require 'googleauth/service_account' require 'googleauth/service_account'
require 'jwt' require 'jwt'
@ -231,6 +232,32 @@ describe Google::Auth::ServiceAccountCredentials do
end end
end 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 end
describe Google::Auth::ServiceAccountJwtHeaderCredentials do describe Google::Auth::ServiceAccountJwtHeaderCredentials do

View File

@ -32,6 +32,7 @@ $LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq! $LOAD_PATH.uniq!
require 'apply_auth_examples' require 'apply_auth_examples'
require 'fakefs/safe'
require 'fileutils' require 'fileutils'
require 'googleauth/user_refresh' require 'googleauth/user_refresh'
require 'jwt' require 'jwt'
@ -185,4 +186,44 @@ describe Google::Auth::UserRefreshCredentials do
end end
end 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 end