googleauth 1.15.0 → 1.16.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Credentials.md +6 -2
- data/README.md +0 -1
- data/lib/googleauth/credentials.rb +49 -34
- data/lib/googleauth/credentials_loader.rb +15 -0
- data/lib/googleauth/default_credentials.rb +62 -39
- data/lib/googleauth/external_account.rb +14 -0
- data/lib/googleauth/impersonated_service_account.rb +53 -1
- data/lib/googleauth/service_account.rb +25 -9
- data/lib/googleauth/user_refresh.rb +17 -9
- data/lib/googleauth/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4cadf68940056f975d386c6cb0cc55b4e1dcebb72a0313a5a5b58347994fa9dd
|
|
4
|
+
data.tar.gz: cad38a2d2014e88abda0e63092786302294c2e963a4e4fca56a681ec7f84f08b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 195efa15c10d767d5ab032291d1c341e760e9bbf326f025f705595e09e6f122f18d582a185ca6c1604a710b8facc11b37cb02e04664b4ba2488b8a85e2318f47
|
|
7
|
+
data.tar.gz: ce881af766994bd312208a9531da24a92efa6f3311abae02cda4f7c1b1d303f54d765b571574745d0cc61cd35e8cc9ceccfdc39a8d9d5368dad6e18f6dbcbd3d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 1.16.0 (2025-11-21)
|
|
4
|
+
|
|
5
|
+
#### Features
|
|
6
|
+
|
|
7
|
+
* Add ADC support for impersonated credentials ([#547](https://github.com/googleapis/google-auth-library-ruby/issues/547))
|
|
8
|
+
#### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* Include security warning in ExternalAccount and ImpersonatedServiceAccount credentials ([#551](https://github.com/googleapis/google-auth-library-ruby/issues/551))
|
|
11
|
+
|
|
12
|
+
### 1.15.1 (2025-10-14)
|
|
13
|
+
|
|
14
|
+
#### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Deprecate method make_creds in DefaultCredentials ([#545](https://github.com/googleapis/google-auth-library-ruby/issues/545))
|
|
17
|
+
|
|
3
18
|
### 1.15.0 (2025-08-25)
|
|
4
19
|
|
|
5
20
|
#### Features
|
data/Credentials.md
CHANGED
|
@@ -38,17 +38,19 @@ that exposes common initialization functionality, such as creating credentials f
|
|
|
38
38
|
- For obtaining authentication tokens from GCE metadata server
|
|
39
39
|
- Used automatically when code is running on Google Compute Engine
|
|
40
40
|
- Fetches tokens from the metadata server with no additional configuration needed
|
|
41
|
+
- This credential type does not have a supported JSON form
|
|
41
42
|
|
|
42
43
|
4. **Google::Auth::IAMCredentials < Signet::OAuth2::Client** - `lib/googleauth/iam.rb`
|
|
43
44
|
- For IAM-based authentication (e.g. service-to-service)
|
|
44
45
|
- Implements authentication-as-a-service for systems already authenticated
|
|
45
46
|
- Exchanges existing credentials for a short-lived access token
|
|
47
|
+
- This credential type does not have a supported JSON form
|
|
46
48
|
|
|
47
49
|
## Service Account Authentication
|
|
48
50
|
|
|
49
51
|
5. **Google::Auth::ServiceAccountCredentials < Signet::OAuth2::Client** - `lib/googleauth/service_account.rb`
|
|
50
52
|
- Authenticates requests using Service Account credentials via an OAuth access token
|
|
51
|
-
- Created from JSON key file downloaded from Google Cloud Console
|
|
53
|
+
- Created from JSON key file downloaded from Google Cloud Console. The JSON form of this credential type has a `"type"` field with the value `"service_account"`.
|
|
52
54
|
- Supports both OAuth access tokens and self-signed JWT authentication
|
|
53
55
|
- Can specify scopes for access token requests
|
|
54
56
|
|
|
@@ -64,6 +66,7 @@ that exposes common initialization functionality, such as creating credentials f
|
|
|
64
66
|
- Allows a GCP principal identified by a set of source credentials to impersonate a service account
|
|
65
67
|
- Useful for delegation of authority and managing permissions across service accounts
|
|
66
68
|
- Source credentials must have the Service Account Token Creator role on the target
|
|
69
|
+
- This credential type supports JSON configuration. The JSON form of this credential type has a `"type"` field with the value `"impersonated_service_account"`.
|
|
67
70
|
|
|
68
71
|
## User Authentication
|
|
69
72
|
|
|
@@ -71,7 +74,7 @@ that exposes common initialization functionality, such as creating credentials f
|
|
|
71
74
|
- For user refresh token authentication (from 3-legged OAuth flow)
|
|
72
75
|
- Authenticates on behalf of a user who has authorized the application
|
|
73
76
|
- Handles token refresh when original access token expires
|
|
74
|
-
- Typically obtained through web or installed application flow
|
|
77
|
+
- Typically obtained through web or installed application flow. The JSON form of this credential type has a `"type"` field with the value `"authorized_user"`.
|
|
75
78
|
|
|
76
79
|
`Google::Auth::UserAuthorizer` (`lib/googleauth/user_authorizer.rb`) and `Google::Auth::WebUserAuthorizer` (`lib/googleauth/web_user_authorizer.rb`)
|
|
77
80
|
are used to facilitate user authentication. The `UserAuthorizer` handles interactive 3-Legged-OAuth2 (3LO) user consent authorization for command-line applications.
|
|
@@ -83,6 +86,7 @@ that exposes common initialization functionality, such as creating credentials f
|
|
|
83
86
|
types based on credential source (similar to `Google::Auth::get_application_default`).
|
|
84
87
|
It is included in all External Account credentials types, and it itself includes `Google::Auth::BaseClient` module so all External
|
|
85
88
|
Account credentials types include `Google::Auth::BaseClient`.
|
|
89
|
+
The JSON form of this credential type has a `"type"` field with the value `"external_account"`.
|
|
86
90
|
|
|
87
91
|
9. **Google::Auth::ExternalAccount::AwsCredentials** - `lib/googleauth/external_account/aws_credentials.rb`
|
|
88
92
|
- Includes `Google::Auth::BaseClient` module
|
data/README.md
CHANGED
|
@@ -292,5 +292,4 @@ hesitate to
|
|
|
292
292
|
about the client or APIs on [StackOverflow](http://stackoverflow.com).
|
|
293
293
|
|
|
294
294
|
[application default credentials]: https://cloud.google.com/docs/authentication/provide-credentials-adc
|
|
295
|
-
[contributing]: https://github.com/googleapis/google-auth-library-ruby/tree/main/.github/CONTRIBUTING.md
|
|
296
295
|
[license]: https://github.com/googleapis/google-auth-library-ruby/tree/main/LICENSE
|
|
@@ -16,6 +16,7 @@ require "forwardable"
|
|
|
16
16
|
require "json"
|
|
17
17
|
require "pathname"
|
|
18
18
|
require "signet/oauth_2/client"
|
|
19
|
+
require "multi_json"
|
|
19
20
|
|
|
20
21
|
require "googleauth/credentials_loader"
|
|
21
22
|
require "googleauth/errors"
|
|
@@ -508,17 +509,61 @@ module Google
|
|
|
508
509
|
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
|
|
509
510
|
audience: options[:audience] || audience
|
|
510
511
|
}
|
|
511
|
-
|
|
512
|
+
|
|
513
|
+
# Determine the class, which consumes the IO stream
|
|
514
|
+
json_key, clz = Google::Auth::DefaultCredentials.determine_creds_class creds_input[:json_key_io]
|
|
515
|
+
|
|
516
|
+
# Re-serialize the parsed JSON and replace the IO stream in creds_input
|
|
517
|
+
creds_input[:json_key_io] = StringIO.new MultiJson.dump(json_key)
|
|
518
|
+
|
|
519
|
+
client = clz.make_creds creds_input
|
|
512
520
|
options = options.select { |k, _v| k == :logger }
|
|
513
521
|
new client, options
|
|
514
522
|
end
|
|
515
523
|
|
|
524
|
+
# @private
|
|
525
|
+
# Initializes the Signet client.
|
|
526
|
+
def self.init_client hash, options = {}
|
|
527
|
+
options = update_client_options options
|
|
528
|
+
io = StringIO.new JSON.generate hash
|
|
529
|
+
|
|
530
|
+
# Determine the class, which consumes the IO stream
|
|
531
|
+
json_key, clz = Google::Auth::DefaultCredentials.determine_creds_class io
|
|
532
|
+
|
|
533
|
+
# Re-serialize the parsed JSON and create a new IO stream.
|
|
534
|
+
new_io = StringIO.new MultiJson.dump(json_key)
|
|
535
|
+
|
|
536
|
+
clz.make_creds options.merge!(json_key_io: new_io)
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
# @private
|
|
540
|
+
# Updates client options with defaults from the credential class
|
|
541
|
+
#
|
|
542
|
+
# @param [Hash] options Options to update
|
|
543
|
+
# @return [Hash] Updated options hash
|
|
544
|
+
# @raise [ArgumentError] If both scope and target_audience are specified
|
|
545
|
+
def self.update_client_options options
|
|
546
|
+
options = options.dup
|
|
547
|
+
|
|
548
|
+
# options have higher priority over constructor defaults
|
|
549
|
+
options[:token_credential_uri] ||= token_credential_uri
|
|
550
|
+
options[:audience] ||= audience
|
|
551
|
+
options[:scope] ||= scope
|
|
552
|
+
options[:target_audience] ||= target_audience
|
|
553
|
+
|
|
554
|
+
if !Array(options[:scope]).empty? && options[:target_audience]
|
|
555
|
+
raise ArgumentError, "Cannot specify both scope and target_audience"
|
|
556
|
+
end
|
|
557
|
+
options.delete :scope unless options[:target_audience].nil?
|
|
558
|
+
|
|
559
|
+
options
|
|
560
|
+
end
|
|
561
|
+
|
|
516
562
|
private_class_method :from_env_vars,
|
|
517
563
|
:from_default_paths,
|
|
518
564
|
:from_application_default,
|
|
519
565
|
:from_io
|
|
520
566
|
|
|
521
|
-
|
|
522
567
|
# Creates a duplicate of these credentials. This method tries to create the duplicate of the
|
|
523
568
|
# wrapped credentials if they support duplication and use them as is if they don't.
|
|
524
569
|
#
|
|
@@ -571,14 +616,6 @@ module Google
|
|
|
571
616
|
raise InitializationError, "The keyfile '#{keyfile}' is not a valid file." unless exists
|
|
572
617
|
end
|
|
573
618
|
|
|
574
|
-
# Initializes the Signet client.
|
|
575
|
-
def init_client hash, options = {}
|
|
576
|
-
options = update_client_options options
|
|
577
|
-
io = StringIO.new JSON.generate hash
|
|
578
|
-
options.merge! json_key_io: io
|
|
579
|
-
Google::Auth::DefaultCredentials.make_creds options
|
|
580
|
-
end
|
|
581
|
-
|
|
582
619
|
# returns a new Hash with string keys instead of symbol keys.
|
|
583
620
|
def stringify_hash_keys hash
|
|
584
621
|
hash.to_h.transform_keys(&:to_s)
|
|
@@ -589,28 +626,6 @@ module Google
|
|
|
589
626
|
hash.to_h.transform_keys(&:to_sym)
|
|
590
627
|
end
|
|
591
628
|
|
|
592
|
-
# Updates client options with defaults from the credential class
|
|
593
|
-
#
|
|
594
|
-
# @param [Hash] options Options to update
|
|
595
|
-
# @return [Hash] Updated options hash
|
|
596
|
-
# @raise [ArgumentError] If both scope and target_audience are specified
|
|
597
|
-
def update_client_options options
|
|
598
|
-
options = options.dup
|
|
599
|
-
|
|
600
|
-
# options have higher priority over constructor defaults
|
|
601
|
-
options[:token_credential_uri] ||= self.class.token_credential_uri
|
|
602
|
-
options[:audience] ||= self.class.audience
|
|
603
|
-
options[:scope] ||= self.class.scope
|
|
604
|
-
options[:target_audience] ||= self.class.target_audience
|
|
605
|
-
|
|
606
|
-
if !Array(options[:scope]).empty? && options[:target_audience]
|
|
607
|
-
raise ArgumentError, "Cannot specify both scope and target_audience"
|
|
608
|
-
end
|
|
609
|
-
options.delete :scope unless options[:target_audience].nil?
|
|
610
|
-
|
|
611
|
-
options
|
|
612
|
-
end
|
|
613
|
-
|
|
614
629
|
def update_from_client client
|
|
615
630
|
@project_id ||= client.project_id if client.respond_to? :project_id
|
|
616
631
|
@quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
|
|
@@ -624,7 +639,7 @@ module Google
|
|
|
624
639
|
hash["target_audience"] ||= options[:target_audience]
|
|
625
640
|
@project_id ||= hash["project_id"] || hash["project"]
|
|
626
641
|
@quota_project_id ||= hash["quota_project_id"]
|
|
627
|
-
@client = init_client hash, options
|
|
642
|
+
@client = self.class.init_client hash, options
|
|
628
643
|
end
|
|
629
644
|
|
|
630
645
|
def update_from_filepath path, options
|
|
@@ -634,7 +649,7 @@ module Google
|
|
|
634
649
|
json["target_audience"] ||= options[:target_audience]
|
|
635
650
|
@project_id ||= json["project_id"] || json["project"]
|
|
636
651
|
@quota_project_id ||= json["quota_project_id"]
|
|
637
|
-
@client = init_client json, options
|
|
652
|
+
@client = self.class.init_client json, options
|
|
638
653
|
end
|
|
639
654
|
|
|
640
655
|
def setup_logging logger: :default
|
|
@@ -158,6 +158,21 @@ module Google
|
|
|
158
158
|
nil
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
+
# @private
|
|
162
|
+
# Loads a JSON key from an IO object, verifies its type, and rewinds the IO.
|
|
163
|
+
#
|
|
164
|
+
# @param json_key_io [IO] An IO object containing the JSON key.
|
|
165
|
+
# @param expected_type [String] The expected credential type name.
|
|
166
|
+
# @raise [Google::Auth::InitializationError] If the JSON key type does not match the expected type.
|
|
167
|
+
def load_and_verify_json_key_type json_key_io, expected_type
|
|
168
|
+
json_key = MultiJson.load json_key_io.read
|
|
169
|
+
json_key_io.rewind # Rewind the stream so it can be read again.
|
|
170
|
+
return if json_key["type"] == expected_type
|
|
171
|
+
raise Google::Auth::InitializationError,
|
|
172
|
+
"The provided credentials were not of type '#{expected_type}'. " \
|
|
173
|
+
"Instead, the type was '#{json_key['type']}'."
|
|
174
|
+
end
|
|
175
|
+
|
|
161
176
|
private
|
|
162
177
|
|
|
163
178
|
def interpret_options scope, options
|
|
@@ -21,6 +21,7 @@ require "googleauth/external_account"
|
|
|
21
21
|
require "googleauth/service_account"
|
|
22
22
|
require "googleauth/service_account_jwt_header"
|
|
23
23
|
require "googleauth/user_refresh"
|
|
24
|
+
require "googleauth/impersonated_service_account"
|
|
24
25
|
|
|
25
26
|
module Google
|
|
26
27
|
# Module Auth provides classes that provide Google-specific authorization
|
|
@@ -43,61 +44,83 @@ module Google
|
|
|
43
44
|
# information, refer to [Validate credential configurations from external
|
|
44
45
|
# sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
|
45
46
|
#
|
|
47
|
+
# @deprecated This method is deprecated and will be removed in a future version.
|
|
48
|
+
# Please use the `make_creds` method on the specific credential class you intend to load,
|
|
49
|
+
# e.g., `Google::Auth::ServiceAccountCredentials.make_creds`.
|
|
50
|
+
#
|
|
51
|
+
# This method does not validate the credential configuration. The security
|
|
52
|
+
# risk occurs when a credential configuration is accepted from a source that
|
|
53
|
+
# is not under your control and used without validation on your side.
|
|
54
|
+
#
|
|
55
|
+
# If you know that you will be loading credential configurations of a
|
|
56
|
+
# specific type, it is recommended to use a credential-type-specific
|
|
57
|
+
# `make_creds` method.
|
|
58
|
+
# This will ensure that an unexpected credential type with potential for
|
|
59
|
+
# malicious intent is not loaded unintentionally. You might still have to do
|
|
60
|
+
# validation for certain credential types. Please follow the recommendation
|
|
61
|
+
# for that method. For example, if you want to load only service accounts,
|
|
62
|
+
# you can use:
|
|
63
|
+
# ```
|
|
64
|
+
# creds = Google::Auth::ServiceAccountCredentials.make_creds
|
|
65
|
+
# ```
|
|
66
|
+
# @see Google::Auth::ServiceAccountCredentials.make_creds
|
|
67
|
+
#
|
|
68
|
+
# If you are loading your credential configuration from an untrusted source and have
|
|
69
|
+
# not mitigated the risks (e.g. by validating the configuration yourself), make
|
|
70
|
+
# these changes as soon as possible to prevent security risks to your environment.
|
|
71
|
+
#
|
|
72
|
+
# Regardless of the method used, it is always your responsibility to validate
|
|
73
|
+
# configurations received from external sources.
|
|
74
|
+
#
|
|
75
|
+
# See https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details.
|
|
76
|
+
#
|
|
46
77
|
# @param options [Hash] Options for creating the credentials
|
|
47
78
|
# @return [Google::Auth::Credentials] The credentials instance
|
|
48
79
|
# @raise [Google::Auth::InitializationError] If the credentials cannot be determined
|
|
49
80
|
def self.make_creds options = {}
|
|
50
81
|
json_key_io = options[:json_key_io]
|
|
51
|
-
|
|
52
|
-
|
|
82
|
+
json_key, clz = determine_creds_class json_key_io
|
|
83
|
+
if json_key
|
|
53
84
|
io = StringIO.new MultiJson.dump(json_key)
|
|
54
85
|
clz.make_creds options.merge(json_key_io: io)
|
|
55
86
|
else
|
|
56
|
-
clz = read_creds
|
|
57
87
|
clz.make_creds options
|
|
58
88
|
end
|
|
59
89
|
end
|
|
60
90
|
|
|
61
|
-
# Reads the credential type from environment and returns the appropriate class
|
|
62
|
-
#
|
|
63
|
-
# @return [Class] The credential class to use
|
|
64
|
-
# @raise [Google::Auth::InitializationError] If the credentials type is undefined or unsupported
|
|
65
|
-
def self.read_creds
|
|
66
|
-
env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
|
|
67
|
-
type = ENV[env_var]
|
|
68
|
-
raise InitializationError, "#{env_var} is undefined in env" unless type
|
|
69
|
-
case type
|
|
70
|
-
when "service_account"
|
|
71
|
-
ServiceAccountCredentials
|
|
72
|
-
when "authorized_user"
|
|
73
|
-
UserRefreshCredentials
|
|
74
|
-
when "external_account"
|
|
75
|
-
ExternalAccount::Credentials
|
|
76
|
-
else
|
|
77
|
-
raise InitializationError, "credentials type '#{type}' is not supported"
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
91
|
# Reads the input json and determines which creds class to use.
|
|
82
92
|
#
|
|
83
|
-
# @param json_key_io [IO] An IO object containing the JSON key
|
|
84
|
-
#
|
|
85
|
-
# @
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
[
|
|
94
|
-
when "authorized_user"
|
|
95
|
-
[json_key, UserRefreshCredentials]
|
|
96
|
-
when "external_account"
|
|
97
|
-
[json_key, ExternalAccount::Credentials]
|
|
93
|
+
# @param json_key_io [IO, nil] An optional IO object containing the JSON key.
|
|
94
|
+
# If nil, the credential type is determined from environment variables.
|
|
95
|
+
# @return [Array(Hash, Class)] The JSON key (or nil if from environment) and the credential class to use
|
|
96
|
+
# @raise [Google::Auth::InitializationError] If the JSON is missing the type field or has an unsupported type,
|
|
97
|
+
# or if the environment variable is undefined or unsupported.
|
|
98
|
+
def self.determine_creds_class json_key_io = nil
|
|
99
|
+
if json_key_io
|
|
100
|
+
json_key = MultiJson.load json_key_io.read
|
|
101
|
+
key = "type"
|
|
102
|
+
raise InitializationError, "the json is missing the '#{key}' field" unless json_key.key? key
|
|
103
|
+
type = json_key[key]
|
|
98
104
|
else
|
|
99
|
-
|
|
105
|
+
env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
|
|
106
|
+
type = ENV[env_var]
|
|
107
|
+
raise InitializationError, "#{env_var} is undefined in env" unless type
|
|
108
|
+
json_key = nil
|
|
100
109
|
end
|
|
110
|
+
|
|
111
|
+
clz = case type
|
|
112
|
+
when ServiceAccountCredentials::CREDENTIAL_TYPE_NAME
|
|
113
|
+
ServiceAccountCredentials
|
|
114
|
+
when UserRefreshCredentials::CREDENTIAL_TYPE_NAME
|
|
115
|
+
UserRefreshCredentials
|
|
116
|
+
when ExternalAccount::Credentials::CREDENTIAL_TYPE_NAME
|
|
117
|
+
ExternalAccount::Credentials
|
|
118
|
+
when ImpersonatedServiceAccountCredentials::CREDENTIAL_TYPE_NAME
|
|
119
|
+
ImpersonatedServiceAccountCredentials
|
|
120
|
+
else
|
|
121
|
+
raise InitializationError, "credentials type '#{type}' is not supported"
|
|
122
|
+
end
|
|
123
|
+
[json_key, clz]
|
|
101
124
|
end
|
|
102
125
|
end
|
|
103
126
|
end
|
|
@@ -34,8 +34,21 @@ module Google
|
|
|
34
34
|
MISSING_CREDENTIAL_SOURCE = "missing credential source for external account".freeze
|
|
35
35
|
INVALID_EXTERNAL_ACCOUNT_TYPE = "credential source is not supported external account type".freeze
|
|
36
36
|
|
|
37
|
+
# @private
|
|
38
|
+
# @type [::String] The type name for this credential.
|
|
39
|
+
CREDENTIAL_TYPE_NAME = "external_account".freeze
|
|
40
|
+
|
|
37
41
|
# Create a ExternalAccount::Credentials
|
|
38
42
|
#
|
|
43
|
+
# @note Warning:
|
|
44
|
+
# This method does not validate the credential configuration. A security
|
|
45
|
+
# risk occurs when a credential configuration configured with malicious urls
|
|
46
|
+
# is used.
|
|
47
|
+
# When the credential configuration is accepted from an
|
|
48
|
+
# untrusted source, you should validate it before using with this method.
|
|
49
|
+
# See https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
|
50
|
+
# for more details.
|
|
51
|
+
#
|
|
39
52
|
# @param options [Hash] Options for creating credentials
|
|
40
53
|
# @option options [IO] :json_key_io (required) An IO object containing the JSON key
|
|
41
54
|
# @option options [String,Array,nil] :scope The scope(s) to access
|
|
@@ -49,6 +62,7 @@ module Google
|
|
|
49
62
|
json_key_io, scope = options.values_at :json_key_io, :scope
|
|
50
63
|
|
|
51
64
|
raise InitializationError, "A json file is required for external account credentials." unless json_key_io
|
|
65
|
+
CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME
|
|
52
66
|
user_creds = read_json_key json_key_io
|
|
53
67
|
|
|
54
68
|
# AWS credentials is determined by aws subject token type
|
|
@@ -23,6 +23,9 @@ module Google
|
|
|
23
23
|
# and then that claim is exchanged for a short-lived token at an IAMCredentials endpoint.
|
|
24
24
|
# The short-lived token and its expiration time are cached.
|
|
25
25
|
class ImpersonatedServiceAccountCredentials
|
|
26
|
+
# @private
|
|
27
|
+
CREDENTIAL_TYPE_NAME = "impersonated_service_account".freeze
|
|
28
|
+
|
|
26
29
|
# @private
|
|
27
30
|
ERROR_SUFFIX = <<~ERROR.freeze
|
|
28
31
|
when trying to get security access token
|
|
@@ -69,6 +72,15 @@ module Google
|
|
|
69
72
|
# and request short-lived credentials for a service account
|
|
70
73
|
# that has the authorization that your use case requires.
|
|
71
74
|
#
|
|
75
|
+
# @note Warning:
|
|
76
|
+
# This method does not validate the credential configuration. A security
|
|
77
|
+
# risk occurs when a credential configuration configured with malicious urls
|
|
78
|
+
# is used.
|
|
79
|
+
# When the credential configuration is accepted from an
|
|
80
|
+
# untrusted source, you should validate it before using with this method.
|
|
81
|
+
# See https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
|
82
|
+
# for more details.
|
|
83
|
+
#
|
|
72
84
|
# @param options [Hash] A hash of options to configure the credentials.
|
|
73
85
|
# @option options [Object] :base_credentials (required) The authenticated principal.
|
|
74
86
|
# It will be used as following:
|
|
@@ -84,11 +96,50 @@ module Google
|
|
|
84
96
|
# defining the permissions required for the token.
|
|
85
97
|
# @option options [Object] :source_credentials The authenticated principal that will be used
|
|
86
98
|
# to fetch the short-lived impersonation access token. It is an alternative to providing the base credentials.
|
|
99
|
+
# @option options [IO] :json_key_io The IO object that contains the credential configuration.
|
|
100
|
+
# It is exclusive with `:base_credentials` and `:source_credentials` options.
|
|
87
101
|
#
|
|
88
102
|
# @return [Google::Auth::ImpersonatedServiceAccountCredentials]
|
|
89
103
|
def self.make_creds options = {}
|
|
90
|
-
|
|
104
|
+
if options[:json_key_io]
|
|
105
|
+
make_creds_from_json options
|
|
106
|
+
else
|
|
107
|
+
new options
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @private
|
|
112
|
+
def self.make_creds_from_json options
|
|
113
|
+
json_key_io = options[:json_key_io]
|
|
114
|
+
if options[:base_credentials] || options[:source_credentials]
|
|
115
|
+
raise Google::Auth::InitializationError,
|
|
116
|
+
"json_key_io is not compatible with base_credentials or source_credentials"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
require "googleauth/default_credentials"
|
|
120
|
+
impersonated_json = MultiJson.load json_key_io.read
|
|
121
|
+
source_credentials_info = impersonated_json["source_credentials"]
|
|
122
|
+
|
|
123
|
+
if source_credentials_info["type"] == CREDENTIAL_TYPE_NAME
|
|
124
|
+
raise Google::Auth::InitializationError,
|
|
125
|
+
"Source credentials can't be of type impersonated_service_account, " \
|
|
126
|
+
"use delegates to chain impersonation."
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
source_credentials = DefaultCredentials.make_creds(
|
|
130
|
+
json_key_io: StringIO.new(MultiJson.dump(source_credentials_info))
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
impersonation_url = impersonated_json["service_account_impersonation_url"]
|
|
134
|
+
scope = options[:scope] || impersonated_json["scopes"]
|
|
135
|
+
|
|
136
|
+
new(
|
|
137
|
+
source_credentials: source_credentials,
|
|
138
|
+
impersonation_url: impersonation_url,
|
|
139
|
+
scope: scope
|
|
140
|
+
)
|
|
91
141
|
end
|
|
142
|
+
private_class_method :make_creds_from_json
|
|
92
143
|
|
|
93
144
|
# Initializes a new instance of ImpersonatedServiceAccountCredentials.
|
|
94
145
|
#
|
|
@@ -105,6 +156,7 @@ module Google
|
|
|
105
156
|
# - `{source_sa_email}` is the email address of the service account to impersonate.
|
|
106
157
|
# @option options [Array<String>, String] :scope (required) The scope(s) for the short-lived impersonation token,
|
|
107
158
|
# defining the permissions required for the token.
|
|
159
|
+
# It will override the scope from the `json_key_io` file if provided.
|
|
108
160
|
# @option options [Object] :source_credentials The authenticated principal that will be used
|
|
109
161
|
# to fetch the short-lived impersonation access token. It is an alternative to providing the base credentials.
|
|
110
162
|
# It is redundant to provide both source and base credentials as only source will be used,
|
|
@@ -41,6 +41,10 @@ module Google
|
|
|
41
41
|
attr_reader :project_id
|
|
42
42
|
attr_reader :quota_project_id
|
|
43
43
|
|
|
44
|
+
# @private
|
|
45
|
+
# @type [::String] The type name for this credential.
|
|
46
|
+
CREDENTIAL_TYPE_NAME = "service_account".freeze
|
|
47
|
+
|
|
44
48
|
def enable_self_signed_jwt?
|
|
45
49
|
# Use a self-singed JWT if there's no information that can be used to
|
|
46
50
|
# obtain an OAuth token, OR if there are scopes but also an assertion
|
|
@@ -60,15 +64,13 @@ module Google
|
|
|
60
64
|
:audience, :token_credential_uri
|
|
61
65
|
raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
universe_domain = nil
|
|
71
|
-
end
|
|
67
|
+
private_key, client_email, project_id, quota_project_id, universe_domain =
|
|
68
|
+
if json_key_io
|
|
69
|
+
CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME
|
|
70
|
+
read_json_key json_key_io
|
|
71
|
+
else
|
|
72
|
+
creds_from_env
|
|
73
|
+
end
|
|
72
74
|
project_id ||= CredentialsLoader.load_gcloud_project_id
|
|
73
75
|
|
|
74
76
|
new(token_credential_uri: token_credential_uri || TOKEN_CRED_URI,
|
|
@@ -190,6 +192,20 @@ module Google
|
|
|
190
192
|
alt.logger = logger
|
|
191
193
|
alt.apply! a_hash
|
|
192
194
|
end
|
|
195
|
+
|
|
196
|
+
# @private
|
|
197
|
+
# Loads service account credential details from environment variables.
|
|
198
|
+
#
|
|
199
|
+
# @return [Array<String, String, String, nil, nil>] An array containing private_key,
|
|
200
|
+
# client_email, project_id, quota_project_id, and universe_domain.
|
|
201
|
+
def self.creds_from_env
|
|
202
|
+
private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
|
203
|
+
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
|
204
|
+
project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
|
205
|
+
[private_key, client_email, project_id, nil, nil]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
private_class_method :creds_from_env
|
|
193
209
|
end
|
|
194
210
|
end
|
|
195
211
|
end
|
|
@@ -39,21 +39,29 @@ module Google
|
|
|
39
39
|
attr_reader :project_id
|
|
40
40
|
attr_reader :quota_project_id
|
|
41
41
|
|
|
42
|
+
# @private
|
|
43
|
+
# @type [::String] The type name for this credential.
|
|
44
|
+
CREDENTIAL_TYPE_NAME = "authorized_user".freeze
|
|
45
|
+
|
|
42
46
|
# Create a UserRefreshCredentials.
|
|
43
47
|
#
|
|
44
48
|
# @param json_key_io [IO] An IO object containing the JSON key
|
|
45
49
|
# @param scope [string|array|nil] the scope(s) to access
|
|
46
50
|
def self.make_creds options = {}
|
|
47
51
|
json_key_io, scope = options.values_at :json_key_io, :scope
|
|
48
|
-
user_creds =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
user_creds = if json_key_io
|
|
53
|
+
CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME
|
|
54
|
+
read_json_key json_key_io
|
|
55
|
+
else
|
|
56
|
+
{
|
|
57
|
+
"client_id" => ENV[CredentialsLoader::CLIENT_ID_VAR],
|
|
58
|
+
"client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
|
|
59
|
+
"refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
|
|
60
|
+
"project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
|
|
61
|
+
"quota_project_id" => nil,
|
|
62
|
+
"universe_domain" => nil
|
|
63
|
+
}
|
|
64
|
+
end
|
|
57
65
|
new(token_credential_uri: TOKEN_CRED_URI,
|
|
58
66
|
client_id: user_creds["client_id"],
|
|
59
67
|
client_secret: user_creds["client_secret"],
|
data/lib/googleauth/version.rb
CHANGED