googleauth 1.8.1 → 1.9.0

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.

Potentially problematic release.


This version of googleauth might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37795e56189392a97d5941b4982730ffa2ee1dd53ec63593bbb7f5ad50be044e
4
- data.tar.gz: 72d6b584c89c321698485bda1e4aef33f361ed35da4762bd22b5dc4634556f8f
3
+ metadata.gz: a9993f17d33eb7e12e34c5c865560e1352081f9ddebfd46f595bfc81cbef8465
4
+ data.tar.gz: 55a1521b2e5057c1a95cbb2cb61a9b3293d362a338cb684c0ab08a646deed75c
5
5
  SHA512:
6
- metadata.gz: 1dcfc8f1e8e65f9b4b27c4933e71faffd2998d11766307cf464d0551d5f37e41c266e840a340a9aba41d49f44d05005006077526851b1b33e277ce3155d16370
7
- data.tar.gz: f036b3403998c93f41eae33c472ad714758e6c6d06e9a1b197b53d118d182c2abb9caf40636ff63c152e2186642430de824e7929dcd2978cd358a5bd03194c1c
6
+ metadata.gz: 78f4e0778cdd6f52c7898b2da5db2e5824553aaec8b83585fbdeac1ec9d88267cf1fad3d7428d87408782f758b54b9ca8216ba3f4fb0d18c0844acf511de31f1
7
+ data.tar.gz: b6d0a6c0437a9ce59984638415e86b907231051e01044a9f1e810851ee951542ba0f5528a2d945b4e99084426b0336864840c812d154a6267ba93be781d34fda
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Release History
2
2
 
3
+ ### 1.9.0 (2023-12-07)
4
+
5
+ #### Features
6
+
7
+ * Include universe_domain in credentials ([#460](https://github.com/googleapis/google-auth-library-ruby/issues/460))
8
+ * Use google-cloud-env for more robust Metadata Service access ([#459](https://github.com/googleapis/google-auth-library-ruby/issues/459))
9
+
3
10
  ### 1.8.1 (2023-09-19)
4
11
 
5
12
  #### Documentation
@@ -55,11 +55,7 @@ module Google
55
55
  DefaultCredentials.from_well_known_path(scope, options) ||
56
56
  DefaultCredentials.from_system_default_path(scope, options)
57
57
  return creds unless creds.nil?
58
- unless GCECredentials.on_gce? options
59
- # Clear cache of the result of GCECredentials.on_gce?
60
- GCECredentials.reset_cache
61
- raise NOT_FOUND_ERROR
62
- end
58
+ raise NOT_FOUND_ERROR unless GCECredentials.on_gce? options
63
59
  GCECredentials.new options.merge(scope: scope)
64
60
  end
65
61
  end
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require "faraday"
15
+ require "google-cloud-env"
16
16
  require "googleauth/signet"
17
17
 
18
18
  module Google
@@ -33,83 +33,69 @@ module Google
33
33
  # Extends Signet::OAuth2::Client so that the auth token is obtained from
34
34
  # the GCE metadata server.
35
35
  class GCECredentials < Signet::OAuth2::Client
36
- # The IP Address is used in the URIs to speed up failures on non-GCE
37
- # systems.
36
+ # @private Unused and deprecated but retained to prevent breaking changes
38
37
  DEFAULT_METADATA_HOST = "169.254.169.254".freeze
39
38
 
40
- # @private Unused and deprecated
39
+ # @private Unused and deprecated but retained to prevent breaking changes
41
40
  COMPUTE_AUTH_TOKEN_URI =
42
41
  "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
43
- # @private Unused and deprecated
42
+ # @private Unused and deprecated but retained to prevent breaking changes
44
43
  COMPUTE_ID_TOKEN_URI =
45
44
  "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity".freeze
46
- # @private Unused and deprecated
45
+ # @private Unused and deprecated but retained to prevent breaking changes
47
46
  COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
48
47
 
49
- @on_gce_cache = {}
50
-
51
48
  class << self
49
+ # @private Unused and deprecated
52
50
  def metadata_host
53
51
  ENV.fetch "GCE_METADATA_HOST", DEFAULT_METADATA_HOST
54
52
  end
55
53
 
54
+ # @private Unused and deprecated
56
55
  def compute_check_uri
57
56
  "http://#{metadata_host}".freeze
58
57
  end
59
58
 
59
+ # @private Unused and deprecated
60
60
  def compute_auth_token_uri
61
61
  "#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/token".freeze
62
62
  end
63
63
 
64
+ # @private Unused and deprecated
64
65
  def compute_id_token_uri
65
66
  "#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/identity".freeze
66
67
  end
67
68
 
68
69
  # Detect if this appear to be a GCE instance, by checking if metadata
69
70
  # is available.
70
- def on_gce? options = {}, reload = false # rubocop:disable Style/OptionalBooleanParameter
71
- # We can follow OptionalBooleanParameter here because it's a public interface, we can't change it.
72
- @on_gce_cache.delete options if reload
73
- @on_gce_cache.fetch options do
74
- @on_gce_cache[options] = begin
75
- # TODO: This should use google-cloud-env instead.
76
- c = options[:connection] || Faraday.default_connection
77
- headers = { "Metadata-Flavor" => "Google" }
78
- resp = c.get compute_check_uri, nil, headers do |req|
79
- req.options.timeout = 1.0
80
- req.options.open_timeout = 0.1
81
- end
82
- return false unless resp.status == 200
83
- resp.headers["Metadata-Flavor"] == "Google"
84
- rescue Faraday::TimeoutError, Faraday::ConnectionFailed
85
- false
86
- end
87
- end
71
+ # The parameters are deprecated and unused.
72
+ def on_gce? _options = {}, _reload = false # rubocop:disable Style/OptionalBooleanParameter
73
+ Google::Cloud.env.metadata?
88
74
  end
89
75
 
90
76
  def reset_cache
91
- @on_gce_cache.clear
77
+ Google::Cloud.env.compute_metadata.reset_existence!
78
+ Google::Cloud.env.compute_metadata.cache.expire_all!
92
79
  end
93
80
  alias unmemoize_all reset_cache
94
81
  end
95
82
 
96
83
  # Overrides the super class method to change how access tokens are
97
84
  # fetched.
98
- def fetch_access_token options = {}
99
- c = options[:connection] || Faraday.default_connection
100
- retry_with_error do
101
- uri = target_audience ? GCECredentials.compute_id_token_uri : GCECredentials.compute_auth_token_uri
102
- query = target_audience ? { "audience" => target_audience, "format" => "full" } : {}
103
- query[:scopes] = Array(scope).join "," if scope
104
- resp = c.get uri, query, "Metadata-Flavor" => "Google"
85
+ def fetch_access_token _options = {}
86
+ if token_type == :id_token
87
+ query = { "audience" => target_audience, "format" => "full" }
88
+ entry = "service-accounts/default/identity"
89
+ else
90
+ query = {}
91
+ entry = "service-accounts/default/token"
92
+ end
93
+ query[:scopes] = Array(scope).join "," if scope
94
+ begin
95
+ resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query
105
96
  case resp.status
106
97
  when 200
107
- content_type = resp.headers["content-type"]
108
- if ["text/html", "application/text"].include? content_type
109
- { (target_audience ? "id_token" : "access_token") => resp.body }
110
- else
111
- Signet::OAuth2.parse_credentials resp.body, content_type
112
- end
98
+ build_token_hash resp.body, resp.headers["content-type"]
113
99
  when 403, 500
114
100
  msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
115
101
  raise Signet::UnexpectedStatusError, msg
@@ -119,8 +105,25 @@ module Google
119
105
  msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
120
106
  raise Signet::AuthorizationError, msg
121
107
  end
108
+ rescue Google::Cloud::Env::MetadataServerNotResponding => e
109
+ raise Signet::AuthorizationError, e.message
122
110
  end
123
111
  end
112
+
113
+ private
114
+
115
+ def build_token_hash body, content_type
116
+ hash =
117
+ if ["text/html", "application/text"].include? content_type
118
+ { token_type.to_s => body }
119
+ else
120
+ Signet::OAuth2.parse_credentials body, content_type
121
+ end
122
+ universe_domain = Google::Cloud.env.lookup_metadata "universe", "universe_domain"
123
+ universe_domain = "googleapis.com" if !universe_domain || universe_domain.empty?
124
+ hash["universe_domain"] = universe_domain.strip
125
+ hash
126
+ end
124
127
  end
125
128
  end
126
129
  end
@@ -259,7 +259,7 @@ module Google
259
259
  # @return [Object] The value
260
260
  #
261
261
  def self.lookup_auth_param name, method_name = name
262
- val = instance_variable_get "@#{name}".to_sym
262
+ val = instance_variable_get :"@#{name}"
263
263
  val = yield if val.nil? && block_given?
264
264
  return val unless val.nil?
265
265
  return superclass.send method_name if superclass.respond_to? method_name
@@ -328,9 +328,13 @@ module Google
328
328
  # @return [Proc] Returns a reference to the {Signet::OAuth2::Client#apply} method,
329
329
  # suitable for passing as a closure.
330
330
  #
331
+ # @!attribute [rw] universe_domain
332
+ # @return [String] The universe domain issuing these credentials.
333
+ #
331
334
  def_delegators :@client,
332
335
  :token_credential_uri, :audience,
333
- :scope, :issuer, :signing_key, :updater_proc, :target_audience
336
+ :scope, :issuer, :signing_key, :updater_proc, :target_audience,
337
+ :universe_domain, :universe_domain=
334
338
 
335
339
  ##
336
340
  # Creates a new Credentials instance with the provided auth credentials, and with the default
@@ -506,12 +510,15 @@ module Google
506
510
 
507
511
  needs_scope = options["target_audience"].nil?
508
512
  # client options for initializing signet client
509
- { token_credential_uri: options["token_credential_uri"],
513
+ {
514
+ token_credential_uri: options["token_credential_uri"],
510
515
  audience: options["audience"],
511
516
  scope: (needs_scope ? Array(options["scope"]) : nil),
512
517
  target_audience: options["target_audience"],
513
518
  issuer: options["client_email"],
514
- signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
519
+ signing_key: OpenSSL::PKey::RSA.new(options["private_key"]),
520
+ universe_domain: options["universe_domain"] || "googleapis.com"
521
+ }
515
522
  end
516
523
 
517
524
  # rubocop:enable Metrics/AbcSize
@@ -526,7 +533,7 @@ module Google
526
533
  hash = stringify_hash_keys hash
527
534
  hash["scope"] ||= options[:scope]
528
535
  hash["target_audience"] ||= options[:target_audience]
529
- @project_id ||= (hash["project_id"] || hash["project"])
536
+ @project_id ||= hash["project_id"] || hash["project"]
530
537
  @quota_project_id ||= hash["quota_project_id"]
531
538
  @client = init_client hash, options
532
539
  end
@@ -536,7 +543,7 @@ module Google
536
543
  json = JSON.parse ::File.read(path)
537
544
  json["scope"] ||= options[:scope]
538
545
  json["target_audience"] ||= options[:target_audience]
539
- @project_id ||= (json["project_id"] || json["project"])
546
+ @project_id ||= json["project_id"] || json["project"]
540
547
  @quota_project_id ||= json["quota_project_id"]
541
548
  @client = init_client json, options
542
549
  end
@@ -42,6 +42,7 @@ module Google
42
42
 
43
43
  attr_reader :expires_at
44
44
  attr_accessor :access_token
45
+ attr_accessor :universe_domain
45
46
 
46
47
  def expires_within? seconds
47
48
  # This method is needed for BaseClient
@@ -85,8 +86,7 @@ module Google
85
86
  # true if the credentials represent a workforce pool.
86
87
  # false if they represent a workload.
87
88
  def is_workforce_pool?
88
- pattern = "//iam\.googleapis\.com/locations/[^/]+/workforcePools/"
89
- /#{pattern}/.match?(@audience || "")
89
+ %r{/iam\.googleapis\.com/locations/[^/]+/workforcePools/}.match?(@audience || "")
90
90
  end
91
91
 
92
92
  private
@@ -111,6 +111,7 @@ module Google
111
111
  @quota_project_id = options[:quota_project_id]
112
112
  @project_id = nil
113
113
  @workforce_pool_user_project = options[:workforce_pool_user_project]
114
+ @universe_domain = options[:universe_domain] || "googleapis.com"
114
115
 
115
116
  @expires_at = nil
116
117
  @access_token = nil
@@ -73,7 +73,8 @@ module Google
73
73
  subject_token_type: user_creds[:subject_token_type],
74
74
  token_url: user_creds[:token_url],
75
75
  credential_source: user_creds[:credential_source],
76
- service_account_impersonation_url: user_creds[:service_account_impersonation_url]
76
+ service_account_impersonation_url: user_creds[:service_account_impersonation_url],
77
+ universe_domain: user_creds[:universe_domain]
77
78
  )
78
79
  end
79
80
 
@@ -27,7 +27,8 @@ module Google
27
27
  json_key["private_key"],
28
28
  json_key["client_email"],
29
29
  json_key["project_id"],
30
- json_key["quota_project_id"]
30
+ json_key["quota_project_id"],
31
+ json_key["universe_domain"]
31
32
  ]
32
33
  end
33
34
  end
@@ -53,12 +53,13 @@ module Google
53
53
  raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
54
54
 
55
55
  if json_key_io
56
- private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
56
+ private_key, client_email, project_id, quota_project_id, universe_domain = read_json_key json_key_io
57
57
  else
58
58
  private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
59
59
  client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
60
60
  project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
61
61
  quota_project_id = nil
62
+ universe_domain = nil
62
63
  end
63
64
  project_id ||= CredentialsLoader.load_gcloud_project_id
64
65
 
@@ -70,7 +71,8 @@ module Google
70
71
  issuer: client_email,
71
72
  signing_key: OpenSSL::PKey::RSA.new(private_key),
72
73
  project_id: project_id,
73
- quota_project_id: quota_project_id)
74
+ quota_project_id: quota_project_id,
75
+ universe_domain: universe_domain || "googleapis.com")
74
76
  .configure_connection(options)
75
77
  end
76
78
 
@@ -95,8 +97,9 @@ module Google
95
97
  def apply! a_hash, opts = {}
96
98
  # Use a self-singed JWT if there's no information that can be used to
97
99
  # obtain an OAuth token, OR if there are scopes but also an assertion
98
- # that they are default scopes that shouldn't be used to fetch a token.
99
- if target_audience.nil? && (scope.nil? || enable_self_signed_jwt?)
100
+ # that they are default scopes that shouldn't be used to fetch a token,
101
+ # OR we are not in the default universe and thus OAuth isn't supported.
102
+ if target_audience.nil? && (scope.nil? || enable_self_signed_jwt? || universe_domain != "googleapis.com")
100
103
  apply_self_signed_jwt! a_hash
101
104
  else
102
105
  super
@@ -138,6 +141,7 @@ module Google
138
141
  extend JsonKeyReader
139
142
  attr_reader :project_id
140
143
  attr_reader :quota_project_id
144
+ attr_accessor :universe_domain
141
145
 
142
146
  # Create a ServiceAccountJwtHeaderCredentials.
143
147
  #
@@ -154,14 +158,16 @@ module Google
154
158
  def initialize options = {}
155
159
  json_key_io = options[:json_key_io]
156
160
  if json_key_io
157
- @private_key, @issuer, @project_id, @quota_project_id =
161
+ @private_key, @issuer, @project_id, @quota_project_id, @universe_domain =
158
162
  self.class.read_json_key json_key_io
159
163
  else
160
164
  @private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
161
165
  @issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
162
166
  @project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
163
167
  @quota_project_id = nil
168
+ @universe_domain = nil
164
169
  end
170
+ @universe_domain ||= "googleapis.com"
165
171
  @project_id ||= CredentialsLoader.load_gcloud_project_id
166
172
  @signing_key = OpenSSL::PKey::RSA.new @private_key
167
173
  @scope = options[:scope]
@@ -25,6 +25,15 @@ module Signet
25
25
  class Client
26
26
  include Google::Auth::BaseClient
27
27
 
28
+ alias update_token_signet_base update_token!
29
+
30
+ def update_token! options = {}
31
+ options = deep_hash_normalize options
32
+ update_token_signet_base options
33
+ self.universe_domain = options[:universe_domain] if options.key? :universe_domain
34
+ self
35
+ end
36
+
28
37
  def configure_connection options
29
38
  @connection_info =
30
39
  options[:connection_builder] || options[:default_connection]
@@ -36,6 +45,9 @@ module Signet
36
45
  target_audience ? :id_token : :access_token
37
46
  end
38
47
 
48
+ # Set the universe domain
49
+ attr_accessor :universe_domain
50
+
39
51
  alias orig_fetch_access_token! fetch_access_token!
40
52
  def fetch_access_token! options = {}
41
53
  unless options[:connection]
@@ -50,7 +50,8 @@ module Google
50
50
  "client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
51
51
  "refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
52
52
  "project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
53
- "quota_project_id" => nil
53
+ "quota_project_id" => nil,
54
+ "universe_domain" => nil
54
55
  }
55
56
  new(token_credential_uri: TOKEN_CRED_URI,
56
57
  client_id: user_creds["client_id"],
@@ -58,7 +59,8 @@ module Google
58
59
  refresh_token: user_creds["refresh_token"],
59
60
  project_id: user_creds["project_id"],
60
61
  quota_project_id: user_creds["quota_project_id"],
61
- scope: scope)
62
+ scope: scope,
63
+ universe_domain: user_creds["universe_domain"] || "googleapis.com")
62
64
  .configure_connection(options)
63
65
  end
64
66
 
@@ -16,6 +16,6 @@ module Google
16
16
  # Module Auth provides classes that provide Google-specific authorization
17
17
  # used to access Google APIs.
18
18
  module Auth
19
- VERSION = "1.8.1".freeze
19
+ VERSION = "1.9.0".freeze
20
20
  end
21
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googleauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Emiola
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-20 00:00:00.000000000 Z
11
+ date: 2023-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.17.3
19
+ version: '1.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: 3.a
@@ -26,10 +26,30 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.17.3
29
+ version: '1.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 3.a
33
+ - !ruby/object:Gem::Dependency
34
+ name: google-cloud-env
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.0.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '2.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 2.0.1
33
53
  - !ruby/object:Gem::Dependency
34
54
  name: jwt
35
55
  requirement: !ruby/object:Gem::Requirement
@@ -165,7 +185,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
185
  requirements:
166
186
  - - ">="
167
187
  - !ruby/object:Gem::Version
168
- version: '2.6'
188
+ version: '2.7'
169
189
  required_rubygems_version: !ruby/object:Gem::Requirement
170
190
  requirements:
171
191
  - - ">="