googleauth 0.14.0 → 0.15.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/.github/workflows/release.yml +36 -0
- data/CHANGELOG.md +41 -27
- data/lib/googleauth/compute_engine.rb +5 -4
- data/lib/googleauth/credentials.rb +132 -36
- data/lib/googleauth/service_account.rb +30 -20
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +3 -6
- data/spec/googleauth/compute_engine_spec.rb +18 -0
- data/spec/googleauth/credentials_spec.rb +223 -106
- data/spec/googleauth/service_account_spec.rb +8 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b15478e865e5cfea5a21aaf18b55e6f7839c0c6a81fd127a249d414ce7f62589
|
|
4
|
+
data.tar.gz: 0a5ea3ff83f4706367b710ac200ef1936f042f8c124174c0ab5857aa435e940c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a096b40f4f8559d1263e9f7fd8d28742ee63199dfc5ae77c486602f14bb9f03ed1331b86e64a0afe32bcafef38bb9e26140226615ac546817e1bc8d4c96812ba
|
|
7
|
+
data.tar.gz: fefa616d20dbfb6b11b71e7869d08e470b919cce6ea991d91930f5036f2945fb652cbb6dfe83f50e93c626c953ba4d9b82483b28891abc97bbb8f4fae863013c
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
on:
|
|
2
|
+
schedule:
|
|
3
|
+
- cron: '29 9 * * 1'
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
name: release
|
|
7
|
+
jobs:
|
|
8
|
+
release-please:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: ReleasePlease
|
|
12
|
+
id: release-please
|
|
13
|
+
uses: GoogleCloudPlatform/release-please-action@v2
|
|
14
|
+
with:
|
|
15
|
+
command: release-pr
|
|
16
|
+
token: ${{ secrets.YOSHI_CODE_BOT_TOKEN }}
|
|
17
|
+
fork: true
|
|
18
|
+
release-type: ruby
|
|
19
|
+
package-name: google-auth-library-ruby
|
|
20
|
+
version-file: lib/googleauth/version.rb
|
|
21
|
+
monorepo-tags: true
|
|
22
|
+
bump-minor-pre-major: true
|
|
23
|
+
- name: ReleaseLabel
|
|
24
|
+
id: release-label
|
|
25
|
+
if: ${{ steps.release-please.outputs.pr }}
|
|
26
|
+
uses: actions/github-script@v2
|
|
27
|
+
with:
|
|
28
|
+
github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
|
|
29
|
+
script: |
|
|
30
|
+
core.info("Labeling release");
|
|
31
|
+
github.issues.addLabels({
|
|
32
|
+
owner: 'googleapis',
|
|
33
|
+
repo: 'google-auth-library-ruby',
|
|
34
|
+
issue_number: ${{ steps.release-please.outputs.pr }},
|
|
35
|
+
labels: ["autorelease: pending", "kokoro:force-run"]
|
|
36
|
+
});
|
data/CHANGELOG.md
CHANGED
|
@@ -1,30 +1,44 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## [0.15.0](https://www.github.com/googleapis/google-auth-library-ruby/compare/v0.14.0...v0.15.0) (2021-01-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Credential parameters inherit from superclasses ([4fa4720](https://www.github.com/googleapis/google-auth-library-ruby/commit/4fa47206dbd62f8bbdd1b9d3721f6baee9fd1d62))
|
|
9
|
+
* Service accounts apply a self-signed JWT if scopes are marked as default ([d22acb8](https://www.github.com/googleapis/google-auth-library-ruby/commit/d22acb8a510e6711b5674545c31a4816e5a9168f))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* Retry fetch_access_token when GCE metadata server returns unexpected errors ([cd9b012](https://www.github.com/googleapis/google-auth-library-ruby/commit/cd9b0126d3419b9953982f71edc9e6ba3f640e3c))
|
|
15
|
+
* Support correct service account and user refresh behavior for custom credential env variables ([d2dffe5](https://www.github.com/googleapis/google-auth-library-ruby/commit/d2dffe592112b45006291ad9a57f56e00fb208c3))
|
|
16
|
+
|
|
17
|
+
## 0.14.0 / 2020-10-09
|
|
4
18
|
|
|
5
19
|
* Honor GCE_METADATA_HOST environment variable
|
|
6
20
|
* Fix errors in some environments when requesting an access token for multiple scopes
|
|
7
21
|
|
|
8
|
-
|
|
22
|
+
## 0.13.1 / 2020-07-30
|
|
9
23
|
|
|
10
24
|
* Support scopes when using GCE Metadata Server authentication ([@ball-hayden][])
|
|
11
25
|
|
|
12
|
-
|
|
26
|
+
## 0.13.0 / 2020-06-17
|
|
13
27
|
|
|
14
28
|
* Support for validating ID tokens.
|
|
15
29
|
* Fixed header application of ID tokens from service accounts.
|
|
16
30
|
|
|
17
|
-
|
|
31
|
+
## 0.12.0 / 2020-04-08
|
|
18
32
|
|
|
19
33
|
* Support for ID token credentials.
|
|
20
34
|
* Support reading quota_id_project from service account credentials.
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
## 0.11.0 / 2020-02-24
|
|
23
37
|
|
|
24
38
|
* Support Faraday 1.x.
|
|
25
39
|
* Allow special "postmessage" value for redirect_uri.
|
|
26
40
|
|
|
27
|
-
|
|
41
|
+
## 0.10.0 / 2019-10-09
|
|
28
42
|
|
|
29
43
|
Note: This release now requires Ruby 2.4 or later
|
|
30
44
|
|
|
@@ -34,7 +48,7 @@ Note: This release now requires Ruby 2.4 or later
|
|
|
34
48
|
* Set instance variables at initialization to avoid spamming warnings
|
|
35
49
|
* Pass "Metadata-Flavor" header to metadata server when checking for GCE
|
|
36
50
|
|
|
37
|
-
|
|
51
|
+
## 0.9.0 / 2019-08-05
|
|
38
52
|
|
|
39
53
|
* Restore compatibility with Ruby 2.0. This is the last release that will work on end-of-lifed versions of Ruby. The 0.10 release will require Ruby 2.4 or later.
|
|
40
54
|
* Update Credentials to use methods for values that are intended to be changed by users, replacing constants.
|
|
@@ -43,79 +57,79 @@ Note: This release now requires Ruby 2.4 or later
|
|
|
43
57
|
* Add verbosity none to gcloud command
|
|
44
58
|
* Make arity of WebUserAuthorizer#get_credentials compatible with the base class
|
|
45
59
|
|
|
46
|
-
|
|
60
|
+
## 0.8.1 / 2019-03-27
|
|
47
61
|
|
|
48
62
|
* Silence unnecessary gcloud warning
|
|
49
63
|
* Treat empty credentials environment variables as unset
|
|
50
64
|
|
|
51
|
-
|
|
65
|
+
## 0.8.0 / 2019-01-02
|
|
52
66
|
|
|
53
67
|
* Support connection options :default_connection and :connection_builder when creating credentials that need to refresh OAuth tokens. This lets clients provide connection objects with custom settings, such as proxies, needed for the client environment.
|
|
54
68
|
* Removed an unnecessary warning about project IDs.
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
## 0.7.1 / 2018-10-25
|
|
57
71
|
|
|
58
72
|
* Make load_gcloud_project_id module function.
|
|
59
73
|
|
|
60
|
-
|
|
74
|
+
## 0.7.0 / 2018-10-24
|
|
61
75
|
|
|
62
76
|
* Add project_id instance variable to UserRefreshCredentials, ServiceAccountCredentials, and Credentials.
|
|
63
77
|
|
|
64
|
-
|
|
78
|
+
## 0.6.7 / 2018-10-16
|
|
65
79
|
|
|
66
80
|
* Update memoist dependency to ~> 0.16.
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
## 0.6.6 / 2018-08-22
|
|
69
83
|
|
|
70
84
|
* Remove ruby version warnings.
|
|
71
85
|
|
|
72
|
-
|
|
86
|
+
## 0.6.5 / 2018-08-16
|
|
73
87
|
|
|
74
88
|
* Fix incorrect http verb when revoking credentials.
|
|
75
89
|
* Warn on EOL ruby versions.
|
|
76
90
|
|
|
77
|
-
|
|
91
|
+
## 0.6.4 / 2018-08-03
|
|
78
92
|
|
|
79
93
|
* Resolve issue where DefaultCredentials constant was undefined.
|
|
80
94
|
|
|
81
|
-
|
|
95
|
+
## 0.6.3 / 2018-08-02
|
|
82
96
|
|
|
83
97
|
* Resolve issue where token_store was being written to twice
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
## 0.6.2 / 2018-08-01
|
|
86
100
|
|
|
87
101
|
* Add warning when using cloud sdk credentials
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
## 0.6.1 / 2017-10-18
|
|
90
104
|
|
|
91
105
|
* Fix file permissions
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
## 0.6.0 / 2017-10-17
|
|
94
108
|
|
|
95
109
|
* Support ruby-jwt 2.0
|
|
96
110
|
* Add simple credentials class
|
|
97
111
|
|
|
98
|
-
|
|
112
|
+
## 0.5.3 / 2017-07-21
|
|
99
113
|
|
|
100
114
|
* Fix file permissions on the gem's `.rb` files.
|
|
101
115
|
|
|
102
|
-
|
|
116
|
+
## 0.5.2 / 2017-07-19
|
|
103
117
|
|
|
104
118
|
* Add retry mechanism when fetching access tokens in `GCECredentials` and `UserRefreshCredentials` classes.
|
|
105
119
|
* Update Google API OAuth2 token credential URI to v4.
|
|
106
120
|
|
|
107
|
-
|
|
121
|
+
## 0.5.1 / 2016-01-06
|
|
108
122
|
|
|
109
123
|
* Change header name emitted by `Client#apply` from "Authorization" to "authorization" ([@murgatroid99][])
|
|
110
124
|
* Fix ADC not working on some windows machines ([@vsubramani][])
|
|
111
125
|
[#55](https://github.com/google/google-auth-library-ruby/issues/55)
|
|
112
126
|
|
|
113
|
-
|
|
127
|
+
## 0.5.0 / 2015-10-12
|
|
114
128
|
|
|
115
129
|
* Initial support for user credentials ([@sqrrrl][])
|
|
116
130
|
* Update Signet to 0.7
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
## 0.4.2 / 2015-08-05
|
|
119
133
|
|
|
120
134
|
* Updated UserRefreshCredentials hash to use string keys ([@haabaato][])
|
|
121
135
|
[#36](https://github.com/google/google-auth-library-ruby/issues/36)
|
|
@@ -132,16 +146,16 @@ Note: This release now requires Ruby 2.4 or later
|
|
|
132
146
|
* Enables passing credentials via environment variables. ([@haabaato][])
|
|
133
147
|
[#27](https://github.com/google/google-auth-library-ruby/issues/27)
|
|
134
148
|
|
|
135
|
-
|
|
149
|
+
## 0.4.1 / 2015-04-25
|
|
136
150
|
|
|
137
151
|
* Improves handling of --no-scopes GCE authorization ([@tbetbetbe][])
|
|
138
152
|
* Refactoring and cleanup ([@joneslee85][])
|
|
139
153
|
|
|
140
|
-
|
|
154
|
+
## 0.4.0 / 2015-03-25
|
|
141
155
|
|
|
142
156
|
* Adds an implementation of JWT header auth ([@tbetbetbe][])
|
|
143
157
|
|
|
144
|
-
|
|
158
|
+
## 0.3.0 / 2015-03-23
|
|
145
159
|
|
|
146
160
|
* makes the scope parameter's optional in all APIs. ([@tbetbetbe][])
|
|
147
161
|
* changes the scope parameter's position in various constructors. ([@tbetbetbe][])
|
|
@@ -108,8 +108,7 @@ module Google
|
|
|
108
108
|
uri = target_audience ? GCECredentials.compute_id_token_uri : GCECredentials.compute_auth_token_uri
|
|
109
109
|
query = target_audience ? { "audience" => target_audience, "format" => "full" } : {}
|
|
110
110
|
query[:scopes] = Array(scope).join "," if scope
|
|
111
|
-
|
|
112
|
-
resp = c.get uri, query, headers
|
|
111
|
+
resp = c.get uri, query, "Metadata-Flavor" => "Google"
|
|
113
112
|
case resp.status
|
|
114
113
|
when 200
|
|
115
114
|
content_type = resp.headers["content-type"]
|
|
@@ -118,11 +117,13 @@ module Google
|
|
|
118
117
|
else
|
|
119
118
|
Signet::OAuth2.parse_credentials resp.body, content_type
|
|
120
119
|
end
|
|
120
|
+
when 403, 500
|
|
121
|
+
msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
|
122
|
+
raise Signet::UnexpectedStatusError, msg
|
|
121
123
|
when 404
|
|
122
124
|
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
|
123
125
|
else
|
|
124
|
-
msg = "Unexpected error code #{resp.status}"
|
|
125
|
-
"#{UNEXPECTED_ERROR_SUFFIX}"
|
|
126
|
+
msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
|
126
127
|
raise Signet::AuthorizationError, msg
|
|
127
128
|
end
|
|
128
129
|
end
|
|
@@ -36,9 +36,46 @@ require "googleauth/credentials_loader"
|
|
|
36
36
|
module Google
|
|
37
37
|
module Auth
|
|
38
38
|
##
|
|
39
|
-
# Credentials is
|
|
40
|
-
#
|
|
41
|
-
|
|
39
|
+
# Credentials is a high-level base class used by Google's API client
|
|
40
|
+
# libraries to represent the authentication when connecting to an API.
|
|
41
|
+
# In most cases, it is subclassed by API-specific credential classes that
|
|
42
|
+
# can be instantiated by clients.
|
|
43
|
+
#
|
|
44
|
+
# ## Options
|
|
45
|
+
#
|
|
46
|
+
# Credentials classes are configured with options that dictate default
|
|
47
|
+
# values for parameters such as scope and audience. These defaults are
|
|
48
|
+
# expressed as class attributes, and may differ from endpoint to endpoint.
|
|
49
|
+
# Normally, an API client will provide subclasses specific to each
|
|
50
|
+
# endpoint, configured with appropriate values.
|
|
51
|
+
#
|
|
52
|
+
# Note that these options inherit up the class hierarchy. If a particular
|
|
53
|
+
# options is not set for a subclass, its superclass is queried.
|
|
54
|
+
#
|
|
55
|
+
# Some older users of this class set options via constants. This usage is
|
|
56
|
+
# deprecated. For example, instead of setting the `AUDIENCE` constant on
|
|
57
|
+
# your subclass, call the `audience=` method.
|
|
58
|
+
#
|
|
59
|
+
# ## Example
|
|
60
|
+
#
|
|
61
|
+
# class MyCredentials < Google::Auth::Credentials
|
|
62
|
+
# # Set the default scope for these credentials
|
|
63
|
+
# self.scope = "http://example.com/my_scope"
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# # creds is a credentials object suitable for Google API clients
|
|
67
|
+
# creds = MyCredentials.default
|
|
68
|
+
# creds.scope # => ["http://example.com/my_scope"]
|
|
69
|
+
#
|
|
70
|
+
# class SubCredentials < MyCredentials
|
|
71
|
+
# # Override the default scope for this subclass
|
|
72
|
+
# self.scope = "http://example.com/sub_scope"
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# creds2 = SubCredentials.default
|
|
76
|
+
# creds2.scope # => ["http://example.com/sub_scope"]
|
|
77
|
+
#
|
|
78
|
+
class Credentials # rubocop:disable Metrics/ClassLength
|
|
42
79
|
##
|
|
43
80
|
# The default token credential URI to be used when none is provided during initialization.
|
|
44
81
|
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token".freeze
|
|
@@ -47,7 +84,7 @@ module Google
|
|
|
47
84
|
# The default target audience ID to be used when none is provided during initialization.
|
|
48
85
|
AUDIENCE = "https://oauth2.googleapis.com/token".freeze
|
|
49
86
|
|
|
50
|
-
@audience = @scope = @target_audience = @env_vars = @paths = nil
|
|
87
|
+
@audience = @scope = @target_audience = @env_vars = @paths = @token_credential_uri = nil
|
|
51
88
|
|
|
52
89
|
##
|
|
53
90
|
# The default token credential URI to be used when none is provided during initialization.
|
|
@@ -57,9 +94,9 @@ module Google
|
|
|
57
94
|
# @return [String]
|
|
58
95
|
#
|
|
59
96
|
def self.token_credential_uri
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
97
|
+
lookup_auth_param :token_credential_uri do
|
|
98
|
+
lookup_local_constant :TOKEN_CREDENTIAL_URI
|
|
99
|
+
end
|
|
63
100
|
end
|
|
64
101
|
|
|
65
102
|
##
|
|
@@ -79,9 +116,9 @@ module Google
|
|
|
79
116
|
# @return [String]
|
|
80
117
|
#
|
|
81
118
|
def self.audience
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
119
|
+
lookup_auth_param :audience do
|
|
120
|
+
lookup_local_constant :AUDIENCE
|
|
121
|
+
end
|
|
85
122
|
end
|
|
86
123
|
|
|
87
124
|
##
|
|
@@ -106,9 +143,10 @@ module Google
|
|
|
106
143
|
# @return [String, Array<String>]
|
|
107
144
|
#
|
|
108
145
|
def self.scope
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
146
|
+
lookup_auth_param :scope do
|
|
147
|
+
vals = lookup_local_constant :SCOPE
|
|
148
|
+
vals ? Array(vals).flatten.uniq : nil
|
|
149
|
+
end
|
|
112
150
|
end
|
|
113
151
|
|
|
114
152
|
##
|
|
@@ -137,7 +175,7 @@ module Google
|
|
|
137
175
|
# @return [String]
|
|
138
176
|
#
|
|
139
177
|
def self.target_audience
|
|
140
|
-
|
|
178
|
+
lookup_auth_param :target_audience
|
|
141
179
|
end
|
|
142
180
|
|
|
143
181
|
##
|
|
@@ -161,13 +199,12 @@ module Google
|
|
|
161
199
|
# @return [Array<String>]
|
|
162
200
|
#
|
|
163
201
|
def self.env_vars
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
tmp_env_vars.flatten.uniq
|
|
202
|
+
lookup_auth_param :env_vars do
|
|
203
|
+
# Pull values when PATH_ENV_VARS or JSON_ENV_VARS constants exists.
|
|
204
|
+
path_env_vars = lookup_local_constant :PATH_ENV_VARS
|
|
205
|
+
json_env_vars = lookup_local_constant :JSON_ENV_VARS
|
|
206
|
+
(Array(path_env_vars) + Array(json_env_vars)).flatten.uniq if path_env_vars || json_env_vars
|
|
207
|
+
end
|
|
171
208
|
end
|
|
172
209
|
|
|
173
210
|
##
|
|
@@ -187,12 +224,11 @@ module Google
|
|
|
187
224
|
# @return [Array<String>]
|
|
188
225
|
#
|
|
189
226
|
def self.paths
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
tmp_paths.flatten.uniq
|
|
227
|
+
lookup_auth_param :paths do
|
|
228
|
+
# Pull in values if the DEFAULT_PATHS constant exists.
|
|
229
|
+
vals = lookup_local_constant :DEFAULT_PATHS
|
|
230
|
+
vals ? Array(vals).flatten.uniq : nil
|
|
231
|
+
end
|
|
196
232
|
end
|
|
197
233
|
|
|
198
234
|
##
|
|
@@ -206,6 +242,39 @@ module Google
|
|
|
206
242
|
@paths = new_paths
|
|
207
243
|
end
|
|
208
244
|
|
|
245
|
+
##
|
|
246
|
+
# @private
|
|
247
|
+
# Return the given parameter value, defaulting up the class hierarchy.
|
|
248
|
+
#
|
|
249
|
+
# First returns the value of the instance variable, if set.
|
|
250
|
+
# Next, calls the given block if provided. (This is generally used to
|
|
251
|
+
# look up legacy constant-based values.)
|
|
252
|
+
# Otherwise, calls the superclass method if present.
|
|
253
|
+
# Returns nil if all steps fail.
|
|
254
|
+
#
|
|
255
|
+
# @param [Symbol] The parameter name
|
|
256
|
+
# @return [Object] The value
|
|
257
|
+
#
|
|
258
|
+
def self.lookup_auth_param name
|
|
259
|
+
val = instance_variable_get "@#{name}".to_sym
|
|
260
|
+
val = yield if val.nil? && block_given?
|
|
261
|
+
return val unless val.nil?
|
|
262
|
+
return superclass.send name if superclass.respond_to? name
|
|
263
|
+
nil
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
##
|
|
267
|
+
# @private
|
|
268
|
+
# Return the value of the given constant if it is defined directly in
|
|
269
|
+
# this class, or nil if not.
|
|
270
|
+
#
|
|
271
|
+
# @param [Symbol] Name of the constant
|
|
272
|
+
# @return [Object] The value
|
|
273
|
+
#
|
|
274
|
+
def self.lookup_local_constant name
|
|
275
|
+
const_defined?(name, false) ? const_get(name) : nil
|
|
276
|
+
end
|
|
277
|
+
|
|
209
278
|
##
|
|
210
279
|
# The Signet::OAuth2::Client object the Credentials instance is using.
|
|
211
280
|
#
|
|
@@ -336,8 +405,15 @@ module Google
|
|
|
336
405
|
env_vars.each do |env_var|
|
|
337
406
|
str = ENV[env_var]
|
|
338
407
|
next if str.nil?
|
|
339
|
-
|
|
340
|
-
|
|
408
|
+
io =
|
|
409
|
+
if ::File.file? str
|
|
410
|
+
::StringIO.new ::File.read str
|
|
411
|
+
else
|
|
412
|
+
json = ::JSON.parse str rescue nil
|
|
413
|
+
json ? ::StringIO.new(str) : nil
|
|
414
|
+
end
|
|
415
|
+
next if io.nil?
|
|
416
|
+
return from_io io, options
|
|
341
417
|
end
|
|
342
418
|
nil
|
|
343
419
|
end
|
|
@@ -345,11 +421,11 @@ module Google
|
|
|
345
421
|
##
|
|
346
422
|
# @private Lookup Credentials from default file paths.
|
|
347
423
|
def self.from_default_paths options
|
|
348
|
-
paths
|
|
349
|
-
|
|
350
|
-
.
|
|
351
|
-
|
|
352
|
-
|
|
424
|
+
paths.each do |path|
|
|
425
|
+
next unless path && ::File.file?(path)
|
|
426
|
+
io = ::StringIO.new ::File.read path
|
|
427
|
+
return from_io io, options
|
|
428
|
+
end
|
|
353
429
|
nil
|
|
354
430
|
end
|
|
355
431
|
|
|
@@ -357,14 +433,34 @@ module Google
|
|
|
357
433
|
# @private Lookup Credentials using Google::Auth.get_application_default.
|
|
358
434
|
def self.from_application_default options
|
|
359
435
|
scope = options[:scope] || self.scope
|
|
360
|
-
auth_opts = {
|
|
436
|
+
auth_opts = {
|
|
437
|
+
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
|
|
438
|
+
audience: options[:audience] || audience,
|
|
439
|
+
target_audience: options[:target_audience] || target_audience,
|
|
440
|
+
enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?
|
|
441
|
+
}
|
|
361
442
|
client = Google::Auth.get_application_default scope, auth_opts
|
|
362
443
|
new client, options
|
|
363
444
|
end
|
|
364
445
|
|
|
446
|
+
# @private Read credentials from a JSON stream.
|
|
447
|
+
def self.from_io io, options
|
|
448
|
+
creds_input = {
|
|
449
|
+
json_key_io: io,
|
|
450
|
+
scope: options[:scope] || scope,
|
|
451
|
+
target_audience: options[:target_audience] || target_audience,
|
|
452
|
+
enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?,
|
|
453
|
+
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
|
|
454
|
+
audience: options[:audience] || audience
|
|
455
|
+
}
|
|
456
|
+
client = Google::Auth::DefaultCredentials.make_creds creds_input
|
|
457
|
+
new client
|
|
458
|
+
end
|
|
459
|
+
|
|
365
460
|
private_class_method :from_env_vars,
|
|
366
461
|
:from_default_paths,
|
|
367
|
-
:from_application_default
|
|
462
|
+
:from_application_default,
|
|
463
|
+
:from_io
|
|
368
464
|
|
|
369
465
|
protected
|
|
370
466
|
|
|
@@ -53,12 +53,18 @@ module Google
|
|
|
53
53
|
attr_reader :project_id
|
|
54
54
|
attr_reader :quota_project_id
|
|
55
55
|
|
|
56
|
+
def enable_self_signed_jwt?
|
|
57
|
+
@enable_self_signed_jwt
|
|
58
|
+
end
|
|
59
|
+
|
|
56
60
|
# Creates a ServiceAccountCredentials.
|
|
57
61
|
#
|
|
58
62
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
|
59
63
|
# @param scope [string|array|nil] the scope(s) to access
|
|
60
64
|
def self.make_creds options = {}
|
|
61
|
-
json_key_io, scope, target_audience
|
|
65
|
+
json_key_io, scope, enable_self_signed_jwt, target_audience, audience, token_credential_uri =
|
|
66
|
+
options.values_at :json_key_io, :scope, :enable_self_signed_jwt, :target_audience,
|
|
67
|
+
:audience, :token_credential_uri
|
|
62
68
|
raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
|
|
63
69
|
|
|
64
70
|
if json_key_io
|
|
@@ -71,14 +77,15 @@ module Google
|
|
|
71
77
|
end
|
|
72
78
|
project_id ||= CredentialsLoader.load_gcloud_project_id
|
|
73
79
|
|
|
74
|
-
new(token_credential_uri: TOKEN_CRED_URI,
|
|
75
|
-
audience:
|
|
76
|
-
scope:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
new(token_credential_uri: token_credential_uri || TOKEN_CRED_URI,
|
|
81
|
+
audience: audience || TOKEN_CRED_URI,
|
|
82
|
+
scope: scope,
|
|
83
|
+
enable_self_signed_jwt: enable_self_signed_jwt,
|
|
84
|
+
target_audience: target_audience,
|
|
85
|
+
issuer: client_email,
|
|
86
|
+
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
|
87
|
+
project_id: project_id,
|
|
88
|
+
quota_project_id: quota_project_id)
|
|
82
89
|
.configure_connection(options)
|
|
83
90
|
end
|
|
84
91
|
|
|
@@ -94,30 +101,33 @@ module Google
|
|
|
94
101
|
def initialize options = {}
|
|
95
102
|
@project_id = options[:project_id]
|
|
96
103
|
@quota_project_id = options[:quota_project_id]
|
|
104
|
+
@enable_self_signed_jwt = options[:enable_self_signed_jwt] ? true : false
|
|
97
105
|
super options
|
|
98
106
|
end
|
|
99
107
|
|
|
100
|
-
# Extends the base class
|
|
101
|
-
#
|
|
102
|
-
# If scope(s) is not set, it creates a transient
|
|
103
|
-
# ServiceAccountJwtHeaderCredentials instance and uses that to
|
|
104
|
-
# authenticate instead.
|
|
108
|
+
# Extends the base class to use a transient
|
|
109
|
+
# ServiceAccountJwtHeaderCredentials for certain cases.
|
|
105
110
|
def apply! a_hash, opts = {}
|
|
106
|
-
# Use
|
|
107
|
-
|
|
111
|
+
# Use a self-singed JWT if there's no information that can be used to
|
|
112
|
+
# obtain an OAuth token, OR if there are scopes but also an assertion
|
|
113
|
+
# that they are default scopes that shouldn't be used to fetch a token.
|
|
114
|
+
if target_audience.nil? && (scope.nil? || enable_self_signed_jwt?)
|
|
115
|
+
apply_self_signed_jwt! a_hash
|
|
116
|
+
else
|
|
108
117
|
super
|
|
109
|
-
return
|
|
110
118
|
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
111
122
|
|
|
123
|
+
def apply_self_signed_jwt! a_hash
|
|
112
124
|
# Use the ServiceAccountJwtHeaderCredentials using the same cred values
|
|
113
|
-
# if no scopes are set.
|
|
114
125
|
cred_json = {
|
|
115
126
|
private_key: @signing_key.to_s,
|
|
116
127
|
client_email: @issuer
|
|
117
128
|
}
|
|
118
|
-
alt_clz = ServiceAccountJwtHeaderCredentials
|
|
119
129
|
key_io = StringIO.new MultiJson.dump(cred_json)
|
|
120
|
-
alt =
|
|
130
|
+
alt = ServiceAccountJwtHeaderCredentials.make_creds json_key_io: key_io
|
|
121
131
|
alt.apply! a_hash
|
|
122
132
|
end
|
|
123
133
|
end
|
data/lib/googleauth/version.rb
CHANGED
|
@@ -58,12 +58,9 @@ module Google
|
|
|
58
58
|
# end
|
|
59
59
|
#
|
|
60
60
|
# Instead of implementing the callback directly, applications are
|
|
61
|
-
# encouraged to use {Google::Auth::
|
|
61
|
+
# encouraged to use {Google::Auth::WebUserAuthorizer::CallbackApp} instead.
|
|
62
62
|
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# @see {Google::Auth::AuthCallbackApp}
|
|
66
|
-
# @see {Google::Auth::ControllerHelpers}
|
|
63
|
+
# @see CallbackApp
|
|
67
64
|
# @note Requires sessions are enabled
|
|
68
65
|
class WebUserAuthorizer < Google::Auth::UserAuthorizer
|
|
69
66
|
STATE_PARAM = "state".freeze
|
|
@@ -261,7 +258,7 @@ module Google
|
|
|
261
258
|
# Google::Auth::WebUserAuthorizer::CallbackApp.call(env)
|
|
262
259
|
# end
|
|
263
260
|
#
|
|
264
|
-
# @see
|
|
261
|
+
# @see Google::Auth::WebUserAuthorizer
|
|
265
262
|
class CallbackApp
|
|
266
263
|
LOCATION_HEADER = "Location".freeze
|
|
267
264
|
REDIR_STATUS = 302
|
|
@@ -90,6 +90,24 @@ describe Google::Auth::GCECredentials do
|
|
|
90
90
|
expect(stub).to have_been_requested
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
it "should fail if the metadata request returns a 403" do
|
|
94
|
+
stub = stub_request(:get, MD_ACCESS_URI)
|
|
95
|
+
.to_return(status: 403,
|
|
96
|
+
headers: { "Metadata-Flavor" => "Google" })
|
|
97
|
+
expect { @client.fetch_access_token! }
|
|
98
|
+
.to raise_error Signet::AuthorizationError
|
|
99
|
+
expect(stub).to have_been_requested.times(6)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should fail if the metadata request returns a 500" do
|
|
103
|
+
stub = stub_request(:get, MD_ACCESS_URI)
|
|
104
|
+
.to_return(status: 500,
|
|
105
|
+
headers: { "Metadata-Flavor" => "Google" })
|
|
106
|
+
expect { @client.fetch_access_token! }
|
|
107
|
+
.to raise_error Signet::AuthorizationError
|
|
108
|
+
expect(stub).to have_been_requested.times(6)
|
|
109
|
+
end
|
|
110
|
+
|
|
93
111
|
it "should fail if the metadata request returns an unexpected code" do
|
|
94
112
|
stub = stub_request(:get, MD_ACCESS_URI)
|
|
95
113
|
.to_return(status: 503,
|
|
@@ -46,37 +46,37 @@ describe Google::Auth::Credentials, :private do
|
|
|
46
46
|
}
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
def mock_signet
|
|
50
50
|
mocked_signet = double "Signet::OAuth2::Client"
|
|
51
51
|
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
|
52
52
|
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
53
53
|
allow(mocked_signet).to receive(:client_id)
|
|
54
54
|
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
55
|
+
yield options if block_given?
|
|
56
|
+
mocked_signet
|
|
57
|
+
end
|
|
58
|
+
mocked_signet
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "uses a default scope" do
|
|
62
|
+
mock_signet do |options|
|
|
55
63
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
56
64
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
57
65
|
expect(options[:scope]).to eq([])
|
|
58
66
|
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
|
|
59
67
|
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
60
|
-
|
|
61
|
-
mocked_signet
|
|
62
68
|
end
|
|
63
69
|
|
|
64
70
|
Google::Auth::Credentials.new default_keyfile_hash
|
|
65
71
|
end
|
|
66
72
|
|
|
67
73
|
it "uses a custom scope" do
|
|
68
|
-
|
|
69
|
-
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
|
70
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
71
|
-
allow(mocked_signet).to receive(:client_id)
|
|
72
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
74
|
+
mock_signet do |options|
|
|
73
75
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
74
76
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
75
77
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
76
78
|
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
|
|
77
79
|
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
78
|
-
|
|
79
|
-
mocked_signet
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
Google::Auth::Credentials.new default_keyfile_hash, scope: "http://example.com/scope"
|
|
@@ -101,21 +101,22 @@ describe Google::Auth::Credentials, :private do
|
|
|
101
101
|
allow(::File).to receive(:file?).with(test_path_env_val) { false }
|
|
102
102
|
allow(::File).to receive(:file?).with(test_json_env_val) { false }
|
|
103
103
|
|
|
104
|
-
mocked_signet =
|
|
105
|
-
|
|
106
|
-
allow(
|
|
107
|
-
allow(mocked_signet).to receive(:client_id)
|
|
108
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
104
|
+
mocked_signet = mock_signet
|
|
105
|
+
|
|
106
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
109
107
|
expect(options[:token_credential_uri]).to eq("https://example.com/token")
|
|
110
108
|
expect(options[:audience]).to eq("https://example.com/audience")
|
|
111
109
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
112
|
-
expect(options[:
|
|
113
|
-
expect(options[:
|
|
110
|
+
expect(options[:enable_self_signed_jwt]).to eq(true)
|
|
111
|
+
expect(options[:target_audience]).to be_nil
|
|
112
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
|
114
113
|
|
|
115
|
-
|
|
114
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
115
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
116
|
+
default_keyfile_hash
|
|
116
117
|
end
|
|
117
118
|
|
|
118
|
-
creds = TestCredentials1.default
|
|
119
|
+
creds = TestCredentials1.default enable_self_signed_jwt: true
|
|
119
120
|
expect(creds).to be_a_kind_of(TestCredentials1)
|
|
120
121
|
expect(creds.client).to eq(mocked_signet)
|
|
121
122
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
|
@@ -130,25 +131,28 @@ describe Google::Auth::Credentials, :private do
|
|
|
130
131
|
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
|
|
131
132
|
end
|
|
132
133
|
|
|
134
|
+
json_content = JSON.generate default_keyfile_hash
|
|
135
|
+
|
|
133
136
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
|
134
137
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
|
135
138
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
|
136
139
|
allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
|
|
137
140
|
allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
|
|
138
|
-
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") {
|
|
141
|
+
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { json_content }
|
|
139
142
|
|
|
140
|
-
mocked_signet =
|
|
141
|
-
|
|
142
|
-
allow(
|
|
143
|
-
allow(mocked_signet).to receive(:client_id)
|
|
144
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
143
|
+
mocked_signet = mock_signet
|
|
144
|
+
|
|
145
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
145
146
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
146
147
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
147
148
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
148
|
-
expect(options[:
|
|
149
|
-
expect(options[:
|
|
149
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
150
|
+
expect(options[:target_audience]).to be_nil
|
|
151
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
154
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
155
|
+
default_keyfile_hash
|
|
152
156
|
end
|
|
153
157
|
|
|
154
158
|
creds = TestCredentials2.default
|
|
@@ -175,18 +179,19 @@ describe Google::Auth::Credentials, :private do
|
|
|
175
179
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
176
180
|
allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
|
|
177
181
|
|
|
178
|
-
mocked_signet =
|
|
179
|
-
|
|
180
|
-
allow(
|
|
181
|
-
allow(mocked_signet).to receive(:client_id)
|
|
182
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
182
|
+
mocked_signet = mock_signet
|
|
183
|
+
|
|
184
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
183
185
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
184
186
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
185
187
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
186
|
-
expect(options[:
|
|
187
|
-
expect(options[:
|
|
188
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
189
|
+
expect(options[:target_audience]).to be_nil
|
|
190
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
|
188
191
|
|
|
189
|
-
|
|
192
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
193
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
194
|
+
default_keyfile_hash
|
|
190
195
|
end
|
|
191
196
|
|
|
192
197
|
creds = TestCredentials3.default
|
|
@@ -204,25 +209,28 @@ describe Google::Auth::Credentials, :private do
|
|
|
204
209
|
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
|
|
205
210
|
end
|
|
206
211
|
|
|
212
|
+
json_content = JSON.generate default_keyfile_hash
|
|
213
|
+
|
|
207
214
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
|
208
215
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
|
209
216
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
|
210
217
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
211
218
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
|
|
212
|
-
allow(::File).to receive(:read).with("~/default/path/to/file.txt") {
|
|
219
|
+
allow(::File).to receive(:read).with("~/default/path/to/file.txt") { json_content }
|
|
220
|
+
|
|
221
|
+
mocked_signet = mock_signet
|
|
213
222
|
|
|
214
|
-
|
|
215
|
-
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
|
216
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
217
|
-
allow(mocked_signet).to receive(:client_id)
|
|
218
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
223
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
219
224
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
220
225
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
221
226
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
222
|
-
expect(options[:
|
|
223
|
-
expect(options[:
|
|
227
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
228
|
+
expect(options[:target_audience]).to be_nil
|
|
229
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
|
224
230
|
|
|
225
|
-
|
|
231
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
232
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
233
|
+
default_keyfile_hash
|
|
226
234
|
end
|
|
227
235
|
|
|
228
236
|
creds = TestCredentials4.default
|
|
@@ -246,26 +254,18 @@ describe Google::Auth::Credentials, :private do
|
|
|
246
254
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
247
255
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
|
248
256
|
|
|
249
|
-
mocked_signet =
|
|
250
|
-
|
|
251
|
-
allow(
|
|
252
|
-
allow(mocked_signet).to receive(:client_id)
|
|
253
|
-
allow(Google::Auth).to receive(:get_application_default) do |scope|
|
|
257
|
+
mocked_signet = mock_signet
|
|
258
|
+
|
|
259
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
|
254
260
|
expect(scope).to eq([TestCredentials5::SCOPE])
|
|
261
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
262
|
+
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
263
|
+
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
255
264
|
|
|
256
265
|
# This should really be a Signet::OAuth2::Client object,
|
|
257
266
|
# but mocking is making that difficult, so return a valid hash instead.
|
|
258
267
|
default_keyfile_hash
|
|
259
268
|
end
|
|
260
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
261
|
-
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
262
|
-
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
263
|
-
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
264
|
-
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
|
|
265
|
-
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
266
|
-
|
|
267
|
-
mocked_signet
|
|
268
|
-
end
|
|
269
269
|
|
|
270
270
|
creds = TestCredentials5.default
|
|
271
271
|
expect(creds).to be_a_kind_of(TestCredentials5)
|
|
@@ -273,6 +273,31 @@ describe Google::Auth::Credentials, :private do
|
|
|
273
273
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
|
274
274
|
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
|
275
275
|
end
|
|
276
|
+
|
|
277
|
+
it "can be subclassed to pass in other env paths" do
|
|
278
|
+
class TestCredentials6 < Google::Auth::Credentials
|
|
279
|
+
TOKEN_CREDENTIAL_URI = "https://example.com/token".freeze
|
|
280
|
+
AUDIENCE = "https://example.com/audience".freeze
|
|
281
|
+
SCOPE = "http://example.com/scope".freeze
|
|
282
|
+
PATH_ENV_VARS = ["TEST_PATH"].freeze
|
|
283
|
+
JSON_ENV_VARS = ["TEST_JSON_VARS"].freeze
|
|
284
|
+
DEFAULT_PATHS = ["~/default/path/to/file.txt"]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
class TestCredentials7 < TestCredentials6
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
expect(TestCredentials7.token_credential_uri).to eq("https://example.com/token")
|
|
291
|
+
expect(TestCredentials7.audience).to eq("https://example.com/audience")
|
|
292
|
+
expect(TestCredentials7.scope).to eq(["http://example.com/scope"])
|
|
293
|
+
expect(TestCredentials7.env_vars).to eq(["TEST_PATH", "TEST_JSON_VARS"])
|
|
294
|
+
expect(TestCredentials7.paths).to eq(["~/default/path/to/file.txt"])
|
|
295
|
+
|
|
296
|
+
TestCredentials7::TOKEN_CREDENTIAL_URI = "https://example.com/token2"
|
|
297
|
+
expect(TestCredentials7.token_credential_uri).to eq("https://example.com/token2")
|
|
298
|
+
TestCredentials7::AUDIENCE = nil
|
|
299
|
+
expect(TestCredentials7.audience).to eq("https://example.com/audience")
|
|
300
|
+
end
|
|
276
301
|
end
|
|
277
302
|
|
|
278
303
|
describe "using class methods" do
|
|
@@ -293,18 +318,19 @@ describe Google::Auth::Credentials, :private do
|
|
|
293
318
|
allow(::File).to receive(:file?).with(test_path_env_val) { false }
|
|
294
319
|
allow(::File).to receive(:file?).with(test_json_env_val) { false }
|
|
295
320
|
|
|
296
|
-
mocked_signet =
|
|
297
|
-
|
|
298
|
-
allow(
|
|
299
|
-
allow(mocked_signet).to receive(:client_id)
|
|
300
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
321
|
+
mocked_signet = mock_signet
|
|
322
|
+
|
|
323
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
301
324
|
expect(options[:token_credential_uri]).to eq("https://example.com/token")
|
|
302
325
|
expect(options[:audience]).to eq("https://example.com/audience")
|
|
303
326
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
304
|
-
expect(options[:
|
|
305
|
-
expect(options[:
|
|
327
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
328
|
+
expect(options[:target_audience]).to be_nil
|
|
329
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
|
306
330
|
|
|
307
|
-
|
|
331
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
332
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
333
|
+
default_keyfile_hash
|
|
308
334
|
end
|
|
309
335
|
|
|
310
336
|
creds = TestCredentials11.default
|
|
@@ -321,25 +347,28 @@ describe Google::Auth::Credentials, :private do
|
|
|
321
347
|
self.paths = ["~/default/path/to/file.txt"]
|
|
322
348
|
end
|
|
323
349
|
|
|
350
|
+
json_content = JSON.generate default_keyfile_hash
|
|
351
|
+
|
|
324
352
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
|
325
353
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
|
326
354
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
|
327
355
|
allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
|
|
328
356
|
allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
|
|
329
|
-
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") {
|
|
357
|
+
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { json_content }
|
|
330
358
|
|
|
331
|
-
mocked_signet =
|
|
332
|
-
|
|
333
|
-
allow(
|
|
334
|
-
allow(mocked_signet).to receive(:client_id)
|
|
335
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
359
|
+
mocked_signet = mock_signet
|
|
360
|
+
|
|
361
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
336
362
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
337
363
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
338
364
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
339
|
-
expect(options[:
|
|
340
|
-
expect(options[:
|
|
365
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
366
|
+
expect(options[:target_audience]).to be_nil
|
|
367
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
|
341
368
|
|
|
342
|
-
|
|
369
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
370
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
371
|
+
default_keyfile_hash
|
|
343
372
|
end
|
|
344
373
|
|
|
345
374
|
creds = TestCredentials12.default
|
|
@@ -365,18 +394,19 @@ describe Google::Auth::Credentials, :private do
|
|
|
365
394
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
366
395
|
allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
|
|
367
396
|
|
|
368
|
-
mocked_signet =
|
|
369
|
-
|
|
370
|
-
allow(
|
|
371
|
-
allow(mocked_signet).to receive(:client_id)
|
|
372
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
397
|
+
mocked_signet = mock_signet
|
|
398
|
+
|
|
399
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
373
400
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
374
401
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
375
402
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
376
|
-
expect(options[:
|
|
377
|
-
expect(options[:
|
|
403
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
404
|
+
expect(options[:target_audience]).to be_nil
|
|
405
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
|
378
406
|
|
|
379
|
-
|
|
407
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
408
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
409
|
+
default_keyfile_hash
|
|
380
410
|
end
|
|
381
411
|
|
|
382
412
|
creds = TestCredentials13.default
|
|
@@ -393,25 +423,28 @@ describe Google::Auth::Credentials, :private do
|
|
|
393
423
|
self.paths = ["~/default/path/to/file.txt"]
|
|
394
424
|
end
|
|
395
425
|
|
|
426
|
+
json_content = JSON.generate default_keyfile_hash
|
|
427
|
+
|
|
396
428
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
|
397
429
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
|
398
430
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
|
399
431
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
400
432
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
|
|
401
|
-
allow(::File).to receive(:read).with("~/default/path/to/file.txt") {
|
|
433
|
+
allow(::File).to receive(:read).with("~/default/path/to/file.txt") { json_content }
|
|
434
|
+
|
|
435
|
+
mocked_signet = mock_signet
|
|
402
436
|
|
|
403
|
-
|
|
404
|
-
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
|
405
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
406
|
-
allow(mocked_signet).to receive(:client_id)
|
|
407
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
437
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
|
408
438
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
409
439
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
410
440
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
411
|
-
expect(options[:
|
|
412
|
-
expect(options[:
|
|
441
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
442
|
+
expect(options[:target_audience]).to be_nil
|
|
443
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
|
413
444
|
|
|
414
|
-
|
|
445
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
446
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
447
|
+
default_keyfile_hash
|
|
415
448
|
end
|
|
416
449
|
|
|
417
450
|
creds = TestCredentials14.default
|
|
@@ -421,7 +454,7 @@ describe Google::Auth::Credentials, :private do
|
|
|
421
454
|
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
|
422
455
|
end
|
|
423
456
|
|
|
424
|
-
it "subclasses that find no matches default to Google::Auth.get_application_default" do
|
|
457
|
+
it "subclasses that find no matches default to Google::Auth.get_application_default with self-signed jwt enabled" do
|
|
425
458
|
class TestCredentials15 < Google::Auth::Credentials
|
|
426
459
|
self.scope = "http://example.com/scope"
|
|
427
460
|
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
|
|
@@ -434,33 +467,117 @@ describe Google::Auth::Credentials, :private do
|
|
|
434
467
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
435
468
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
|
436
469
|
|
|
437
|
-
mocked_signet =
|
|
438
|
-
|
|
439
|
-
allow(
|
|
440
|
-
allow(mocked_signet).to receive(:client_id)
|
|
441
|
-
allow(Google::Auth).to receive(:get_application_default) do |scope|
|
|
470
|
+
mocked_signet = mock_signet
|
|
471
|
+
|
|
472
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
|
442
473
|
expect(scope).to eq(TestCredentials15.scope)
|
|
474
|
+
expect(options[:enable_self_signed_jwt]).to eq(true)
|
|
475
|
+
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
476
|
+
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
443
477
|
|
|
444
478
|
# This should really be a Signet::OAuth2::Client object,
|
|
445
479
|
# but mocking is making that difficult, so return a valid hash instead.
|
|
446
480
|
default_keyfile_hash
|
|
447
481
|
end
|
|
448
|
-
|
|
482
|
+
|
|
483
|
+
creds = TestCredentials15.default enable_self_signed_jwt: true
|
|
484
|
+
expect(creds).to be_a_kind_of(TestCredentials15)
|
|
485
|
+
expect(creds.client).to eq(mocked_signet)
|
|
486
|
+
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
|
487
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
it "subclasses that find no matches default to Google::Auth.get_application_default with self-signed jwt disabled" do
|
|
491
|
+
class TestCredentials16 < Google::Auth::Credentials
|
|
492
|
+
self.scope = "http://example.com/scope"
|
|
493
|
+
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
|
|
494
|
+
self.paths = ["~/default/path/to/file.txt"]
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
|
498
|
+
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
|
499
|
+
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
|
500
|
+
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
501
|
+
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
|
502
|
+
|
|
503
|
+
mocked_signet = mock_signet
|
|
504
|
+
|
|
505
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
|
506
|
+
expect(scope).to eq(TestCredentials16.scope)
|
|
507
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
|
449
508
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
|
450
509
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
|
451
|
-
expect(options[:scope]).to eq(["http://example.com/scope"])
|
|
452
|
-
expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
|
|
453
|
-
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
454
510
|
|
|
455
|
-
|
|
511
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
512
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
513
|
+
default_keyfile_hash
|
|
456
514
|
end
|
|
457
515
|
|
|
458
|
-
creds =
|
|
459
|
-
expect(creds).to be_a_kind_of(
|
|
516
|
+
creds = TestCredentials16.default
|
|
517
|
+
expect(creds).to be_a_kind_of(TestCredentials16)
|
|
460
518
|
expect(creds.client).to eq(mocked_signet)
|
|
461
519
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
|
462
520
|
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
|
463
521
|
end
|
|
522
|
+
|
|
523
|
+
it "subclasses that find no matches default to Google::Auth.get_application_default with custom values" do
|
|
524
|
+
scope2 = "http://example.com/scope2"
|
|
525
|
+
|
|
526
|
+
class TestCredentials17 < Google::Auth::Credentials
|
|
527
|
+
self.scope = "http://example.com/scope"
|
|
528
|
+
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
|
|
529
|
+
self.paths = ["~/default/path/to/file.txt"]
|
|
530
|
+
self.token_credential_uri = "https://example.com/token2"
|
|
531
|
+
self.audience = "https://example.com/token3"
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
|
535
|
+
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
|
536
|
+
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
|
537
|
+
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
|
538
|
+
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
|
539
|
+
|
|
540
|
+
mocked_signet = mock_signet
|
|
541
|
+
|
|
542
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
|
543
|
+
expect(scope).to eq(scope2)
|
|
544
|
+
expect(options[:enable_self_signed_jwt]).to eq(false)
|
|
545
|
+
expect(options[:token_credential_uri]).to eq("https://example.com/token2")
|
|
546
|
+
expect(options[:audience]).to eq("https://example.com/token3")
|
|
547
|
+
|
|
548
|
+
# This should really be a Signet::OAuth2::Client object,
|
|
549
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
|
550
|
+
default_keyfile_hash
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
creds = TestCredentials17.default scope: scope2, enable_self_signed_jwt: true
|
|
554
|
+
expect(creds).to be_a_kind_of(TestCredentials17)
|
|
555
|
+
expect(creds.client).to eq(mocked_signet)
|
|
556
|
+
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
|
557
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
it "subclasses delegate up the class hierarchy" do
|
|
561
|
+
class TestCredentials18 < Google::Auth::Credentials
|
|
562
|
+
self.scope = "http://example.com/scope"
|
|
563
|
+
self.target_audience = "https://example.com/target_audience"
|
|
564
|
+
self.env_vars = ["TEST_PATH", "TEST_JSON_VARS"]
|
|
565
|
+
self.paths = ["~/default/path/to/file.txt"]
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
class TestCredentials19 < TestCredentials18
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
expect(TestCredentials19.scope).to eq(["http://example.com/scope"])
|
|
572
|
+
expect(TestCredentials19.target_audience).to eq("https://example.com/target_audience")
|
|
573
|
+
expect(TestCredentials19.env_vars).to eq(["TEST_PATH", "TEST_JSON_VARS"])
|
|
574
|
+
expect(TestCredentials19.paths).to eq(["~/default/path/to/file.txt"])
|
|
575
|
+
|
|
576
|
+
TestCredentials19.token_credential_uri = "https://example.com/token2"
|
|
577
|
+
expect(TestCredentials19.token_credential_uri).to eq("https://example.com/token2")
|
|
578
|
+
TestCredentials19.token_credential_uri = nil
|
|
579
|
+
expect(TestCredentials19.token_credential_uri).to eq("https://oauth2.googleapis.com/token")
|
|
580
|
+
end
|
|
464
581
|
end
|
|
465
582
|
|
|
466
583
|
it "warns when cloud sdk credentials are used" do
|
|
@@ -169,6 +169,14 @@ describe Google::Auth::ServiceAccountCredentials do
|
|
|
169
169
|
it_behaves_like "jwt header auth"
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
+
context "when enable_self_signed_jwt is set" do
|
|
173
|
+
before :example do
|
|
174
|
+
@client.instance_variable_set(:@enable_self_signed_jwt, true)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it_behaves_like "jwt header auth"
|
|
178
|
+
end
|
|
179
|
+
|
|
172
180
|
describe "#from_env" do
|
|
173
181
|
before :example do
|
|
174
182
|
@var_name = ENV_VAR
|
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: 0.
|
|
4
|
+
version: 0.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tim Emiola
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-01-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -140,6 +140,7 @@ files:
|
|
|
140
140
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
|
141
141
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
|
142
142
|
- ".github/ISSUE_TEMPLATE/support_request.md"
|
|
143
|
+
- ".github/workflows/release.yml"
|
|
143
144
|
- ".gitignore"
|
|
144
145
|
- ".kokoro/build.bat"
|
|
145
146
|
- ".kokoro/build.sh"
|
|
@@ -232,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
232
233
|
- !ruby/object:Gem::Version
|
|
233
234
|
version: '0'
|
|
234
235
|
requirements: []
|
|
235
|
-
rubygems_version: 3.
|
|
236
|
+
rubygems_version: 3.2.6
|
|
236
237
|
signing_key:
|
|
237
238
|
specification_version: 4
|
|
238
239
|
summary: Google Auth Library for Ruby
|