googleauth 0.5.1 → 0.14.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.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +5 -4
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  6. data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
  7. data/.kokoro/build.bat +16 -0
  8. data/.kokoro/build.sh +4 -0
  9. data/.kokoro/continuous/common.cfg +24 -0
  10. data/.kokoro/continuous/linux.cfg +25 -0
  11. data/.kokoro/continuous/osx.cfg +8 -0
  12. data/.kokoro/continuous/post.cfg +30 -0
  13. data/.kokoro/continuous/windows.cfg +29 -0
  14. data/.kokoro/osx.sh +4 -0
  15. data/.kokoro/presubmit/common.cfg +24 -0
  16. data/.kokoro/presubmit/linux.cfg +24 -0
  17. data/.kokoro/presubmit/osx.cfg +8 -0
  18. data/.kokoro/presubmit/windows.cfg +29 -0
  19. data/.kokoro/release.cfg +94 -0
  20. data/.kokoro/trampoline.bat +10 -0
  21. data/.kokoro/trampoline.sh +4 -0
  22. data/.repo-metadata.json +5 -0
  23. data/.rubocop.yml +19 -1
  24. data/CHANGELOG.md +112 -19
  25. data/CODE_OF_CONDUCT.md +43 -0
  26. data/Gemfile +19 -13
  27. data/{COPYING → LICENSE} +0 -0
  28. data/README.md +58 -18
  29. data/Rakefile +126 -9
  30. data/googleauth.gemspec +28 -25
  31. data/integration/helper.rb +31 -0
  32. data/integration/id_tokens/key_source_test.rb +74 -0
  33. data/lib/googleauth.rb +7 -96
  34. data/lib/googleauth/application_default.rb +81 -0
  35. data/lib/googleauth/client_id.rb +21 -19
  36. data/lib/googleauth/compute_engine.rb +70 -43
  37. data/lib/googleauth/credentials.rb +442 -0
  38. data/lib/googleauth/credentials_loader.rb +117 -43
  39. data/lib/googleauth/default_credentials.rb +93 -0
  40. data/lib/googleauth/iam.rb +11 -11
  41. data/lib/googleauth/id_tokens.rb +233 -0
  42. data/lib/googleauth/id_tokens/errors.rb +71 -0
  43. data/lib/googleauth/id_tokens/key_sources.rb +394 -0
  44. data/lib/googleauth/id_tokens/verifier.rb +144 -0
  45. data/lib/googleauth/json_key_reader.rb +50 -0
  46. data/lib/googleauth/scope_util.rb +12 -12
  47. data/lib/googleauth/service_account.rb +74 -63
  48. data/lib/googleauth/signet.rb +55 -13
  49. data/lib/googleauth/stores/file_token_store.rb +8 -8
  50. data/lib/googleauth/stores/redis_token_store.rb +22 -22
  51. data/lib/googleauth/token_store.rb +6 -6
  52. data/lib/googleauth/user_authorizer.rb +80 -68
  53. data/lib/googleauth/user_refresh.rb +44 -35
  54. data/lib/googleauth/version.rb +1 -1
  55. data/lib/googleauth/web_user_authorizer.rb +77 -68
  56. data/rakelib/devsite_builder.rb +45 -0
  57. data/rakelib/link_checker.rb +64 -0
  58. data/rakelib/repo_metadata.rb +59 -0
  59. data/spec/googleauth/apply_auth_examples.rb +74 -50
  60. data/spec/googleauth/client_id_spec.rb +75 -55
  61. data/spec/googleauth/compute_engine_spec.rb +98 -46
  62. data/spec/googleauth/credentials_spec.rb +478 -0
  63. data/spec/googleauth/get_application_default_spec.rb +149 -111
  64. data/spec/googleauth/iam_spec.rb +25 -25
  65. data/spec/googleauth/scope_util_spec.rb +26 -24
  66. data/spec/googleauth/service_account_spec.rb +269 -144
  67. data/spec/googleauth/signet_spec.rb +101 -30
  68. data/spec/googleauth/stores/file_token_store_spec.rb +12 -13
  69. data/spec/googleauth/stores/redis_token_store_spec.rb +11 -11
  70. data/spec/googleauth/stores/store_examples.rb +16 -16
  71. data/spec/googleauth/user_authorizer_spec.rb +153 -124
  72. data/spec/googleauth/user_refresh_spec.rb +186 -121
  73. data/spec/googleauth/web_user_authorizer_spec.rb +82 -69
  74. data/spec/spec_helper.rb +21 -19
  75. data/test/helper.rb +33 -0
  76. data/test/id_tokens/key_sources_test.rb +240 -0
  77. data/test/id_tokens/verifier_test.rb +269 -0
  78. metadata +87 -34
  79. data/.rubocop_todo.yml +0 -32
  80. data/.travis.yml +0 -37
@@ -27,114 +27,134 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'spec_helper'
35
- require 'fakefs/safe'
36
- require 'googleauth'
34
+ require "spec_helper"
35
+ require "fakefs/safe"
36
+ require "googleauth"
37
37
 
38
38
  describe Google::Auth::ClientId do
39
- shared_examples 'it has a valid config' do
40
- it 'should include a valid id' do
41
- expect(client_id.id).to eql 'abc@example.com'
39
+ shared_examples "it has a valid config" do
40
+ it "should include a valid id" do
41
+ expect(client_id.id).to eql "abc@example.com"
42
42
  end
43
43
 
44
- it 'should include a valid secret' do
45
- expect(client_id.secret).to eql 'notasecret'
44
+ it "should include a valid secret" do
45
+ expect(client_id.secret).to eql "notasecret"
46
46
  end
47
47
  end
48
48
 
49
- shared_examples 'it can successfully load client_id' do
50
- context 'loaded from hash' do
51
- let(:client_id) { Google::Auth::ClientId.from_hash(config) }
49
+ shared_examples "it can successfully load client_id" do
50
+ context "loaded from hash" do
51
+ let(:client_id) { Google::Auth::ClientId.from_hash config }
52
52
 
53
- it_behaves_like 'it has a valid config'
53
+ it_behaves_like "it has a valid config"
54
54
  end
55
55
 
56
- context 'loaded from file' do
57
- file_path = '/client_secrets.json'
56
+ context "loaded from file" do
57
+ file_path = "/client_secrets.json"
58
58
 
59
- let(:client_id) do
59
+ let :client_id do
60
60
  FakeFS do
61
- content = MultiJson.dump(config)
62
- File.write(file_path, content)
63
- Google::Auth::ClientId.from_file(file_path)
61
+ content = MultiJson.dump config
62
+ File.write file_path, content
63
+ Google::Auth::ClientId.from_file file_path
64
64
  end
65
65
  end
66
66
 
67
- it_behaves_like 'it has a valid config'
67
+ it_behaves_like "it has a valid config"
68
68
  end
69
69
  end
70
70
 
71
- describe 'with web config' do
72
- let(:config) do
71
+ describe "with web config" do
72
+ let :config do
73
73
  {
74
- 'web' => {
75
- 'client_id' => 'abc@example.com',
76
- 'client_secret' => 'notasecret'
74
+ "web" => {
75
+ "client_id" => "abc@example.com",
76
+ "client_secret" => "notasecret"
77
77
  }
78
78
  }
79
79
  end
80
- it_behaves_like 'it can successfully load client_id'
80
+ it_behaves_like "it can successfully load client_id"
81
81
  end
82
82
 
83
- describe 'with installed app config' do
84
- let(:config) do
83
+ describe "with installed app config" do
84
+ let :config do
85
85
  {
86
- 'installed' => {
87
- 'client_id' => 'abc@example.com',
88
- 'client_secret' => 'notasecret'
86
+ "installed" => {
87
+ "client_id" => "abc@example.com",
88
+ "client_secret" => "notasecret"
89
89
  }
90
90
  }
91
91
  end
92
- it_behaves_like 'it can successfully load client_id'
92
+ it_behaves_like "it can successfully load client_id"
93
93
  end
94
94
 
95
- context 'with missing top level property' do
96
- let(:config) do
95
+ context "with missing top level property" do
96
+ let :config do
97
97
  {
98
- 'notvalid' => {
99
- 'client_id' => 'abc@example.com',
100
- 'client_secret' => 'notasecret'
98
+ "notvalid" => {
99
+ "client_id" => "abc@example.com",
100
+ "client_secret" => "notasecret"
101
101
  }
102
102
  }
103
103
  end
104
104
 
105
- it 'should raise error' do
106
- expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
107
- /Expected top level property/)
105
+ it "should raise error" do
106
+ expect { Google::Auth::ClientId.from_hash config }.to raise_error(
107
+ /Expected top level property/
108
+ )
108
109
  end
109
110
  end
110
111
 
111
- context 'with missing client id' do
112
- let(:config) do
112
+ context "with missing client id" do
113
+ let :config do
113
114
  {
114
- 'web' => {
115
- 'client_secret' => 'notasecret'
115
+ "web" => {
116
+ "client_secret" => "notasecret"
116
117
  }
117
118
  }
118
119
  end
119
120
 
120
- it 'should raise error' do
121
- expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
122
- /Client id can not be nil/)
121
+ it "should raise error" do
122
+ expect { Google::Auth::ClientId.from_hash config }.to raise_error(
123
+ /Client id can not be nil/
124
+ )
123
125
  end
124
126
  end
125
127
 
126
- context 'with missing client secret' do
127
- let(:config) do
128
+ context "with missing client secret" do
129
+ let :config do
128
130
  {
129
- 'web' => {
130
- 'client_id' => 'abc@example.com'
131
+ "web" => {
132
+ "client_id" => "abc@example.com"
131
133
  }
132
134
  }
133
135
  end
134
136
 
135
- it 'should raise error' do
136
- expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
137
- /Client secret can not be nil/)
137
+ it "should raise error" do
138
+ expect { Google::Auth::ClientId.from_hash config }.to raise_error(
139
+ /Client secret can not be nil/
140
+ )
141
+ end
142
+ end
143
+
144
+ context "with cloud sdk credentials" do
145
+ let :config do
146
+ {
147
+ "web" => {
148
+ "client_id" => Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID,
149
+ "client_secret" => "notasecret"
150
+ }
151
+ }
152
+ end
153
+
154
+ it "should raise warning" do
155
+ expect { Google::Auth::ClientId.from_hash config }.to output(
156
+ Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
157
+ ).to_stderr
138
158
  end
139
159
  end
140
160
  end
@@ -27,82 +27,134 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'apply_auth_examples'
35
- require 'faraday'
36
- require 'googleauth/compute_engine'
37
- require 'spec_helper'
34
+ require "apply_auth_examples"
35
+ require "faraday"
36
+ require "googleauth/compute_engine"
37
+ require "spec_helper"
38
38
 
39
39
  describe Google::Auth::GCECredentials do
40
- MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'
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
- before(:example) do
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
- access_token = opts[:access_token] || ''
49
- body = MultiJson.dump('access_token' => access_token,
50
- 'token_type' => 'Bearer',
51
- 'expires_in' => 3600)
52
- stub_request(:get, MD_URI)
53
- .with(headers: { 'Metadata-Flavor' => 'Google' })
54
- .to_return(body: body,
55
- status: 200,
56
- headers: { 'Content-Type' => 'application/json' })
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
+
55
+ uri = MD_ACCESS_URI
56
+ uri += "?scopes=#{Array(opts[:scope]).join ','}" if opts[:scope]
57
+
58
+ stub_request(:get, uri)
59
+ .with(headers: { "Metadata-Flavor" => "Google" })
60
+ .to_return(body: body,
61
+ status: 200,
62
+ headers: { "Content-Type" => "application/json" })
63
+ elsif opts[:id_token]
64
+ stub_request(:get, MD_ID_URI)
65
+ .with(headers: { "Metadata-Flavor" => "Google" })
66
+ .to_return(body: opts[:id_token],
67
+ status: 200,
68
+ headers: { "Content-Type" => "text/html" })
69
+ end
57
70
  end
58
71
 
59
- it_behaves_like 'apply/apply! are OK'
72
+ it_behaves_like "apply/apply! are OK"
60
73
 
61
- context 'metadata is unavailable' do
62
- describe '#fetch_access_token' do
63
- it 'should fail if the metadata request returns a 404' do
64
- stub = stub_request(:get, MD_URI)
65
- .to_return(status: 404,
66
- headers: { 'Metadata-Flavor' => 'Google' })
67
- blk = proc { @client.fetch_access_token! }
68
- expect(&blk).to raise_error Signet::AuthorizationError
74
+ context "metadata is unavailable" do
75
+ describe "#fetch_access_token" do
76
+ it "should pass scopes when requesting an access token" do
77
+ scopes = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/bigtable.data"]
78
+ stub = make_auth_stubs access_token: "1/abcdef1234567890", scope: scopes
79
+ @client = GCECredentials.new(scope: scopes)
80
+ @client.fetch_access_token!
69
81
  expect(stub).to have_been_requested
70
82
  end
71
83
 
72
- it 'should fail if the metadata request returns an unexpected code' do
73
- stub = stub_request(:get, MD_URI)
74
- .to_return(status: 503,
75
- headers: { 'Metadata-Flavor' => 'Google' })
76
- blk = proc { @client.fetch_access_token! }
77
- expect(&blk).to raise_error Signet::AuthorizationError
84
+ it "should fail if the metadata request returns a 404" do
85
+ stub = stub_request(:get, MD_ACCESS_URI)
86
+ .to_return(status: 404,
87
+ headers: { "Metadata-Flavor" => "Google" })
88
+ expect { @client.fetch_access_token! }
89
+ .to raise_error Signet::AuthorizationError
78
90
  expect(stub).to have_been_requested
79
91
  end
92
+
93
+ it "should fail if the metadata request returns an unexpected code" do
94
+ stub = stub_request(:get, MD_ACCESS_URI)
95
+ .to_return(status: 503,
96
+ headers: { "Metadata-Flavor" => "Google" })
97
+ expect { @client.fetch_access_token! }
98
+ .to raise_error Signet::AuthorizationError
99
+ expect(stub).to have_been_requested
100
+ end
101
+
102
+ it "should fail with Signet::AuthorizationError if request times out" do
103
+ allow_any_instance_of(Faraday::Connection).to receive(:get)
104
+ .and_raise(Faraday::TimeoutError)
105
+ expect { @client.fetch_access_token! }
106
+ .to raise_error Signet::AuthorizationError
107
+ end
108
+
109
+ it "should fail with Signet::AuthorizationError if request fails" do
110
+ allow_any_instance_of(Faraday::Connection).to receive(:get)
111
+ .and_raise(Faraday::ConnectionFailed, nil)
112
+ expect { @client.fetch_access_token! }
113
+ .to raise_error Signet::AuthorizationError
114
+ end
80
115
  end
81
116
  end
82
117
 
83
- describe '#on_gce?' do
84
- it 'should be true when Metadata-Flavor is Google' do
85
- stub = stub_request(:get, 'http://169.254.169.254')
86
- .to_return(status: 200,
87
- headers: { 'Metadata-Flavor' => 'Google' })
118
+ describe "#on_gce?" do
119
+ it "should be true when Metadata-Flavor is Google" do
120
+ stub = stub_request(:get, "http://169.254.169.254")
121
+ .with(headers: { "Metadata-Flavor" => "Google" })
122
+ .to_return(status: 200,
123
+ headers: { "Metadata-Flavor" => "Google" })
88
124
  expect(GCECredentials.on_gce?({}, true)).to eq(true)
89
125
  expect(stub).to have_been_requested
90
126
  end
91
127
 
92
- it 'should be false when Metadata-Flavor is not Google' do
93
- stub = stub_request(:get, 'http://169.254.169.254')
94
- .to_return(status: 200,
95
- headers: { 'Metadata-Flavor' => 'NotGoogle' })
128
+ it "should be false when Metadata-Flavor is not Google" do
129
+ stub = stub_request(:get, "http://169.254.169.254")
130
+ .with(headers: { "Metadata-Flavor" => "Google" })
131
+ .to_return(status: 200,
132
+ headers: { "Metadata-Flavor" => "NotGoogle" })
96
133
  expect(GCECredentials.on_gce?({}, true)).to eq(false)
97
134
  expect(stub).to have_been_requested
98
135
  end
99
136
 
100
- it 'should be false if the response is not 200' do
101
- stub = stub_request(:get, 'http://169.254.169.254')
102
- .to_return(status: 404,
103
- headers: { 'Metadata-Flavor' => 'NotGoogle' })
137
+ it "should be false if the response is not 200" do
138
+ stub = stub_request(:get, "http://169.254.169.254")
139
+ .with(headers: { "Metadata-Flavor" => "Google" })
140
+ .to_return(status: 404,
141
+ headers: { "Metadata-Flavor" => "NotGoogle" })
104
142
  expect(GCECredentials.on_gce?({}, true)).to eq(false)
105
143
  expect(stub).to have_been_requested
106
144
  end
145
+
146
+ it "should honor GCE_METADATA_HOST environment variable" do
147
+ ENV["GCE_METADATA_HOST"] = "mymetadata.example.com"
148
+ begin
149
+ stub = stub_request(:get, "http://mymetadata.example.com")
150
+ .with(headers: { "Metadata-Flavor" => "Google" })
151
+ .to_return(status: 200,
152
+ headers: { "Metadata-Flavor" => "Google" })
153
+ expect(GCECredentials.on_gce?({}, true)).to eq(true)
154
+ expect(stub).to have_been_requested
155
+ ensure
156
+ ENV.delete "GCE_METADATA_HOST"
157
+ end
158
+ end
107
159
  end
108
160
  end
@@ -0,0 +1,478 @@
1
+ # Copyright 2017, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require "googleauth"
31
+
32
+
33
+ # This test is testing the private class Google::Auth::Credentials. We want to
34
+ # make sure that the passed in scope propogates to the Signet object. This means
35
+ # testing the private API, which is generally frowned on.
36
+ describe Google::Auth::Credentials, :private do
37
+ let :default_keyfile_hash do
38
+ {
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"
46
+ }
47
+ end
48
+
49
+ it "uses a default scope" do
50
+ mocked_signet = double "Signet::OAuth2::Client"
51
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
52
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
53
+ allow(mocked_signet).to receive(:client_id)
54
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
55
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
56
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
57
+ expect(options[:scope]).to eq([])
58
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
59
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
60
+
61
+ mocked_signet
62
+ end
63
+
64
+ Google::Auth::Credentials.new default_keyfile_hash
65
+ end
66
+
67
+ it "uses a custom scope" do
68
+ mocked_signet = double "Signet::OAuth2::Client"
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|
73
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
74
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
75
+ expect(options[:scope]).to eq(["http://example.com/scope"])
76
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
77
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
78
+
79
+ mocked_signet
80
+ end
81
+
82
+ Google::Auth::Credentials.new default_keyfile_hash, scope: "http://example.com/scope"
83
+ end
84
+
85
+ describe "using CONSTANTS" do
86
+ it "can be subclassed to pass in other env paths" do
87
+ test_path_env_val = "/unknown/path/to/file.txt".freeze
88
+ test_json_env_val = JSON.generate default_keyfile_hash
89
+
90
+ ENV["TEST_PATH"] = test_path_env_val
91
+ ENV["TEST_JSON_VARS"] = test_json_env_val
92
+
93
+ class TestCredentials1 < Google::Auth::Credentials
94
+ TOKEN_CREDENTIAL_URI = "https://example.com/token".freeze
95
+ AUDIENCE = "https://example.com/audience".freeze
96
+ SCOPE = "http://example.com/scope".freeze
97
+ PATH_ENV_VARS = ["TEST_PATH"].freeze
98
+ JSON_ENV_VARS = ["TEST_JSON_VARS"].freeze
99
+ end
100
+
101
+ allow(::File).to receive(:file?).with(test_path_env_val) { false }
102
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
103
+
104
+ mocked_signet = double "Signet::OAuth2::Client"
105
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
106
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
107
+ allow(mocked_signet).to receive(:client_id)
108
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
109
+ expect(options[:token_credential_uri]).to eq("https://example.com/token")
110
+ expect(options[:audience]).to eq("https://example.com/audience")
111
+ expect(options[:scope]).to eq(["http://example.com/scope"])
112
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
113
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
114
+
115
+ mocked_signet
116
+ end
117
+
118
+ creds = TestCredentials1.default
119
+ expect(creds).to be_a_kind_of(TestCredentials1)
120
+ expect(creds.client).to eq(mocked_signet)
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"])
123
+ end
124
+
125
+ it "subclasses can use PATH_ENV_VARS to get keyfile path" do
126
+ class TestCredentials2 < Google::Auth::Credentials
127
+ SCOPE = "http://example.com/scope".freeze
128
+ PATH_ENV_VARS = %w[PATH_ENV_DUMMY PATH_ENV_TEST].freeze
129
+ JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
130
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
131
+ end
132
+
133
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
134
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
135
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
136
+ allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
137
+ allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
138
+ allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
139
+
140
+ mocked_signet = double "Signet::OAuth2::Client"
141
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
142
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
143
+ allow(mocked_signet).to receive(:client_id)
144
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
145
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
146
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
147
+ expect(options[:scope]).to eq(["http://example.com/scope"])
148
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
149
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
150
+
151
+ mocked_signet
152
+ end
153
+
154
+ creds = TestCredentials2.default
155
+ expect(creds).to be_a_kind_of(TestCredentials2)
156
+ expect(creds.client).to eq(mocked_signet)
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"])
159
+ end
160
+
161
+ it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
162
+ test_json_env_val = JSON.generate default_keyfile_hash
163
+
164
+ class TestCredentials3 < Google::Auth::Credentials
165
+ SCOPE = "http://example.com/scope".freeze
166
+ PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
167
+ JSON_ENV_VARS = %w[JSON_ENV_DUMMY JSON_ENV_TEST].freeze
168
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
169
+ end
170
+
171
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
172
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
173
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
174
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
175
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
176
+ allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
177
+
178
+ mocked_signet = double "Signet::OAuth2::Client"
179
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
180
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
181
+ allow(mocked_signet).to receive(:client_id)
182
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
183
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
184
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
185
+ expect(options[:scope]).to eq(["http://example.com/scope"])
186
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
187
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
188
+
189
+ mocked_signet
190
+ end
191
+
192
+ creds = TestCredentials3.default
193
+ expect(creds).to be_a_kind_of(TestCredentials3)
194
+ expect(creds.client).to eq(mocked_signet)
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"])
197
+ end
198
+
199
+ it "subclasses can use DEFAULT_PATHS to get keyfile path" do
200
+ class TestCredentials4 < Google::Auth::Credentials
201
+ SCOPE = "http://example.com/scope".freeze
202
+ PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
203
+ JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
204
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
205
+ end
206
+
207
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
208
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
209
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
210
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
211
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
212
+ allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
213
+
214
+ mocked_signet = double "Signet::OAuth2::Client"
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|
219
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
220
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
221
+ expect(options[:scope]).to eq(["http://example.com/scope"])
222
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
223
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
224
+
225
+ mocked_signet
226
+ end
227
+
228
+ creds = TestCredentials4.default
229
+ expect(creds).to be_a_kind_of(TestCredentials4)
230
+ expect(creds.client).to eq(mocked_signet)
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"])
233
+ end
234
+
235
+ it "subclasses that find no matches default to Google::Auth.get_application_default" do
236
+ class TestCredentials5 < Google::Auth::Credentials
237
+ SCOPE = "http://example.com/scope".freeze
238
+ PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
239
+ JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
240
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
241
+ end
242
+
243
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
244
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
245
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
246
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
247
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
248
+
249
+ mocked_signet = double "Signet::OAuth2::Client"
250
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
251
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
252
+ allow(mocked_signet).to receive(:client_id)
253
+ allow(Google::Auth).to receive(:get_application_default) do |scope|
254
+ expect(scope).to eq([TestCredentials5::SCOPE])
255
+
256
+ # This should really be a Signet::OAuth2::Client object,
257
+ # but mocking is making that difficult, so return a valid hash instead.
258
+ default_keyfile_hash
259
+ 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
+
270
+ creds = TestCredentials5.default
271
+ expect(creds).to be_a_kind_of(TestCredentials5)
272
+ expect(creds.client).to eq(mocked_signet)
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"])
275
+ end
276
+ end
277
+
278
+ describe "using class methods" do
279
+ it "can be subclassed to pass in other env paths" do
280
+ test_path_env_val = "/unknown/path/to/file.txt".freeze
281
+ test_json_env_val = JSON.generate default_keyfile_hash
282
+
283
+ ENV["TEST_PATH"] = test_path_env_val
284
+ ENV["TEST_JSON_VARS"] = test_json_env_val
285
+
286
+ class TestCredentials11 < Google::Auth::Credentials
287
+ self.token_credential_uri = "https://example.com/token"
288
+ self.audience = "https://example.com/audience"
289
+ self.scope = "http://example.com/scope"
290
+ self.env_vars = ["TEST_PATH", "TEST_JSON_VARS"]
291
+ end
292
+
293
+ allow(::File).to receive(:file?).with(test_path_env_val) { false }
294
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
295
+
296
+ mocked_signet = double "Signet::OAuth2::Client"
297
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
298
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
299
+ allow(mocked_signet).to receive(:client_id)
300
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
301
+ expect(options[:token_credential_uri]).to eq("https://example.com/token")
302
+ expect(options[:audience]).to eq("https://example.com/audience")
303
+ expect(options[:scope]).to eq(["http://example.com/scope"])
304
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
305
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
306
+
307
+ mocked_signet
308
+ end
309
+
310
+ creds = TestCredentials11.default
311
+ expect(creds).to be_a_kind_of(TestCredentials11)
312
+ expect(creds.client).to eq(mocked_signet)
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"])
315
+ end
316
+
317
+ it "subclasses can use PATH_ENV_VARS to get keyfile path" do
318
+ class TestCredentials12 < Google::Auth::Credentials
319
+ self.scope = "http://example.com/scope"
320
+ self.env_vars = %w[PATH_ENV_DUMMY PATH_ENV_TEST JSON_ENV_DUMMY]
321
+ self.paths = ["~/default/path/to/file.txt"]
322
+ end
323
+
324
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
325
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
326
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
327
+ allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
328
+ allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
329
+ allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
330
+
331
+ mocked_signet = double "Signet::OAuth2::Client"
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|
336
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
337
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
338
+ expect(options[:scope]).to eq(["http://example.com/scope"])
339
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
340
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
341
+
342
+ mocked_signet
343
+ end
344
+
345
+ creds = TestCredentials12.default
346
+ expect(creds).to be_a_kind_of(TestCredentials12)
347
+ expect(creds.client).to eq(mocked_signet)
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"])
350
+ end
351
+
352
+ it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
353
+ test_json_env_val = JSON.generate default_keyfile_hash
354
+
355
+ class TestCredentials13 < Google::Auth::Credentials
356
+ self.scope = "http://example.com/scope"
357
+ self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY JSON_ENV_TEST]
358
+ self.paths = ["~/default/path/to/file.txt"]
359
+ end
360
+
361
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
362
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
363
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
364
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
365
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
366
+ allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
367
+
368
+ mocked_signet = double "Signet::OAuth2::Client"
369
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
370
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
371
+ allow(mocked_signet).to receive(:client_id)
372
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
373
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
374
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
375
+ expect(options[:scope]).to eq(["http://example.com/scope"])
376
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
377
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
378
+
379
+ mocked_signet
380
+ end
381
+
382
+ creds = TestCredentials13.default
383
+ expect(creds).to be_a_kind_of(TestCredentials13)
384
+ expect(creds.client).to eq(mocked_signet)
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"])
387
+ end
388
+
389
+ it "subclasses can use DEFAULT_PATHS to get keyfile path" do
390
+ class TestCredentials14 < Google::Auth::Credentials
391
+ self.scope = "http://example.com/scope"
392
+ self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
393
+ self.paths = ["~/default/path/to/file.txt"]
394
+ end
395
+
396
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
397
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
398
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
399
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
400
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
401
+ allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
402
+
403
+ mocked_signet = double "Signet::OAuth2::Client"
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|
408
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
409
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
410
+ expect(options[:scope]).to eq(["http://example.com/scope"])
411
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
412
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
413
+
414
+ mocked_signet
415
+ end
416
+
417
+ creds = TestCredentials14.default
418
+ expect(creds).to be_a_kind_of(TestCredentials14)
419
+ expect(creds.client).to eq(mocked_signet)
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"])
422
+ end
423
+
424
+ it "subclasses that find no matches default to Google::Auth.get_application_default" do
425
+ class TestCredentials15 < Google::Auth::Credentials
426
+ self.scope = "http://example.com/scope"
427
+ self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
428
+ self.paths = ["~/default/path/to/file.txt"]
429
+ end
430
+
431
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
432
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
433
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
434
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
435
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
436
+
437
+ mocked_signet = double "Signet::OAuth2::Client"
438
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
439
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
440
+ allow(mocked_signet).to receive(:client_id)
441
+ allow(Google::Auth).to receive(:get_application_default) do |scope|
442
+ expect(scope).to eq(TestCredentials15.scope)
443
+
444
+ # This should really be a Signet::OAuth2::Client object,
445
+ # but mocking is making that difficult, so return a valid hash instead.
446
+ default_keyfile_hash
447
+ end
448
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
449
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
450
+ 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
+
455
+ mocked_signet
456
+ end
457
+
458
+ creds = TestCredentials15.default
459
+ expect(creds).to be_a_kind_of(TestCredentials15)
460
+ expect(creds.client).to eq(mocked_signet)
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"])
463
+ end
464
+ end
465
+
466
+ it "warns when cloud sdk credentials are used" do
467
+ mocked_signet = double "Signet::OAuth2::Client"
468
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
469
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
470
+ allow(Signet::OAuth2::Client).to receive(:new) do |_options|
471
+ mocked_signet
472
+ end
473
+ allow(mocked_signet).to receive(:client_id).and_return(Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID)
474
+ expect { Google::Auth::Credentials.new default_keyfile_hash }.to output(
475
+ Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
476
+ ).to_stderr
477
+ end
478
+ end