googleauth 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -0
- data/googleauth.gemspec +2 -2
- data/lib/googleauth/compute_engine.rb +13 -5
- data/lib/googleauth/credentials.rb +89 -22
- data/lib/googleauth/json_key_reader.rb +6 -2
- data/lib/googleauth/service_account.rb +13 -4
- data/lib/googleauth/signet.rb +3 -2
- data/lib/googleauth/version.rb +1 -1
- data/spec/googleauth/apply_auth_examples.rb +16 -5
- data/spec/googleauth/compute_engine_spec.rb +22 -13
- data/spec/googleauth/credentials_spec.rb +17 -6
- data/spec/googleauth/service_account_spec.rb +23 -16
- data/spec/googleauth/signet_spec.rb +15 -7
- data/spec/googleauth/user_refresh_spec.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f7661bafaadfe1aa8d9a574ace1bae7cb6d708dea4420e54097464d2b449eb0
|
4
|
+
data.tar.gz: 6ddd0f1591487041e8cce3d563381877ba27793454e0b8ce1f773186d2fd497e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57006f2ea4dd1c5a097b51ae66fc451b2c8598ea8915a6ec74446f7c2d87b0033b0c28fd23b8eae978ce75e17d73ac54c86d7aa03601f3aa61dcee62e85c7cbd
|
7
|
+
data.tar.gz: 81306dc3b804115ef426925535b2153916bf37752f81a55e47a3631a77a5e976d40432351eabd452058ad918ff141da2db406128666f993dc64e40d605006a22
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/googleauth.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.version = Google::Auth::VERSION
|
10
10
|
gem.authors = ["Tim Emiola"]
|
11
11
|
gem.email = "temiola@google.com"
|
12
|
-
gem.homepage = "https://github.com/
|
12
|
+
gem.homepage = "https://github.com/googleapis/google-auth-library-ruby"
|
13
13
|
gem.summary = "Google Auth Library for Ruby"
|
14
14
|
gem.license = "Apache-2.0"
|
15
15
|
gem.description = <<-DESCRIPTION
|
@@ -32,6 +32,6 @@ Gem::Specification.new do |gem|
|
|
32
32
|
gem.add_dependency "memoist", "~> 0.16"
|
33
33
|
gem.add_dependency "multi_json", "~> 1.11"
|
34
34
|
gem.add_dependency "os", ">= 0.9", "< 2.0"
|
35
|
-
gem.add_dependency "signet", "~> 0.
|
35
|
+
gem.add_dependency "signet", "~> 0.14"
|
36
36
|
gem.add_development_dependency "yard", "~> 0.9"
|
37
37
|
end
|
@@ -51,8 +51,10 @@ module Google
|
|
51
51
|
class GCECredentials < Signet::OAuth2::Client
|
52
52
|
# The IP Address is used in the URIs to speed up failures on non-GCE
|
53
53
|
# systems.
|
54
|
-
COMPUTE_AUTH_TOKEN_URI =
|
55
|
-
|
54
|
+
COMPUTE_AUTH_TOKEN_URI =
|
55
|
+
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
56
|
+
COMPUTE_ID_TOKEN_URI =
|
57
|
+
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity".freeze
|
56
58
|
COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
|
57
59
|
|
58
60
|
class << self
|
@@ -82,12 +84,18 @@ module Google
|
|
82
84
|
def fetch_access_token options = {}
|
83
85
|
c = options[:connection] || Faraday.default_connection
|
84
86
|
retry_with_error do
|
87
|
+
uri = target_audience ? COMPUTE_ID_TOKEN_URI : COMPUTE_AUTH_TOKEN_URI
|
88
|
+
query = target_audience ? { "audience" => target_audience, "format" => "full" } : nil
|
85
89
|
headers = { "Metadata-Flavor" => "Google" }
|
86
|
-
resp = c.get
|
90
|
+
resp = c.get uri, query, headers
|
87
91
|
case resp.status
|
88
92
|
when 200
|
89
|
-
|
90
|
-
|
93
|
+
content_type = resp.headers["content-type"]
|
94
|
+
if content_type == "text/html"
|
95
|
+
{ (target_audience ? "id_token" : "access_token") => resp.body }
|
96
|
+
else
|
97
|
+
Signet::OAuth2.parse_credentials resp.body, content_type
|
98
|
+
end
|
91
99
|
when 404
|
92
100
|
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
93
101
|
else
|
@@ -47,6 +47,8 @@ module Google
|
|
47
47
|
# The default target audience ID to be used when none is provided during initialization.
|
48
48
|
AUDIENCE = "https://oauth2.googleapis.com/token".freeze
|
49
49
|
|
50
|
+
@audience = @scope = @target_audience = @env_vars = @paths = nil
|
51
|
+
|
50
52
|
##
|
51
53
|
# The default token credential URI to be used when none is provided during initialization.
|
52
54
|
# The URI is the authorization server's HTTP endpoint capable of issuing tokens and
|
@@ -97,20 +99,25 @@ module Google
|
|
97
99
|
# A scope is an access range defined by the authorization server.
|
98
100
|
# The scope can be a single value or a list of values.
|
99
101
|
#
|
102
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
103
|
+
# If {#scope} is set, this credential will produce access tokens.
|
104
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
105
|
+
#
|
100
106
|
# @return [String, Array<String>]
|
101
107
|
#
|
102
108
|
def self.scope
|
103
109
|
return @scope unless @scope.nil?
|
104
110
|
|
105
|
-
|
106
|
-
# Pull in values is the SCOPE constant exists.
|
107
|
-
tmp_scope << const_get(:SCOPE) if const_defined? :SCOPE
|
108
|
-
tmp_scope.flatten.uniq
|
111
|
+
Array(const_get(:SCOPE)).flatten.uniq if const_defined? :SCOPE
|
109
112
|
end
|
110
113
|
|
111
114
|
##
|
112
115
|
# Sets the default scope to be used when none is provided during initialization.
|
113
116
|
#
|
117
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
118
|
+
# If {#scope} is set, this credential will produce access tokens.
|
119
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
120
|
+
#
|
114
121
|
# @param [String, Array<String>] new_scope
|
115
122
|
# @return [String, Array<String>]
|
116
123
|
#
|
@@ -119,6 +126,34 @@ module Google
|
|
119
126
|
@scope = new_scope
|
120
127
|
end
|
121
128
|
|
129
|
+
##
|
130
|
+
# The default final target audience for ID tokens, to be used when none
|
131
|
+
# is provided during initialization.
|
132
|
+
#
|
133
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
134
|
+
# If {#scope} is set, this credential will produce access tokens.
|
135
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
136
|
+
#
|
137
|
+
# @return [String]
|
138
|
+
#
|
139
|
+
def self.target_audience
|
140
|
+
@target_audience
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Sets the default final target audience for ID tokens, to be used when none
|
145
|
+
# is provided during initialization.
|
146
|
+
#
|
147
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
148
|
+
# If {#scope} is set, this credential will produce access tokens.
|
149
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
150
|
+
#
|
151
|
+
# @param [String] new_target_audience
|
152
|
+
#
|
153
|
+
def self.target_audience= new_target_audience
|
154
|
+
@target_audience = new_target_audience
|
155
|
+
end
|
156
|
+
|
122
157
|
##
|
123
158
|
# The environment variables to search for credentials. Values can either be a file path to the
|
124
159
|
# credentials file, or the JSON contents of the credentials file.
|
@@ -185,6 +220,13 @@ module Google
|
|
185
220
|
#
|
186
221
|
attr_reader :project_id
|
187
222
|
|
223
|
+
##
|
224
|
+
# Identifier for a separate project used for billing/quota, if any.
|
225
|
+
#
|
226
|
+
# @return [String,nil]
|
227
|
+
#
|
228
|
+
attr_reader :quota_project_id
|
229
|
+
|
188
230
|
# @private Delegate client methods to the client object.
|
189
231
|
extend Forwardable
|
190
232
|
|
@@ -201,6 +243,9 @@ module Google
|
|
201
243
|
# @return [String, Array<String>] The scope for this client. A scope is an access range
|
202
244
|
# defined by the authorization server. The scope can be a single value or a list of values.
|
203
245
|
#
|
246
|
+
# @!attribute [r] target_audience
|
247
|
+
# @return [String] The final target audience for ID tokens returned by this credential.
|
248
|
+
#
|
204
249
|
# @!attribute [r] issuer
|
205
250
|
# @return [String] The issuer ID associated with this client.
|
206
251
|
#
|
@@ -213,9 +258,7 @@ module Google
|
|
213
258
|
#
|
214
259
|
def_delegators :@client,
|
215
260
|
:token_credential_uri, :audience,
|
216
|
-
:scope, :issuer, :signing_key, :updater_proc
|
217
|
-
|
218
|
-
# rubocop:disable Metrics/AbcSize
|
261
|
+
:scope, :issuer, :signing_key, :updater_proc, :target_audience
|
219
262
|
|
220
263
|
##
|
221
264
|
# Creates a new Credentials instance with the provided auth credentials, and with the default
|
@@ -236,23 +279,15 @@ module Google
|
|
236
279
|
# * +:default_connection+ - the default connection to use for the client
|
237
280
|
#
|
238
281
|
def initialize keyfile, options = {}
|
239
|
-
scope = options[:scope]
|
240
282
|
verify_keyfile_provided! keyfile
|
241
283
|
@project_id = options["project_id"] || options["project"]
|
284
|
+
@quota_project_id = options["quota_project_id"]
|
242
285
|
if keyfile.is_a? Signet::OAuth2::Client
|
243
|
-
|
244
|
-
@project_id ||= keyfile.project_id if keyfile.respond_to? :project_id
|
286
|
+
update_from_signet keyfile
|
245
287
|
elsif keyfile.is_a? Hash
|
246
|
-
|
247
|
-
hash["scope"] ||= scope
|
248
|
-
@client = init_client hash, options
|
249
|
-
@project_id ||= (hash["project_id"] || hash["project"])
|
288
|
+
update_from_hash keyfile, options
|
250
289
|
else
|
251
|
-
|
252
|
-
json = JSON.parse ::File.read(keyfile)
|
253
|
-
json["scope"] ||= scope
|
254
|
-
@project_id ||= (json["project_id"] || json["project"])
|
255
|
-
@client = init_client json, options
|
290
|
+
update_from_filepath keyfile, options
|
256
291
|
end
|
257
292
|
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
|
258
293
|
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
@@ -261,7 +296,6 @@ module Google
|
|
261
296
|
@paths = nil
|
262
297
|
@scope = nil
|
263
298
|
end
|
264
|
-
# rubocop:enable Metrics/AbcSize
|
265
299
|
|
266
300
|
##
|
267
301
|
# Creates a new Credentials instance with auth credentials acquired by searching the
|
@@ -323,7 +357,8 @@ module Google
|
|
323
357
|
# @private Lookup Credentials using Google::Auth.get_application_default.
|
324
358
|
def self.from_application_default options
|
325
359
|
scope = options[:scope] || self.scope
|
326
|
-
|
360
|
+
auth_opts = { target_audience: options[:target_audience] || target_audience }
|
361
|
+
client = Google::Auth.get_application_default scope, auth_opts
|
327
362
|
new client, options
|
328
363
|
end
|
329
364
|
|
@@ -362,14 +397,46 @@ module Google
|
|
362
397
|
options["token_credential_uri"] ||= self.class.token_credential_uri
|
363
398
|
options["audience"] ||= self.class.audience
|
364
399
|
options["scope"] ||= self.class.scope
|
400
|
+
options["target_audience"] ||= self.class.target_audience
|
365
401
|
|
402
|
+
if !Array(options["scope"]).empty? && options["target_audience"]
|
403
|
+
raise ArgumentError, "Cannot specify both scope and target_audience"
|
404
|
+
end
|
405
|
+
|
406
|
+
needs_scope = options["target_audience"].nil?
|
366
407
|
# client options for initializing signet client
|
367
408
|
{ token_credential_uri: options["token_credential_uri"],
|
368
409
|
audience: options["audience"],
|
369
|
-
scope: Array(options["scope"]),
|
410
|
+
scope: (needs_scope ? Array(options["scope"]) : nil),
|
411
|
+
target_audience: options["target_audience"],
|
370
412
|
issuer: options["client_email"],
|
371
413
|
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
|
372
414
|
end
|
415
|
+
|
416
|
+
def update_from_signet client
|
417
|
+
@project_id ||= client.project_id if client.respond_to? :project_id
|
418
|
+
@quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
|
419
|
+
@client = client
|
420
|
+
end
|
421
|
+
|
422
|
+
def update_from_hash hash, options
|
423
|
+
hash = stringify_hash_keys hash
|
424
|
+
hash["scope"] ||= options[:scope]
|
425
|
+
hash["target_audience"] ||= options[:target_audience]
|
426
|
+
@project_id ||= (hash["project_id"] || hash["project"])
|
427
|
+
@quota_project_id ||= hash["quota_project_id"]
|
428
|
+
@client = init_client hash, options
|
429
|
+
end
|
430
|
+
|
431
|
+
def update_from_filepath path, options
|
432
|
+
verify_keyfile_exists! path
|
433
|
+
json = JSON.parse ::File.read(path)
|
434
|
+
json["scope"] ||= options[:scope]
|
435
|
+
json["target_audience"] ||= options[:target_audience]
|
436
|
+
@project_id ||= (json["project_id"] || json["project"])
|
437
|
+
@quota_project_id ||= json["quota_project_id"]
|
438
|
+
@client = init_client json, options
|
439
|
+
end
|
373
440
|
end
|
374
441
|
end
|
375
442
|
end
|
@@ -38,8 +38,12 @@ module Google
|
|
38
38
|
json_key = MultiJson.load json_key_io.read
|
39
39
|
raise "missing client_email" unless json_key.key? "client_email"
|
40
40
|
raise "missing private_key" unless json_key.key? "private_key"
|
41
|
-
|
42
|
-
|
41
|
+
[
|
42
|
+
json_key["private_key"],
|
43
|
+
json_key["client_email"],
|
44
|
+
json_key["project_id"],
|
45
|
+
json_key["quota_project_id"]
|
46
|
+
]
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
@@ -51,28 +51,34 @@ module Google
|
|
51
51
|
extend CredentialsLoader
|
52
52
|
extend JsonKeyReader
|
53
53
|
attr_reader :project_id
|
54
|
+
attr_reader :quota_project_id
|
54
55
|
|
55
56
|
# Creates a ServiceAccountCredentials.
|
56
57
|
#
|
57
58
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
58
59
|
# @param scope [string|array|nil] the scope(s) to access
|
59
60
|
def self.make_creds options = {}
|
60
|
-
json_key_io, scope = options.values_at :json_key_io, :scope
|
61
|
+
json_key_io, scope, target_audience = options.values_at :json_key_io, :scope, :target_audience
|
62
|
+
raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
|
63
|
+
|
61
64
|
if json_key_io
|
62
|
-
private_key, client_email, project_id = read_json_key json_key_io
|
65
|
+
private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
|
63
66
|
else
|
64
67
|
private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
65
68
|
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
66
69
|
project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
70
|
+
quota_project_id = nil
|
67
71
|
end
|
68
72
|
project_id ||= CredentialsLoader.load_gcloud_project_id
|
69
73
|
|
70
74
|
new(token_credential_uri: TOKEN_CRED_URI,
|
71
75
|
audience: TOKEN_CRED_URI,
|
72
76
|
scope: scope,
|
77
|
+
target_audience: target_audience,
|
73
78
|
issuer: client_email,
|
74
79
|
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
75
|
-
project_id: project_id
|
80
|
+
project_id: project_id,
|
81
|
+
quota_project_id: quota_project_id)
|
76
82
|
.configure_connection(options)
|
77
83
|
end
|
78
84
|
|
@@ -87,6 +93,7 @@ module Google
|
|
87
93
|
|
88
94
|
def initialize options = {}
|
89
95
|
@project_id = options[:project_id]
|
96
|
+
@quota_project_id = options[:quota_project_id]
|
90
97
|
super options
|
91
98
|
end
|
92
99
|
|
@@ -133,6 +140,7 @@ module Google
|
|
133
140
|
extend CredentialsLoader
|
134
141
|
extend JsonKeyReader
|
135
142
|
attr_reader :project_id
|
143
|
+
attr_reader :quota_project_id
|
136
144
|
|
137
145
|
# make_creds proxies the construction of a credentials instance
|
138
146
|
#
|
@@ -151,12 +159,13 @@ module Google
|
|
151
159
|
def initialize options = {}
|
152
160
|
json_key_io = options[:json_key_io]
|
153
161
|
if json_key_io
|
154
|
-
@private_key, @issuer, @project_id =
|
162
|
+
@private_key, @issuer, @project_id, @quota_project_id =
|
155
163
|
self.class.read_json_key json_key_io
|
156
164
|
else
|
157
165
|
@private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
158
166
|
@issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
159
167
|
@project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
168
|
+
@quota_project_id = nil
|
160
169
|
end
|
161
170
|
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
162
171
|
@signing_key = OpenSSL::PKey::RSA.new @private_key
|
data/lib/googleauth/signet.rb
CHANGED
@@ -48,8 +48,9 @@ module Signet
|
|
48
48
|
def apply! a_hash, opts = {}
|
49
49
|
# fetch the access token there is currently not one, or if the client
|
50
50
|
# has expired
|
51
|
-
|
52
|
-
|
51
|
+
token_type = target_audience ? :id_token : :access_token
|
52
|
+
fetch_access_token! opts if send(token_type).nil? || expires_within?(60)
|
53
|
+
a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
|
53
54
|
end
|
54
55
|
|
55
56
|
# Returns a clone of a_hash updated with the authentication token
|
data/lib/googleauth/version.rb
CHANGED
@@ -45,26 +45,37 @@ shared_examples "apply/apply! are OK" do
|
|
45
45
|
# auth client
|
46
46
|
describe "#fetch_access_token" do
|
47
47
|
let(:token) { "1/abcdef1234567890" }
|
48
|
-
let :
|
48
|
+
let :access_stub do
|
49
49
|
make_auth_stubs access_token: token
|
50
50
|
end
|
51
|
+
let :id_stub do
|
52
|
+
make_auth_stubs id_token: token
|
53
|
+
end
|
51
54
|
|
52
55
|
it "should set access_token to the fetched value" do
|
53
|
-
|
56
|
+
access_stub
|
54
57
|
@client.fetch_access_token!
|
55
58
|
expect(@client.access_token).to eq(token)
|
56
|
-
expect(
|
59
|
+
expect(access_stub).to have_been_requested
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should set id_token to the fetched value" do
|
63
|
+
skip unless @id_client
|
64
|
+
id_stub
|
65
|
+
@id_client.fetch_access_token!
|
66
|
+
expect(@id_client.id_token).to eq(token)
|
67
|
+
expect(id_stub).to have_been_requested
|
57
68
|
end
|
58
69
|
|
59
70
|
it "should notify refresh listeners after updating" do
|
60
|
-
|
71
|
+
access_stub
|
61
72
|
expect do |b|
|
62
73
|
@client.on_refresh(&b)
|
63
74
|
@client.fetch_access_token!
|
64
75
|
end.to yield_with_args(have_attributes(
|
65
76
|
access_token: "1/abcdef1234567890"
|
66
77
|
))
|
67
|
-
expect(
|
78
|
+
expect(access_stub).to have_been_requested
|
68
79
|
end
|
69
80
|
end
|
70
81
|
|
@@ -37,23 +37,32 @@ require "googleauth/compute_engine"
|
|
37
37
|
require "spec_helper"
|
38
38
|
|
39
39
|
describe Google::Auth::GCECredentials do
|
40
|
-
|
40
|
+
MD_ACCESS_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
41
|
+
MD_ID_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://pubsub.googleapis.com/&format=full".freeze
|
41
42
|
GCECredentials = Google::Auth::GCECredentials
|
42
43
|
|
43
44
|
before :example do
|
44
45
|
@client = GCECredentials.new
|
46
|
+
@id_client = GCECredentials.new target_audience: "https://pubsub.googleapis.com/"
|
45
47
|
end
|
46
48
|
|
47
|
-
def make_auth_stubs opts
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
def make_auth_stubs opts
|
50
|
+
if opts[:access_token]
|
51
|
+
body = MultiJson.dump("access_token" => opts[:access_token],
|
52
|
+
"token_type" => "Bearer",
|
53
|
+
"expires_in" => 3600)
|
54
|
+
stub_request(:get, MD_ACCESS_URI)
|
55
|
+
.with(headers: { "Metadata-Flavor" => "Google" })
|
56
|
+
.to_return(body: body,
|
57
|
+
status: 200,
|
58
|
+
headers: { "Content-Type" => "application/json" })
|
59
|
+
elsif opts[:id_token]
|
60
|
+
stub_request(:get, MD_ID_URI)
|
61
|
+
.with(headers: { "Metadata-Flavor" => "Google" })
|
62
|
+
.to_return(body: opts[:id_token],
|
63
|
+
status: 200,
|
64
|
+
headers: { "Content-Type" => "text/html" })
|
65
|
+
end
|
57
66
|
end
|
58
67
|
|
59
68
|
it_behaves_like "apply/apply! are OK"
|
@@ -61,7 +70,7 @@ describe Google::Auth::GCECredentials do
|
|
61
70
|
context "metadata is unavailable" do
|
62
71
|
describe "#fetch_access_token" do
|
63
72
|
it "should fail if the metadata request returns a 404" do
|
64
|
-
stub = stub_request(:get,
|
73
|
+
stub = stub_request(:get, MD_ACCESS_URI)
|
65
74
|
.to_return(status: 404,
|
66
75
|
headers: { "Metadata-Flavor" => "Google" })
|
67
76
|
expect { @client.fetch_access_token! }
|
@@ -70,7 +79,7 @@ describe Google::Auth::GCECredentials do
|
|
70
79
|
end
|
71
80
|
|
72
81
|
it "should fail if the metadata request returns an unexpected code" do
|
73
|
-
stub = stub_request(:get,
|
82
|
+
stub = stub_request(:get, MD_ACCESS_URI)
|
74
83
|
.to_return(status: 503,
|
75
84
|
headers: { "Metadata-Flavor" => "Google" })
|
76
85
|
expect { @client.fetch_access_token! }
|
@@ -36,12 +36,13 @@ require "googleauth"
|
|
36
36
|
describe Google::Auth::Credentials, :private do
|
37
37
|
let :default_keyfile_hash do
|
38
38
|
{
|
39
|
-
"private_key_id"
|
40
|
-
"private_key"
|
41
|
-
"client_email"
|
42
|
-
"client_id"
|
43
|
-
"type"
|
44
|
-
"project_id"
|
39
|
+
"private_key_id" => "testabc1234567890xyz",
|
40
|
+
"private_key" => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
|
41
|
+
"client_email" => "credz-testabc1234567890xyz@developer.gserviceaccount.com",
|
42
|
+
"client_id" => "credz-testabc1234567890xyz.apps.googleusercontent.com",
|
43
|
+
"type" => "service_account",
|
44
|
+
"project_id" => "a_project_id",
|
45
|
+
"quota_project_id" => "b_project_id"
|
45
46
|
}
|
46
47
|
end
|
47
48
|
|
@@ -118,6 +119,7 @@ describe Google::Auth::Credentials, :private do
|
|
118
119
|
expect(creds).to be_a_kind_of(TestCredentials1)
|
119
120
|
expect(creds.client).to eq(mocked_signet)
|
120
121
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
122
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
121
123
|
end
|
122
124
|
|
123
125
|
it "subclasses can use PATH_ENV_VARS to get keyfile path" do
|
@@ -153,6 +155,7 @@ describe Google::Auth::Credentials, :private do
|
|
153
155
|
expect(creds).to be_a_kind_of(TestCredentials2)
|
154
156
|
expect(creds.client).to eq(mocked_signet)
|
155
157
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
158
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
156
159
|
end
|
157
160
|
|
158
161
|
it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
|
@@ -190,6 +193,7 @@ describe Google::Auth::Credentials, :private do
|
|
190
193
|
expect(creds).to be_a_kind_of(TestCredentials3)
|
191
194
|
expect(creds.client).to eq(mocked_signet)
|
192
195
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
196
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
193
197
|
end
|
194
198
|
|
195
199
|
it "subclasses can use DEFAULT_PATHS to get keyfile path" do
|
@@ -225,6 +229,7 @@ describe Google::Auth::Credentials, :private do
|
|
225
229
|
expect(creds).to be_a_kind_of(TestCredentials4)
|
226
230
|
expect(creds.client).to eq(mocked_signet)
|
227
231
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
232
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
228
233
|
end
|
229
234
|
|
230
235
|
it "subclasses that find no matches default to Google::Auth.get_application_default" do
|
@@ -266,6 +271,7 @@ describe Google::Auth::Credentials, :private do
|
|
266
271
|
expect(creds).to be_a_kind_of(TestCredentials5)
|
267
272
|
expect(creds.client).to eq(mocked_signet)
|
268
273
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
274
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
269
275
|
end
|
270
276
|
end
|
271
277
|
|
@@ -305,6 +311,7 @@ describe Google::Auth::Credentials, :private do
|
|
305
311
|
expect(creds).to be_a_kind_of(TestCredentials11)
|
306
312
|
expect(creds.client).to eq(mocked_signet)
|
307
313
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
314
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
308
315
|
end
|
309
316
|
|
310
317
|
it "subclasses can use PATH_ENV_VARS to get keyfile path" do
|
@@ -339,6 +346,7 @@ describe Google::Auth::Credentials, :private do
|
|
339
346
|
expect(creds).to be_a_kind_of(TestCredentials12)
|
340
347
|
expect(creds.client).to eq(mocked_signet)
|
341
348
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
349
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
342
350
|
end
|
343
351
|
|
344
352
|
it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
|
@@ -375,6 +383,7 @@ describe Google::Auth::Credentials, :private do
|
|
375
383
|
expect(creds).to be_a_kind_of(TestCredentials13)
|
376
384
|
expect(creds.client).to eq(mocked_signet)
|
377
385
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
386
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
378
387
|
end
|
379
388
|
|
380
389
|
it "subclasses can use DEFAULT_PATHS to get keyfile path" do
|
@@ -409,6 +418,7 @@ describe Google::Auth::Credentials, :private do
|
|
409
418
|
expect(creds).to be_a_kind_of(TestCredentials14)
|
410
419
|
expect(creds.client).to eq(mocked_signet)
|
411
420
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
421
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
412
422
|
end
|
413
423
|
|
414
424
|
it "subclasses that find no matches default to Google::Auth.get_application_default" do
|
@@ -449,6 +459,7 @@ describe Google::Auth::Credentials, :private do
|
|
449
459
|
expect(creds).to be_a_kind_of(TestCredentials15)
|
450
460
|
expect(creds.client).to eq(mocked_signet)
|
451
461
|
expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
|
462
|
+
expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
|
452
463
|
end
|
453
464
|
end
|
454
465
|
|
@@ -112,12 +112,13 @@ describe Google::Auth::ServiceAccountCredentials do
|
|
112
112
|
let(:client_email) { "app@developer.gserviceaccount.com" }
|
113
113
|
let :cred_json do
|
114
114
|
{
|
115
|
-
private_key_id:
|
116
|
-
private_key:
|
117
|
-
client_email:
|
118
|
-
client_id:
|
119
|
-
type:
|
120
|
-
project_id:
|
115
|
+
private_key_id: "a_private_key_id",
|
116
|
+
private_key: @key.to_pem,
|
117
|
+
client_email: client_email,
|
118
|
+
client_id: "app.apps.googleusercontent.com",
|
119
|
+
type: "service_account",
|
120
|
+
project_id: "a_project_id",
|
121
|
+
quota_project_id: "b_project_id"
|
121
122
|
}
|
122
123
|
end
|
123
124
|
|
@@ -127,24 +128,28 @@ describe Google::Auth::ServiceAccountCredentials do
|
|
127
128
|
json_key_io: StringIO.new(cred_json_text),
|
128
129
|
scope: "https://www.googleapis.com/auth/userinfo.profile"
|
129
130
|
)
|
131
|
+
@id_client = ServiceAccountCredentials.make_creds(
|
132
|
+
json_key_io: StringIO.new(cred_json_text),
|
133
|
+
target_audience: "https://pubsub.googleapis.com/"
|
134
|
+
)
|
130
135
|
end
|
131
136
|
|
132
|
-
def make_auth_stubs opts
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
+
def make_auth_stubs opts
|
138
|
+
body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
|
139
|
+
body_fields["access_token"] = opts[:access_token] if opts[:access_token]
|
140
|
+
body_fields["id_token"] = opts[:id_token] if opts[:id_token]
|
141
|
+
body = MultiJson.dump body_fields
|
137
142
|
blk = proc do |request|
|
138
143
|
params = Addressable::URI.form_unencode request.body
|
139
|
-
|
140
|
-
|
141
|
-
|
144
|
+
claim, _header = JWT.decode(params.assoc("assertion").last,
|
145
|
+
@key.public_key, true,
|
146
|
+
algorithm: "RS256")
|
147
|
+
!opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
|
142
148
|
end
|
143
149
|
stub_request(:post, "https://www.googleapis.com/oauth2/v4/token")
|
144
150
|
.with(body: hash_including(
|
145
151
|
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
146
|
-
),
|
147
|
-
&blk)
|
152
|
+
), &blk)
|
148
153
|
.to_return(body: body,
|
149
154
|
status: 200,
|
150
155
|
headers: { "Content-Type" => "application/json" })
|
@@ -285,6 +290,7 @@ describe Google::Auth::ServiceAccountCredentials do
|
|
285
290
|
ENV["APPDATA"] = dir
|
286
291
|
credentials = @clz.from_well_known_path @scope
|
287
292
|
expect(credentials.project_id).to eq(cred_json[:project_id])
|
293
|
+
expect(credentials.quota_project_id).to eq(cred_json[:quota_project_id])
|
288
294
|
end
|
289
295
|
end
|
290
296
|
|
@@ -476,6 +482,7 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
|
|
476
482
|
ENV["APPDATA"] = dir
|
477
483
|
credentials = clz.from_well_known_path @scope
|
478
484
|
expect(credentials.project_id).to eq(cred_json[:project_id])
|
485
|
+
expect(credentials.quota_project_id).to be_nil
|
479
486
|
end
|
480
487
|
end
|
481
488
|
end
|
@@ -47,18 +47,26 @@ describe Signet::OAuth2::Client do
|
|
47
47
|
audience: "https://oauth2.googleapis.com/token",
|
48
48
|
signing_key: @key
|
49
49
|
)
|
50
|
+
@id_client = Signet::OAuth2::Client.new(
|
51
|
+
token_credential_uri: "https://oauth2.googleapis.com/token",
|
52
|
+
target_audience: "https://pubsub.googleapis.com/",
|
53
|
+
issuer: "app@example.com",
|
54
|
+
audience: "https://oauth2.googleapis.com/token",
|
55
|
+
signing_key: @key
|
56
|
+
)
|
50
57
|
end
|
51
58
|
|
52
59
|
def make_auth_stubs opts
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
|
61
|
+
body_fields["access_token"] = opts[:access_token] if opts[:access_token]
|
62
|
+
body_fields["id_token"] = opts[:id_token] if opts[:id_token]
|
63
|
+
body = MultiJson.dump body_fields
|
57
64
|
blk = proc do |request|
|
58
65
|
params = Addressable::URI.form_unencode request.body
|
59
|
-
|
60
|
-
|
61
|
-
|
66
|
+
claim, _header = JWT.decode(params.assoc("assertion").last,
|
67
|
+
@key.public_key, true,
|
68
|
+
algorithm: "RS256")
|
69
|
+
!opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
|
62
70
|
end
|
63
71
|
with_params = { body: hash_including(
|
64
72
|
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
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.12.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: 2020-
|
11
|
+
date: 2020-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -104,14 +104,14 @@ dependencies:
|
|
104
104
|
requirements:
|
105
105
|
- - "~>"
|
106
106
|
- !ruby/object:Gem::Version
|
107
|
-
version: '0.
|
107
|
+
version: '0.14'
|
108
108
|
type: :runtime
|
109
109
|
prerelease: false
|
110
110
|
version_requirements: !ruby/object:Gem::Requirement
|
111
111
|
requirements:
|
112
112
|
- - "~>"
|
113
113
|
- !ruby/object:Gem::Version
|
114
|
-
version: '0.
|
114
|
+
version: '0.14'
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
116
|
name: yard
|
117
117
|
requirement: !ruby/object:Gem::Requirement
|
@@ -203,7 +203,7 @@ files:
|
|
203
203
|
- spec/googleauth/user_refresh_spec.rb
|
204
204
|
- spec/googleauth/web_user_authorizer_spec.rb
|
205
205
|
- spec/spec_helper.rb
|
206
|
-
homepage: https://github.com/
|
206
|
+
homepage: https://github.com/googleapis/google-auth-library-ruby
|
207
207
|
licenses:
|
208
208
|
- Apache-2.0
|
209
209
|
metadata: {}
|