googleauth 0.1.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/CONTRIBUTING.md +74 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  6. data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
  7. data/.github/workflows/ci.yml +55 -0
  8. data/.github/workflows/release-please.yml +39 -0
  9. data/.gitignore +3 -0
  10. data/.kokoro/populate-secrets.sh +76 -0
  11. data/.kokoro/release.cfg +52 -0
  12. data/.kokoro/release.sh +18 -0
  13. data/.kokoro/trampoline_v2.sh +489 -0
  14. data/.repo-metadata.json +5 -0
  15. data/.rubocop.yml +17 -0
  16. data/.toys/.toys.rb +45 -0
  17. data/.toys/ci.rb +43 -0
  18. data/.toys/kokoro/.toys.rb +66 -0
  19. data/.toys/kokoro/publish-docs.rb +67 -0
  20. data/.toys/kokoro/publish-gem.rb +53 -0
  21. data/.toys/linkinator.rb +43 -0
  22. data/.trampolinerc +48 -0
  23. data/CHANGELOG.md +192 -0
  24. data/CODE_OF_CONDUCT.md +43 -0
  25. data/Gemfile +22 -1
  26. data/{COPYING → LICENSE} +0 -0
  27. data/README.md +140 -17
  28. data/googleauth.gemspec +28 -28
  29. data/integration/helper.rb +31 -0
  30. data/integration/id_tokens/key_source_test.rb +74 -0
  31. data/lib/googleauth.rb +7 -37
  32. data/lib/googleauth/application_default.rb +81 -0
  33. data/lib/googleauth/client_id.rb +104 -0
  34. data/lib/googleauth/compute_engine.rb +73 -26
  35. data/lib/googleauth/credentials.rb +561 -0
  36. data/lib/googleauth/credentials_loader.rb +207 -0
  37. data/lib/googleauth/default_credentials.rb +93 -0
  38. data/lib/googleauth/iam.rb +75 -0
  39. data/lib/googleauth/id_tokens.rb +233 -0
  40. data/lib/googleauth/id_tokens/errors.rb +71 -0
  41. data/lib/googleauth/id_tokens/key_sources.rb +396 -0
  42. data/lib/googleauth/id_tokens/verifier.rb +142 -0
  43. data/lib/googleauth/json_key_reader.rb +50 -0
  44. data/lib/googleauth/scope_util.rb +61 -0
  45. data/lib/googleauth/service_account.rb +175 -67
  46. data/lib/googleauth/signet.rb +69 -8
  47. data/lib/googleauth/stores/file_token_store.rb +65 -0
  48. data/lib/googleauth/stores/redis_token_store.rb +96 -0
  49. data/lib/googleauth/token_store.rb +69 -0
  50. data/lib/googleauth/user_authorizer.rb +285 -0
  51. data/lib/googleauth/user_refresh.rb +129 -0
  52. data/lib/googleauth/version.rb +1 -1
  53. data/lib/googleauth/web_user_authorizer.rb +295 -0
  54. data/spec/googleauth/apply_auth_examples.rb +96 -94
  55. data/spec/googleauth/client_id_spec.rb +160 -0
  56. data/spec/googleauth/compute_engine_spec.rb +125 -55
  57. data/spec/googleauth/credentials_spec.rb +600 -0
  58. data/spec/googleauth/get_application_default_spec.rb +232 -80
  59. data/spec/googleauth/iam_spec.rb +80 -0
  60. data/spec/googleauth/scope_util_spec.rb +77 -0
  61. data/spec/googleauth/service_account_spec.rb +422 -68
  62. data/spec/googleauth/signet_spec.rb +101 -25
  63. data/spec/googleauth/stores/file_token_store_spec.rb +57 -0
  64. data/spec/googleauth/stores/redis_token_store_spec.rb +50 -0
  65. data/spec/googleauth/stores/store_examples.rb +58 -0
  66. data/spec/googleauth/user_authorizer_spec.rb +343 -0
  67. data/spec/googleauth/user_refresh_spec.rb +359 -0
  68. data/spec/googleauth/web_user_authorizer_spec.rb +172 -0
  69. data/spec/spec_helper.rb +51 -10
  70. data/test/helper.rb +33 -0
  71. data/test/id_tokens/key_sources_test.rb +240 -0
  72. data/test/id_tokens/verifier_test.rb +269 -0
  73. metadata +112 -75
  74. data/.travis.yml +0 -18
  75. data/CONTRIBUTING.md +0 -32
  76. data/Rakefile +0 -15
@@ -27,116 +27,470 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'apply_auth_examples'
35
- require 'fileutils'
36
- require 'googleauth/service_account'
37
- require 'jwt'
38
- require 'multi_json'
39
- require 'openssl'
40
- require 'spec_helper'
41
- require 'tmpdir'
34
+ require "apply_auth_examples"
35
+ require "fakefs/safe"
36
+ require "fileutils"
37
+ require "googleauth/service_account"
38
+ require "jwt"
39
+ require "multi_json"
40
+ require "openssl"
41
+ require "spec_helper"
42
+ require "tmpdir"
43
+ require "os"
44
+
45
+ include Google::Auth::CredentialsLoader
46
+
47
+ shared_examples "jwt header auth" do
48
+ context "when jwt_aud_uri is present" do
49
+ let(:test_uri) { "https://www.googleapis.com/myservice" }
50
+ let(:auth_prefix) { "Bearer " }
51
+ let(:auth_key) { ServiceAccountJwtHeaderCredentials::AUTH_METADATA_KEY }
52
+ let(:jwt_uri_key) { ServiceAccountJwtHeaderCredentials::JWT_AUD_URI_KEY }
53
+
54
+ def expect_is_encoded_jwt hdr
55
+ expect(hdr).to_not be_nil
56
+ expect(hdr.start_with?(auth_prefix)).to be true
57
+ authorization = hdr[auth_prefix.length..-1]
58
+ payload, = JWT.decode authorization, @key.public_key, true, algorithm: "RS256"
59
+ expect(payload["aud"]).to eq(test_uri)
60
+ expect(payload["iss"]).to eq(client_email)
61
+ end
62
+
63
+ describe "#apply!" do
64
+ it "should update the target hash with a jwt token" do
65
+ md = { foo: "bar" }
66
+ md[jwt_uri_key] = test_uri
67
+ @client.apply! md
68
+ auth_header = md[auth_key]
69
+ expect_is_encoded_jwt auth_header
70
+ expect(md[jwt_uri_key]).to be_nil
71
+ end
72
+ end
73
+
74
+ describe "updater_proc" do
75
+ it "should provide a proc that updates a hash with a jwt token" do
76
+ md = { foo: "bar" }
77
+ md[jwt_uri_key] = test_uri
78
+ the_proc = @client.updater_proc
79
+ got = the_proc.call md
80
+ auth_header = got[auth_key]
81
+ expect_is_encoded_jwt auth_header
82
+ expect(got[jwt_uri_key]).to be_nil
83
+ expect(md[jwt_uri_key]).to_not be_nil
84
+ end
85
+ end
86
+
87
+ describe "#apply" do
88
+ it "should not update the original hash with a jwt token" do
89
+ md = { foo: "bar" }
90
+ md[jwt_uri_key] = test_uri
91
+ the_proc = @client.updater_proc
92
+ got = the_proc.call md
93
+ auth_header = md[auth_key]
94
+ expect(auth_header).to be_nil
95
+ expect(got[jwt_uri_key]).to be_nil
96
+ expect(md[jwt_uri_key]).to_not be_nil
97
+ end
98
+
99
+ it "should add a jwt token to the returned hash" do
100
+ md = { foo: "bar" }
101
+ md[jwt_uri_key] = test_uri
102
+ got = @client.apply md
103
+ auth_header = got[auth_key]
104
+ expect_is_encoded_jwt auth_header
105
+ end
106
+ end
107
+ end
108
+ end
42
109
 
43
110
  describe Google::Auth::ServiceAccountCredentials do
44
111
  ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
112
+ let(:client_email) { "app@developer.gserviceaccount.com" }
113
+ let :cred_json do
114
+ {
115
+ private_key_id: "a_private_key_id",
116
+ private_key: @key.to_pem,
117
+ client_email: client_email,
118
+ client_id: "app.apps.googleusercontent.com",
119
+ type: "service_account",
120
+ project_id: "a_project_id",
121
+ quota_project_id: "b_project_id"
122
+ }
123
+ end
45
124
 
46
- before(:example) do
47
- @key = OpenSSL::PKey::RSA.new(2048)
48
- @client = ServiceAccountCredentials.new(
49
- 'https://www.googleapis.com/auth/userinfo.profile',
50
- StringIO.new(cred_json_text))
125
+ before :example do
126
+ @key = OpenSSL::PKey::RSA.new 2048
127
+ @client = ServiceAccountCredentials.make_creds(
128
+ json_key_io: StringIO.new(cred_json_text),
129
+ scope: "https://www.googleapis.com/auth/userinfo.profile"
130
+ )
131
+ @id_client = ServiceAccountCredentials.make_creds(
132
+ json_key_io: StringIO.new(cred_json_text),
133
+ target_audience: "https://pubsub.googleapis.com/"
134
+ )
51
135
  end
52
136
 
53
- def make_auth_stubs(access_token: '')
54
- Faraday::Adapter::Test::Stubs.new do |stub|
55
- stub.post('/oauth2/v3/token') do |env|
56
- params = Addressable::URI.form_unencode(env[:body])
57
- _claim, _header = JWT.decode(params.assoc('assertion').last,
58
- @key.public_key)
59
- want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
60
- expect(params.assoc('grant_type')).to eq(want)
61
- build_access_token_json(access_token)
62
- end
137
+ def make_auth_stubs opts
138
+ body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
139
+ body_fields["access_token"] = opts[:access_token] if opts[:access_token]
140
+ body_fields["id_token"] = opts[:id_token] if opts[:id_token]
141
+ body = MultiJson.dump body_fields
142
+ blk = proc do |request|
143
+ params = Addressable::URI.form_unencode request.body
144
+ claim, _header = JWT.decode(params.assoc("assertion").last,
145
+ @key.public_key, true,
146
+ algorithm: "RS256")
147
+ !opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
63
148
  end
149
+ stub_request(:post, "https://www.googleapis.com/oauth2/v4/token")
150
+ .with(body: hash_including(
151
+ "grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
152
+ ), &blk)
153
+ .to_return(body: body,
154
+ status: 200,
155
+ headers: { "Content-Type" => "application/json" })
64
156
  end
65
157
 
66
158
  def cred_json_text
67
- cred_json = {
68
- private_key_id: 'a_private_key_id',
69
- private_key: @key.to_pem,
70
- client_email: 'app@developer.gserviceaccount.com',
71
- client_id: 'app.apps.googleusercontent.com',
72
- type: 'service_account'
73
- }
74
- MultiJson.dump(cred_json)
159
+ MultiJson.dump cred_json
160
+ end
161
+
162
+ it_behaves_like "apply/apply! are OK"
163
+
164
+ context "when scope is nil" do
165
+ before :example do
166
+ @client.scope = nil
167
+ end
168
+
169
+ it_behaves_like "jwt header auth"
170
+ end
171
+
172
+ context "when enable_self_signed_jwt is set" do
173
+ before :example do
174
+ @client.instance_variable_set(:@enable_self_signed_jwt, true)
175
+ end
176
+
177
+ it_behaves_like "jwt header auth"
75
178
  end
76
179
 
77
- it_behaves_like 'apply/apply! are OK'
180
+ describe "#from_env" do
181
+ before :example do
182
+ @var_name = ENV_VAR
183
+ @credential_vars = [
184
+ ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
185
+ ]
186
+ @original_env_vals = {}
187
+ @credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
188
+ ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
78
189
 
79
- describe '#from_env' do
80
- before(:example) do
81
- @var_name = ServiceAccountCredentials::ENV_VAR
82
- @orig = ENV[@var_name]
83
- @scope = 'https://www.googleapis.com/auth/userinfo.profile'
190
+ @scope = "https://www.googleapis.com/auth/userinfo.profile"
191
+ @clz = ServiceAccountCredentials
84
192
  end
85
193
 
86
- after(:example) do
87
- ENV[@var_name] = @orig unless @orig.nil?
194
+ after :example do
195
+ @credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
88
196
  end
89
197
 
90
- it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
91
- ENV.delete(@var_name) unless ENV[@var_name].nil?
198
+ it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset" do
199
+ ENV.delete @var_name unless ENV[@var_name].nil?
92
200
  expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
93
201
  end
94
202
 
95
- it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
96
- ENV.delete(@var_name) unless ENV[@var_name].nil?
203
+ it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is empty" do
204
+ ENV[@var_name] = ""
205
+ expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
206
+ end
207
+
208
+ it "fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist" do
209
+ ENV.delete @var_name unless ENV[@var_name].nil?
97
210
  expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
98
211
  Dir.mktmpdir do |dir|
99
- key_path = File.join(dir, 'does-not-exist')
212
+ key_path = File.join dir, "does-not-exist"
100
213
  ENV[@var_name] = key_path
101
- expect { sac.from_env(@scope) }.to raise_error
214
+ expect { @clz.from_env @scope }.to raise_error RuntimeError
102
215
  end
103
216
  end
104
217
 
105
- it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
106
- sac = ServiceAccountCredentials # shortens name
218
+ it "succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid" do
107
219
  Dir.mktmpdir do |dir|
108
- key_path = File.join(dir, 'my_cert_file')
109
- FileUtils.mkdir_p(File.dirname(key_path))
110
- File.write(key_path, cred_json_text)
220
+ key_path = File.join dir, "my_cert_file"
221
+ FileUtils.mkdir_p File.dirname(key_path)
222
+ File.write key_path, cred_json_text
111
223
  ENV[@var_name] = key_path
112
- expect(sac.from_env(@scope)).to_not be_nil
224
+ expect(@clz.from_env(@scope)).to_not be_nil
113
225
  end
114
226
  end
227
+
228
+ it "succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are"\
229
+ " valid" do
230
+ ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
231
+ ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
232
+ expect(@clz.from_env(@scope)).to_not be_nil
233
+ end
234
+
235
+ it "sets project_id when the PROJECT_ID_VAR env var is set" do
236
+ ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
237
+ ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
238
+ ENV[PROJECT_ID_VAR] = cred_json[:project_id]
239
+ ENV[ENV_VAR] = nil
240
+ credentials = @clz.from_env @scope
241
+ expect(credentials.project_id).to eq(cred_json[:project_id])
242
+ end
243
+
244
+ it "succeeds when GOOGLE_PRIVATE_KEY is escaped" do
245
+ escaped_key = cred_json[:private_key].gsub "\n", '\n'
246
+ ENV[PRIVATE_KEY_VAR] = "\"#{escaped_key}\""
247
+ ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
248
+ expect(@clz.from_env(@scope)).to_not be_nil
249
+ end
250
+
251
+ it "propagates default_connection option" do
252
+ ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
253
+ ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
254
+ connection = Faraday.new headers: { "User-Agent" => "hello" }
255
+ creds = @clz.from_env @scope, default_connection: connection
256
+ expect(creds.build_default_connection).to be connection
257
+ end
115
258
  end
116
259
 
117
- describe '#from_well_known_path' do
118
- before(:example) do
119
- @home = ENV['HOME']
120
- @scope = 'https://www.googleapis.com/auth/userinfo.profile'
260
+ describe "#from_well_known_path" do
261
+ before :example do
262
+ @home = ENV["HOME"]
263
+ @app_data = ENV["APPDATA"]
264
+ @scope = "https://www.googleapis.com/auth/userinfo.profile"
265
+ @known_path = WELL_KNOWN_PATH
266
+ @clz = ServiceAccountCredentials
121
267
  end
122
268
 
123
- after(:example) do
124
- ENV['HOME'] = @home unless @home == ENV['HOME']
269
+ after :example do
270
+ ENV["HOME"] = @home unless @home == ENV["HOME"]
271
+ ENV["APPDATA"] = @app_data unless @app_data == ENV["APPDATA"]
125
272
  end
126
273
 
127
- it 'is nil if no file exists' do
128
- ENV['HOME'] = File.dirname(__FILE__)
274
+ it "is nil if no file exists" do
275
+ ENV["HOME"] = File.dirname __FILE__
129
276
  expect(ServiceAccountCredentials.from_well_known_path(@scope)).to be_nil
130
277
  end
131
278
 
132
- it 'successfully loads the file when it is present' do
133
- sac = ServiceAccountCredentials # shortens name
279
+ it "successfully loads the file when it is present" do
280
+ Dir.mktmpdir do |dir|
281
+ key_path = File.join dir, ".config", @known_path
282
+ key_path = File.join dir, WELL_KNOWN_PATH if OS.windows?
283
+ FileUtils.mkdir_p File.dirname(key_path)
284
+ File.write key_path, cred_json_text
285
+ ENV["HOME"] = dir
286
+ ENV["APPDATA"] = dir
287
+ expect(@clz.from_well_known_path(@scope)).to_not be_nil
288
+ end
289
+ end
290
+
291
+ it "successfully sets project_id when file is present" do
292
+ Dir.mktmpdir do |dir|
293
+ key_path = File.join dir, ".config", @known_path
294
+ key_path = File.join dir, WELL_KNOWN_PATH if OS.windows?
295
+ FileUtils.mkdir_p File.dirname(key_path)
296
+ File.write key_path, cred_json_text
297
+ ENV["HOME"] = dir
298
+ ENV["APPDATA"] = dir
299
+ credentials = @clz.from_well_known_path @scope
300
+ expect(credentials.project_id).to eq(cred_json[:project_id])
301
+ expect(credentials.quota_project_id).to eq(cred_json[:quota_project_id])
302
+ end
303
+ end
304
+
305
+ it "propagates default_connection option" do
306
+ Dir.mktmpdir do |dir|
307
+ key_path = File.join dir, ".config", @known_path
308
+ key_path = File.join dir, WELL_KNOWN_PATH if OS.windows?
309
+ FileUtils.mkdir_p File.dirname(key_path)
310
+ File.write key_path, cred_json_text
311
+ ENV["HOME"] = dir
312
+ ENV["APPDATA"] = dir
313
+ connection = Faraday.new headers: { "User-Agent" => "hello" }
314
+ creds = @clz.from_well_known_path @scope, default_connection: connection
315
+ expect(creds.build_default_connection).to be connection
316
+ end
317
+ end
318
+ end
319
+
320
+ describe "#from_system_default_path" do
321
+ before :example do
322
+ @scope = "https://www.googleapis.com/auth/userinfo.profile"
323
+ @program_data = ENV["ProgramData"]
324
+ @prefix = OS.windows? ? "/etc/Google/Auth/" : "/etc/google/auth/"
325
+ @path = File.join @prefix, CREDENTIALS_FILE_NAME
326
+ @clz = ServiceAccountCredentials
327
+ end
328
+
329
+ after :example do
330
+ ENV["ProgramData"] = @program_data
331
+ end
332
+
333
+ it "is nil if no file exists" do
334
+ FakeFS do
335
+ expect(ServiceAccountCredentials.from_system_default_path(@scope))
336
+ .to be_nil
337
+ end
338
+ end
339
+
340
+ it "successfully loads the file when it is present" do
341
+ FakeFS do
342
+ ENV["ProgramData"] = "/etc"
343
+ FileUtils.mkdir_p File.dirname(@path)
344
+ File.write @path, cred_json_text
345
+ expect(@clz.from_system_default_path(@scope)).to_not be_nil
346
+ File.delete @path
347
+ end
348
+ end
349
+
350
+ it "propagates default_connection option" do
351
+ FakeFS do
352
+ ENV["ProgramData"] = "/etc"
353
+ FileUtils.mkdir_p File.dirname(@path)
354
+ File.write @path, cred_json_text
355
+ connection = Faraday.new headers: { "User-Agent" => "hello" }
356
+ creds = @clz.from_system_default_path @scope, default_connection: connection
357
+ expect(creds.build_default_connection).to be connection
358
+ File.delete @path
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
365
+ ServiceAccountJwtHeaderCredentials =
366
+ Google::Auth::ServiceAccountJwtHeaderCredentials
367
+
368
+ let(:client_email) { "app@developer.gserviceaccount.com" }
369
+ let(:clz) { Google::Auth::ServiceAccountJwtHeaderCredentials }
370
+ let :cred_json do
371
+ {
372
+ private_key_id: "a_private_key_id",
373
+ private_key: @key.to_pem,
374
+ client_email: client_email,
375
+ client_id: "app.apps.googleusercontent.com",
376
+ type: "service_account",
377
+ project_id: "a_project_id"
378
+ }
379
+ end
380
+
381
+ before :example do
382
+ @key = OpenSSL::PKey::RSA.new 2048
383
+ @client = clz.make_creds json_key_io: StringIO.new(cred_json_text)
384
+ end
385
+
386
+ def cred_json_text
387
+ MultiJson.dump cred_json
388
+ end
389
+
390
+ it_behaves_like "jwt header auth"
391
+
392
+ describe "#from_env" do
393
+ before :example do
394
+ @var_name = ENV_VAR
395
+ @credential_vars = [
396
+ ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
397
+ ]
398
+ @original_env_vals = {}
399
+ @credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
400
+ ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
401
+ end
402
+
403
+ after :example do
404
+ @credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
405
+ end
406
+
407
+ it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset" do
408
+ ENV.delete @var_name unless ENV[@var_name].nil?
409
+ expect(clz.from_env).to be_nil
410
+ end
411
+
412
+ it "returns nil if the GOOGLE_APPLICATION_CREDENTIALS is empty" do
413
+ ENV[@var_name] = ""
414
+ expect(clz.from_env).to be_nil
415
+ end
416
+
417
+ it "fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist" do
418
+ ENV.delete @var_name unless ENV[@var_name].nil?
419
+ expect(clz.from_env).to be_nil
420
+ Dir.mktmpdir do |dir|
421
+ key_path = File.join dir, "does-not-exist"
422
+ ENV[@var_name] = key_path
423
+ expect { clz.from_env }.to raise_error RuntimeError
424
+ end
425
+ end
426
+
427
+ it "succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid" do
428
+ Dir.mktmpdir do |dir|
429
+ key_path = File.join dir, "my_cert_file"
430
+ FileUtils.mkdir_p File.dirname(key_path)
431
+ File.write key_path, cred_json_text
432
+ ENV[@var_name] = key_path
433
+ expect(clz.from_env).to_not be_nil
434
+ end
435
+ end
436
+
437
+ it "succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are"\
438
+ " valid" do
439
+ ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
440
+ ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
441
+ expect(clz.from_env(@scope)).to_not be_nil
442
+ end
443
+
444
+ it "sets project_id when the PROJECT_ID_VAR env var is set" do
445
+ ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
446
+ ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
447
+ ENV[PROJECT_ID_VAR] = cred_json[:project_id]
448
+ ENV[ENV_VAR] = nil
449
+ credentials = clz.from_env @scope
450
+ expect(credentials).to_not be_nil
451
+ expect(credentials.project_id).to eq(cred_json[:project_id])
452
+ end
453
+ end
454
+
455
+ describe "#from_well_known_path" do
456
+ before :example do
457
+ @home = ENV["HOME"]
458
+ @app_data = ENV["APPDATA"]
459
+ end
460
+
461
+ after :example do
462
+ ENV["HOME"] = @home unless @home == ENV["HOME"]
463
+ ENV["APPDATA"] = @app_data unless @app_data == ENV["APPDATA"]
464
+ end
465
+
466
+ it "is nil if no file exists" do
467
+ ENV["HOME"] = File.dirname __FILE__
468
+ expect(clz.from_well_known_path).to be_nil
469
+ end
470
+
471
+ it "successfully loads the file when it is present" do
472
+ Dir.mktmpdir do |dir|
473
+ key_path = File.join dir, ".config", WELL_KNOWN_PATH
474
+ key_path = File.join dir, WELL_KNOWN_PATH if OS.windows?
475
+ FileUtils.mkdir_p File.dirname(key_path)
476
+ File.write key_path, cred_json_text
477
+ ENV["HOME"] = dir
478
+ ENV["APPDATA"] = dir
479
+ expect(clz.from_well_known_path).to_not be_nil
480
+ end
481
+ end
482
+
483
+ it "successfully sets project_id when file is present" do
134
484
  Dir.mktmpdir do |dir|
135
- key_path = File.join(dir, '.config', sac::WELL_KNOWN_PATH)
136
- FileUtils.mkdir_p(File.dirname(key_path))
137
- File.write(key_path, cred_json_text)
138
- ENV['HOME'] = dir
139
- expect(sac.from_well_known_path(@scope)).to_not be_nil
485
+ key_path = File.join dir, ".config", WELL_KNOWN_PATH
486
+ key_path = File.join dir, WELL_KNOWN_PATH if OS.windows?
487
+ FileUtils.mkdir_p File.dirname(key_path)
488
+ File.write key_path, cred_json_text
489
+ ENV["HOME"] = dir
490
+ ENV["APPDATA"] = dir
491
+ credentials = clz.from_well_known_path @scope
492
+ expect(credentials.project_id).to eq(cred_json[:project_id])
493
+ expect(credentials.quota_project_id).to be_nil
140
494
  end
141
495
  end
142
496
  end