googleauth 1.11.1 → 1.13.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,
|