googleauth 0.8.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +7 -0
- data/.github/workflows/release.yml +39 -0
- data/.kokoro/build.bat +9 -1
- data/.kokoro/continuous/linux.cfg +12 -2
- data/.kokoro/continuous/osx.cfg +5 -0
- data/.kokoro/continuous/post.cfg +30 -0
- data/.kokoro/continuous/windows.cfg +27 -1
- data/.kokoro/presubmit/linux.cfg +11 -1
- data/.kokoro/presubmit/osx.cfg +5 -0
- data/.kokoro/presubmit/windows.cfg +27 -1
- data/.kokoro/release.cfg +42 -1
- data/.kokoro/trampoline.bat +10 -0
- data/.repo-metadata.json +5 -0
- data/.rubocop.yml +8 -2
- data/CHANGELOG.md +94 -20
- data/Gemfile +7 -7
- data/{COPYING → LICENSE} +0 -0
- data/README.md +12 -15
- data/Rakefile +48 -5
- data/googleauth.gemspec +7 -3
- data/integration/helper.rb +31 -0
- data/integration/id_tokens/key_source_test.rb +74 -0
- data/lib/googleauth.rb +1 -0
- data/lib/googleauth/application_default.rb +2 -2
- data/lib/googleauth/compute_engine.rb +45 -20
- data/lib/googleauth/credentials.rb +445 -71
- data/lib/googleauth/credentials_loader.rb +11 -9
- data/lib/googleauth/iam.rb +1 -1
- data/lib/googleauth/id_tokens.rb +233 -0
- data/lib/googleauth/id_tokens/errors.rb +71 -0
- data/lib/googleauth/id_tokens/key_sources.rb +396 -0
- data/lib/googleauth/id_tokens/verifier.rb +142 -0
- data/lib/googleauth/json_key_reader.rb +6 -2
- data/lib/googleauth/scope_util.rb +1 -1
- data/lib/googleauth/service_account.rb +42 -23
- data/lib/googleauth/signet.rb +9 -6
- data/lib/googleauth/stores/file_token_store.rb +1 -0
- data/lib/googleauth/stores/redis_token_store.rb +1 -0
- data/lib/googleauth/user_authorizer.rb +6 -1
- data/lib/googleauth/user_refresh.rb +2 -2
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +16 -14
- data/rakelib/devsite_builder.rb +45 -0
- data/rakelib/link_checker.rb +64 -0
- data/rakelib/repo_metadata.rb +59 -0
- data/spec/googleauth/apply_auth_examples.rb +28 -5
- data/spec/googleauth/compute_engine_spec.rb +69 -13
- data/spec/googleauth/credentials_spec.rb +492 -165
- data/spec/googleauth/service_account_spec.rb +31 -16
- data/spec/googleauth/signet_spec.rb +46 -7
- data/spec/googleauth/user_authorizer_spec.rb +21 -1
- data/spec/googleauth/user_refresh_spec.rb +1 -1
- data/spec/googleauth/web_user_authorizer_spec.rb +6 -0
- data/test/helper.rb +33 -0
- data/test/id_tokens/key_sources_test.rb +240 -0
- data/test/id_tokens/verifier_test.rb +269 -0
- metadata +49 -13
- data/.kokoro/windows.sh +0 -4
data/Gemfile
CHANGED
@@ -8,18 +8,18 @@ group :development do
|
|
8
8
|
gem "coveralls", "~> 0.7"
|
9
9
|
gem "fakefs", "~> 0.6"
|
10
10
|
gem "fakeredis", "~> 0.5"
|
11
|
-
gem "google-style", "~>
|
11
|
+
gem "google-style", "~> 1.25.1"
|
12
12
|
gem "logging", "~> 2.0"
|
13
|
+
gem "minitest", "~> 5.14"
|
14
|
+
gem "minitest-focus", "~> 1.1"
|
13
15
|
gem "rack-test", "~> 0.6"
|
14
|
-
gem "rake", "~>
|
16
|
+
gem "rake", "~> 13.0"
|
15
17
|
gem "redis", "~> 3.2"
|
16
18
|
gem "rspec", "~> 3.0"
|
17
19
|
gem "simplecov", "~> 0.9"
|
18
20
|
gem "sinatra"
|
19
|
-
gem "webmock", "~>
|
21
|
+
gem "webmock", "~> 3.8"
|
20
22
|
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
24
|
+
gem "faraday", ">= 0.17.3", "< 2.0"
|
25
|
+
gem "gems", "~> 1.2"
|
data/{COPYING → LICENSE}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
# Google Auth Library for Ruby
|
2
2
|
|
3
3
|
<dl>
|
4
|
-
<dt>Homepage</dt><dd><a href="http://www.github.com/
|
4
|
+
<dt>Homepage</dt><dd><a href="http://www.github.com/googleapis/google-auth-library-ruby">http://www.github.com/googleapis/google-auth-library-ruby</a></dd>
|
5
5
|
<dt>Authors</dt><dd><a href="mailto:temiola@google.com">Tim Emiola</a></dd>
|
6
6
|
<dt>Copyright</dt><dd>Copyright © 2015 Google, Inc.</dd>
|
7
7
|
<dt>License</dt><dd>Apache 2.0</dd>
|
8
8
|
</dl>
|
9
9
|
|
10
10
|
[](http://badge.fury.io/rb/googleauth)
|
11
|
-
[](http://travis-ci.org/google/google-auth-library-ruby)
|
12
|
-
[](https://coveralls.io/r/google/google-auth-library-ruby)
|
13
11
|
|
14
12
|
## Description
|
15
13
|
|
@@ -180,18 +178,18 @@ access and refresh tokens. Two storage implementations are included:
|
|
180
178
|
* Google::Auth::Stores::RedisTokenStore
|
181
179
|
|
182
180
|
Custom storage implementations can also be used. See
|
183
|
-
[token_store.rb](
|
181
|
+
[token_store.rb](https://googleapis.dev/ruby/googleauth/latest/Google/Auth/TokenStore.html) for additional details.
|
184
182
|
|
185
183
|
## Supported Ruby Versions
|
186
184
|
|
187
|
-
This library is
|
185
|
+
This library is supported on Ruby 2.5+.
|
188
186
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
187
|
+
Google provides official support for Ruby versions that are actively supported
|
188
|
+
by Ruby Core—that is, Ruby versions that are either in normal maintenance or in
|
189
|
+
security maintenance, and not end of life. Currently, this means Ruby 2.5 and
|
190
|
+
later. Older versions of Ruby _may_ still work, but are unsupported and not
|
191
|
+
recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
|
192
|
+
about the Ruby support schedule.
|
195
193
|
|
196
194
|
## License
|
197
195
|
|
@@ -210,7 +208,6 @@ hesitate to
|
|
210
208
|
[ask questions](http://stackoverflow.com/questions/tagged/google-auth-library-ruby)
|
211
209
|
about the client or APIs on [StackOverflow](http://stackoverflow.com).
|
212
210
|
|
213
|
-
[
|
214
|
-
[
|
215
|
-
[
|
216
|
-
[copying]: https://github.com/google/google-auth-library-ruby/tree/master/COPYING
|
211
|
+
[application default credentials]: https://developers.google.com/accounts/docs/application-default-credentials
|
212
|
+
[contributing]: https://github.com/googleapis/google-auth-library-ruby/tree/master/.github/CONTRIBUTING.md
|
213
|
+
[copying]: https://github.com/googleapis/google-auth-library-ruby/tree/master/COPYING
|
data/Rakefile
CHANGED
@@ -1,18 +1,40 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
require "json"
|
2
3
|
require "bundler/gem_tasks"
|
3
4
|
|
5
|
+
require "rubocop/rake_task"
|
6
|
+
RuboCop::RakeTask.new
|
7
|
+
|
8
|
+
require "rake/testtask"
|
9
|
+
|
10
|
+
desc "Run tests."
|
11
|
+
Rake::TestTask.new do |t|
|
12
|
+
t.libs << "test"
|
13
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
14
|
+
t.warning = false
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run integration tests."
|
18
|
+
Rake::TestTask.new("integration") do |t|
|
19
|
+
t.libs << "integration"
|
20
|
+
t.test_files = FileList["integration/**/*_test.rb"]
|
21
|
+
t.warning = false
|
22
|
+
end
|
23
|
+
|
4
24
|
task :ci do
|
5
25
|
header "Using Ruby - #{RUBY_VERSION}"
|
6
26
|
sh "bundle exec rubocop"
|
27
|
+
Rake::Task["test"].invoke
|
28
|
+
Rake::Task["integration"].invoke
|
7
29
|
sh "bundle exec rspec"
|
8
30
|
end
|
9
31
|
|
10
|
-
task :
|
32
|
+
task :release_gem, :tag do |_t, args|
|
11
33
|
tag = args[:tag]
|
12
34
|
raise "You must provide a tag to release." if tag.nil?
|
13
35
|
|
14
36
|
# Verify the tag format "vVERSION"
|
15
|
-
m = tag.match
|
37
|
+
m = tag.match /v(?<version>\S*)/
|
16
38
|
raise "Tag #{tag} does not match the expected format." if m.nil?
|
17
39
|
|
18
40
|
version = m[:version]
|
@@ -33,17 +55,24 @@ task :release, :tag do |_t, args|
|
|
33
55
|
sh "bundle exec rake build"
|
34
56
|
end
|
35
57
|
|
36
|
-
path_to_be_pushed = "pkg
|
58
|
+
path_to_be_pushed = "pkg/googleauth-#{version}.gem"
|
59
|
+
gem_was_published = nil
|
37
60
|
if File.file? path_to_be_pushed
|
38
61
|
begin
|
39
|
-
::Gems.push File.new(path_to_be_pushed)
|
62
|
+
response = ::Gems.push File.new(path_to_be_pushed)
|
63
|
+
puts response
|
64
|
+
raise unless response.include? "Successfully registered gem:"
|
65
|
+
gem_was_published = true
|
40
66
|
puts "Successfully built and pushed googleauth for version #{version}"
|
41
67
|
rescue StandardError => e
|
68
|
+
gem_was_published = false
|
42
69
|
puts "Error while releasing googleauth version #{version}: #{e.message}"
|
43
70
|
end
|
44
71
|
else
|
45
72
|
raise "Cannot build googleauth for version #{version}"
|
46
73
|
end
|
74
|
+
|
75
|
+
Rake::Task["kokoro:publish_docs"].invoke if gem_was_published
|
47
76
|
end
|
48
77
|
|
49
78
|
namespace :kokoro do
|
@@ -63,6 +92,14 @@ namespace :kokoro do
|
|
63
92
|
Rake::Task["ci"].invoke
|
64
93
|
end
|
65
94
|
|
95
|
+
task :post do
|
96
|
+
require_relative "rakelib/link_checker.rb"
|
97
|
+
|
98
|
+
link_checker = LinkChecker.new
|
99
|
+
link_checker.run
|
100
|
+
exit link_checker.exit_status
|
101
|
+
end
|
102
|
+
|
66
103
|
task :nightly do
|
67
104
|
Rake::Task["ci"].invoke
|
68
105
|
end
|
@@ -75,7 +112,13 @@ namespace :kokoro do
|
|
75
112
|
.first.split("(").last.split(")").first || "0.1.0"
|
76
113
|
end
|
77
114
|
Rake::Task["kokoro:load_env_vars"].invoke
|
78
|
-
Rake::Task["
|
115
|
+
Rake::Task["release_gem"].invoke "v#{version}"
|
116
|
+
end
|
117
|
+
|
118
|
+
task :publish_docs do
|
119
|
+
require_relative "rakelib/devsite_builder.rb"
|
120
|
+
|
121
|
+
DevsiteBuilder.new(__dir__).publish
|
79
122
|
end
|
80
123
|
end
|
81
124
|
|
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
|
@@ -24,12 +24,16 @@ Gem::Specification.new do |gem|
|
|
24
24
|
File.basename f
|
25
25
|
end
|
26
26
|
gem.require_paths = ["lib"]
|
27
|
+
|
27
28
|
gem.platform = Gem::Platform::RUBY
|
29
|
+
gem.required_ruby_version = ">= 2.5"
|
28
30
|
|
29
|
-
gem.add_dependency "faraday", "
|
31
|
+
gem.add_dependency "faraday", ">= 0.17.3", "< 2.0"
|
30
32
|
gem.add_dependency "jwt", ">= 1.4", "< 3.0"
|
31
33
|
gem.add_dependency "memoist", "~> 0.16"
|
32
34
|
gem.add_dependency "multi_json", "~> 1.11"
|
33
35
|
gem.add_dependency "os", ">= 0.9", "< 2.0"
|
34
|
-
gem.add_dependency "signet", "~> 0.
|
36
|
+
gem.add_dependency "signet", "~> 0.14"
|
37
|
+
|
38
|
+
gem.add_development_dependency "yard", "~> 0.9"
|
35
39
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright 2020 Google LLC
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are
|
5
|
+
# met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright
|
8
|
+
# notice, this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above
|
10
|
+
# copyright notice, this list of conditions and the following disclaimer
|
11
|
+
# in the documentation and/or other materials provided with the
|
12
|
+
# distribution.
|
13
|
+
# * Neither the name of Google Inc. nor the names of its
|
14
|
+
# contributors may be used to endorse or promote products derived from
|
15
|
+
# this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
21
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
22
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
23
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
24
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
25
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
require "minitest/autorun"
|
30
|
+
require "minitest/focus"
|
31
|
+
require "googleauth"
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright 2020 Google LLC
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are
|
5
|
+
# met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright
|
8
|
+
# notice, this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above
|
10
|
+
# copyright notice, this list of conditions and the following disclaimer
|
11
|
+
# in the documentation and/or other materials provided with the
|
12
|
+
# distribution.
|
13
|
+
# * Neither the name of Google Inc. nor the names of its
|
14
|
+
# contributors may be used to endorse or promote products derived from
|
15
|
+
# this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
21
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
22
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
23
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
24
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
25
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
require "helper"
|
30
|
+
|
31
|
+
describe Google::Auth::IDTokens do
|
32
|
+
describe "key source" do
|
33
|
+
let(:legacy_oidc_key_source) {
|
34
|
+
Google::Auth::IDTokens::X509CertHttpKeySource.new "https://www.googleapis.com/oauth2/v1/certs"
|
35
|
+
}
|
36
|
+
let(:oidc_key_source) { Google::Auth::IDTokens.oidc_key_source }
|
37
|
+
let(:iap_key_source) { Google::Auth::IDTokens.iap_key_source }
|
38
|
+
|
39
|
+
it "Gets real keys from the OAuth2 V1 cert URL" do
|
40
|
+
keys = legacy_oidc_key_source.refresh_keys
|
41
|
+
refute_empty keys
|
42
|
+
keys.each do |key|
|
43
|
+
assert_kind_of OpenSSL::PKey::RSA, key.key
|
44
|
+
refute key.key.private?
|
45
|
+
assert_equal "RS256", key.algorithm
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "Gets real keys from the OAuth2 V3 cert URL" do
|
50
|
+
keys = oidc_key_source.refresh_keys
|
51
|
+
refute_empty keys
|
52
|
+
keys.each do |key|
|
53
|
+
assert_kind_of OpenSSL::PKey::RSA, key.key
|
54
|
+
refute key.key.private?
|
55
|
+
assert_equal "RS256", key.algorithm
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "Gets the same keys from the OAuth2 V1 and V3 cert URLs" do
|
60
|
+
keys_v1 = legacy_oidc_key_source.refresh_keys.map(&:key).map(&:export).sort
|
61
|
+
keys_v3 = oidc_key_source.refresh_keys.map(&:key).map(&:export).sort
|
62
|
+
assert_equal keys_v1, keys_v3
|
63
|
+
end
|
64
|
+
|
65
|
+
it "Gets real keys from the IAP public key URL" do
|
66
|
+
keys = iap_key_source.refresh_keys
|
67
|
+
refute_empty keys
|
68
|
+
keys.each do |key|
|
69
|
+
assert_kind_of OpenSSL::PKey::EC, key.key
|
70
|
+
assert_equal "ES256", key.algorithm
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/googleauth.rb
CHANGED
@@ -31,5 +31,6 @@ require "googleauth/application_default"
|
|
31
31
|
require "googleauth/client_id"
|
32
32
|
require "googleauth/credentials"
|
33
33
|
require "googleauth/default_credentials"
|
34
|
+
require "googleauth/id_tokens"
|
34
35
|
require "googleauth/user_authorizer"
|
35
36
|
require "googleauth/web_user_authorizer"
|
@@ -47,7 +47,7 @@ module Google
|
|
47
47
|
#
|
48
48
|
# Use this to obtain the Application Default Credentials for accessing
|
49
49
|
# Google APIs. Application Default Credentials are described in detail
|
50
|
-
# at
|
50
|
+
# at https://cloud.google.com/docs/authentication/production.
|
51
51
|
#
|
52
52
|
# If supplied, scope is used to create the credentials instance, when it can
|
53
53
|
# be applied. E.g, on google compute engine and for user credentials the
|
@@ -75,7 +75,7 @@ module Google
|
|
75
75
|
GCECredentials.unmemoize_all
|
76
76
|
raise NOT_FOUND_ERROR
|
77
77
|
end
|
78
|
-
GCECredentials.new
|
78
|
+
GCECredentials.new scope: scope
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -51,30 +51,47 @@ 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
|
-
|
55
|
-
|
54
|
+
DEFAULT_METADATA_HOST = "169.254.169.254".freeze
|
55
|
+
|
56
|
+
# @private Unused and deprecated
|
57
|
+
COMPUTE_AUTH_TOKEN_URI =
|
58
|
+
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
59
|
+
# @private Unused and deprecated
|
60
|
+
COMPUTE_ID_TOKEN_URI =
|
61
|
+
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity".freeze
|
62
|
+
# @private Unused and deprecated
|
56
63
|
COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
|
57
64
|
|
58
65
|
class << self
|
59
66
|
extend Memoist
|
60
67
|
|
68
|
+
def metadata_host
|
69
|
+
ENV.fetch "GCE_METADATA_HOST", DEFAULT_METADATA_HOST
|
70
|
+
end
|
71
|
+
|
72
|
+
def compute_check_uri
|
73
|
+
"http://#{metadata_host}".freeze
|
74
|
+
end
|
75
|
+
|
76
|
+
def compute_auth_token_uri
|
77
|
+
"#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
78
|
+
end
|
79
|
+
|
80
|
+
def compute_id_token_uri
|
81
|
+
"#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/identity".freeze
|
82
|
+
end
|
83
|
+
|
61
84
|
# Detect if this appear to be a GCE instance, by checking if metadata
|
62
|
-
# is available
|
85
|
+
# is available.
|
63
86
|
def on_gce? options = {}
|
87
|
+
# TODO: This should use google-cloud-env instead.
|
64
88
|
c = options[:connection] || Faraday.default_connection
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# issue is that resolving an unknown host on some networks will take
|
70
|
-
# 20-30 seconds; making this timeout short fixes the issue, but
|
71
|
-
# could lead to false negatives in the event that we are on GCE, but
|
72
|
-
# the metadata resolution was particularly slow. The latter case is
|
73
|
-
# "unlikely".
|
74
|
-
req.options.timeout = 0.1
|
89
|
+
headers = { "Metadata-Flavor" => "Google" }
|
90
|
+
resp = c.get compute_check_uri, nil, headers do |req|
|
91
|
+
req.options.timeout = 1.0
|
92
|
+
req.options.open_timeout = 0.1
|
75
93
|
end
|
76
94
|
return false unless resp.status == 200
|
77
|
-
return false unless resp.headers.key? "Metadata-Flavor"
|
78
95
|
resp.headers["Metadata-Flavor"] == "Google"
|
79
96
|
rescue Faraday::TimeoutError, Faraday::ConnectionFailed
|
80
97
|
false
|
@@ -88,17 +105,25 @@ module Google
|
|
88
105
|
def fetch_access_token options = {}
|
89
106
|
c = options[:connection] || Faraday.default_connection
|
90
107
|
retry_with_error do
|
91
|
-
|
92
|
-
|
108
|
+
uri = target_audience ? GCECredentials.compute_id_token_uri : GCECredentials.compute_auth_token_uri
|
109
|
+
query = target_audience ? { "audience" => target_audience, "format" => "full" } : {}
|
110
|
+
query[:scopes] = Array(scope).join "," if scope
|
111
|
+
resp = c.get uri, query, "Metadata-Flavor" => "Google"
|
93
112
|
case resp.status
|
94
113
|
when 200
|
95
|
-
|
96
|
-
|
114
|
+
content_type = resp.headers["content-type"]
|
115
|
+
if content_type == "text/html"
|
116
|
+
{ (target_audience ? "id_token" : "access_token") => resp.body }
|
117
|
+
else
|
118
|
+
Signet::OAuth2.parse_credentials resp.body, content_type
|
119
|
+
end
|
120
|
+
when 403, 500
|
121
|
+
msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
122
|
+
raise Signet::UnexpectedStatusError, msg
|
97
123
|
when 404
|
98
124
|
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
99
125
|
else
|
100
|
-
msg = "Unexpected error code #{resp.status}"
|
101
|
-
"#{UNEXPECTED_ERROR_SUFFIX}"
|
126
|
+
msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
|
102
127
|
raise Signet::AuthorizationError, msg
|
103
128
|
end
|
104
129
|
end
|
@@ -35,63 +35,382 @@ require "googleauth/credentials_loader"
|
|
35
35
|
|
36
36
|
module Google
|
37
37
|
module Auth
|
38
|
-
|
39
|
-
#
|
40
|
-
|
38
|
+
##
|
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
|
79
|
+
##
|
80
|
+
# The default token credential URI to be used when none is provided during initialization.
|
41
81
|
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token".freeze
|
82
|
+
|
83
|
+
##
|
84
|
+
# The default target audience ID to be used when none is provided during initialization.
|
42
85
|
AUDIENCE = "https://oauth2.googleapis.com/token".freeze
|
43
|
-
SCOPE = [].freeze
|
44
|
-
PATH_ENV_VARS = [].freeze
|
45
|
-
JSON_ENV_VARS = [].freeze
|
46
|
-
DEFAULT_PATHS = [].freeze
|
47
86
|
|
87
|
+
@audience = @scope = @target_audience = @env_vars = @paths = @token_credential_uri = nil
|
88
|
+
|
89
|
+
##
|
90
|
+
# The default token credential URI to be used when none is provided during initialization.
|
91
|
+
# The URI is the authorization server's HTTP endpoint capable of issuing tokens and
|
92
|
+
# refreshing expired tokens.
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
#
|
96
|
+
def self.token_credential_uri
|
97
|
+
lookup_auth_param :token_credential_uri do
|
98
|
+
lookup_local_constant :TOKEN_CREDENTIAL_URI
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Set the default token credential URI to be used when none is provided during initialization.
|
104
|
+
#
|
105
|
+
# @param [String] new_token_credential_uri
|
106
|
+
#
|
107
|
+
def self.token_credential_uri= new_token_credential_uri
|
108
|
+
@token_credential_uri = new_token_credential_uri
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# The default target audience ID to be used when none is provided during initialization.
|
113
|
+
# Used only by the assertion grant type.
|
114
|
+
#
|
115
|
+
# @return [String]
|
116
|
+
#
|
117
|
+
def self.audience
|
118
|
+
lookup_auth_param :audience do
|
119
|
+
lookup_local_constant :AUDIENCE
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Sets the default target audience ID to be used when none is provided during initialization.
|
125
|
+
#
|
126
|
+
# @param [String] new_audience
|
127
|
+
#
|
128
|
+
def self.audience= new_audience
|
129
|
+
@audience = new_audience
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# The default scope to be used when none is provided during initialization.
|
134
|
+
# A scope is an access range defined by the authorization server.
|
135
|
+
# The scope can be a single value or a list of values.
|
136
|
+
#
|
137
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
138
|
+
# If {#scope} is set, this credential will produce access tokens.
|
139
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
140
|
+
#
|
141
|
+
# @return [String, Array<String>, nil]
|
142
|
+
#
|
143
|
+
def self.scope
|
144
|
+
lookup_auth_param :scope do
|
145
|
+
vals = lookup_local_constant :SCOPE
|
146
|
+
vals ? Array(vals).flatten.uniq : nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Sets the default scope to be used when none is provided during initialization.
|
152
|
+
#
|
153
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
154
|
+
# If {#scope} is set, this credential will produce access tokens.
|
155
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
156
|
+
#
|
157
|
+
# @param [String, Array<String>, nil] new_scope
|
158
|
+
#
|
159
|
+
def self.scope= new_scope
|
160
|
+
new_scope = Array new_scope unless new_scope.nil?
|
161
|
+
@scope = new_scope
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# The default final target audience for ID tokens, to be used when none
|
166
|
+
# is provided during initialization.
|
167
|
+
#
|
168
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
169
|
+
# If {#scope} is set, this credential will produce access tokens.
|
170
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
171
|
+
#
|
172
|
+
# @return [String, nil]
|
173
|
+
#
|
174
|
+
def self.target_audience
|
175
|
+
lookup_auth_param :target_audience
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# Sets the default final target audience for ID tokens, to be used when none
|
180
|
+
# is provided during initialization.
|
181
|
+
#
|
182
|
+
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
183
|
+
# If {#scope} is set, this credential will produce access tokens.
|
184
|
+
# If {#target_audience} is set, this credential will produce ID tokens.
|
185
|
+
#
|
186
|
+
# @param [String, nil] new_target_audience
|
187
|
+
#
|
188
|
+
def self.target_audience= new_target_audience
|
189
|
+
@target_audience = new_target_audience
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# The environment variables to search for credentials. Values can either be a file path to the
|
194
|
+
# credentials file, or the JSON contents of the credentials file.
|
195
|
+
# The env_vars will never be nil. If there are no vars, the empty array is returned.
|
196
|
+
#
|
197
|
+
# @return [Array<String>]
|
198
|
+
#
|
199
|
+
def self.env_vars
|
200
|
+
env_vars_internal || []
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# @private
|
205
|
+
# Internal recursive lookup for env_vars.
|
206
|
+
#
|
207
|
+
def self.env_vars_internal
|
208
|
+
lookup_auth_param :env_vars, :env_vars_internal do
|
209
|
+
# Pull values when PATH_ENV_VARS or JSON_ENV_VARS constants exists.
|
210
|
+
path_env_vars = lookup_local_constant :PATH_ENV_VARS
|
211
|
+
json_env_vars = lookup_local_constant :JSON_ENV_VARS
|
212
|
+
(Array(path_env_vars) + Array(json_env_vars)).flatten.uniq if path_env_vars || json_env_vars
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Sets the environment variables to search for credentials.
|
218
|
+
# Setting to `nil` "unsets" the value, and defaults to the superclass
|
219
|
+
# (or to the empty array if there is no superclass).
|
220
|
+
#
|
221
|
+
# @param [String, Array<String>, nil] new_env_vars
|
222
|
+
#
|
223
|
+
def self.env_vars= new_env_vars
|
224
|
+
new_env_vars = Array new_env_vars unless new_env_vars.nil?
|
225
|
+
@env_vars = new_env_vars
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# The file paths to search for credentials files.
|
230
|
+
# The paths will never be nil. If there are no paths, the empty array is returned.
|
231
|
+
#
|
232
|
+
# @return [Array<String>]
|
233
|
+
#
|
234
|
+
def self.paths
|
235
|
+
paths_internal || []
|
236
|
+
end
|
237
|
+
|
238
|
+
##
|
239
|
+
# @private
|
240
|
+
# Internal recursive lookup for paths.
|
241
|
+
#
|
242
|
+
def self.paths_internal
|
243
|
+
lookup_auth_param :paths, :paths_internal do
|
244
|
+
# Pull in values if the DEFAULT_PATHS constant exists.
|
245
|
+
vals = lookup_local_constant :DEFAULT_PATHS
|
246
|
+
vals ? Array(vals).flatten.uniq : nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
##
|
251
|
+
# Set the file paths to search for credentials files.
|
252
|
+
# Setting to `nil` "unsets" the value, and defaults to the superclass
|
253
|
+
# (or to the empty array if there is no superclass).
|
254
|
+
#
|
255
|
+
# @param [String, Array<String>, nil] new_paths
|
256
|
+
#
|
257
|
+
def self.paths= new_paths
|
258
|
+
new_paths = Array new_paths unless new_paths.nil?
|
259
|
+
@paths = new_paths
|
260
|
+
end
|
261
|
+
|
262
|
+
##
|
263
|
+
# @private
|
264
|
+
# Return the given parameter value, defaulting up the class hierarchy.
|
265
|
+
#
|
266
|
+
# First returns the value of the instance variable, if set.
|
267
|
+
# Next, calls the given block if provided. (This is generally used to
|
268
|
+
# look up legacy constant-based values.)
|
269
|
+
# Otherwise, calls the superclass method if present.
|
270
|
+
# Returns nil if all steps fail.
|
271
|
+
#
|
272
|
+
# @param name [Symbol] The parameter name
|
273
|
+
# @param method_name [Symbol] The lookup method name, if different
|
274
|
+
# @return [Object] The value
|
275
|
+
#
|
276
|
+
def self.lookup_auth_param name, method_name = name
|
277
|
+
val = instance_variable_get "@#{name}".to_sym
|
278
|
+
val = yield if val.nil? && block_given?
|
279
|
+
return val unless val.nil?
|
280
|
+
return superclass.send method_name if superclass.respond_to? method_name
|
281
|
+
nil
|
282
|
+
end
|
283
|
+
|
284
|
+
##
|
285
|
+
# @private
|
286
|
+
# Return the value of the given constant if it is defined directly in
|
287
|
+
# this class, or nil if not.
|
288
|
+
#
|
289
|
+
# @param [Symbol] Name of the constant
|
290
|
+
# @return [Object] The value
|
291
|
+
#
|
292
|
+
def self.lookup_local_constant name
|
293
|
+
const_defined?(name, false) ? const_get(name) : nil
|
294
|
+
end
|
295
|
+
|
296
|
+
##
|
297
|
+
# The Signet::OAuth2::Client object the Credentials instance is using.
|
298
|
+
#
|
299
|
+
# @return [Signet::OAuth2::Client]
|
300
|
+
#
|
48
301
|
attr_accessor :client
|
49
|
-
attr_reader :project_id
|
50
302
|
|
51
|
-
|
303
|
+
##
|
304
|
+
# Identifier for the project the client is authenticating with.
|
305
|
+
#
|
306
|
+
# @return [String]
|
307
|
+
#
|
308
|
+
attr_reader :project_id
|
309
|
+
|
310
|
+
##
|
311
|
+
# Identifier for a separate project used for billing/quota, if any.
|
312
|
+
#
|
313
|
+
# @return [String,nil]
|
314
|
+
#
|
315
|
+
attr_reader :quota_project_id
|
316
|
+
|
317
|
+
# @private Delegate client methods to the client object.
|
52
318
|
extend Forwardable
|
319
|
+
|
320
|
+
##
|
321
|
+
# @!attribute [r] token_credential_uri
|
322
|
+
# @return [String] The token credential URI. The URI is the authorization server's HTTP
|
323
|
+
# endpoint capable of issuing tokens and refreshing expired tokens.
|
324
|
+
#
|
325
|
+
# @!attribute [r] audience
|
326
|
+
# @return [String] The target audience ID when issuing assertions. Used only by the
|
327
|
+
# assertion grant type.
|
328
|
+
#
|
329
|
+
# @!attribute [r] scope
|
330
|
+
# @return [String, Array<String>] The scope for this client. A scope is an access range
|
331
|
+
# defined by the authorization server. The scope can be a single value or a list of values.
|
332
|
+
#
|
333
|
+
# @!attribute [r] target_audience
|
334
|
+
# @return [String] The final target audience for ID tokens returned by this credential.
|
335
|
+
#
|
336
|
+
# @!attribute [r] issuer
|
337
|
+
# @return [String] The issuer ID associated with this client.
|
338
|
+
#
|
339
|
+
# @!attribute [r] signing_key
|
340
|
+
# @return [String, OpenSSL::PKey] The signing key associated with this client.
|
341
|
+
#
|
342
|
+
# @!attribute [r] updater_proc
|
343
|
+
# @return [Proc] Returns a reference to the {Signet::OAuth2::Client#apply} method,
|
344
|
+
# suitable for passing as a closure.
|
345
|
+
#
|
53
346
|
def_delegators :@client,
|
54
347
|
:token_credential_uri, :audience,
|
55
|
-
:scope, :issuer, :signing_key, :updater_proc
|
348
|
+
:scope, :issuer, :signing_key, :updater_proc, :target_audience
|
56
349
|
|
57
|
-
|
350
|
+
##
|
351
|
+
# Creates a new Credentials instance with the provided auth credentials, and with the default
|
352
|
+
# values configured on the class.
|
353
|
+
#
|
354
|
+
# @param [String, Hash, Signet::OAuth2::Client] keyfile
|
355
|
+
# The keyfile can be provided as one of the following:
|
356
|
+
#
|
357
|
+
# * The path to a JSON keyfile (as a +String+)
|
358
|
+
# * The contents of a JSON keyfile (as a +Hash+)
|
359
|
+
# * A +Signet::OAuth2::Client+ object
|
360
|
+
# @param [Hash] options
|
361
|
+
# The options for configuring the credentials instance. The following is supported:
|
362
|
+
#
|
363
|
+
# * +:scope+ - the scope for the client
|
364
|
+
# * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
|
365
|
+
# * +:connection_builder+ - the connection builder to use for the client
|
366
|
+
# * +:default_connection+ - the default connection to use for the client
|
367
|
+
#
|
58
368
|
def initialize keyfile, options = {}
|
59
|
-
scope = options[:scope]
|
60
369
|
verify_keyfile_provided! keyfile
|
61
370
|
@project_id = options["project_id"] || options["project"]
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@client = init_client hash, options
|
69
|
-
@project_id ||= (hash["project_id"] || hash["project"])
|
371
|
+
@quota_project_id = options["quota_project_id"]
|
372
|
+
case keyfile
|
373
|
+
when Signet::OAuth2::Client
|
374
|
+
update_from_signet keyfile
|
375
|
+
when Hash
|
376
|
+
update_from_hash keyfile, options
|
70
377
|
else
|
71
|
-
|
72
|
-
json = JSON.parse ::File.read(keyfile)
|
73
|
-
json["scope"] ||= scope
|
74
|
-
@project_id ||= (json["project_id"] || json["project"])
|
75
|
-
@client = init_client json, options
|
378
|
+
update_from_filepath keyfile, options
|
76
379
|
end
|
77
380
|
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
|
78
381
|
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
79
382
|
@client.fetch_access_token!
|
383
|
+
@env_vars = nil
|
384
|
+
@paths = nil
|
385
|
+
@scope = nil
|
80
386
|
end
|
81
|
-
# rubocop:enable Metrics/AbcSize
|
82
387
|
|
83
|
-
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
388
|
+
##
|
389
|
+
# Creates a new Credentials instance with auth credentials acquired by searching the
|
390
|
+
# environment variables and paths configured on the class, and with the default values
|
391
|
+
# configured on the class.
|
392
|
+
#
|
393
|
+
# The auth credentials are searched for in the following order:
|
394
|
+
#
|
395
|
+
# 1. configured environment variables (see {Credentials.env_vars})
|
396
|
+
# 2. configured default file paths (see {Credentials.paths})
|
397
|
+
# 3. application default (see {Google::Auth.get_application_default})
|
398
|
+
#
|
399
|
+
# @param [Hash] options
|
400
|
+
# The options for configuring the credentials instance. The following is supported:
|
401
|
+
#
|
402
|
+
# * +:scope+ - the scope for the client
|
403
|
+
# * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
|
404
|
+
# * +:connection_builder+ - the connection builder to use for the client
|
405
|
+
# * +:default_connection+ - the default connection to use for the client
|
406
|
+
#
|
407
|
+
# @return [Credentials]
|
408
|
+
#
|
87
409
|
def self.default options = {}
|
88
|
-
# First try to find keyfile file from environment variables.
|
89
|
-
client =
|
410
|
+
# First try to find keyfile file or json from environment variables.
|
411
|
+
client = from_env_vars options
|
90
412
|
|
91
|
-
# Second try to find keyfile
|
92
|
-
client ||= from_json_vars options
|
93
|
-
|
94
|
-
# Third try to find keyfile file from known file paths.
|
413
|
+
# Second try to find keyfile file from known file paths.
|
95
414
|
client ||= from_default_paths options
|
96
415
|
|
97
416
|
# Finally get instantiated client from Google::Auth
|
@@ -99,49 +418,68 @@ module Google
|
|
99
418
|
client
|
100
419
|
end
|
101
420
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
json = lambda do |v|
|
115
|
-
unless ENV[v].nil?
|
116
|
-
begin
|
117
|
-
JSON.parse ENV[v]
|
118
|
-
rescue StandardError
|
119
|
-
nil
|
421
|
+
##
|
422
|
+
# @private Lookup Credentials from environment variables.
|
423
|
+
def self.from_env_vars options
|
424
|
+
env_vars.each do |env_var|
|
425
|
+
str = ENV[env_var]
|
426
|
+
next if str.nil?
|
427
|
+
io =
|
428
|
+
if ::File.file? str
|
429
|
+
::StringIO.new ::File.read str
|
430
|
+
else
|
431
|
+
json = ::JSON.parse str rescue nil
|
432
|
+
json ? ::StringIO.new(str) : nil
|
120
433
|
end
|
121
|
-
|
434
|
+
next if io.nil?
|
435
|
+
return from_io io, options
|
122
436
|
end
|
123
|
-
self::JSON_ENV_VARS.map(&json).compact.each { |hash| return new hash, options }
|
124
437
|
nil
|
125
438
|
end
|
126
439
|
|
440
|
+
##
|
441
|
+
# @private Lookup Credentials from default file paths.
|
127
442
|
def self.from_default_paths options
|
128
|
-
|
129
|
-
|
130
|
-
.
|
131
|
-
|
132
|
-
|
443
|
+
paths.each do |path|
|
444
|
+
next unless path && ::File.file?(path)
|
445
|
+
io = ::StringIO.new ::File.read path
|
446
|
+
return from_io io, options
|
447
|
+
end
|
133
448
|
nil
|
134
449
|
end
|
135
450
|
|
451
|
+
##
|
452
|
+
# @private Lookup Credentials using Google::Auth.get_application_default.
|
136
453
|
def self.from_application_default options
|
137
|
-
scope = options[:scope] || self
|
138
|
-
|
454
|
+
scope = options[:scope] || self.scope
|
455
|
+
auth_opts = {
|
456
|
+
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
|
457
|
+
audience: options[:audience] || audience,
|
458
|
+
target_audience: options[:target_audience] || target_audience,
|
459
|
+
enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?
|
460
|
+
}
|
461
|
+
client = Google::Auth.get_application_default scope, auth_opts
|
139
462
|
new client, options
|
140
463
|
end
|
141
|
-
|
142
|
-
|
464
|
+
|
465
|
+
# @private Read credentials from a JSON stream.
|
466
|
+
def self.from_io io, options
|
467
|
+
creds_input = {
|
468
|
+
json_key_io: io,
|
469
|
+
scope: options[:scope] || scope,
|
470
|
+
target_audience: options[:target_audience] || target_audience,
|
471
|
+
enable_self_signed_jwt: options[:enable_self_signed_jwt] && options[:scope].nil?,
|
472
|
+
token_credential_uri: options[:token_credential_uri] || token_credential_uri,
|
473
|
+
audience: options[:audience] || audience
|
474
|
+
}
|
475
|
+
client = Google::Auth::DefaultCredentials.make_creds creds_input
|
476
|
+
new client
|
477
|
+
end
|
478
|
+
|
479
|
+
private_class_method :from_env_vars,
|
143
480
|
:from_default_paths,
|
144
|
-
:from_application_default
|
481
|
+
:from_application_default,
|
482
|
+
:from_io
|
145
483
|
|
146
484
|
protected
|
147
485
|
|
@@ -166,22 +504,58 @@ module Google
|
|
166
504
|
|
167
505
|
# returns a new Hash with string keys instead of symbol keys.
|
168
506
|
def stringify_hash_keys hash
|
169
|
-
|
507
|
+
hash.to_h.transform_keys(&:to_s)
|
170
508
|
end
|
171
509
|
|
510
|
+
# rubocop:disable Metrics/AbcSize
|
511
|
+
|
172
512
|
def client_options options
|
173
513
|
# Keyfile options have higher priority over constructor defaults
|
174
|
-
options["token_credential_uri"] ||= self.class
|
175
|
-
options["audience"] ||= self.class
|
176
|
-
options["scope"] ||= self.class
|
514
|
+
options["token_credential_uri"] ||= self.class.token_credential_uri
|
515
|
+
options["audience"] ||= self.class.audience
|
516
|
+
options["scope"] ||= self.class.scope
|
517
|
+
options["target_audience"] ||= self.class.target_audience
|
518
|
+
|
519
|
+
if !Array(options["scope"]).empty? && options["target_audience"]
|
520
|
+
raise ArgumentError, "Cannot specify both scope and target_audience"
|
521
|
+
end
|
177
522
|
|
523
|
+
needs_scope = options["target_audience"].nil?
|
178
524
|
# client options for initializing signet client
|
179
525
|
{ token_credential_uri: options["token_credential_uri"],
|
180
526
|
audience: options["audience"],
|
181
|
-
scope: Array(options["scope"]),
|
527
|
+
scope: (needs_scope ? Array(options["scope"]) : nil),
|
528
|
+
target_audience: options["target_audience"],
|
182
529
|
issuer: options["client_email"],
|
183
530
|
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
|
184
531
|
end
|
532
|
+
|
533
|
+
# rubocop:enable Metrics/AbcSize
|
534
|
+
|
535
|
+
def update_from_signet client
|
536
|
+
@project_id ||= client.project_id if client.respond_to? :project_id
|
537
|
+
@quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
|
538
|
+
@client = client
|
539
|
+
end
|
540
|
+
|
541
|
+
def update_from_hash hash, options
|
542
|
+
hash = stringify_hash_keys hash
|
543
|
+
hash["scope"] ||= options[:scope]
|
544
|
+
hash["target_audience"] ||= options[:target_audience]
|
545
|
+
@project_id ||= (hash["project_id"] || hash["project"])
|
546
|
+
@quota_project_id ||= hash["quota_project_id"]
|
547
|
+
@client = init_client hash, options
|
548
|
+
end
|
549
|
+
|
550
|
+
def update_from_filepath path, options
|
551
|
+
verify_keyfile_exists! path
|
552
|
+
json = JSON.parse ::File.read(path)
|
553
|
+
json["scope"] ||= options[:scope]
|
554
|
+
json["target_audience"] ||= options[:target_audience]
|
555
|
+
@project_id ||= (json["project_id"] || json["project"])
|
556
|
+
@quota_project_id ||= json["quota_project_id"]
|
557
|
+
@client = init_client json, options
|
558
|
+
end
|
185
559
|
end
|
186
560
|
end
|
187
561
|
end
|