googleauth 1.11.1 → 1.13.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +9 -0
- data/lib/googleauth/base_client.rb +13 -1
- data/lib/googleauth/compute_engine.rb +130 -25
- data/lib/googleauth/credentials.rb +139 -29
- data/lib/googleauth/default_credentials.rb +12 -1
- data/lib/googleauth/external_account/base_credentials.rb +33 -2
- data/lib/googleauth/id_tokens.rb +0 -2
- data/lib/googleauth/impersonated_service_account.rb +282 -0
- data/lib/googleauth/service_account.rb +114 -7
- data/lib/googleauth/signet.rb +120 -2
- data/lib/googleauth/user_refresh.rb +43 -0
- data/lib/googleauth/version.rb +1 -1
- metadata +20 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d8ca5b2b0c7f4ce54f7971d8de2f23f3ee0837d08d7d3c568c503308fcf82ab
|
4
|
+
data.tar.gz: d5f8b8fd2fcb4fef4240db58bf90f54a8bfd021c550a7bc9063c9087285f3921
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a4de2b23f4dc0310a18568e0618c5d81fad54dfc8d57fe3c27b954c4bd21272fcc467c2c313f98f80fa127eab11ebe0d0fc55ceed7e6c5439764500e334df49
|
7
|
+
data.tar.gz: f4aff68138105ea19875bb7a51d4b6ced9ec2e7185ce725eca205ea1dc11beb9e6019c9dd89449866598ca6e4d3abb06b91f277f34e51ac7726d79a39fc40c67
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,45 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 1.13.1 (2025-01-24)
|
4
|
+
|
5
|
+
#### Bug Fixes
|
6
|
+
|
7
|
+
* Signet client subclasses no longer make the update! method private ([#516](https://github.com/googleapis/google-auth-library-ruby/issues/516))
|
8
|
+
|
9
|
+
### 1.13.0 (2025-01-22)
|
10
|
+
|
11
|
+
#### Features
|
12
|
+
|
13
|
+
* create impersonated service credentials ([#499](https://github.com/googleapis/google-auth-library-ruby/issues/499))
|
14
|
+
#### Documentation
|
15
|
+
|
16
|
+
* Include note about validating externally-provided credentials ([#512](https://github.com/googleapis/google-auth-library-ruby/issues/512))
|
17
|
+
|
18
|
+
### 1.12.2 (2024-12-19)
|
19
|
+
|
20
|
+
#### Bug Fixes
|
21
|
+
|
22
|
+
* GCECredentials lazily fetches from the metadata server to ensure a universe domain is known ([#509](https://github.com/googleapis/google-auth-library-ruby/issues/509))
|
23
|
+
|
24
|
+
### 1.12.1 (2024-12-17)
|
25
|
+
|
26
|
+
#### Bug Fixes
|
27
|
+
|
28
|
+
* Restored previous behavior where the apply! method returns the auth header ([#506](https://github.com/googleapis/google-auth-library-ruby/issues/506))
|
29
|
+
|
30
|
+
### 1.12.0 (2024-12-05)
|
31
|
+
|
32
|
+
#### Features
|
33
|
+
|
34
|
+
* provided opt-in debug logging ([#490](https://github.com/googleapis/google-auth-library-ruby/issues/490))
|
35
|
+
|
36
|
+
### 1.11.2 (2024-10-23)
|
37
|
+
|
38
|
+
#### Bug Fixes
|
39
|
+
|
40
|
+
* Temporarily disable universe domain query from GCE metadata server ([#493](https://github.com/googleapis/google-auth-library-ruby/issues/493))
|
41
|
+
* Use updated metadata path for universe-domain ([#496](https://github.com/googleapis/google-auth-library-ruby/issues/496))
|
42
|
+
|
3
43
|
### 1.11.1 (2024-10-04)
|
4
44
|
|
5
45
|
#### Bug Fixes
|
data/README.md
CHANGED
@@ -64,6 +64,15 @@ well as a web variant tailored toward Rack-based applications.
|
|
64
64
|
The authorizers are intended for authorization use cases. For sign-on,
|
65
65
|
see [Google Identity Platform](https://developers.google.com/identity/)
|
66
66
|
|
67
|
+
## Important notes
|
68
|
+
|
69
|
+
If you accept a credential configuration (credential JSON/File/Stream) from an
|
70
|
+
external source for authentication to Google Cloud, you must validate it before
|
71
|
+
providing it to any Google API or library. Providing an unvalidated credential
|
72
|
+
configuration to Google APIs can compromise the security of your systems and data.
|
73
|
+
For more information, refer to [Validate credential configurations from external
|
74
|
+
sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
75
|
+
|
67
76
|
### Example (Web)
|
68
77
|
|
69
78
|
```ruby
|
@@ -12,6 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
+
require "google/logging/message"
|
16
|
+
|
15
17
|
module Google
|
16
18
|
# Module Auth provides classes that provide Google-specific authorization
|
17
19
|
# used to access Google APIs.
|
@@ -29,7 +31,14 @@ module Google
|
|
29
31
|
# fetch the access token there is currently not one, or if the client
|
30
32
|
# has expired
|
31
33
|
fetch_access_token! opts if needs_access_token?
|
32
|
-
|
34
|
+
token = send token_type
|
35
|
+
a_hash[AUTH_METADATA_KEY] = "Bearer #{token}"
|
36
|
+
logger&.debug do
|
37
|
+
hash = Digest::SHA256.hexdigest token
|
38
|
+
Google::Logging::Message.from message: "Sending auth token. (sha256:#{hash})"
|
39
|
+
end
|
40
|
+
|
41
|
+
a_hash[AUTH_METADATA_KEY]
|
33
42
|
end
|
34
43
|
|
35
44
|
# Returns a clone of a_hash updated with the authentication token
|
@@ -66,6 +75,9 @@ module Google
|
|
66
75
|
raise NoMethodError, "expires_within? not implemented"
|
67
76
|
end
|
68
77
|
|
78
|
+
# The logger used to log operations on this client, such as token refresh.
|
79
|
+
attr_accessor :logger
|
80
|
+
|
69
81
|
private
|
70
82
|
|
71
83
|
def token_type
|
@@ -80,46 +80,139 @@ module Google
|
|
80
80
|
alias unmemoize_all reset_cache
|
81
81
|
end
|
82
82
|
|
83
|
+
# @private Temporary; remove when universe domain metadata endpoint is stable (see b/349488459).
|
84
|
+
attr_accessor :disable_universe_domain_check
|
85
|
+
|
83
86
|
# Construct a GCECredentials
|
84
87
|
def initialize options = {}
|
85
88
|
# Override the constructor to remember whether the universe domain was
|
86
89
|
# overridden by a constructor argument.
|
87
90
|
@universe_domain_overridden = options["universe_domain"] || options[:universe_domain] ? true : false
|
91
|
+
# TODO: Remove when universe domain metadata endpoint is stable (see b/349488459).
|
92
|
+
@disable_universe_domain_check = true
|
88
93
|
super options
|
89
94
|
end
|
90
95
|
|
96
|
+
# Creates a duplicate of these credentials
|
97
|
+
# without the Signet::OAuth2::Client-specific
|
98
|
+
# transient state (e.g. cached tokens)
|
99
|
+
#
|
100
|
+
# @param options [Hash] Overrides for the credentials parameters.
|
101
|
+
# The following keys are recognized in addition to keys in the
|
102
|
+
# Signet::OAuth2::Client
|
103
|
+
# * `:universe_domain_overridden` Whether the universe domain was
|
104
|
+
# overriden during credentials creation
|
105
|
+
def duplicate options = {}
|
106
|
+
options = deep_hash_normalize options
|
107
|
+
super(
|
108
|
+
{
|
109
|
+
universe_domain_overridden: @universe_domain_overridden
|
110
|
+
}.merge(options)
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
# @private
|
115
|
+
# Overrides universe_domain getter to fetch lazily if it hasn't been
|
116
|
+
# fetched yet. This is necessary specifically for Compute Engine because
|
117
|
+
# the universe comes from the metadata service, and isn't known
|
118
|
+
# immediately on credential construction. All other credential types read
|
119
|
+
# the universe from their json key or other immediate input.
|
120
|
+
def universe_domain
|
121
|
+
value = super
|
122
|
+
return value unless value.nil?
|
123
|
+
fetch_access_token!
|
124
|
+
super
|
125
|
+
end
|
126
|
+
|
91
127
|
# Overrides the super class method to change how access tokens are
|
92
128
|
# fetched.
|
93
129
|
def fetch_access_token _options = {}
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
130
|
+
query, entry =
|
131
|
+
if token_type == :id_token
|
132
|
+
[{ "audience" => target_audience, "format" => "full" }, "service-accounts/default/identity"]
|
133
|
+
else
|
134
|
+
[{}, "service-accounts/default/token"]
|
135
|
+
end
|
101
136
|
query[:scopes] = Array(scope).join "," if scope
|
102
137
|
begin
|
138
|
+
log_fetch_query
|
103
139
|
resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query
|
140
|
+
log_fetch_resp resp
|
104
141
|
case resp.status
|
105
142
|
when 200
|
106
143
|
build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time
|
107
144
|
when 403, 500
|
108
|
-
|
109
|
-
raise Signet::UnexpectedStatusError, msg
|
145
|
+
raise Signet::UnexpectedStatusError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
110
146
|
when 404
|
111
147
|
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
112
148
|
else
|
113
|
-
|
114
|
-
raise Signet::AuthorizationError, msg
|
149
|
+
raise Signet::AuthorizationError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
115
150
|
end
|
116
151
|
rescue Google::Cloud::Env::MetadataServerNotResponding => e
|
152
|
+
log_fetch_err e
|
117
153
|
raise Signet::AuthorizationError, e.message
|
118
154
|
end
|
119
155
|
end
|
120
156
|
|
157
|
+
# Destructively updates these credentials.
|
158
|
+
#
|
159
|
+
# This method is called by `Signet::OAuth2::Client`'s constructor
|
160
|
+
#
|
161
|
+
# @param options [Hash] Overrides for the credentials parameters.
|
162
|
+
# The following keys are recognized in addition to keys in the
|
163
|
+
# Signet::OAuth2::Client
|
164
|
+
# * `:universe_domain_overridden` Whether the universe domain was
|
165
|
+
# overriden during credentials creation
|
166
|
+
# @return [Google::Auth::GCECredentials]
|
167
|
+
def update! options = {}
|
168
|
+
# Normalize all keys to symbols to allow indifferent access.
|
169
|
+
options = deep_hash_normalize options
|
170
|
+
|
171
|
+
@universe_domain_overridden = options[:universe_domain_overridden] if options.key? :universe_domain_overridden
|
172
|
+
|
173
|
+
super(options)
|
174
|
+
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
121
178
|
private
|
122
179
|
|
180
|
+
def log_fetch_query
|
181
|
+
if token_type == :id_token
|
182
|
+
logger&.info do
|
183
|
+
Google::Logging::Message.from(
|
184
|
+
message: "Requesting id token from MDS with aud=#{target_audience}",
|
185
|
+
"credentialsId" => object_id
|
186
|
+
)
|
187
|
+
end
|
188
|
+
else
|
189
|
+
logger&.info do
|
190
|
+
Google::Logging::Message.from(
|
191
|
+
message: "Requesting access token from MDS",
|
192
|
+
"credentialsId" => object_id
|
193
|
+
)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def log_fetch_resp resp
|
199
|
+
logger&.info do
|
200
|
+
Google::Logging::Message.from(
|
201
|
+
message: "Received #{resp.status} from MDS",
|
202
|
+
"credentialsId" => object_id
|
203
|
+
)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def log_fetch_err _err
|
208
|
+
logger&.info do
|
209
|
+
Google::Logging::Message.from(
|
210
|
+
message: "MDS did not respond to token request",
|
211
|
+
"credentialsId" => object_id
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
123
216
|
def build_token_hash body, content_type, retrieval_time
|
124
217
|
hash =
|
125
218
|
if ["text/html", "application/text"].include? content_type
|
@@ -127,20 +220,8 @@ module Google
|
|
127
220
|
else
|
128
221
|
Signet::OAuth2.parse_credentials body, content_type
|
129
222
|
end
|
130
|
-
|
131
|
-
|
132
|
-
universe_domain = "googleapis.com" if !universe_domain || universe_domain.empty?
|
133
|
-
hash["universe_domain"] = universe_domain.strip
|
134
|
-
end
|
135
|
-
# The response might have been cached, which means expires_in might be
|
136
|
-
# stale. Update it based on the time since the data was retrieved.
|
137
|
-
# We also ensure expires_in is conservative; subtracting at least 1
|
138
|
-
# second to offset any skew from metadata server latency.
|
139
|
-
if hash["expires_in"].is_a? Numeric
|
140
|
-
offset = 1 + (Process.clock_gettime(Process::CLOCK_MONOTONIC) - retrieval_time).round
|
141
|
-
hash["expires_in"] -= offset if offset.positive?
|
142
|
-
hash["expires_in"] = 0 if hash["expires_in"].negative?
|
143
|
-
end
|
223
|
+
add_universe_domain_to hash
|
224
|
+
adjust_for_stale_expires_in hash, retrieval_time
|
144
225
|
hash
|
145
226
|
end
|
146
227
|
|
@@ -152,6 +233,30 @@ module Google
|
|
152
233
|
end
|
153
234
|
hash
|
154
235
|
end
|
236
|
+
|
237
|
+
def add_universe_domain_to hash
|
238
|
+
return if @universe_domain_overridden
|
239
|
+
universe_domain =
|
240
|
+
if disable_universe_domain_check
|
241
|
+
# TODO: Remove when universe domain metadata endpoint is stable (see b/349488459).
|
242
|
+
"googleapis.com"
|
243
|
+
else
|
244
|
+
Google::Cloud.env.lookup_metadata "universe", "universe-domain"
|
245
|
+
end
|
246
|
+
universe_domain = "googleapis.com" if !universe_domain || universe_domain.empty?
|
247
|
+
hash["universe_domain"] = universe_domain.strip
|
248
|
+
end
|
249
|
+
|
250
|
+
# The response might have been cached, which means expires_in might be
|
251
|
+
# stale. Update it based on the time since the data was retrieved.
|
252
|
+
# We also ensure expires_in is conservative; subtracting at least 1
|
253
|
+
# second to offset any skew from metadata server latency.
|
254
|
+
def adjust_for_stale_expires_in hash, retrieval_time
|
255
|
+
return unless hash["expires_in"].is_a? Numeric
|
256
|
+
offset = 1 + (Process.clock_gettime(Process::CLOCK_MONOTONIC) - retrieval_time).round
|
257
|
+
hash["expires_in"] -= offset if offset.positive?
|
258
|
+
hash["expires_in"] = 0 if hash["expires_in"].negative?
|
259
|
+
end
|
155
260
|
end
|
156
261
|
end
|
157
262
|
end
|
@@ -26,6 +26,14 @@ module Google
|
|
26
26
|
# In most cases, it is subclassed by API-specific credential classes that
|
27
27
|
# can be instantiated by clients.
|
28
28
|
#
|
29
|
+
# **Important:** If you accept a credential configuration (credential
|
30
|
+
# JSON/File/Stream) from an external source for authentication to Google
|
31
|
+
# Cloud, you must validate it before providing it to any Google API or
|
32
|
+
# library. Providing an unvalidated credential configuration to Google APIs
|
33
|
+
# can compromise the security of your systems and data. For more
|
34
|
+
# information, refer to [Validate credential configurations from external
|
35
|
+
# sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
36
|
+
#
|
29
37
|
# ## Options
|
30
38
|
#
|
31
39
|
# Credentials classes are configured with options that dictate default
|
@@ -299,6 +307,12 @@ module Google
|
|
299
307
|
#
|
300
308
|
attr_reader :quota_project_id
|
301
309
|
|
310
|
+
# @private Temporary; remove when universe domain metadata endpoint is stable (see b/349488459).
|
311
|
+
def disable_universe_domain_check
|
312
|
+
return false unless @client.respond_to? :disable_universe_domain_check
|
313
|
+
@client.disable_universe_domain_check
|
314
|
+
end
|
315
|
+
|
302
316
|
# @private Delegate client methods to the client object.
|
303
317
|
extend Forwardable
|
304
318
|
|
@@ -315,9 +329,6 @@ module Google
|
|
315
329
|
# @return [String, Array<String>] The scope for this client. A scope is an access range
|
316
330
|
# defined by the authorization server. The scope can be a single value or a list of values.
|
317
331
|
#
|
318
|
-
# @!attribute [r] target_audience
|
319
|
-
# @return [String] The final target audience for ID tokens returned by this credential.
|
320
|
-
#
|
321
332
|
# @!attribute [r] issuer
|
322
333
|
# @return [String] The issuer ID associated with this client.
|
323
334
|
#
|
@@ -328,47 +339,74 @@ module Google
|
|
328
339
|
# @return [Proc] Returns a reference to the {Signet::OAuth2::Client#apply} method,
|
329
340
|
# suitable for passing as a closure.
|
330
341
|
#
|
342
|
+
# @!attribute [r] target_audience
|
343
|
+
# @return [String] The final target audience for ID tokens returned by this credential.
|
344
|
+
#
|
331
345
|
# @!attribute [rw] universe_domain
|
332
346
|
# @return [String] The universe domain issuing these credentials.
|
333
347
|
#
|
348
|
+
# @!attribute [rw] logger
|
349
|
+
# @return [Logger] The logger used to log credential operations such as token refresh.
|
350
|
+
#
|
334
351
|
def_delegators :@client,
|
335
352
|
:token_credential_uri, :audience,
|
336
353
|
:scope, :issuer, :signing_key, :updater_proc, :target_audience,
|
337
|
-
:universe_domain, :universe_domain=
|
354
|
+
:universe_domain, :universe_domain=, :logger, :logger=
|
338
355
|
|
339
356
|
##
|
340
357
|
# Creates a new Credentials instance with the provided auth credentials, and with the default
|
341
358
|
# values configured on the class.
|
342
359
|
#
|
343
|
-
# @param [String, Hash, Signet::OAuth2::Client]
|
344
|
-
# The
|
360
|
+
# @param [String, Hash, Signet::OAuth2::Client] source_creds
|
361
|
+
# The source of credentials. It can be provided as one of the following:
|
362
|
+
#
|
363
|
+
# * The path to a JSON keyfile (as a `String`)
|
364
|
+
# * The contents of a JSON keyfile (as a `Hash`)
|
365
|
+
# * A `Signet::OAuth2::Client` credentials object
|
366
|
+
# * Any credentials object that supports the methods this wrapper delegates to an inner client.
|
367
|
+
#
|
368
|
+
# If this parameter is an object (`Signet::OAuth2::Client` or other) it will be used as an inner client.
|
369
|
+
# Otherwise the inner client will be constructed from the JSON keyfile or the contens of the hash.
|
345
370
|
#
|
346
|
-
# * The path to a JSON keyfile (as a +String+)
|
347
|
-
# * The contents of a JSON keyfile (as a +Hash+)
|
348
|
-
# * A +Signet::OAuth2::Client+ object
|
349
371
|
# @param [Hash] options
|
350
|
-
# The options for configuring
|
372
|
+
# The options for configuring this wrapper credentials object and the inner client.
|
373
|
+
# The options hash is used in two ways:
|
351
374
|
#
|
352
|
-
#
|
353
|
-
#
|
354
|
-
#
|
355
|
-
#
|
375
|
+
# 1. **Configuring the wrapper object:** Some options are used to directly
|
376
|
+
# configure the wrapper `Credentials` instance. These include:
|
377
|
+
#
|
378
|
+
# * `:project_id` (and optionally `:project`) - the project identifier for the client
|
379
|
+
# * `:quota_project_id` - the quota project identifier for the client
|
380
|
+
# * `:logger` - the logger used to log credential operations such as token refresh.
|
356
381
|
#
|
357
|
-
|
358
|
-
|
382
|
+
# 2. **Configuring the inner client:** When the `source_creds` parameter
|
383
|
+
# is a `String` or `Hash`, a new `Signet::OAuth2::Client` is created
|
384
|
+
# internally. The following options are used to configure this inner client:
|
385
|
+
#
|
386
|
+
# * `:scope` - the scope for the client
|
387
|
+
# * `:target_audience` - the target audience for the client
|
388
|
+
#
|
389
|
+
# Any other options in the `options` hash are passed directly to the
|
390
|
+
# inner client constructor. This allows you to configure additional
|
391
|
+
# parameters of the `Signet::OAuth2::Client`, such as connection parameters,
|
392
|
+
# timeouts, etc.
|
393
|
+
#
|
394
|
+
def initialize source_creds, options = {}
|
395
|
+
raise "The source credentials passed to Google::Auth::Credentials.new were nil." if source_creds.nil?
|
396
|
+
|
359
397
|
options = symbolize_hash_keys options
|
360
398
|
@project_id = options[:project_id] || options[:project]
|
361
399
|
@quota_project_id = options[:quota_project_id]
|
362
|
-
case
|
363
|
-
when
|
364
|
-
|
400
|
+
case source_creds
|
401
|
+
when String
|
402
|
+
update_from_filepath source_creds, options
|
365
403
|
when Hash
|
366
|
-
update_from_hash
|
404
|
+
update_from_hash source_creds, options
|
367
405
|
else
|
368
|
-
|
406
|
+
update_from_client source_creds
|
369
407
|
end
|
408
|
+
setup_logging logger: options.fetch(:logger, :default)
|
370
409
|
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
371
|
-
@client.fetch_access_token! if @client.needs_access_token?
|
372
410
|
@env_vars = nil
|
373
411
|
@paths = nil
|
374
412
|
@scope = nil
|
@@ -462,7 +500,8 @@ module Google
|
|
462
500
|
audience: options[:audience] || audience
|
463
501
|
}
|
464
502
|
client = Google::Auth::DefaultCredentials.make_creds creds_input
|
465
|
-
|
503
|
+
options = options.select { |k, _v| k == :logger }
|
504
|
+
new client, options
|
466
505
|
end
|
467
506
|
|
468
507
|
private_class_method :from_env_vars,
|
@@ -470,14 +509,50 @@ module Google
|
|
470
509
|
:from_application_default,
|
471
510
|
:from_io
|
472
511
|
|
473
|
-
protected
|
474
512
|
|
475
|
-
#
|
476
|
-
|
477
|
-
|
478
|
-
|
513
|
+
# Creates a duplicate of these credentials. This method tries to create the duplicate of the
|
514
|
+
# wrapped credentials if they support duplication and use them as is if they don't.
|
515
|
+
#
|
516
|
+
# The wrapped credentials are typically `Signet::OAuth2::Client` objects and they keep
|
517
|
+
# the transient state (token, refresh token, etc). The duplication discards that state,
|
518
|
+
# allowing e.g. to get the token with a different scope.
|
519
|
+
#
|
520
|
+
# @param options [Hash] Overrides for the credentials parameters.
|
521
|
+
#
|
522
|
+
# The options hash is used in two ways:
|
523
|
+
#
|
524
|
+
# 1. **Configuring the duplicate of the wrapper object:** Some options are used to directly
|
525
|
+
# configure the wrapper `Credentials` instance. These include:
|
526
|
+
#
|
527
|
+
# * `:project_id` (and optionally `:project`) - the project identifier for the credentials
|
528
|
+
# * `:quota_project_id` - the quota project identifier for the credentials
|
529
|
+
#
|
530
|
+
# 2. **Configuring the duplicate of the inner client:** If the inner client supports duplication
|
531
|
+
# the options hash is passed to it. This allows for configuration of additional parameters,
|
532
|
+
# most importantly (but not limited to) the following:
|
533
|
+
#
|
534
|
+
# * `:scope` - the scope for the client
|
535
|
+
#
|
536
|
+
# @return [Credentials]
|
537
|
+
def duplicate options = {}
|
538
|
+
options = deep_hash_normalize options
|
539
|
+
|
540
|
+
options = {
|
541
|
+
project_id: @project_id,
|
542
|
+
quota_project_id: @quota_project_id
|
543
|
+
}.merge(options)
|
544
|
+
|
545
|
+
new_client = if @client.respond_to? :duplicate
|
546
|
+
@client.duplicate options
|
547
|
+
else
|
548
|
+
@client
|
549
|
+
end
|
550
|
+
|
551
|
+
self.class.new new_client, options
|
479
552
|
end
|
480
553
|
|
554
|
+
protected
|
555
|
+
|
481
556
|
# Verify that the keyfile argument is a file.
|
482
557
|
def verify_keyfile_exists! keyfile
|
483
558
|
exists = ::File.file? keyfile
|
@@ -519,11 +594,12 @@ module Google
|
|
519
594
|
options
|
520
595
|
end
|
521
596
|
|
522
|
-
def
|
597
|
+
def update_from_client client
|
523
598
|
@project_id ||= client.project_id if client.respond_to? :project_id
|
524
599
|
@quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
|
525
600
|
@client = client
|
526
601
|
end
|
602
|
+
alias update_from_signet update_from_client
|
527
603
|
|
528
604
|
def update_from_hash hash, options
|
529
605
|
hash = stringify_hash_keys hash
|
@@ -543,6 +619,40 @@ module Google
|
|
543
619
|
@quota_project_id ||= json["quota_project_id"]
|
544
620
|
@client = init_client json, options
|
545
621
|
end
|
622
|
+
|
623
|
+
def setup_logging logger: :default
|
624
|
+
return unless @client.respond_to? :logger=
|
625
|
+
logging_env = ENV["GOOGLE_SDK_RUBY_LOGGING_GEMS"].to_s.downcase
|
626
|
+
if ["false", "none"].include? logging_env
|
627
|
+
logger = nil
|
628
|
+
elsif @client.logger
|
629
|
+
logger = @client.logger
|
630
|
+
elsif logger == :default
|
631
|
+
logger = nil
|
632
|
+
if ["true", "all"].include?(logging_env) || logging_env.split(",").include?("googleauth")
|
633
|
+
formatter = Google::Logging::StructuredFormatter.new if Google::Cloud::Env.get.logging_agent_expected?
|
634
|
+
logger = Logger.new $stderr, progname: "googleauth", formatter: formatter
|
635
|
+
end
|
636
|
+
end
|
637
|
+
@client.logger = logger
|
638
|
+
end
|
639
|
+
|
640
|
+
private
|
641
|
+
|
642
|
+
# Convert all keys in this hash (nested) to symbols for uniform retrieval
|
643
|
+
def recursive_hash_normalize_keys val
|
644
|
+
if val.is_a? Hash
|
645
|
+
deep_hash_normalize val
|
646
|
+
else
|
647
|
+
val
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
def deep_hash_normalize old_hash
|
652
|
+
sym_hash = {}
|
653
|
+
old_hash&.each { |k, v| sym_hash[k.to_sym] = recursive_hash_normalize_keys v }
|
654
|
+
sym_hash
|
655
|
+
end
|
546
656
|
end
|
547
657
|
end
|
548
658
|
end
|
@@ -19,6 +19,7 @@ require "googleauth/credentials_loader"
|
|
19
19
|
require "googleauth/service_account"
|
20
20
|
require "googleauth/user_refresh"
|
21
21
|
require "googleauth/external_account"
|
22
|
+
require "googleauth/impersonated_service_account"
|
22
23
|
|
23
24
|
module Google
|
24
25
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -29,8 +30,18 @@ module Google
|
|
29
30
|
class DefaultCredentials
|
30
31
|
extend CredentialsLoader
|
31
32
|
|
32
|
-
|
33
|
+
##
|
34
|
+
# Override CredentialsLoader#make_creds to use the class determined by
|
33
35
|
# loading the json.
|
36
|
+
#
|
37
|
+
# **Important:** If you accept a credential configuration (credential
|
38
|
+
# JSON/File/Stream) from an external source for authentication to Google
|
39
|
+
# Cloud, you must validate it before providing it to any Google API or
|
40
|
+
# library. Providing an unvalidated credential configuration to Google
|
41
|
+
# APIs can compromise the security of your systems and data. For more
|
42
|
+
# information, refer to [Validate credential configurations from external
|
43
|
+
# sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
44
|
+
#
|
34
45
|
def self.make_creds options = {}
|
35
46
|
json_key_io = options[:json_key_io]
|
36
47
|
if json_key_io
|
@@ -129,7 +129,7 @@ module Google
|
|
129
129
|
if @client_id.nil? && @workforce_pool_user_project
|
130
130
|
additional_options = { userProject: @workforce_pool_user_project }
|
131
131
|
end
|
132
|
-
|
132
|
+
token_request = {
|
133
133
|
audience: @audience,
|
134
134
|
grant_type: STS_GRANT_TYPE,
|
135
135
|
subject_token: retrieve_subject_token!,
|
@@ -137,10 +137,31 @@ module Google
|
|
137
137
|
scopes: @service_account_impersonation_url ? IAM_SCOPE : @scope,
|
138
138
|
requested_token_type: STS_REQUESTED_TOKEN_TYPE,
|
139
139
|
additional_options: additional_options
|
140
|
-
|
140
|
+
}
|
141
|
+
log_token_request token_request
|
142
|
+
@sts_client.exchange_token token_request
|
143
|
+
end
|
144
|
+
|
145
|
+
def log_token_request token_request
|
146
|
+
logger&.info do
|
147
|
+
Google::Logging::Message.from(
|
148
|
+
message: "Requesting access token from #{token_request[:grant_type]}",
|
149
|
+
"credentialsId" => object_id
|
150
|
+
)
|
151
|
+
end
|
152
|
+
logger&.debug do
|
153
|
+
digest = Digest::SHA256.hexdigest token_request[:subject_token].to_s
|
154
|
+
loggable_request = token_request.merge subject_token: "(sha256:#{digest})"
|
155
|
+
Google::Logging::Message.from(
|
156
|
+
message: "Request data",
|
157
|
+
"request" => loggable_request,
|
158
|
+
"credentialsId" => object_id
|
159
|
+
)
|
160
|
+
end
|
141
161
|
end
|
142
162
|
|
143
163
|
def get_impersonated_access_token token, _options = {}
|
164
|
+
log_impersonated_token_request token
|
144
165
|
response = connection.post @service_account_impersonation_url do |req|
|
145
166
|
req.headers["Authorization"] = "Bearer #{token}"
|
146
167
|
req.headers["Content-Type"] = "application/json"
|
@@ -153,6 +174,16 @@ module Google
|
|
153
174
|
|
154
175
|
MultiJson.load response.body
|
155
176
|
end
|
177
|
+
|
178
|
+
def log_impersonated_token_request original_token
|
179
|
+
logger&.info do
|
180
|
+
digest = Digest::SHA256.hexdigest original_token
|
181
|
+
Google::Logging::Message.from(
|
182
|
+
message: "Requesting impersonated access token with original token (sha256:#{digest})",
|
183
|
+
"credentialsId" => object_id
|
184
|
+
)
|
185
|
+
end
|
186
|
+
end
|
156
187
|
end
|
157
188
|
end
|
158
189
|
end
|
data/lib/googleauth/id_tokens.rb
CHANGED
@@ -168,7 +168,6 @@ module Google
|
|
168
168
|
aud: nil,
|
169
169
|
azp: nil,
|
170
170
|
iss: OIDC_ISSUERS
|
171
|
-
|
172
171
|
verifier = Verifier.new key_source: oidc_key_source,
|
173
172
|
aud: aud,
|
174
173
|
azp: azp,
|
@@ -206,7 +205,6 @@ module Google
|
|
206
205
|
aud: nil,
|
207
206
|
azp: nil,
|
208
207
|
iss: IAP_ISSUERS
|
209
|
-
|
210
208
|
verifier = Verifier.new key_source: iap_key_source,
|
211
209
|
aud: aud,
|
212
210
|
azp: azp,
|