Add project_id instance variable (#167)
This commit is contained in:
parent
818a031f5a
commit
f0b0c6f8e8
|
@ -1,3 +1,7 @@
|
|||
## 0.7.0 (2018/10/23)
|
||||
|
||||
* Add project_id instance variable to UserRefreshCredentials, ServiceAccountCredentials, and Credentials.
|
||||
|
||||
## 0.6.7 (2018/10/16)
|
||||
|
||||
* Update memoist dependency to ~> 0.16.
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
# (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'
|
||||
|
@ -46,6 +48,7 @@ module Google
|
|||
DEFAULT_PATHS = [].freeze
|
||||
|
||||
attr_accessor :client
|
||||
attr_reader :project_id
|
||||
|
||||
# Delegate client methods to the client object.
|
||||
extend Forwardable
|
||||
|
@ -56,19 +59,24 @@ module Google
|
|||
def initialize(keyfile, options = {})
|
||||
scope = options[:scope]
|
||||
verify_keyfile_provided! keyfile
|
||||
@project_id = options['project_id'] || options['project']
|
||||
if keyfile.is_a? Signet::OAuth2::Client
|
||||
@client = keyfile
|
||||
@project_id ||= keyfile.project_id if keyfile.respond_to? :project_id
|
||||
elsif keyfile.is_a? Hash
|
||||
hash = stringify_hash_keys keyfile
|
||||
hash['scope'] ||= scope
|
||||
@client = init_client hash
|
||||
@project_id ||= (hash['project_id'] || hash['project'])
|
||||
else
|
||||
verify_keyfile_exists! keyfile
|
||||
json = JSON.parse ::File.read(keyfile)
|
||||
json['scope'] ||= scope
|
||||
@project_id ||= (json['project_id'] || json['project'])
|
||||
@client = init_client json
|
||||
end
|
||||
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
|
||||
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
||||
@client.fetch_access_token!
|
||||
end
|
||||
|
||||
|
|
|
@ -40,13 +40,16 @@ module Google
|
|||
module CredentialsLoader
|
||||
extend Memoist
|
||||
ENV_VAR = 'GOOGLE_APPLICATION_CREDENTIALS'.freeze
|
||||
|
||||
PRIVATE_KEY_VAR = 'GOOGLE_PRIVATE_KEY'.freeze
|
||||
CLIENT_EMAIL_VAR = 'GOOGLE_CLIENT_EMAIL'.freeze
|
||||
CLIENT_ID_VAR = 'GOOGLE_CLIENT_ID'.freeze
|
||||
CLIENT_SECRET_VAR = 'GOOGLE_CLIENT_SECRET'.freeze
|
||||
REFRESH_TOKEN_VAR = 'GOOGLE_REFRESH_TOKEN'.freeze
|
||||
ACCOUNT_TYPE_VAR = 'GOOGLE_ACCOUNT_TYPE'.freeze
|
||||
PROJECT_ID_VAR = 'GOOGLE_PROJECT_ID'.freeze
|
||||
GCLOUD_POSIX_COMMAND = 'gcloud'.freeze
|
||||
GCLOUD_WINDOWS_COMMAND = 'gcloud.cmd'.freeze
|
||||
GCLOUD_CONFIG_COMMAND = 'config config-helper --format json'.freeze
|
||||
|
||||
CREDENTIALS_FILE_NAME = 'application_default_credentials.json'.freeze
|
||||
NOT_FOUND_ERROR =
|
||||
|
@ -136,6 +139,15 @@ module Google
|
|||
end
|
||||
module_function :warn_if_cloud_sdk_credentials
|
||||
|
||||
def load_gcloud_project_id
|
||||
gcloud = GCLOUD_WINDOWS_COMMAND if OS.windows?
|
||||
gcloud = GCLOUD_POSIX_COMMAND unless OS.windows?
|
||||
config = MultiJson.load(`#{gcloud} #{GCLOUD_CONFIG_COMMAND}`)
|
||||
config['configuration']['properties']['core']['project']
|
||||
rescue
|
||||
warn 'Unable to determine project id.'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service_account_env_vars?
|
||||
|
|
|
@ -38,7 +38,8 @@ module Google
|
|||
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')
|
||||
[json_key['private_key'], json_key['client_email']]
|
||||
project_id = json_key['project_id']
|
||||
[json_key['private_key'], json_key['client_email'], project_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,6 +50,7 @@ module Google
|
|||
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v4/token'.freeze
|
||||
extend CredentialsLoader
|
||||
extend JsonKeyReader
|
||||
attr_reader :project_id
|
||||
|
||||
# Creates a ServiceAccountCredentials.
|
||||
#
|
||||
|
@ -58,17 +59,20 @@ module Google
|
|||
def self.make_creds(options = {})
|
||||
json_key_io, scope = options.values_at(:json_key_io, :scope)
|
||||
if json_key_io
|
||||
private_key, client_email = 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]
|
||||
project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
||||
end
|
||||
project_id ||= self.class.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))
|
||||
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
||||
project_id: project_id)
|
||||
end
|
||||
|
||||
# Handles certain escape sequences that sometimes appear in input.
|
||||
|
@ -81,6 +85,7 @@ module Google
|
|||
end
|
||||
|
||||
def initialize(options = {})
|
||||
@project_id = options[:project_id]
|
||||
super(options)
|
||||
end
|
||||
|
||||
|
@ -126,6 +131,7 @@ module Google
|
|||
EXPIRY = 60
|
||||
extend CredentialsLoader
|
||||
extend JsonKeyReader
|
||||
attr_reader :project_id
|
||||
|
||||
# make_creds proxies the construction of a credentials instance
|
||||
#
|
||||
|
@ -144,14 +150,15 @@ module Google
|
|||
def initialize(options = {})
|
||||
json_key_io = options[:json_key_io]
|
||||
if json_key_io
|
||||
private_key, client_email = self.class.read_json_key(json_key_io)
|
||||
@private_key, @issuer, @project_id =
|
||||
self.class.read_json_key(json_key_io)
|
||||
else
|
||||
private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
||||
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
||||
@private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
||||
@issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
||||
@project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
||||
end
|
||||
@private_key = private_key
|
||||
@issuer = client_email
|
||||
@signing_key = OpenSSL::PKey::RSA.new(private_key)
|
||||
@project_id ||= self.class.load_gcloud_project_id
|
||||
@signing_key = OpenSSL::PKey::RSA.new(@private_key)
|
||||
end
|
||||
|
||||
# Construct a jwt token if the JWT_AUD_URI key is present in the input
|
||||
|
|
|
@ -50,6 +50,7 @@ module Google
|
|||
AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth'.freeze
|
||||
REVOKE_TOKEN_URI = 'https://oauth2.googleapis.com/revoke'.freeze
|
||||
extend CredentialsLoader
|
||||
attr_reader :project_id
|
||||
|
||||
# Create a UserRefreshCredentials.
|
||||
#
|
||||
|
@ -61,13 +62,15 @@ module Google
|
|||
user_creds ||= {
|
||||
'client_id' => ENV[CredentialsLoader::CLIENT_ID_VAR],
|
||||
'client_secret' => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
|
||||
'refresh_token' => ENV[CredentialsLoader::REFRESH_TOKEN_VAR]
|
||||
'refresh_token' => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
|
||||
'project_id' => ENV[CredentialsLoader::PROJECT_ID_VAR]
|
||||
}
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
@ -86,6 +89,8 @@ module Google
|
|||
options ||= {}
|
||||
options[:token_credential_uri] ||= TOKEN_CRED_URI
|
||||
options[:authorization_uri] ||= AUTHORIZATION_URI
|
||||
@project_id = options[:project_id]
|
||||
@project_id ||= self.class.load_gcloud_project_id
|
||||
super(options)
|
||||
end
|
||||
|
||||
|
|
|
@ -31,6 +31,6 @@ module Google
|
|||
# Module Auth provides classes that provide Google-specific authorization
|
||||
# used to access Google APIs.
|
||||
module Auth
|
||||
VERSION = '0.6.7'.freeze
|
||||
VERSION = '0.7.0'.freeze
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,7 +40,8 @@ describe Google::Auth::Credentials, :private do
|
|||
'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'
|
||||
'type' => 'service_account',
|
||||
'project_id' => 'a_project_id'
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -110,6 +111,7 @@ 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'])
|
||||
end
|
||||
|
||||
it 'subclasses can use PATH_ENV_VARS to get keyfile path' do
|
||||
|
@ -142,6 +144,7 @@ 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'])
|
||||
end
|
||||
|
||||
it 'subclasses can use JSON_ENV_VARS to get keyfile contents' do
|
||||
|
@ -173,6 +176,7 @@ 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'])
|
||||
end
|
||||
|
||||
it 'subclasses can use DEFAULT_PATHS to get keyfile path' do
|
||||
|
@ -205,6 +209,7 @@ 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'])
|
||||
end
|
||||
|
||||
it 'subclasses that find no matches default to Google::Auth.get_application_default' do
|
||||
|
@ -243,6 +248,7 @@ 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'])
|
||||
end
|
||||
|
||||
it 'warns when cloud sdk credentials are used' do
|
||||
|
|
|
@ -35,6 +35,7 @@ require 'faraday'
|
|||
require 'fakefs/safe'
|
||||
require 'googleauth'
|
||||
require 'spec_helper'
|
||||
require 'os'
|
||||
|
||||
describe '#get_application_default' do
|
||||
# Pass unique options each time to bypass memoization
|
||||
|
@ -173,6 +174,7 @@ describe '#get_application_default' do
|
|||
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'
|
||||
expect { Google::Auth.get_application_default @scope, options }.to output(
|
||||
Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
|
||||
).to_stderr
|
||||
|
@ -260,6 +262,7 @@ describe '#get_application_default' do
|
|||
end
|
||||
|
||||
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]
|
||||
expect do
|
||||
|
|
|
@ -116,7 +116,8 @@ describe Google::Auth::ServiceAccountCredentials do
|
|||
private_key: @key.to_pem,
|
||||
client_email: client_email,
|
||||
client_id: 'app.apps.googleusercontent.com',
|
||||
type: 'service_account'
|
||||
type: 'service_account',
|
||||
project_id: 'a_project_id'
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -213,6 +214,15 @@ describe Google::Auth::ServiceAccountCredentials do
|
|||
expect(@clz.from_env(@scope)).to_not be_nil
|
||||
end
|
||||
|
||||
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)
|
||||
expect(credentials.project_id).to eq(cred_json[:project_id])
|
||||
end
|
||||
|
||||
it 'succeeds when GOOGLE_PRIVATE_KEY is escaped' do
|
||||
escaped_key = cred_json[:private_key].gsub "\n", '\n'
|
||||
ENV[PRIVATE_KEY_VAR] = "\"#{escaped_key}\""
|
||||
|
@ -251,6 +261,19 @@ describe Google::Auth::ServiceAccountCredentials do
|
|||
expect(@clz.from_well_known_path(@scope)).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
expect(credentials.project_id).to eq(cred_json[:project_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#from_system_default_path' do
|
||||
|
@ -297,7 +320,8 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
|
|||
private_key: @key.to_pem,
|
||||
client_email: client_email,
|
||||
client_id: 'app.apps.googleusercontent.com',
|
||||
type: 'service_account'
|
||||
type: 'service_account',
|
||||
project_id: 'a_project_id'
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -358,6 +382,16 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
|
|||
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
|
||||
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)
|
||||
expect(credentials).to_not be_nil
|
||||
expect(credentials.project_id).to eq(cred_json[:project_id])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#from_well_known_path' do
|
||||
|
@ -387,5 +421,18 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
|
|||
expect(clz.from_well_known_path).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
expect(credentials.project_id).to eq(cred_json[:project_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -94,6 +94,7 @@ describe Google::Auth::UserRefreshCredentials do
|
|||
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
|
||||
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
|
||||
@clz = UserRefreshCredentials
|
||||
@project_id = 'a_project_id'
|
||||
end
|
||||
|
||||
after(:example) do
|
||||
|
@ -140,6 +141,7 @@ describe Google::Auth::UserRefreshCredentials 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]
|
||||
|
@ -150,6 +152,17 @@ describe Google::Auth::UserRefreshCredentials do
|
|||
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
|
||||
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)
|
||||
expect(creds.project_id).to eq(@project_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#from_well_known_path' do
|
||||
|
@ -198,6 +211,20 @@ describe Google::Auth::UserRefreshCredentials do
|
|||
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
|
||||
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
|
||||
ENV[PROJECT_ID_VAR] = nil
|
||||
expect(@clz).to receive(:load_gcloud_project_id).with(no_args)
|
||||
@clz.from_well_known_path(@scope)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#from_system_default_path' do
|
||||
|
|
Loading…
Reference in New Issue