googleauth 0.5.1 → 0.11.0

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