googleauth 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,140 @@
1
+ # Copyright 2015, 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
+ spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
+ $LOAD_PATH.unshift(spec_dir)
32
+ $LOAD_PATH.uniq!
33
+
34
+ require 'spec_helper'
35
+ require 'fakefs/safe'
36
+ require 'googleauth'
37
+
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'
42
+ end
43
+
44
+ it 'should include a valid secret' do
45
+ expect(client_id.secret).to eql 'notasecret'
46
+ end
47
+ end
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) }
52
+
53
+ it_behaves_like 'it has a valid config'
54
+ end
55
+
56
+ context 'loaded from file' do
57
+ file_path = '/client_secrets.json'
58
+
59
+ let(:client_id) do
60
+ FakeFS do
61
+ content = MultiJson.dump(config)
62
+ File.write(file_path, content)
63
+ Google::Auth::ClientId.from_file(file_path)
64
+ end
65
+ end
66
+
67
+ it_behaves_like 'it has a valid config'
68
+ end
69
+ end
70
+
71
+ describe 'with web config' do
72
+ let(:config) do
73
+ {
74
+ 'web' => {
75
+ 'client_id' => 'abc@example.com',
76
+ 'client_secret' => 'notasecret'
77
+ }
78
+ }
79
+ end
80
+ it_behaves_like 'it can successfully load client_id'
81
+ end
82
+
83
+ describe 'with installed app config' do
84
+ let(:config) do
85
+ {
86
+ 'installed' => {
87
+ 'client_id' => 'abc@example.com',
88
+ 'client_secret' => 'notasecret'
89
+ }
90
+ }
91
+ end
92
+ it_behaves_like 'it can successfully load client_id'
93
+ end
94
+
95
+ context 'with missing top level property' do
96
+ let(:config) do
97
+ {
98
+ 'notvalid' => {
99
+ 'client_id' => 'abc@example.com',
100
+ 'client_secret' => 'notasecret'
101
+ }
102
+ }
103
+ end
104
+
105
+ it 'should raise error' do
106
+ expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
107
+ /Expected top level property/)
108
+ end
109
+ end
110
+
111
+ context 'with missing client id' do
112
+ let(:config) do
113
+ {
114
+ 'web' => {
115
+ 'client_secret' => 'notasecret'
116
+ }
117
+ }
118
+ end
119
+
120
+ it 'should raise error' do
121
+ expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
122
+ /Client id can not be nil/)
123
+ end
124
+ end
125
+
126
+ context 'with missing client secret' do
127
+ let(:config) do
128
+ {
129
+ 'web' => {
130
+ 'client_id' => 'abc@example.com'
131
+ }
132
+ }
133
+ end
134
+
135
+ it 'should raise error' do
136
+ expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
137
+ /Client secret can not be nil/)
138
+ end
139
+ end
140
+ end
@@ -37,7 +37,7 @@ require 'googleauth/compute_engine'
37
37
  require 'spec_helper'
38
38
 
39
39
  describe Google::Auth::GCECredentials do
40
- MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
40
+ MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'
41
41
  GCECredentials = Google::Auth::GCECredentials
42
42
 
43
43
  before(:example) do
@@ -46,16 +46,14 @@ describe Google::Auth::GCECredentials do
46
46
 
47
47
  def make_auth_stubs(opts = {})
48
48
  access_token = opts[:access_token] || ''
49
- Faraday::Adapter::Test::Stubs.new do |stub|
50
- stub.get(MD_URI) do |env|
51
- headers = env[:request_headers]
52
- expect(headers['Metadata-Flavor']).to eq('Google')
53
- build_json_response(
54
- 'access_token' => access_token,
55
- 'token_type' => 'Bearer',
56
- 'expires_in' => 3600)
57
- end
58
- end
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' })
59
57
  end
60
58
 
61
59
  it_behaves_like 'apply/apply! are OK'
@@ -63,83 +61,48 @@ describe Google::Auth::GCECredentials do
63
61
  context 'metadata is unavailable' do
64
62
  describe '#fetch_access_token' do
65
63
  it 'should fail if the metadata request returns a 404' do
66
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
67
- stub.get(MD_URI) do |_env|
68
- [404,
69
- { 'Metadata-Flavor' => 'Google' },
70
- '']
71
- end
72
- end
73
- c = Faraday.new do |b|
74
- b.adapter(:test, stubs)
75
- end
76
- blk = proc { @client.fetch_access_token!(connection: c) }
64
+ stub = stub_request(:get, MD_URI)
65
+ .to_return(status: 404,
66
+ headers: { 'Metadata-Flavor' => 'Google' })
67
+ blk = proc { @client.fetch_access_token! }
77
68
  expect(&blk).to raise_error Signet::AuthorizationError
78
- stubs.verify_stubbed_calls
69
+ expect(stub).to have_been_requested
79
70
  end
80
71
 
81
72
  it 'should fail if the metadata request returns an unexpected code' do
82
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
83
- stub.get(MD_URI) do |_env|
84
- [503,
85
- { 'Metadata-Flavor' => 'Google' },
86
- '']
87
- end
88
- end
89
- c = Faraday.new do |b|
90
- b.adapter(:test, stubs)
91
- end
92
- blk = proc { @client.fetch_access_token!(connection: c) }
73
+ stub = stub_request(:get, MD_URI)
74
+ .to_return(status: 503,
75
+ headers: { 'Metadata-Flavor' => 'Google' })
76
+ blk = proc { @client.fetch_access_token! }
93
77
  expect(&blk).to raise_error Signet::AuthorizationError
94
- stubs.verify_stubbed_calls
78
+ expect(stub).to have_been_requested
95
79
  end
96
80
  end
97
81
  end
98
82
 
99
83
  describe '#on_gce?' do
100
84
  it 'should be true when Metadata-Flavor is Google' do
101
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
102
- stub.get('/') do |_env|
103
- [200,
104
- { 'Metadata-Flavor' => 'Google' },
105
- '']
106
- end
107
- end
108
- c = Faraday.new do |b|
109
- b.adapter(:test, stubs)
110
- end
111
- expect(GCECredentials.on_gce?(connection: c)).to eq(true)
112
- stubs.verify_stubbed_calls
85
+ stub = stub_request(:get, 'http://169.254.169.254')
86
+ .to_return(status: 200,
87
+ headers: { 'Metadata-Flavor' => 'Google' })
88
+ expect(GCECredentials.on_gce?({}, true)).to eq(true)
89
+ expect(stub).to have_been_requested
113
90
  end
114
91
 
115
92
  it 'should be false when Metadata-Flavor is not Google' do
116
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
117
- stub.get('/') do |_env|
118
- [200,
119
- { 'Metadata-Flavor' => 'NotGoogle' },
120
- '']
121
- end
122
- end
123
- c = Faraday.new do |b|
124
- b.adapter(:test, stubs)
125
- end
126
- expect(GCECredentials.on_gce?(connection: c)).to eq(false)
127
- stubs.verify_stubbed_calls
93
+ stub = stub_request(:get, 'http://169.254.169.254')
94
+ .to_return(status: 200,
95
+ headers: { 'Metadata-Flavor' => 'NotGoogle' })
96
+ expect(GCECredentials.on_gce?({}, true)).to eq(false)
97
+ expect(stub).to have_been_requested
128
98
  end
129
99
 
130
100
  it 'should be false if the response is not 200' do
131
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
132
- stub.get('/') do |_env|
133
- [404,
134
- { 'Metadata-Flavor' => 'Google' },
135
- '']
136
- end
137
- end
138
- c = Faraday.new do |b|
139
- b.adapter(:test, stubs)
140
- end
141
- expect(GCECredentials.on_gce?(connection: c)).to eq(false)
142
- stubs.verify_stubbed_calls
101
+ stub = stub_request(:get, 'http://169.254.169.254')
102
+ .to_return(status: 404,
103
+ headers: { 'Metadata-Flavor' => 'NotGoogle' })
104
+ expect(GCECredentials.on_gce?({}, true)).to eq(false)
105
+ expect(stub).to have_been_requested
143
106
  end
144
107
  end
145
108
  end
@@ -37,6 +37,9 @@ require 'googleauth'
37
37
  require 'spec_helper'
38
38
 
39
39
  describe '#get_application_default' do
40
+ # Pass unique options each time to bypass memoization
41
+ let(:options) { |example| { dememoize: example } }
42
+
40
43
  before(:example) do
41
44
  @key = OpenSSL::PKey::RSA.new(2048)
42
45
  @var_name = ENV_VAR
@@ -59,31 +62,24 @@ describe '#get_application_default' do
59
62
  Dir.mktmpdir do |dir|
60
63
  key_path = File.join(dir, 'does-not-exist')
61
64
  ENV[@var_name] = key_path
62
- expect { Google::Auth.get_application_default(@scope) }
65
+ expect { Google::Auth.get_application_default(@scope, options) }
63
66
  .to raise_error RuntimeError
64
67
  end
65
68
  end
66
69
 
67
70
  it 'fails without default file or env if not on compute engine' do
68
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
69
- stub.get('/') do |_env|
70
- [404,
71
- { 'Metadata-Flavor' => 'Google' },
72
- '']
73
- end
74
- end # GCE not detected
71
+ stub = stub_request(:get, 'http://169.254.169.254')
72
+ .to_return(status: 404,
73
+ headers: { 'Metadata-Flavor' => 'NotGoogle' })
75
74
  Dir.mktmpdir do |dir|
76
75
  ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
77
76
  ENV['HOME'] = dir # no config present in this tmp dir
78
- c = Faraday.new do |b|
79
- b.adapter(:test, stubs)
80
- end
81
77
  blk = proc do
82
- Google::Auth.get_application_default(@scope, connection: c)
78
+ Google::Auth.get_application_default(@scope, options)
83
79
  end
84
80
  expect(&blk).to raise_error RuntimeError
85
81
  end
86
- stubs.verify_stubbed_calls
82
+ expect(stub).to have_been_requested
87
83
  end
88
84
  end
89
85
 
@@ -94,7 +90,8 @@ describe '#get_application_default' do
94
90
  FileUtils.mkdir_p(File.dirname(key_path))
95
91
  File.write(key_path, cred_json_text)
96
92
  ENV[@var_name] = key_path
97
- expect(Google::Auth.get_application_default(@scope)).to_not be_nil
93
+ expect(Google::Auth.get_application_default(@scope, options))
94
+ .to_not be_nil
98
95
  end
99
96
  end
100
97
 
@@ -105,7 +102,8 @@ describe '#get_application_default' do
105
102
  FileUtils.mkdir_p(File.dirname(key_path))
106
103
  File.write(key_path, cred_json_text)
107
104
  ENV['HOME'] = dir
108
- expect(Google::Auth.get_application_default(@scope)).to_not be_nil
105
+ expect(Google::Auth.get_application_default(@scope, options))
106
+ .to_not be_nil
109
107
  end
110
108
  end
111
109
 
@@ -116,30 +114,21 @@ describe '#get_application_default' do
116
114
  FileUtils.mkdir_p(File.dirname(key_path))
117
115
  File.write(key_path, cred_json_text)
118
116
  ENV['HOME'] = dir
119
- expect(Google::Auth.get_application_default).to_not be_nil
117
+ expect(Google::Auth.get_application_default(nil, options)).to_not be_nil
120
118
  end
121
119
  end
122
120
 
123
121
  it 'succeeds without default file or env if on compute engine' do
124
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
125
- stub.get('/') do |_env|
126
- [200,
127
- { 'Metadata-Flavor' => 'Google' },
128
- '']
129
- end
130
- end # GCE detected
122
+ stub = stub_request(:get, 'http://169.254.169.254')
123
+ .to_return(status: 200,
124
+ headers: { 'Metadata-Flavor' => 'Google' })
131
125
  Dir.mktmpdir do |dir|
132
126
  ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
133
127
  ENV['HOME'] = dir # no config present in this tmp dir
134
- c = Faraday.new do |b|
135
- b.adapter(:test, stubs)
136
- end
137
- creds = Google::Auth.get_application_default(
138
- @scope,
139
- connection: c)
128
+ creds = Google::Auth.get_application_default(@scope, options)
140
129
  expect(creds).to_not be_nil
141
130
  end
142
- stubs.verify_stubbed_calls
131
+ expect(stub).to have_been_requested
143
132
  end
144
133
 
145
134
  it 'succeeds with system default file' do
@@ -148,7 +137,8 @@ describe '#get_application_default' do
148
137
  key_path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME)
149
138
  FileUtils.mkdir_p(File.dirname(key_path))
150
139
  File.write(key_path, cred_json_text)
151
- expect(Google::Auth.get_application_default(@scope)).to_not be_nil
140
+ expect(Google::Auth.get_application_default(@scope, options))
141
+ .to_not be_nil
152
142
  File.delete(key_path)
153
143
  end
154
144
  end
@@ -161,7 +151,8 @@ describe '#get_application_default' do
161
151
  ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
162
152
  ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
163
153
  ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
164
- expect(Google::Auth.get_application_default(@scope)).to_not be_nil
154
+ expect(Google::Auth.get_application_default(@scope, options))
155
+ .to_not be_nil
165
156
  end
166
157
  end
167
158
 
@@ -225,7 +216,7 @@ describe '#get_application_default' do
225
216
  File.write(key_path, cred_json_text)
226
217
  ENV[@var_name] = key_path
227
218
  blk = proc do
228
- Google::Auth.get_application_default(@scope)
219
+ Google::Auth.get_application_default(@scope, options)
229
220
  end
230
221
  expect(&blk).to raise_error RuntimeError
231
222
  end
@@ -239,7 +230,7 @@ describe '#get_application_default' do
239
230
  File.write(key_path, cred_json_text)
240
231
  ENV['HOME'] = dir
241
232
  blk = proc do
242
- Google::Auth.get_application_default(@scope)
233
+ Google::Auth.get_application_default(@scope, options)
243
234
  end
244
235
  expect(&blk).to raise_error RuntimeError
245
236
  end
@@ -249,7 +240,7 @@ describe '#get_application_default' do
249
240
  ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
250
241
  ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
251
242
  blk = proc do
252
- Google::Auth.get_application_default(@scope)
243
+ Google::Auth.get_application_default(@scope, options)
253
244
  end
254
245
  expect(&blk).to raise_error RuntimeError
255
246
  end
@@ -0,0 +1,75 @@
1
+ # Copyright 2015, 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
+ spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
+ $LOAD_PATH.unshift(spec_dir)
32
+ $LOAD_PATH.uniq!
33
+
34
+ require 'googleauth/scope_util'
35
+
36
+ describe Google::Auth::ScopeUtil do
37
+ shared_examples 'normalizes scopes' do
38
+ let(:normalized) { Google::Auth::ScopeUtil.normalize(source) }
39
+
40
+ it 'normalizes the email scope' do
41
+ expect(normalized).to include(
42
+ 'https://www.googleapis.com/auth/userinfo.email')
43
+ expect(normalized).to_not include 'email'
44
+ end
45
+
46
+ it 'normalizes the profile scope' do
47
+ expect(normalized).to include(
48
+ 'https://www.googleapis.com/auth/userinfo.profile')
49
+ expect(normalized).to_not include 'profile'
50
+ end
51
+
52
+ it 'normalizes the openid scope' do
53
+ expect(normalized).to include 'https://www.googleapis.com/auth/plus.me'
54
+ expect(normalized).to_not include 'openid'
55
+ end
56
+
57
+ it 'leaves other other scopes as-is' do
58
+ expect(normalized).to include 'https://www.googleapis.com/auth/drive'
59
+ end
60
+ end
61
+
62
+ context 'with scope as string' do
63
+ let(:source) do
64
+ 'email profile openid https://www.googleapis.com/auth/drive'
65
+ end
66
+ it_behaves_like 'normalizes scopes'
67
+ end
68
+
69
+ context 'with scope as Array' do
70
+ let(:source) do
71
+ %w(email profile openid https://www.googleapis.com/auth/drive)
72
+ end
73
+ it_behaves_like 'normalizes scopes'
74
+ end
75
+ end