googleauth 0.5.1 → 0.14.0

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