googleauth 0.14.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/renovate.json +6 -0
  3. data/.github/sync-repo-settings.yaml +18 -0
  4. data/.github/workflows/ci.yml +55 -0
  5. data/.github/workflows/release-please.yml +39 -0
  6. data/.gitignore +3 -0
  7. data/.kokoro/populate-secrets.sh +76 -0
  8. data/.kokoro/release.cfg +7 -49
  9. data/.kokoro/release.sh +18 -0
  10. data/.kokoro/trampoline_v2.sh +489 -0
  11. data/.rubocop.yml +0 -2
  12. data/.toys/.toys.rb +45 -0
  13. data/.toys/ci.rb +43 -0
  14. data/.toys/kokoro/.toys.rb +66 -0
  15. data/.toys/kokoro/publish-docs.rb +67 -0
  16. data/.toys/kokoro/publish-gem.rb +53 -0
  17. data/.toys/linkinator.rb +43 -0
  18. data/.trampolinerc +48 -0
  19. data/CHANGELOG.md +69 -27
  20. data/Gemfile +2 -7
  21. data/README.md +9 -7
  22. data/googleauth.gemspec +2 -1
  23. data/lib/googleauth/compute_engine.rb +6 -5
  24. data/lib/googleauth/credentials.rb +167 -48
  25. data/lib/googleauth/credentials_loader.rb +1 -1
  26. data/lib/googleauth/iam.rb +1 -1
  27. data/lib/googleauth/id_tokens/key_sources.rb +7 -5
  28. data/lib/googleauth/id_tokens/verifier.rb +7 -9
  29. data/lib/googleauth/scope_util.rb +1 -1
  30. data/lib/googleauth/service_account.rb +35 -23
  31. data/lib/googleauth/signet.rb +1 -1
  32. data/lib/googleauth/stores/file_token_store.rb +1 -0
  33. data/lib/googleauth/stores/redis_token_store.rb +1 -0
  34. data/lib/googleauth/version.rb +1 -1
  35. data/lib/googleauth/web_user_authorizer.rb +4 -7
  36. data/spec/googleauth/compute_engine_spec.rb +18 -0
  37. data/spec/googleauth/credentials_spec.rb +228 -106
  38. data/spec/googleauth/service_account_spec.rb +8 -0
  39. metadata +18 -22
  40. data/.kokoro/build.bat +0 -16
  41. data/.kokoro/build.sh +0 -4
  42. data/.kokoro/continuous/common.cfg +0 -24
  43. data/.kokoro/continuous/linux.cfg +0 -25
  44. data/.kokoro/continuous/osx.cfg +0 -8
  45. data/.kokoro/continuous/post.cfg +0 -30
  46. data/.kokoro/continuous/windows.cfg +0 -29
  47. data/.kokoro/osx.sh +0 -4
  48. data/.kokoro/presubmit/common.cfg +0 -24
  49. data/.kokoro/presubmit/linux.cfg +0 -24
  50. data/.kokoro/presubmit/osx.cfg +0 -8
  51. data/.kokoro/presubmit/windows.cfg +0 -29
  52. data/.kokoro/trampoline.bat +0 -10
  53. data/.kokoro/trampoline.sh +0 -4
  54. data/Rakefile +0 -132
  55. data/rakelib/devsite_builder.rb +0 -45
  56. data/rakelib/link_checker.rb +0 -64
  57. data/rakelib/repo_metadata.rb +0 -59
data/Gemfile CHANGED
@@ -8,7 +8,7 @@ group :development do
8
8
  gem "coveralls", "~> 0.7"
9
9
  gem "fakefs", "~> 0.6"
10
10
  gem "fakeredis", "~> 0.5"
11
- gem "google-style", "~> 1.24.0"
11
+ gem "google-style", "~> 1.25.1"
12
12
  gem "logging", "~> 2.0"
13
13
  gem "minitest", "~> 5.14"
14
14
  gem "minitest-focus", "~> 1.1"
@@ -21,10 +21,5 @@ group :development do
21
21
  gem "webmock", "~> 3.8"
22
22
  end
23
23
 
24
- platforms :jruby do
25
- group :development do
26
- end
27
- end
28
-
29
- gem "faraday", "~> 0.17"
24
+ gem "faraday", ">= 0.17.3", "< 2.0"
30
25
  gem "gems", "~> 1.2"
data/README.md CHANGED
@@ -182,17 +182,19 @@ Custom storage implementations can also be used. See
182
182
 
183
183
  ## Supported Ruby Versions
184
184
 
185
- This library requires Ruby 2.4 or later.
185
+ This library is supported on Ruby 2.5+.
186
186
 
187
- In general, this library supports Ruby versions that are considered current and
188
- supported by Ruby Core (that is, Ruby versions that are either in normal
189
- maintenance or in security maintenance).
190
- See https://www.ruby-lang.org/en/downloads/branches/ for further details.
187
+ Google provides official support for Ruby versions that are actively supported
188
+ by Ruby Corethat is, Ruby versions that are either in normal maintenance or in
189
+ security maintenance, and not end of life. Currently, this means Ruby 2.5 and
190
+ later. Older versions of Ruby _may_ still work, but are unsupported and not
191
+ recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
192
+ about the Ruby support schedule.
191
193
 
192
194
  ## License
193
195
 
194
196
  This library is licensed under Apache 2.0. Full license text is
195
- available in [COPYING][copying].
197
+ available in [LICENSE][license].
196
198
 
197
199
  ## Contributing
198
200
 
@@ -208,4 +210,4 @@ about the client or APIs on [StackOverflow](http://stackoverflow.com).
208
210
 
209
211
  [application default credentials]: https://developers.google.com/accounts/docs/application-default-credentials
210
212
  [contributing]: https://github.com/googleapis/google-auth-library-ruby/tree/master/.github/CONTRIBUTING.md
211
- [copying]: https://github.com/googleapis/google-auth-library-ruby/tree/master/COPYING
213
+ [license]: https://github.com/googleapis/google-auth-library-ruby/tree/master/LICENSE
data/googleauth.gemspec CHANGED
@@ -24,8 +24,9 @@ Gem::Specification.new do |gem|
24
24
  File.basename f
25
25
  end
26
26
  gem.require_paths = ["lib"]
27
+
27
28
  gem.platform = Gem::Platform::RUBY
28
- gem.required_ruby_version = ">= 2.4.0"
29
+ gem.required_ruby_version = ">= 2.5"
29
30
 
30
31
  gem.add_dependency "faraday", ">= 0.17.3", "< 2.0"
31
32
  gem.add_dependency "jwt", ">= 1.4", "< 3.0"
@@ -108,21 +108,22 @@ module Google
108
108
  uri = target_audience ? GCECredentials.compute_id_token_uri : GCECredentials.compute_auth_token_uri
109
109
  query = target_audience ? { "audience" => target_audience, "format" => "full" } : {}
110
110
  query[:scopes] = Array(scope).join "," if scope
111
- headers = { "Metadata-Flavor" => "Google" }
112
- resp = c.get uri, query, headers
111
+ resp = c.get uri, query, "Metadata-Flavor" => "Google"
113
112
  case resp.status
114
113
  when 200
115
114
  content_type = resp.headers["content-type"]
116
- if content_type == "text/html"
115
+ if ["text/html", "application/text"].include? content_type
117
116
  { (target_audience ? "id_token" : "access_token") => resp.body }
118
117
  else
119
118
  Signet::OAuth2.parse_credentials resp.body, content_type
120
119
  end
120
+ when 403, 500
121
+ msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
122
+ raise Signet::UnexpectedStatusError, msg
121
123
  when 404
122
124
  raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
123
125
  else
124
- msg = "Unexpected error code #{resp.status}" \
125
- "#{UNEXPECTED_ERROR_SUFFIX}"
126
+ msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
126
127
  raise Signet::AuthorizationError, msg
127
128
  end
128
129
  end
@@ -36,9 +36,46 @@ require "googleauth/credentials_loader"
36
36
  module Google
37
37
  module Auth
38
38
  ##
39
- # Credentials is responsible for representing the authentication when connecting to an API. This
40
- # class is also intended to be inherited by API-specific classes.
41
- class Credentials
39
+ # Credentials is a high-level base class used by Google's API client
40
+ # libraries to represent the authentication when connecting to an API.
41
+ # In most cases, it is subclassed by API-specific credential classes that
42
+ # can be instantiated by clients.
43
+ #
44
+ # ## Options
45
+ #
46
+ # Credentials classes are configured with options that dictate default
47
+ # values for parameters such as scope and audience. These defaults are
48
+ # expressed as class attributes, and may differ from endpoint to endpoint.
49
+ # Normally, an API client will provide subclasses specific to each
50
+ # endpoint, configured with appropriate values.
51
+ #
52
+ # Note that these options inherit up the class hierarchy. If a particular
53
+ # options is not set for a subclass, its superclass is queried.
54
+ #
55
+ # Some older users of this class set options via constants. This usage is
56
+ # deprecated. For example, instead of setting the `AUDIENCE` constant on
57
+ # your subclass, call the `audience=` method.
58
+ #
59
+ # ## Example
60
+ #
61
+ # class MyCredentials < Google::Auth::Credentials
62
+ # # Set the default scope for these credentials
63
+ # self.scope = "http://example.com/my_scope"
64
+ # end
65
+ #
66
+ # # creds is a credentials object suitable for Google API clients
67
+ # creds = MyCredentials.default
68
+ # creds.scope # => ["http://example.com/my_scope"]
69
+ #
70
+ # class SubCredentials < MyCredentials
71
+ # # Override the default scope for this subclass
72
+ # self.scope = "http://example.com/sub_scope"
73
+ # end
74
+ #
75
+ # creds2 = SubCredentials.default
76
+ # creds2.scope # => ["http://example.com/sub_scope"]
77
+ #
78
+ class Credentials # rubocop:disable Metrics/ClassLength
42
79
  ##
43
80
  # The default token credential URI to be used when none is provided during initialization.
44
81
  TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token".freeze
@@ -47,7 +84,7 @@ module Google
47
84
  # The default target audience ID to be used when none is provided during initialization.
48
85
  AUDIENCE = "https://oauth2.googleapis.com/token".freeze
49
86
 
50
- @audience = @scope = @target_audience = @env_vars = @paths = nil
87
+ @audience = @scope = @target_audience = @env_vars = @paths = @token_credential_uri = nil
51
88
 
52
89
  ##
53
90
  # The default token credential URI to be used when none is provided during initialization.
@@ -57,16 +94,15 @@ module Google
57
94
  # @return [String]
58
95
  #
59
96
  def self.token_credential_uri
60
- return @token_credential_uri unless @token_credential_uri.nil?
61
-
62
- const_get :TOKEN_CREDENTIAL_URI if const_defined? :TOKEN_CREDENTIAL_URI
97
+ lookup_auth_param :token_credential_uri do
98
+ lookup_local_constant :TOKEN_CREDENTIAL_URI
99
+ end
63
100
  end
64
101
 
65
102
  ##
66
103
  # Set the default token credential URI to be used when none is provided during initialization.
67
104
  #
68
105
  # @param [String] new_token_credential_uri
69
- # @return [String]
70
106
  #
71
107
  def self.token_credential_uri= new_token_credential_uri
72
108
  @token_credential_uri = new_token_credential_uri
@@ -79,16 +115,15 @@ module Google
79
115
  # @return [String]
80
116
  #
81
117
  def self.audience
82
- return @audience unless @audience.nil?
83
-
84
- const_get :AUDIENCE if const_defined? :AUDIENCE
118
+ lookup_auth_param :audience do
119
+ lookup_local_constant :AUDIENCE
120
+ end
85
121
  end
86
122
 
87
123
  ##
88
124
  # Sets the default target audience ID to be used when none is provided during initialization.
89
125
  #
90
126
  # @param [String] new_audience
91
- # @return [String]
92
127
  #
93
128
  def self.audience= new_audience
94
129
  @audience = new_audience
@@ -103,12 +138,13 @@ module Google
103
138
  # If {#scope} is set, this credential will produce access tokens.
104
139
  # If {#target_audience} is set, this credential will produce ID tokens.
105
140
  #
106
- # @return [String, Array<String>]
141
+ # @return [String, Array<String>, nil]
107
142
  #
108
143
  def self.scope
109
- return @scope unless @scope.nil?
110
-
111
- Array(const_get(:SCOPE)).flatten.uniq if const_defined? :SCOPE
144
+ lookup_auth_param :scope do
145
+ vals = lookup_local_constant :SCOPE
146
+ vals ? Array(vals).flatten.uniq : nil
147
+ end
112
148
  end
113
149
 
114
150
  ##
@@ -118,8 +154,7 @@ module Google
118
154
  # If {#scope} is set, this credential will produce access tokens.
119
155
  # If {#target_audience} is set, this credential will produce ID tokens.
120
156
  #
121
- # @param [String, Array<String>] new_scope
122
- # @return [String, Array<String>]
157
+ # @param [String, Array<String>, nil] new_scope
123
158
  #
124
159
  def self.scope= new_scope
125
160
  new_scope = Array new_scope unless new_scope.nil?
@@ -134,10 +169,10 @@ module Google
134
169
  # If {#scope} is set, this credential will produce access tokens.
135
170
  # If {#target_audience} is set, this credential will produce ID tokens.
136
171
  #
137
- # @return [String]
172
+ # @return [String, nil]
138
173
  #
139
174
  def self.target_audience
140
- @target_audience
175
+ lookup_auth_param :target_audience
141
176
  end
142
177
 
143
178
  ##
@@ -148,7 +183,7 @@ module Google
148
183
  # If {#scope} is set, this credential will produce access tokens.
149
184
  # If {#target_audience} is set, this credential will produce ID tokens.
150
185
  #
151
- # @param [String] new_target_audience
186
+ # @param [String, nil] new_target_audience
152
187
  #
153
188
  def self.target_audience= new_target_audience
154
189
  @target_audience = new_target_audience
@@ -157,24 +192,33 @@ module Google
157
192
  ##
158
193
  # The environment variables to search for credentials. Values can either be a file path to the
159
194
  # credentials file, or the JSON contents of the credentials file.
195
+ # The env_vars will never be nil. If there are no vars, the empty array is returned.
160
196
  #
161
197
  # @return [Array<String>]
162
198
  #
163
199
  def self.env_vars
164
- return @env_vars unless @env_vars.nil?
200
+ env_vars_internal || []
201
+ end
165
202
 
166
- # Pull values when PATH_ENV_VARS or JSON_ENV_VARS constants exists.
167
- tmp_env_vars = []
168
- tmp_env_vars << const_get(:PATH_ENV_VARS) if const_defined? :PATH_ENV_VARS
169
- tmp_env_vars << const_get(:JSON_ENV_VARS) if const_defined? :JSON_ENV_VARS
170
- tmp_env_vars.flatten.uniq
203
+ ##
204
+ # @private
205
+ # Internal recursive lookup for env_vars.
206
+ #
207
+ def self.env_vars_internal
208
+ lookup_auth_param :env_vars, :env_vars_internal do
209
+ # Pull values when PATH_ENV_VARS or JSON_ENV_VARS constants exists.
210
+ path_env_vars = lookup_local_constant :PATH_ENV_VARS
211
+ json_env_vars = lookup_local_constant :JSON_ENV_VARS
212
+ (Array(path_env_vars) + Array(json_env_vars)).flatten.uniq if path_env_vars || json_env_vars
213
+ end
171
214
  end
172
215
 
173
216
  ##
174
217
  # Sets the environment variables to search for credentials.
218
+ # Setting to `nil` "unsets" the value, and defaults to the superclass
219
+ # (or to the empty array if there is no superclass).
175
220
  #
176
- # @param [Array<String>] new_env_vars
177
- # @return [Array<String>]
221
+ # @param [String, Array<String>, nil] new_env_vars
178
222
  #
179
223
  def self.env_vars= new_env_vars
180
224
  new_env_vars = Array new_env_vars unless new_env_vars.nil?
@@ -183,29 +227,72 @@ module Google
183
227
 
184
228
  ##
185
229
  # The file paths to search for credentials files.
230
+ # The paths will never be nil. If there are no paths, the empty array is returned.
186
231
  #
187
232
  # @return [Array<String>]
188
233
  #
189
234
  def self.paths
190
- return @paths unless @paths.nil?
235
+ paths_internal || []
236
+ end
191
237
 
192
- tmp_paths = []
193
- # Pull in values is the DEFAULT_PATHS constant exists.
194
- tmp_paths << const_get(:DEFAULT_PATHS) if const_defined? :DEFAULT_PATHS
195
- tmp_paths.flatten.uniq
238
+ ##
239
+ # @private
240
+ # Internal recursive lookup for paths.
241
+ #
242
+ def self.paths_internal
243
+ lookup_auth_param :paths, :paths_internal do
244
+ # Pull in values if the DEFAULT_PATHS constant exists.
245
+ vals = lookup_local_constant :DEFAULT_PATHS
246
+ vals ? Array(vals).flatten.uniq : nil
247
+ end
196
248
  end
197
249
 
198
250
  ##
199
251
  # Set the file paths to search for credentials files.
252
+ # Setting to `nil` "unsets" the value, and defaults to the superclass
253
+ # (or to the empty array if there is no superclass).
200
254
  #
201
- # @param [Array<String>] new_paths
202
- # @return [Array<String>]
255
+ # @param [String, Array<String>, nil] new_paths
203
256
  #
204
257
  def self.paths= new_paths
205
258
  new_paths = Array new_paths unless new_paths.nil?
206
259
  @paths = new_paths
207
260
  end
208
261
 
262
+ ##
263
+ # @private
264
+ # Return the given parameter value, defaulting up the class hierarchy.
265
+ #
266
+ # First returns the value of the instance variable, if set.
267
+ # Next, calls the given block if provided. (This is generally used to
268
+ # look up legacy constant-based values.)
269
+ # Otherwise, calls the superclass method if present.
270
+ # Returns nil if all steps fail.
271
+ #
272
+ # @param name [Symbol] The parameter name
273
+ # @param method_name [Symbol] The lookup method name, if different
274
+ # @return [Object] The value
275
+ #
276
+ def self.lookup_auth_param name, method_name = name
277
+ val = instance_variable_get "@#{name}".to_sym
278
+ val = yield if val.nil? && block_given?
279
+ return val unless val.nil?
280
+ return superclass.send method_name if superclass.respond_to? method_name
281
+ nil
282
+ end
283
+
284
+ ##
285
+ # @private
286
+ # Return the value of the given constant if it is defined directly in
287
+ # this class, or nil if not.
288
+ #
289
+ # @param [Symbol] Name of the constant
290
+ # @return [Object] The value
291
+ #
292
+ def self.lookup_local_constant name
293
+ const_defined?(name, false) ? const_get(name) : nil
294
+ end
295
+
209
296
  ##
210
297
  # The Signet::OAuth2::Client object the Credentials instance is using.
211
298
  #
@@ -282,9 +369,10 @@ module Google
282
369
  verify_keyfile_provided! keyfile
283
370
  @project_id = options["project_id"] || options["project"]
284
371
  @quota_project_id = options["quota_project_id"]
285
- if keyfile.is_a? Signet::OAuth2::Client
372
+ case keyfile
373
+ when Signet::OAuth2::Client
286
374
  update_from_signet keyfile
287
- elsif keyfile.is_a? Hash
375
+ when Hash
288
376
  update_from_hash keyfile, options
289
377
  else
290
378
  update_from_filepath keyfile, options
@@ -336,8 +424,15 @@ module Google
336
424
  env_vars.each do |env_var|
337
425
  str = ENV[env_var]
338
426
  next if str.nil?
339
- return new str, options if ::File.file? str
340
- return new ::JSON.parse(str), options rescue nil
427
+ io =
428
+ if ::File.file? str
429
+ ::StringIO.new ::File.read str
430
+ else
431
+ json = ::JSON.parse str rescue nil
432
+ json ? ::StringIO.new(str) : nil
433
+ end
434
+ next if io.nil?
435
+ return from_io io, options
341
436
  end
342
437
  nil
343
438
  end
@@ -345,11 +440,11 @@ module Google
345
440
  ##
346
441
  # @private Lookup Credentials from default file paths.
347
442
  def self.from_default_paths options
348
- paths
349
- .select { |p| ::File.file? p }
350
- .each do |file|
351
- return new file, options
352
- end
443
+ paths.each do |path|
444
+ next unless path && ::File.file?(path)
445
+ io = ::StringIO.new ::File.read path
446
+ return from_io io, options
447
+ end
353
448
  nil
354
449
  end
355
450
 
@@ -357,14 +452,34 @@ module Google
357
452
  # @private Lookup Credentials using Google::Auth.get_application_default.
358
453
  def self.from_application_default options
359
454
  scope = options[:scope] || self.scope
360
- auth_opts = { target_audience: options[:target_audience] || target_audience }
455
+ auth_opts = {
456
+ token_credential_uri: options[:token_credential_uri] || token_credential_uri,
457
+ audience: options[:audience] || audience,
458
+ target_audience: options[:target_audience] || target_audience,
459
+ enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?
460
+ }
361
461
  client = Google::Auth.get_application_default scope, auth_opts
362
462
  new client, options
363
463
  end
364
464
 
465
+ # @private Read credentials from a JSON stream.
466
+ def self.from_io io, options
467
+ creds_input = {
468
+ json_key_io: io,
469
+ scope: options[:scope] || scope,
470
+ target_audience: options[:target_audience] || target_audience,
471
+ enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?,
472
+ token_credential_uri: options[:token_credential_uri] || token_credential_uri,
473
+ audience: options[:audience] || audience
474
+ }
475
+ client = Google::Auth::DefaultCredentials.make_creds creds_input
476
+ new client
477
+ end
478
+
365
479
  private_class_method :from_env_vars,
366
480
  :from_default_paths,
367
- :from_application_default
481
+ :from_application_default,
482
+ :from_io
368
483
 
369
484
  protected
370
485
 
@@ -389,9 +504,11 @@ module Google
389
504
 
390
505
  # returns a new Hash with string keys instead of symbol keys.
391
506
  def stringify_hash_keys hash
392
- Hash[hash.map { |k, v| [k.to_s, v] }]
507
+ hash.to_h.transform_keys(&:to_s)
393
508
  end
394
509
 
510
+ # rubocop:disable Metrics/AbcSize
511
+
395
512
  def client_options options
396
513
  # Keyfile options have higher priority over constructor defaults
397
514
  options["token_credential_uri"] ||= self.class.token_credential_uri
@@ -413,6 +530,8 @@ module Google
413
530
  signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
414
531
  end
415
532
 
533
+ # rubocop:enable Metrics/AbcSize
534
+
416
535
  def update_from_signet client
417
536
  @project_id ||= client.project_id if client.respond_to? :project_id
418
537
  @quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id