googleauth 0.14.0 → 0.16.2
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/renovate.json +6 -0
- data/.github/sync-repo-settings.yaml +18 -0
- data/.github/workflows/ci.yml +55 -0
- data/.github/workflows/release-please.yml +39 -0
- data/.gitignore +3 -0
- data/.kokoro/populate-secrets.sh +76 -0
- data/.kokoro/release.cfg +7 -49
- data/.kokoro/release.sh +18 -0
- data/.kokoro/trampoline_v2.sh +489 -0
- data/.rubocop.yml +0 -2
- data/.toys/.toys.rb +45 -0
- data/.toys/ci.rb +43 -0
- data/.toys/kokoro/.toys.rb +66 -0
- data/.toys/kokoro/publish-docs.rb +67 -0
- data/.toys/kokoro/publish-gem.rb +53 -0
- data/.toys/linkinator.rb +43 -0
- data/.trampolinerc +48 -0
- data/CHANGELOG.md +69 -27
- data/Gemfile +2 -7
- data/README.md +9 -7
- data/googleauth.gemspec +2 -1
- data/lib/googleauth/compute_engine.rb +6 -5
- data/lib/googleauth/credentials.rb +167 -48
- data/lib/googleauth/credentials_loader.rb +1 -1
- data/lib/googleauth/iam.rb +1 -1
- data/lib/googleauth/id_tokens/key_sources.rb +7 -5
- data/lib/googleauth/id_tokens/verifier.rb +7 -9
- data/lib/googleauth/scope_util.rb +1 -1
- data/lib/googleauth/service_account.rb +35 -23
- data/lib/googleauth/signet.rb +1 -1
- data/lib/googleauth/stores/file_token_store.rb +1 -0
- data/lib/googleauth/stores/redis_token_store.rb +1 -0
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +4 -7
- data/spec/googleauth/compute_engine_spec.rb +18 -0
- data/spec/googleauth/credentials_spec.rb +228 -106
- data/spec/googleauth/service_account_spec.rb +8 -0
- metadata +18 -22
- data/.kokoro/build.bat +0 -16
- data/.kokoro/build.sh +0 -4
- data/.kokoro/continuous/common.cfg +0 -24
- data/.kokoro/continuous/linux.cfg +0 -25
- data/.kokoro/continuous/osx.cfg +0 -8
- data/.kokoro/continuous/post.cfg +0 -30
- data/.kokoro/continuous/windows.cfg +0 -29
- data/.kokoro/osx.sh +0 -4
- data/.kokoro/presubmit/common.cfg +0 -24
- data/.kokoro/presubmit/linux.cfg +0 -24
- data/.kokoro/presubmit/osx.cfg +0 -8
- data/.kokoro/presubmit/windows.cfg +0 -29
- data/.kokoro/trampoline.bat +0 -10
- data/.kokoro/trampoline.sh +0 -4
- data/Rakefile +0 -132
- data/rakelib/devsite_builder.rb +0 -45
- data/rakelib/link_checker.rb +0 -64
- data/rakelib/repo_metadata.rb +0 -59
@@ -103,7 +103,7 @@ module Google
|
|
103
103
|
return make_creds options.merge(json_key_io: f)
|
104
104
|
end
|
105
105
|
elsif service_account_env_vars? || authorized_user_env_vars?
|
106
|
-
|
106
|
+
make_creds options
|
107
107
|
end
|
108
108
|
rescue StandardError => e
|
109
109
|
raise "#{NOT_FOUND_ERROR}: #{e}"
|
data/lib/googleauth/iam.rb
CHANGED
@@ -171,7 +171,9 @@ module Google
|
|
171
171
|
curve_name = CURVE_NAME_MAP[jwk[:crv]]
|
172
172
|
raise KeySourceError, "Unsupported EC curve #{jwk[:crv]}" unless curve_name
|
173
173
|
group = OpenSSL::PKey::EC::Group.new curve_name
|
174
|
-
|
174
|
+
x_hex = x_data.unpack1 "H*"
|
175
|
+
y_hex = y_data.unpack1 "H*"
|
176
|
+
bn = OpenSSL::BN.new ["04#{x_hex}#{y_hex}"].pack("H*"), 2
|
175
177
|
key = OpenSSL::PKey::EC.new curve_name
|
176
178
|
key.public_key = OpenSSL::PKey::EC::Point.new group, bn
|
177
179
|
key
|
@@ -284,10 +286,10 @@ module Google
|
|
284
286
|
raise KeySourceError, "Unable to retrieve data from #{uri}" unless response.is_a? Net::HTTPSuccess
|
285
287
|
|
286
288
|
data = begin
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
289
|
+
JSON.parse response.body
|
290
|
+
rescue JSON::ParserError
|
291
|
+
raise KeySourceError, "Unable to parse JSON"
|
292
|
+
end
|
291
293
|
|
292
294
|
@current_keys = Array(interpret_json(data))
|
293
295
|
end
|
@@ -105,15 +105,13 @@ module Google
|
|
105
105
|
def decode_token token, keys, aud, azp, iss
|
106
106
|
payload = nil
|
107
107
|
keys.find do |key|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
nil # Try the next key
|
116
|
-
end
|
108
|
+
options = { algorithms: key.algorithm }
|
109
|
+
decoded_token = JWT.decode token, key.key, true, options
|
110
|
+
payload = decoded_token.first
|
111
|
+
rescue JWT::ExpiredSignature
|
112
|
+
raise ExpiredTokenError, "Token signature is expired"
|
113
|
+
rescue JWT::DecodeError
|
114
|
+
nil # Try the next key
|
117
115
|
end
|
118
116
|
|
119
117
|
normalize_and_verify_payload payload, aud, azp, iss
|
@@ -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,35 @@ 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
|
-
private_key:
|
116
|
-
client_email: @issuer
|
126
|
+
private_key: @signing_key.to_s,
|
127
|
+
client_email: @issuer,
|
128
|
+
project_id: @project_id,
|
129
|
+
quota_project_id: @quota_project_id
|
117
130
|
}
|
118
|
-
alt_clz = ServiceAccountJwtHeaderCredentials
|
119
131
|
key_io = StringIO.new MultiJson.dump(cred_json)
|
120
|
-
alt =
|
132
|
+
alt = ServiceAccountJwtHeaderCredentials.make_creds json_key_io: key_io
|
121
133
|
alt.apply! a_hash
|
122
134
|
end
|
123
135
|
end
|
@@ -193,7 +205,7 @@ module Google
|
|
193
205
|
# Returns a reference to the #apply method, suitable for passing as
|
194
206
|
# a closure
|
195
207
|
def updater_proc
|
196
|
-
|
208
|
+
proc { |a_hash, opts = {}| apply a_hash, opts }
|
197
209
|
end
|
198
210
|
|
199
211
|
protected
|
data/lib/googleauth/signet.rb
CHANGED
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
|
@@ -192,7 +189,7 @@ module Google
|
|
192
189
|
# May raise an error if an authorization code is present in the session
|
193
190
|
# and exchange of the code fails
|
194
191
|
def get_credentials user_id, request = nil, scope = nil
|
195
|
-
if request
|
192
|
+
if request&.session&.key? CALLBACK_STATE_KEY
|
196
193
|
# Note - in theory, no need to check required scope as this is
|
197
194
|
# expected to be called immediately after a return from authorization
|
198
195
|
state_json = request.session.delete CALLBACK_STATE_KEY
|
@@ -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,42 +46,47 @@ 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"
|
83
83
|
end
|
84
84
|
|
85
|
+
it "uses empty paths and env_vars by default" do
|
86
|
+
expect(Google::Auth::Credentials.paths).to eq([])
|
87
|
+
expect(Google::Auth::Credentials.env_vars).to eq([])
|
88
|
+
end
|
89
|
+
|
85
90
|
describe "using CONSTANTS" do
|
86
91
|
it "can be subclassed to pass in other env paths" do
|
87
92
|
test_path_env_val = "/unknown/path/to/file.txt".freeze
|
@@ -101,21 +106,22 @@ describe Google::Auth::Credentials, :private do
|
|
101
106
|
allow(::File).to receive(:file?).with(test_path_env_val) { false }
|
102
107
|
allow(::File).to receive(:file?).with(test_json_env_val) { false }
|
103
108
|
|
104
|
-
mocked_signet =
|
105
|
-
|
106
|
-
allow(
|
107
|
-
allow(mocked_signet).to receive(:client_id)
|
108
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
109
|
+
mocked_signet = mock_signet
|
110
|
+
|
111
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
109
112
|
expect(options[:token_credential_uri]).to eq("https://example.com/token")
|
110
113
|
expect(options[:audience]).to eq("https://example.com/audience")
|
111
114
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
112
|
-
expect(options[:
|
113
|
-
expect(options[:
|
115
|
+
expect(options[:enable_self_signed_jwt]).to eq(true)
|
116
|
+
expect(options[:target_audience]).to be_nil
|
117
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
114
118
|
|
115
|
-
|
119
|
+
# This should really be a Signet::OAuth2::Client object,
|
120
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
121
|
+
default_keyfile_hash
|
116
122
|
end
|
117
123
|
|
118
|
-
creds = TestCredentials1.default
|
124
|
+
creds = TestCredentials1.default enable_self_signed_jwt: true
|
119
125
|
expect(creds).to be_a_kind_of(TestCredentials1)
|
120
126
|
expect(creds.client).to eq(mocked_signet)
|
121
127
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
@@ -130,25 +136,28 @@ describe Google::Auth::Credentials, :private do
|
|
130
136
|
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
|
131
137
|
end
|
132
138
|
|
139
|
+
json_content = JSON.generate default_keyfile_hash
|
140
|
+
|
133
141
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
134
142
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
135
143
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
136
144
|
allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
|
137
145
|
allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
|
138
|
-
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") {
|
146
|
+
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { json_content }
|
139
147
|
|
140
|
-
mocked_signet =
|
141
|
-
|
142
|
-
allow(
|
143
|
-
allow(mocked_signet).to receive(:client_id)
|
144
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
148
|
+
mocked_signet = mock_signet
|
149
|
+
|
150
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
145
151
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
146
152
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
147
153
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
148
|
-
expect(options[:
|
149
|
-
expect(options[:
|
154
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
155
|
+
expect(options[:target_audience]).to be_nil
|
156
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
150
157
|
|
151
|
-
|
158
|
+
# This should really be a Signet::OAuth2::Client object,
|
159
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
160
|
+
default_keyfile_hash
|
152
161
|
end
|
153
162
|
|
154
163
|
creds = TestCredentials2.default
|
@@ -175,18 +184,19 @@ describe Google::Auth::Credentials, :private do
|
|
175
184
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
176
185
|
allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
|
177
186
|
|
178
|
-
mocked_signet =
|
179
|
-
|
180
|
-
allow(
|
181
|
-
allow(mocked_signet).to receive(:client_id)
|
182
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
187
|
+
mocked_signet = mock_signet
|
188
|
+
|
189
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
183
190
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
184
191
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
185
192
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
186
|
-
expect(options[:
|
187
|
-
expect(options[:
|
193
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
194
|
+
expect(options[:target_audience]).to be_nil
|
195
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
188
196
|
|
189
|
-
|
197
|
+
# This should really be a Signet::OAuth2::Client object,
|
198
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
199
|
+
default_keyfile_hash
|
190
200
|
end
|
191
201
|
|
192
202
|
creds = TestCredentials3.default
|
@@ -204,25 +214,28 @@ describe Google::Auth::Credentials, :private do
|
|
204
214
|
DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
|
205
215
|
end
|
206
216
|
|
217
|
+
json_content = JSON.generate default_keyfile_hash
|
218
|
+
|
207
219
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
208
220
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
209
221
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
210
222
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
211
223
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
|
212
|
-
allow(::File).to receive(:read).with("~/default/path/to/file.txt") {
|
224
|
+
allow(::File).to receive(:read).with("~/default/path/to/file.txt") { json_content }
|
225
|
+
|
226
|
+
mocked_signet = mock_signet
|
213
227
|
|
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|
|
228
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
219
229
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
220
230
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
221
231
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
222
|
-
expect(options[:
|
223
|
-
expect(options[:
|
232
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
233
|
+
expect(options[:target_audience]).to be_nil
|
234
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
224
235
|
|
225
|
-
|
236
|
+
# This should really be a Signet::OAuth2::Client object,
|
237
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
238
|
+
default_keyfile_hash
|
226
239
|
end
|
227
240
|
|
228
241
|
creds = TestCredentials4.default
|
@@ -246,26 +259,18 @@ describe Google::Auth::Credentials, :private do
|
|
246
259
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
247
260
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
248
261
|
|
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|
|
262
|
+
mocked_signet = mock_signet
|
263
|
+
|
264
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
254
265
|
expect(scope).to eq([TestCredentials5::SCOPE])
|
266
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
267
|
+
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
268
|
+
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
255
269
|
|
256
270
|
# This should really be a Signet::OAuth2::Client object,
|
257
271
|
# but mocking is making that difficult, so return a valid hash instead.
|
258
272
|
default_keyfile_hash
|
259
273
|
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
274
|
|
270
275
|
creds = TestCredentials5.default
|
271
276
|
expect(creds).to be_a_kind_of(TestCredentials5)
|
@@ -273,6 +278,31 @@ describe Google::Auth::Credentials, :private do
|
|
273
278
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
274
279
|
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
275
280
|
end
|
281
|
+
|
282
|
+
it "can be subclassed to pass in other env paths" do
|
283
|
+
class TestCredentials6 < Google::Auth::Credentials
|
284
|
+
TOKEN_CREDENTIAL_URI = "https://example.com/token".freeze
|
285
|
+
AUDIENCE = "https://example.com/audience".freeze
|
286
|
+
SCOPE = "http://example.com/scope".freeze
|
287
|
+
PATH_ENV_VARS = ["TEST_PATH"].freeze
|
288
|
+
JSON_ENV_VARS = ["TEST_JSON_VARS"].freeze
|
289
|
+
DEFAULT_PATHS = ["~/default/path/to/file.txt"]
|
290
|
+
end
|
291
|
+
|
292
|
+
class TestCredentials7 < TestCredentials6
|
293
|
+
end
|
294
|
+
|
295
|
+
expect(TestCredentials7.token_credential_uri).to eq("https://example.com/token")
|
296
|
+
expect(TestCredentials7.audience).to eq("https://example.com/audience")
|
297
|
+
expect(TestCredentials7.scope).to eq(["http://example.com/scope"])
|
298
|
+
expect(TestCredentials7.env_vars).to eq(["TEST_PATH", "TEST_JSON_VARS"])
|
299
|
+
expect(TestCredentials7.paths).to eq(["~/default/path/to/file.txt"])
|
300
|
+
|
301
|
+
TestCredentials7::TOKEN_CREDENTIAL_URI = "https://example.com/token2"
|
302
|
+
expect(TestCredentials7.token_credential_uri).to eq("https://example.com/token2")
|
303
|
+
TestCredentials7::AUDIENCE = nil
|
304
|
+
expect(TestCredentials7.audience).to eq("https://example.com/audience")
|
305
|
+
end
|
276
306
|
end
|
277
307
|
|
278
308
|
describe "using class methods" do
|
@@ -293,18 +323,19 @@ describe Google::Auth::Credentials, :private do
|
|
293
323
|
allow(::File).to receive(:file?).with(test_path_env_val) { false }
|
294
324
|
allow(::File).to receive(:file?).with(test_json_env_val) { false }
|
295
325
|
|
296
|
-
mocked_signet =
|
297
|
-
|
298
|
-
allow(
|
299
|
-
allow(mocked_signet).to receive(:client_id)
|
300
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
326
|
+
mocked_signet = mock_signet
|
327
|
+
|
328
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
301
329
|
expect(options[:token_credential_uri]).to eq("https://example.com/token")
|
302
330
|
expect(options[:audience]).to eq("https://example.com/audience")
|
303
331
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
304
|
-
expect(options[:
|
305
|
-
expect(options[:
|
332
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
333
|
+
expect(options[:target_audience]).to be_nil
|
334
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
306
335
|
|
307
|
-
|
336
|
+
# This should really be a Signet::OAuth2::Client object,
|
337
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
338
|
+
default_keyfile_hash
|
308
339
|
end
|
309
340
|
|
310
341
|
creds = TestCredentials11.default
|
@@ -321,25 +352,28 @@ describe Google::Auth::Credentials, :private do
|
|
321
352
|
self.paths = ["~/default/path/to/file.txt"]
|
322
353
|
end
|
323
354
|
|
355
|
+
json_content = JSON.generate default_keyfile_hash
|
356
|
+
|
324
357
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
325
358
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
326
359
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
327
360
|
allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
|
328
361
|
allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
|
329
|
-
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") {
|
362
|
+
allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { json_content }
|
363
|
+
|
364
|
+
mocked_signet = mock_signet
|
330
365
|
|
331
|
-
|
332
|
-
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
|
333
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
334
|
-
allow(mocked_signet).to receive(:client_id)
|
335
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
366
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
336
367
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
337
368
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
338
369
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
339
|
-
expect(options[:
|
340
|
-
expect(options[:
|
370
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
371
|
+
expect(options[:target_audience]).to be_nil
|
372
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
341
373
|
|
342
|
-
|
374
|
+
# This should really be a Signet::OAuth2::Client object,
|
375
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
376
|
+
default_keyfile_hash
|
343
377
|
end
|
344
378
|
|
345
379
|
creds = TestCredentials12.default
|
@@ -365,18 +399,19 @@ describe Google::Auth::Credentials, :private do
|
|
365
399
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
366
400
|
allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
|
367
401
|
|
368
|
-
mocked_signet =
|
369
|
-
|
370
|
-
allow(
|
371
|
-
allow(mocked_signet).to receive(:client_id)
|
372
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
402
|
+
mocked_signet = mock_signet
|
403
|
+
|
404
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
373
405
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
374
406
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
375
407
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
376
|
-
expect(options[:
|
377
|
-
expect(options[:
|
408
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
409
|
+
expect(options[:target_audience]).to be_nil
|
410
|
+
expect(options[:json_key_io].read).to eq(test_json_env_val)
|
378
411
|
|
379
|
-
|
412
|
+
# This should really be a Signet::OAuth2::Client object,
|
413
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
414
|
+
default_keyfile_hash
|
380
415
|
end
|
381
416
|
|
382
417
|
creds = TestCredentials13.default
|
@@ -393,25 +428,28 @@ describe Google::Auth::Credentials, :private do
|
|
393
428
|
self.paths = ["~/default/path/to/file.txt"]
|
394
429
|
end
|
395
430
|
|
431
|
+
json_content = JSON.generate default_keyfile_hash
|
432
|
+
|
396
433
|
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
397
434
|
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
398
435
|
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
399
436
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
400
437
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
|
401
|
-
allow(::File).to receive(:read).with("~/default/path/to/file.txt") {
|
438
|
+
allow(::File).to receive(:read).with("~/default/path/to/file.txt") { json_content }
|
439
|
+
|
440
|
+
mocked_signet = mock_signet
|
402
441
|
|
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|
|
442
|
+
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) do |options|
|
408
443
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
409
444
|
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
410
445
|
expect(options[:scope]).to eq(["http://example.com/scope"])
|
411
|
-
expect(options[:
|
412
|
-
expect(options[:
|
446
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
447
|
+
expect(options[:target_audience]).to be_nil
|
448
|
+
expect(options[:json_key_io].read).to eq(json_content)
|
413
449
|
|
414
|
-
|
450
|
+
# This should really be a Signet::OAuth2::Client object,
|
451
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
452
|
+
default_keyfile_hash
|
415
453
|
end
|
416
454
|
|
417
455
|
creds = TestCredentials14.default
|
@@ -421,7 +459,7 @@ describe Google::Auth::Credentials, :private do
|
|
421
459
|
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
422
460
|
end
|
423
461
|
|
424
|
-
it "subclasses that find no matches default to Google::Auth.get_application_default" do
|
462
|
+
it "subclasses that find no matches default to Google::Auth.get_application_default with self-signed jwt enabled" do
|
425
463
|
class TestCredentials15 < Google::Auth::Credentials
|
426
464
|
self.scope = "http://example.com/scope"
|
427
465
|
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
|
@@ -434,33 +472,117 @@ describe Google::Auth::Credentials, :private do
|
|
434
472
|
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
435
473
|
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
436
474
|
|
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|
|
475
|
+
mocked_signet = mock_signet
|
476
|
+
|
477
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
442
478
|
expect(scope).to eq(TestCredentials15.scope)
|
479
|
+
expect(options[:enable_self_signed_jwt]).to eq(true)
|
480
|
+
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
481
|
+
expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
|
443
482
|
|
444
483
|
# This should really be a Signet::OAuth2::Client object,
|
445
484
|
# but mocking is making that difficult, so return a valid hash instead.
|
446
485
|
default_keyfile_hash
|
447
486
|
end
|
448
|
-
|
487
|
+
|
488
|
+
creds = TestCredentials15.default enable_self_signed_jwt: true
|
489
|
+
expect(creds).to be_a_kind_of(TestCredentials15)
|
490
|
+
expect(creds.client).to eq(mocked_signet)
|
491
|
+
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
492
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
493
|
+
end
|
494
|
+
|
495
|
+
it "subclasses that find no matches default to Google::Auth.get_application_default with self-signed jwt disabled" do
|
496
|
+
class TestCredentials16 < Google::Auth::Credentials
|
497
|
+
self.scope = "http://example.com/scope"
|
498
|
+
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
|
499
|
+
self.paths = ["~/default/path/to/file.txt"]
|
500
|
+
end
|
501
|
+
|
502
|
+
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
503
|
+
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
504
|
+
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
505
|
+
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
506
|
+
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
507
|
+
|
508
|
+
mocked_signet = mock_signet
|
509
|
+
|
510
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
511
|
+
expect(scope).to eq(TestCredentials16.scope)
|
512
|
+
expect(options[:enable_self_signed_jwt]).to be_nil
|
449
513
|
expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
|
450
514
|
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
515
|
|
455
|
-
|
516
|
+
# This should really be a Signet::OAuth2::Client object,
|
517
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
518
|
+
default_keyfile_hash
|
456
519
|
end
|
457
520
|
|
458
|
-
creds =
|
459
|
-
expect(creds).to be_a_kind_of(
|
521
|
+
creds = TestCredentials16.default
|
522
|
+
expect(creds).to be_a_kind_of(TestCredentials16)
|
460
523
|
expect(creds.client).to eq(mocked_signet)
|
461
524
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
462
525
|
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
463
526
|
end
|
527
|
+
|
528
|
+
it "subclasses that find no matches default to Google::Auth.get_application_default with custom values" do
|
529
|
+
scope2 = "http://example.com/scope2"
|
530
|
+
|
531
|
+
class TestCredentials17 < Google::Auth::Credentials
|
532
|
+
self.scope = "http://example.com/scope"
|
533
|
+
self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
|
534
|
+
self.paths = ["~/default/path/to/file.txt"]
|
535
|
+
self.token_credential_uri = "https://example.com/token2"
|
536
|
+
self.audience = "https://example.com/token3"
|
537
|
+
end
|
538
|
+
|
539
|
+
allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
|
540
|
+
allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
|
541
|
+
allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
|
542
|
+
allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
|
543
|
+
allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
|
544
|
+
|
545
|
+
mocked_signet = mock_signet
|
546
|
+
|
547
|
+
allow(Google::Auth).to receive(:get_application_default) do |scope, options|
|
548
|
+
expect(scope).to eq(scope2)
|
549
|
+
expect(options[:enable_self_signed_jwt]).to eq(false)
|
550
|
+
expect(options[:token_credential_uri]).to eq("https://example.com/token2")
|
551
|
+
expect(options[:audience]).to eq("https://example.com/token3")
|
552
|
+
|
553
|
+
# This should really be a Signet::OAuth2::Client object,
|
554
|
+
# but mocking is making that difficult, so return a valid hash instead.
|
555
|
+
default_keyfile_hash
|
556
|
+
end
|
557
|
+
|
558
|
+
creds = TestCredentials17.default scope: scope2, enable_self_signed_jwt: true
|
559
|
+
expect(creds).to be_a_kind_of(TestCredentials17)
|
560
|
+
expect(creds.client).to eq(mocked_signet)
|
561
|
+
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
562
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
563
|
+
end
|
564
|
+
|
565
|
+
it "subclasses delegate up the class hierarchy" do
|
566
|
+
class TestCredentials18 < Google::Auth::Credentials
|
567
|
+
self.scope = "http://example.com/scope"
|
568
|
+
self.target_audience = "https://example.com/target_audience"
|
569
|
+
self.env_vars = ["TEST_PATH", "TEST_JSON_VARS"]
|
570
|
+
self.paths = ["~/default/path/to/file.txt"]
|
571
|
+
end
|
572
|
+
|
573
|
+
class TestCredentials19 < TestCredentials18
|
574
|
+
end
|
575
|
+
|
576
|
+
expect(TestCredentials19.scope).to eq(["http://example.com/scope"])
|
577
|
+
expect(TestCredentials19.target_audience).to eq("https://example.com/target_audience")
|
578
|
+
expect(TestCredentials19.env_vars).to eq(["TEST_PATH", "TEST_JSON_VARS"])
|
579
|
+
expect(TestCredentials19.paths).to eq(["~/default/path/to/file.txt"])
|
580
|
+
|
581
|
+
TestCredentials19.token_credential_uri = "https://example.com/token2"
|
582
|
+
expect(TestCredentials19.token_credential_uri).to eq("https://example.com/token2")
|
583
|
+
TestCredentials19.token_credential_uri = nil
|
584
|
+
expect(TestCredentials19.token_credential_uri).to eq("https://oauth2.googleapis.com/token")
|
585
|
+
end
|
464
586
|
end
|
465
587
|
|
466
588
|
it "warns when cloud sdk credentials are used" do
|