googleauth 1.8.1 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37795e56189392a97d5941b4982730ffa2ee1dd53ec63593bbb7f5ad50be044e
4
- data.tar.gz: 72d6b584c89c321698485bda1e4aef33f361ed35da4762bd22b5dc4634556f8f
3
+ metadata.gz: 5950bbb2b0bb696d85ded5cfd31bc2a44192b9a5e42c774260300e467a821316
4
+ data.tar.gz: 937e9ef7634d2667a6e552276e0dc7716511a3597b33de2c20174a865bcb3598
5
5
  SHA512:
6
- metadata.gz: 1dcfc8f1e8e65f9b4b27c4933e71faffd2998d11766307cf464d0551d5f37e41c266e840a340a9aba41d49f44d05005006077526851b1b33e277ce3155d16370
7
- data.tar.gz: f036b3403998c93f41eae33c472ad714758e6c6d06e9a1b197b53d118d182c2abb9caf40636ff63c152e2186642430de824e7929dcd2978cd358a5bd03194c1c
6
+ metadata.gz: 94c41b28ce5fdd2ed5437cf30aeb67d53f04b5863711fd74b1403612cd0747f9e2856fda14678927ef1c026af0f45e6c15a2eef68415a77585b752845f7852a6
7
+ data.tar.gz: 46d4ace82973f0df463733bad339ea00786baa90e1adbb665bdadf3d71999dfeb67177f98b4b26b13464307d02a5ef7b649793bb56dc7619b7f9a076118b3942
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Release History
2
2
 
3
+ ### 1.9.1 (2023-12-12)
4
+
5
+ #### Bug Fixes
6
+
7
+ * update expires_in for cached metadata-retrieved tokens ([#464](https://github.com/googleapis/google-auth-library-ruby/issues/464))
8
+
9
+ ### 1.9.0 (2023-12-07)
10
+
11
+ #### Features
12
+
13
+ * Include universe_domain in credentials ([#460](https://github.com/googleapis/google-auth-library-ruby/issues/460))
14
+ * Use google-cloud-env for more robust Metadata Service access ([#459](https://github.com/googleapis/google-auth-library-ruby/issues/459))
15
+
3
16
  ### 1.8.1 (2023-09-19)
4
17
 
5
18
  #### 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"], resp.retrieval_monotonic_time
113
99
  when 403, 500
114
100
  msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
115
101
  raise Signet::UnexpectedStatusError, msg
@@ -119,7 +105,33 @@ 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
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def build_token_hash body, content_type, retrieval_time
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
+ # The response might have been cached, which means expires_in might be
126
+ # stale. Update it based on the time since the data was retrieved.
127
+ # We also ensure expires_in is conservative; subtracting at least 1
128
+ # second to offset any skew from metadata server latency.
129
+ if hash["expires_in"].is_a? Numeric
130
+ offset = 1 + (Process.clock_gettime(Process::CLOCK_MONOTONIC) - retrieval_time).round
131
+ hash["expires_in"] -= offset if offset.positive?
132
+ hash["expires_in"] = 0 if hash["expires_in"].negative?
122
133
  end
134
+ hash
123
135
  end
124
136
  end
125
137
  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.1".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.1
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-12 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,24 @@ 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.1'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.1'
33
47
  - !ruby/object:Gem::Dependency
34
48
  name: jwt
35
49
  requirement: !ruby/object:Gem::Requirement
@@ -165,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
179
  requirements:
166
180
  - - ">="
167
181
  - !ruby/object:Gem::Version
168
- version: '2.6'
182
+ version: '2.7'
169
183
  required_rubygems_version: !ruby/object:Gem::Requirement
170
184
  requirements:
171
185
  - - ">="