googleauth 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,7 +49,8 @@ module Google
49
49
  PROJECT_ID_VAR = "GOOGLE_PROJECT_ID".freeze
50
50
  GCLOUD_POSIX_COMMAND = "gcloud".freeze
51
51
  GCLOUD_WINDOWS_COMMAND = "gcloud.cmd".freeze
52
- GCLOUD_CONFIG_COMMAND = "config config-helper --format json".freeze
52
+ GCLOUD_CONFIG_COMMAND =
53
+ "config config-helper --format json --verbosity none".freeze
53
54
 
54
55
  CREDENTIALS_FILE_NAME = "application_default_credentials.json".freeze
55
56
  NOT_FOUND_ERROR =
@@ -162,12 +163,11 @@ module Google
162
163
  raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
163
164
  end
164
165
 
165
- module_function
166
-
167
166
  # Issues warning if cloud sdk client id is used
168
167
  def warn_if_cloud_sdk_credentials client_id
169
168
  warn CLOUD_SDK_CREDENTIALS_WARNING if client_id == CLOUD_SDK_CLIENT_ID
170
169
  end
170
+ module_function :warn_if_cloud_sdk_credentials
171
171
 
172
172
  # Finds project_id from gcloud CLI configuration
173
173
  def load_gcloud_project_id
@@ -179,6 +179,7 @@ module Google
179
179
  rescue StandardError
180
180
  nil
181
181
  end
182
+ module_function :load_gcloud_project_id
182
183
 
183
184
  private
184
185
 
@@ -192,13 +193,15 @@ module Google
192
193
  end
193
194
 
194
195
  def service_account_env_vars?
195
- ([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty? &&
196
- !ENV.to_h.fetch_values(PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR).join(" ").empty?
196
+ [PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR].all? do |key|
197
+ ENV[key] && !ENV[key].empty?
198
+ end
197
199
  end
198
200
 
199
201
  def authorized_user_env_vars?
200
- ([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] - ENV.keys).empty? &&
201
- !ENV.to_h.fetch_values(CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR).join(" ").empty?
202
+ [CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR].all? do |key|
203
+ ENV[key] && !ENV[key].empty?
204
+ end
202
205
  end
203
206
  end
204
207
  end
@@ -76,7 +76,9 @@ module Signet
76
76
  connection = build_default_connection
77
77
  options = options.merge connection: connection if connection
78
78
  end
79
- info = orig_fetch_access_token! options
79
+ info = retry_with_error do
80
+ orig_fetch_access_token! options
81
+ end
80
82
  notify_refresh_listeners
81
83
  info
82
84
  end
@@ -89,9 +91,8 @@ module Signet
89
91
  end
90
92
 
91
93
  def build_default_connection
92
- if !defined?(@connection_info)
93
- nil
94
- elsif @connection_info.respond_to? :call
94
+ return nil unless defined?(@connection_info)
95
+ if @connection_info.respond_to? :call
95
96
  @connection_info.call
96
97
  else
97
98
  @connection_info
@@ -129,8 +129,8 @@ module Google
129
129
  data = MultiJson.load saved_token
130
130
 
131
131
  if data.fetch("client_id", @client_id.id) != @client_id.id
132
- raise format(MISMATCHED_CLIENT_ID_ERROR,
133
- data["client_id"], @client_id.id)
132
+ raise sprintf(MISMATCHED_CLIENT_ID_ERROR,
133
+ data["client_id"], @client_id.id)
134
134
  end
135
135
 
136
136
  credentials = UserRefreshCredentials.new(
@@ -79,7 +79,7 @@ module Google
79
79
  # JSON key.
80
80
  def self.read_json_key json_key_io
81
81
  json_key = MultiJson.load json_key_io.read
82
- wanted = %w[client_id client_secret refresh_token]
82
+ wanted = ["client_id", "client_secret", "refresh_token"]
83
83
  wanted.each do |key|
84
84
  raise "the json is missing the #{key} field" unless json_key.key? key
85
85
  end
@@ -31,6 +31,6 @@ module Google
31
31
  # Module Auth provides classes that provide Google-specific authorization
32
32
  # used to access Google APIs.
33
33
  module Auth
34
- VERSION = "0.8.1".freeze
34
+ VERSION = "0.9.0".freeze
35
35
  end
36
36
  end
@@ -154,6 +154,8 @@ module Google
154
154
  # @param [String, Array<String>] scope
155
155
  # Authorization scope to request. Overrides the instance scopes if
156
156
  # not nil.
157
+ # @param [Hash] state
158
+ # Optional key-values to be returned to the oauth callback.
157
159
  # @return [String]
158
160
  # Authorization url
159
161
  def get_authorization_url options = {}
@@ -162,22 +164,25 @@ module Google
162
164
  raise NIL_REQUEST_ERROR if request.nil?
163
165
  raise NIL_SESSION_ERROR if request.session.nil?
164
166
 
167
+ state = options[:state] || {}
168
+
165
169
  redirect_to = options[:redirect_to] || request.url
166
170
  request.session[XSRF_KEY] = SecureRandom.base64
167
- options[:state] = MultiJson.dump(
168
- SESSION_ID_KEY => request.session[XSRF_KEY],
169
- CURRENT_URI_KEY => redirect_to
170
- )
171
+ options[:state] = MultiJson.dump(state.merge(
172
+ SESSION_ID_KEY => request.session[XSRF_KEY],
173
+ CURRENT_URI_KEY => redirect_to
174
+ ))
171
175
  options[:base_url] = request.url
172
176
  super options
173
177
  end
174
178
 
175
- # Fetch stored credentials for the user.
179
+ # Fetch stored credentials for the user from the given request session.
176
180
  #
177
181
  # @param [String] user_id
178
182
  # Unique ID of the user for loading/storing credentials.
179
183
  # @param [Rack::Request] request
180
- # Current request
184
+ # Current request. Optional. If omitted, this will attempt to fall back
185
+ # on the base class behavior of reading from the token store.
181
186
  # @param [Array<String>, String] scope
182
187
  # If specified, only returns credentials that have all the \
183
188
  # requested scopes
@@ -186,8 +191,8 @@ module Google
186
191
  # @raise [Signet::AuthorizationError]
187
192
  # May raise an error if an authorization code is present in the session
188
193
  # and exchange of the code fails
189
- def get_credentials user_id, request, scope = nil
190
- if request.session.key? CALLBACK_STATE_KEY
194
+ def get_credentials user_id, request = nil, scope = nil
195
+ if request && request.session.key?(CALLBACK_STATE_KEY)
191
196
  # Note - in theory, no need to check required scope as this is
192
197
  # expected to be called immediately after a return from authorization
193
198
  state_json = request.session.delete CALLBACK_STATE_KEY
@@ -81,181 +81,367 @@ describe Google::Auth::Credentials, :private do
81
81
  Google::Auth::Credentials.new default_keyfile_hash, scope: "http://example.com/scope"
82
82
  end
83
83
 
84
- it "can be subclassed to pass in other env paths" do
85
- TEST_PATH_ENV_VAR = "TEST_PATH".freeze
86
- TEST_PATH_ENV_VAL = "/unknown/path/to/file.txt".freeze
87
- TEST_JSON_ENV_VAR = "TEST_JSON_VARS".freeze
88
-
89
- ENV[TEST_PATH_ENV_VAR] = TEST_PATH_ENV_VAL
90
- ENV[TEST_JSON_ENV_VAR] = JSON.generate default_keyfile_hash
91
-
92
- class TestCredentials < Google::Auth::Credentials
93
- SCOPE = "http://example.com/scope".freeze
94
- PATH_ENV_VARS = [TEST_PATH_ENV_VAR].freeze
95
- JSON_ENV_VARS = [TEST_JSON_ENV_VAR].freeze
84
+ describe "using CONSTANTS" do
85
+ it "can be subclassed to pass in other env paths" do
86
+ test_path_env_val = "/unknown/path/to/file.txt".freeze
87
+ test_json_env_val = JSON.generate default_keyfile_hash
88
+
89
+ ENV["TEST_PATH"] = test_path_env_val
90
+ ENV["TEST_JSON_VARS"] = test_json_env_val
91
+
92
+ class TestCredentials1 < Google::Auth::Credentials
93
+ TOKEN_CREDENTIAL_URI = "https://example.com/token".freeze
94
+ AUDIENCE = "https://example.com/audience".freeze
95
+ SCOPE = "http://example.com/scope".freeze
96
+ PATH_ENV_VARS = ["TEST_PATH"].freeze
97
+ JSON_ENV_VARS = ["TEST_JSON_VARS"].freeze
98
+ end
99
+
100
+ allow(::File).to receive(:file?).with(test_path_env_val) { false }
101
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
102
+
103
+ mocked_signet = double "Signet::OAuth2::Client"
104
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
105
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
106
+ allow(mocked_signet).to receive(:client_id)
107
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
108
+ expect(options[:token_credential_uri]).to eq("https://example.com/token")
109
+ expect(options[:audience]).to eq("https://example.com/audience")
110
+ expect(options[:scope]).to eq(["http://example.com/scope"])
111
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
112
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
113
+
114
+ mocked_signet
115
+ end
116
+
117
+ creds = TestCredentials1.default
118
+ expect(creds).to be_a_kind_of(TestCredentials1)
119
+ expect(creds.client).to eq(mocked_signet)
120
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
96
121
  end
97
122
 
98
- allow(::File).to receive(:file?).with(TEST_PATH_ENV_VAL) { false }
99
-
100
- mocked_signet = double "Signet::OAuth2::Client"
101
- allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
102
- allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
103
- allow(mocked_signet).to receive(:client_id)
104
- allow(Signet::OAuth2::Client).to receive(:new) do |options|
105
- expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
106
- expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
107
- expect(options[:scope]).to eq(["http://example.com/scope"])
108
- expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
109
- expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
110
-
111
- mocked_signet
123
+ it "subclasses can use PATH_ENV_VARS to get keyfile path" do
124
+ class TestCredentials2 < Google::Auth::Credentials
125
+ SCOPE = "http://example.com/scope".freeze
126
+ PATH_ENV_VARS = %w[PATH_ENV_DUMMY PATH_ENV_TEST].freeze
127
+ JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
128
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
129
+ end
130
+
131
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
132
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
133
+ allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
134
+ allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
135
+ allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
136
+
137
+ mocked_signet = double "Signet::OAuth2::Client"
138
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
139
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
140
+ allow(mocked_signet).to receive(:client_id)
141
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
142
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
143
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
144
+ expect(options[:scope]).to eq(["http://example.com/scope"])
145
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
146
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
147
+
148
+ mocked_signet
149
+ end
150
+
151
+ creds = TestCredentials2.default
152
+ expect(creds).to be_a_kind_of(TestCredentials2)
153
+ expect(creds.client).to eq(mocked_signet)
154
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
112
155
  end
113
156
 
114
- creds = TestCredentials.default
115
- expect(creds).to be_a_kind_of(TestCredentials)
116
- expect(creds.client).to eq(mocked_signet)
117
- expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
118
- end
119
-
120
- it "subclasses can use PATH_ENV_VARS to get keyfile path" do
121
- class TestCredentials < Google::Auth::Credentials
122
- SCOPE = "http://example.com/scope".freeze
123
- PATH_ENV_VARS = %w[PATH_ENV_DUMMY PATH_ENV_TEST].freeze
124
- JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
125
- DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
157
+ it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
158
+ test_json_env_val = JSON.generate default_keyfile_hash
159
+
160
+ class TestCredentials3 < Google::Auth::Credentials
161
+ SCOPE = "http://example.com/scope".freeze
162
+ PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
163
+ JSON_ENV_VARS = %w[JSON_ENV_DUMMY JSON_ENV_TEST].freeze
164
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
165
+ end
166
+
167
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
168
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
169
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
170
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
171
+ allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
172
+
173
+ mocked_signet = double "Signet::OAuth2::Client"
174
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
175
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
176
+ allow(mocked_signet).to receive(:client_id)
177
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
178
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
179
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
180
+ expect(options[:scope]).to eq(["http://example.com/scope"])
181
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
182
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
183
+
184
+ mocked_signet
185
+ end
186
+
187
+ creds = TestCredentials3.default
188
+ expect(creds).to be_a_kind_of(TestCredentials3)
189
+ expect(creds.client).to eq(mocked_signet)
190
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
126
191
  end
127
192
 
128
- allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
129
- allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
130
- allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
131
- allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
132
- allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
133
-
134
- mocked_signet = double "Signet::OAuth2::Client"
135
- allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
136
- allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
137
- allow(mocked_signet).to receive(:client_id)
138
- allow(Signet::OAuth2::Client).to receive(:new) do |options|
139
- expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
140
- expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
141
- expect(options[:scope]).to eq(["http://example.com/scope"])
142
- expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
143
- expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
144
-
145
- mocked_signet
193
+ it "subclasses can use DEFAULT_PATHS to get keyfile path" do
194
+ class TestCredentials4 < Google::Auth::Credentials
195
+ SCOPE = "http://example.com/scope".freeze
196
+ PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
197
+ JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
198
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
199
+ end
200
+
201
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
202
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
203
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
204
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
205
+ allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
206
+
207
+ mocked_signet = double "Signet::OAuth2::Client"
208
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
209
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
210
+ allow(mocked_signet).to receive(:client_id)
211
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
212
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
213
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
214
+ expect(options[:scope]).to eq(["http://example.com/scope"])
215
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
216
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
217
+
218
+ mocked_signet
219
+ end
220
+
221
+ creds = TestCredentials4.default
222
+ expect(creds).to be_a_kind_of(TestCredentials4)
223
+ expect(creds.client).to eq(mocked_signet)
224
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
146
225
  end
147
226
 
148
- creds = TestCredentials.default
149
- expect(creds).to be_a_kind_of(TestCredentials)
150
- expect(creds.client).to eq(mocked_signet)
151
- expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
152
- end
153
-
154
- it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
155
- class TestCredentials < Google::Auth::Credentials
156
- SCOPE = "http://example.com/scope".freeze
157
- PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
158
- JSON_ENV_VARS = %w[JSON_ENV_DUMMY JSON_ENV_TEST].freeze
159
- DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
227
+ it "subclasses that find no matches default to Google::Auth.get_application_default" do
228
+ class TestCredentials5 < Google::Auth::Credentials
229
+ SCOPE = "http://example.com/scope".freeze
230
+ PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
231
+ JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
232
+ DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
233
+ end
234
+
235
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
236
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
237
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
238
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
239
+
240
+ mocked_signet = double "Signet::OAuth2::Client"
241
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
242
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
243
+ allow(mocked_signet).to receive(:client_id)
244
+ allow(Google::Auth).to receive(:get_application_default) do |scope|
245
+ expect(scope).to eq([TestCredentials5::SCOPE])
246
+
247
+ # This should really be a Signet::OAuth2::Client object,
248
+ # but mocking is making that difficult, so return a valid hash instead.
249
+ default_keyfile_hash
250
+ end
251
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
252
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
253
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
254
+ expect(options[:scope]).to eq(["http://example.com/scope"])
255
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
256
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
257
+
258
+ mocked_signet
259
+ end
260
+
261
+ creds = TestCredentials5.default
262
+ expect(creds).to be_a_kind_of(TestCredentials5)
263
+ expect(creds.client).to eq(mocked_signet)
264
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
160
265
  end
161
-
162
- allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
163
- allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
164
- allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
165
- allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { JSON.generate default_keyfile_hash }
166
-
167
- mocked_signet = double "Signet::OAuth2::Client"
168
- allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
169
- allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
170
- allow(mocked_signet).to receive(:client_id)
171
- allow(Signet::OAuth2::Client).to receive(:new) do |options|
172
- expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
173
- expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
174
- expect(options[:scope]).to eq(["http://example.com/scope"])
175
- expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
176
- expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
177
-
178
- mocked_signet
179
- end
180
-
181
- creds = TestCredentials.default
182
- expect(creds).to be_a_kind_of(TestCredentials)
183
- expect(creds.client).to eq(mocked_signet)
184
- expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
185
266
  end
186
267
 
187
- it "subclasses can use DEFAULT_PATHS to get keyfile path" do
188
- class TestCredentials < Google::Auth::Credentials
189
- SCOPE = "http://example.com/scope".freeze
190
- PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
191
- JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
192
- DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
268
+ describe "using class methods" do
269
+ it "can be subclassed to pass in other env paths" do
270
+ test_path_env_val = "/unknown/path/to/file.txt".freeze
271
+ test_json_env_val = JSON.generate default_keyfile_hash
272
+
273
+ ENV["TEST_PATH"] = test_path_env_val
274
+ ENV["TEST_JSON_VARS"] = test_json_env_val
275
+
276
+ class TestCredentials11 < Google::Auth::Credentials
277
+ self.token_credential_uri = "https://example.com/token"
278
+ self.audience = "https://example.com/audience"
279
+ self.scope = "http://example.com/scope"
280
+ self.env_vars = ["TEST_PATH", "TEST_JSON_VARS"]
281
+ end
282
+
283
+ allow(::File).to receive(:file?).with(test_path_env_val) { false }
284
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
285
+
286
+ mocked_signet = double "Signet::OAuth2::Client"
287
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
288
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
289
+ allow(mocked_signet).to receive(:client_id)
290
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
291
+ expect(options[:token_credential_uri]).to eq("https://example.com/token")
292
+ expect(options[:audience]).to eq("https://example.com/audience")
293
+ expect(options[:scope]).to eq(["http://example.com/scope"])
294
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
295
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
296
+
297
+ mocked_signet
298
+ end
299
+
300
+ creds = TestCredentials11.default
301
+ expect(creds).to be_a_kind_of(TestCredentials11)
302
+ expect(creds.client).to eq(mocked_signet)
303
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
193
304
  end
194
305
 
195
- allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
196
- allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
197
- allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
198
- allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
199
- allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
200
-
201
- mocked_signet = double "Signet::OAuth2::Client"
202
- allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
203
- allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
204
- allow(mocked_signet).to receive(:client_id)
205
- allow(Signet::OAuth2::Client).to receive(:new) do |options|
206
- expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
207
- expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
208
- expect(options[:scope]).to eq(["http://example.com/scope"])
209
- expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
210
- expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
211
-
212
- mocked_signet
306
+ it "subclasses can use PATH_ENV_VARS to get keyfile path" do
307
+ class TestCredentials12 < Google::Auth::Credentials
308
+ self.scope = "http://example.com/scope"
309
+ self.env_vars = %w[PATH_ENV_DUMMY PATH_ENV_TEST JSON_ENV_DUMMY]
310
+ self.paths = ["~/default/path/to/file.txt"]
311
+ end
312
+
313
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
314
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
315
+ allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
316
+ allow(::File).to receive(:file?).with("/unknown/path/to/file.txt") { true }
317
+ allow(::File).to receive(:read).with("/unknown/path/to/file.txt") { JSON.generate default_keyfile_hash }
318
+
319
+ mocked_signet = double "Signet::OAuth2::Client"
320
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
321
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
322
+ allow(mocked_signet).to receive(:client_id)
323
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
324
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
325
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
326
+ expect(options[:scope]).to eq(["http://example.com/scope"])
327
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
328
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
329
+
330
+ mocked_signet
331
+ end
332
+
333
+ creds = TestCredentials12.default
334
+ expect(creds).to be_a_kind_of(TestCredentials12)
335
+ expect(creds.client).to eq(mocked_signet)
336
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
213
337
  end
214
338
 
215
- creds = TestCredentials.default
216
- expect(creds).to be_a_kind_of(TestCredentials)
217
- expect(creds.client).to eq(mocked_signet)
218
- expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
219
- end
220
-
221
- it "subclasses that find no matches default to Google::Auth.get_application_default" do
222
- class TestCredentials < Google::Auth::Credentials
223
- SCOPE = "http://example.com/scope".freeze
224
- PATH_ENV_VARS = ["PATH_ENV_DUMMY"].freeze
225
- JSON_ENV_VARS = ["JSON_ENV_DUMMY"].freeze
226
- DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
339
+ it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
340
+ test_json_env_val = JSON.generate default_keyfile_hash
341
+
342
+ class TestCredentials13 < Google::Auth::Credentials
343
+ self.scope = "http://example.com/scope"
344
+ self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY JSON_ENV_TEST]
345
+ self.paths = ["~/default/path/to/file.txt"]
346
+ end
347
+
348
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
349
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
350
+ allow(::File).to receive(:file?).with(test_json_env_val) { false }
351
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
352
+ allow(::ENV).to receive(:[]).with("JSON_ENV_TEST") { test_json_env_val }
353
+
354
+ mocked_signet = double "Signet::OAuth2::Client"
355
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
356
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
357
+ allow(mocked_signet).to receive(:client_id)
358
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
359
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
360
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
361
+ expect(options[:scope]).to eq(["http://example.com/scope"])
362
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
363
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
364
+
365
+ mocked_signet
366
+ end
367
+
368
+ creds = TestCredentials13.default
369
+ expect(creds).to be_a_kind_of(TestCredentials13)
370
+ expect(creds.client).to eq(mocked_signet)
371
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
227
372
  end
228
373
 
229
- allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
230
- allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
231
- allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
232
- allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
233
-
234
- mocked_signet = double "Signet::OAuth2::Client"
235
- allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
236
- allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
237
- allow(mocked_signet).to receive(:client_id)
238
- allow(Google::Auth).to receive(:get_application_default) do |scope|
239
- expect(scope).to eq(TestCredentials::SCOPE)
240
-
241
- # This should really be a Signet::OAuth2::Client object,
242
- # but mocking is making that difficult, so return a valid hash instead.
243
- default_keyfile_hash
374
+ it "subclasses can use DEFAULT_PATHS to get keyfile path" do
375
+ class TestCredentials14 < Google::Auth::Credentials
376
+ self.scope = "http://example.com/scope"
377
+ self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
378
+ self.paths = ["~/default/path/to/file.txt"]
379
+ end
380
+
381
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
382
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
383
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
384
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { true }
385
+ allow(::File).to receive(:read).with("~/default/path/to/file.txt") { JSON.generate default_keyfile_hash }
386
+
387
+ mocked_signet = double "Signet::OAuth2::Client"
388
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
389
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
390
+ allow(mocked_signet).to receive(:client_id)
391
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
392
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
393
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
394
+ expect(options[:scope]).to eq(["http://example.com/scope"])
395
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
396
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
397
+
398
+ mocked_signet
399
+ end
400
+
401
+ creds = TestCredentials14.default
402
+ expect(creds).to be_a_kind_of(TestCredentials14)
403
+ expect(creds.client).to eq(mocked_signet)
404
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
244
405
  end
245
- allow(Signet::OAuth2::Client).to receive(:new) do |options|
246
- expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
247
- expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
248
- expect(options[:scope]).to eq(["http://example.com/scope"])
249
- expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
250
- expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
251
406
 
252
- mocked_signet
407
+ it "subclasses that find no matches default to Google::Auth.get_application_default" do
408
+ class TestCredentials15 < Google::Auth::Credentials
409
+ self.scope = "http://example.com/scope"
410
+ self.env_vars = %w[PATH_ENV_DUMMY JSON_ENV_DUMMY]
411
+ self.paths = ["~/default/path/to/file.txt"]
412
+ end
413
+
414
+ allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
415
+ allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
416
+ allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
417
+ allow(::File).to receive(:file?).with("~/default/path/to/file.txt") { false }
418
+
419
+ mocked_signet = double "Signet::OAuth2::Client"
420
+ allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
421
+ allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
422
+ allow(mocked_signet).to receive(:client_id)
423
+ allow(Google::Auth).to receive(:get_application_default) do |scope|
424
+ expect(scope).to eq(TestCredentials15.scope)
425
+
426
+ # This should really be a Signet::OAuth2::Client object,
427
+ # but mocking is making that difficult, so return a valid hash instead.
428
+ default_keyfile_hash
429
+ end
430
+ allow(Signet::OAuth2::Client).to receive(:new) do |options|
431
+ expect(options[:token_credential_uri]).to eq("https://oauth2.googleapis.com/token")
432
+ expect(options[:audience]).to eq("https://oauth2.googleapis.com/token")
433
+ expect(options[:scope]).to eq(["http://example.com/scope"])
434
+ expect(options[:issuer]).to eq(default_keyfile_hash["client_email"])
435
+ expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
436
+
437
+ mocked_signet
438
+ end
439
+
440
+ creds = TestCredentials15.default
441
+ expect(creds).to be_a_kind_of(TestCredentials15)
442
+ expect(creds.client).to eq(mocked_signet)
443
+ expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
253
444
  end
254
-
255
- creds = TestCredentials.default
256
- expect(creds).to be_a_kind_of(TestCredentials)
257
- expect(creds.client).to eq(mocked_signet)
258
- expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
259
445
  end
260
446
 
261
447
  it "warns when cloud sdk credentials are used" do