googleauth 0.5.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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