blob: e1368962525de38c553c6e96673183cc09351a8c [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -07002#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import json
16import os
17
18import mock
19import pytest
20
21from google.auth import _default
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -070022from google.auth import app_engine
bojeil-googled4d7f382021-02-16 12:33:20 -080023from google.auth import aws
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070024from google.auth import compute_engine
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070025from google.auth import credentials
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070026from google.auth import environment_vars
27from google.auth import exceptions
bojeil-googled4d7f382021-02-16 12:33:20 -080028from google.auth import external_account
29from google.auth import identity_pool
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070030from google.oauth2 import service_account
31import google.oauth2.credentials
32
33
Bu Sun Kim9eec0912019-10-21 17:04:21 -070034DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
35AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070036
37with open(AUTHORIZED_USER_FILE) as fh:
38 AUTHORIZED_USER_FILE_DATA = json.load(fh)
39
Thea Flowersa8d93482018-05-31 14:52:06 -070040AUTHORIZED_USER_CLOUD_SDK_FILE = os.path.join(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070041 DATA_DIR, "authorized_user_cloud_sdk.json"
42)
Thea Flowersa8d93482018-05-31 14:52:06 -070043
arithmetic1728f30b45a2020-06-17 23:36:04 -070044AUTHORIZED_USER_CLOUD_SDK_WITH_QUOTA_PROJECT_ID_FILE = os.path.join(
45 DATA_DIR, "authorized_user_cloud_sdk_with_quota_project_id.json"
46)
47
Bu Sun Kim9eec0912019-10-21 17:04:21 -070048SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, "service_account.json")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070049
Bu Sun Kim15d5fa92020-06-18 14:05:40 -070050CLIENT_SECRETS_FILE = os.path.join(DATA_DIR, "client_secrets.json")
51
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070052with open(SERVICE_ACCOUNT_FILE) as fh:
53 SERVICE_ACCOUNT_FILE_DATA = json.load(fh)
54
bojeil-googled4d7f382021-02-16 12:33:20 -080055SUBJECT_TOKEN_TEXT_FILE = os.path.join(DATA_DIR, "external_subject_token.txt")
56TOKEN_URL = "https://sts.googleapis.com/v1/token"
57AUDIENCE = "//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID"
58REGION_URL = "http://169.254.169.254/latest/meta-data/placement/availability-zone"
59SECURITY_CREDS_URL = "http://169.254.169.254/latest/meta-data/iam/security-credentials"
60CRED_VERIFICATION_URL = (
61 "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"
62)
63IDENTITY_POOL_DATA = {
64 "type": "external_account",
65 "audience": AUDIENCE,
66 "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
67 "token_url": TOKEN_URL,
68 "credential_source": {"file": SUBJECT_TOKEN_TEXT_FILE},
69}
70AWS_DATA = {
71 "type": "external_account",
72 "audience": AUDIENCE,
73 "subject_token_type": "urn:ietf:params:aws:token-type:aws4_request",
74 "token_url": TOKEN_URL,
75 "credential_source": {
76 "environment_id": "aws1",
77 "region_url": REGION_URL,
78 "url": SECURITY_CREDS_URL,
79 "regional_cred_verification_url": CRED_VERIFICATION_URL,
80 },
81}
82
Bu Sun Kim41599ae2020-09-02 12:55:42 -060083MOCK_CREDENTIALS = mock.Mock(spec=credentials.CredentialsWithQuotaProject)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070084MOCK_CREDENTIALS.with_quota_project.return_value = MOCK_CREDENTIALS
85
bojeil-googled4d7f382021-02-16 12:33:20 -080086
87def get_project_id_side_effect(self, request=None):
88 # If no scopes are set, this will always return None.
89 if not self.scopes:
90 return None
91 return mock.sentinel.project_id
92
93
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070094LOAD_FILE_PATCH = mock.patch(
Bu Sun Kim15d5fa92020-06-18 14:05:40 -070095 "google.auth._default.load_credentials_from_file",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070096 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -070097 autospec=True,
98)
bojeil-googled4d7f382021-02-16 12:33:20 -080099EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH = mock.patch.object(
100 external_account.Credentials,
101 "get_project_id",
102 side_effect=get_project_id_side_effect,
103 autospec=True,
104)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700105
106
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700107def test_load_credentials_from_missing_file():
weitaiting6e86c932017-08-12 03:26:59 +0800108 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700109 _default.load_credentials_from_file("")
weitaiting6e86c932017-08-12 03:26:59 +0800110
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700111 assert excinfo.match(r"not found")
weitaiting6e86c932017-08-12 03:26:59 +0800112
113
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700114def test_load_credentials_from_file_invalid_json(tmpdir):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700115 jsonfile = tmpdir.join("invalid.json")
116 jsonfile.write("{")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700117
118 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700119 _default.load_credentials_from_file(str(jsonfile))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700120
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700121 assert excinfo.match(r"not a valid json file")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700122
123
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700124def test_load_credentials_from_file_invalid_type(tmpdir):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700125 jsonfile = tmpdir.join("invalid.json")
126 jsonfile.write(json.dumps({"type": "not-a-real-type"}))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700127
128 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700129 _default.load_credentials_from_file(str(jsonfile))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700130
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700131 assert excinfo.match(r"does not have a valid type")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700132
133
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700134def test_load_credentials_from_file_authorized_user():
135 credentials, project_id = _default.load_credentials_from_file(AUTHORIZED_USER_FILE)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700136 assert isinstance(credentials, google.oauth2.credentials.Credentials)
137 assert project_id is None
138
139
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700140def test_load_credentials_from_file_no_type(tmpdir):
141 # use the client_secrets.json, which is valid json but not a
142 # loadable credentials type
143 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
144 _default.load_credentials_from_file(CLIENT_SECRETS_FILE)
145
146 assert excinfo.match(r"does not have a valid type")
147 assert excinfo.match(r"Type is None")
148
149
150def test_load_credentials_from_file_authorized_user_bad_format(tmpdir):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700151 filename = tmpdir.join("authorized_user_bad.json")
152 filename.write(json.dumps({"type": "authorized_user"}))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700153
154 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700155 _default.load_credentials_from_file(str(filename))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700156
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700157 assert excinfo.match(r"Failed to load authorized user")
158 assert excinfo.match(r"missing fields")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700159
160
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700161def test_load_credentials_from_file_authorized_user_cloud_sdk():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700162 with pytest.warns(UserWarning, match="Cloud SDK"):
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700163 credentials, project_id = _default.load_credentials_from_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700164 AUTHORIZED_USER_CLOUD_SDK_FILE
165 )
Thea Flowersa8d93482018-05-31 14:52:06 -0700166 assert isinstance(credentials, google.oauth2.credentials.Credentials)
167 assert project_id is None
168
arithmetic1728f30b45a2020-06-17 23:36:04 -0700169 # No warning if the json file has quota project id.
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700170 credentials, project_id = _default.load_credentials_from_file(
arithmetic1728f30b45a2020-06-17 23:36:04 -0700171 AUTHORIZED_USER_CLOUD_SDK_WITH_QUOTA_PROJECT_ID_FILE
172 )
173 assert isinstance(credentials, google.oauth2.credentials.Credentials)
174 assert project_id is None
175
Thea Flowersa8d93482018-05-31 14:52:06 -0700176
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700177def test_load_credentials_from_file_authorized_user_cloud_sdk_with_scopes():
178 with pytest.warns(UserWarning, match="Cloud SDK"):
179 credentials, project_id = _default.load_credentials_from_file(
180 AUTHORIZED_USER_CLOUD_SDK_FILE,
181 scopes=["https://www.google.com/calendar/feeds"],
182 )
183 assert isinstance(credentials, google.oauth2.credentials.Credentials)
184 assert project_id is None
185 assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
186
187
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700188def test_load_credentials_from_file_authorized_user_cloud_sdk_with_quota_project():
189 credentials, project_id = _default.load_credentials_from_file(
190 AUTHORIZED_USER_CLOUD_SDK_FILE, quota_project_id="project-foo"
191 )
192
193 assert isinstance(credentials, google.oauth2.credentials.Credentials)
194 assert project_id is None
195 assert credentials.quota_project_id == "project-foo"
196
197
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700198def test_load_credentials_from_file_service_account():
199 credentials, project_id = _default.load_credentials_from_file(SERVICE_ACCOUNT_FILE)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700200 assert isinstance(credentials, service_account.Credentials)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700201 assert project_id == SERVICE_ACCOUNT_FILE_DATA["project_id"]
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700202
203
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700204def test_load_credentials_from_file_service_account_with_scopes():
205 credentials, project_id = _default.load_credentials_from_file(
206 SERVICE_ACCOUNT_FILE, scopes=["https://www.google.com/calendar/feeds"]
207 )
208 assert isinstance(credentials, service_account.Credentials)
209 assert project_id == SERVICE_ACCOUNT_FILE_DATA["project_id"]
210 assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
211
212
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700213def test_load_credentials_from_file_service_account_with_quota_project():
214 credentials, project_id = _default.load_credentials_from_file(
215 SERVICE_ACCOUNT_FILE, quota_project_id="project-foo"
216 )
217 assert isinstance(credentials, service_account.Credentials)
218 assert project_id == SERVICE_ACCOUNT_FILE_DATA["project_id"]
219 assert credentials.quota_project_id == "project-foo"
220
221
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700222def test_load_credentials_from_file_service_account_bad_format(tmpdir):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700223 filename = tmpdir.join("serivce_account_bad.json")
224 filename.write(json.dumps({"type": "service_account"}))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700225
226 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700227 _default.load_credentials_from_file(str(filename))
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700228
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700229 assert excinfo.match(r"Failed to load service account")
230 assert excinfo.match(r"missing fields")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700231
232
bojeil-googled4d7f382021-02-16 12:33:20 -0800233@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
234def test_load_credentials_from_file_external_account_identity_pool(
235 get_project_id, tmpdir
236):
237 config_file = tmpdir.join("config.json")
238 config_file.write(json.dumps(IDENTITY_POOL_DATA))
239 credentials, project_id = _default.load_credentials_from_file(str(config_file))
240
241 assert isinstance(credentials, identity_pool.Credentials)
242 # Since no scopes are specified, the project ID cannot be determined.
243 assert project_id is None
244 assert get_project_id.called
245
246
247@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
248def test_load_credentials_from_file_external_account_aws(get_project_id, tmpdir):
249 config_file = tmpdir.join("config.json")
250 config_file.write(json.dumps(AWS_DATA))
251 credentials, project_id = _default.load_credentials_from_file(str(config_file))
252
253 assert isinstance(credentials, aws.Credentials)
254 # Since no scopes are specified, the project ID cannot be determined.
255 assert project_id is None
256 assert get_project_id.called
257
258
259@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
260def test_load_credentials_from_file_external_account_with_user_and_default_scopes(
261 get_project_id, tmpdir
262):
263 config_file = tmpdir.join("config.json")
264 config_file.write(json.dumps(IDENTITY_POOL_DATA))
265 credentials, project_id = _default.load_credentials_from_file(
266 str(config_file),
267 scopes=["https://www.google.com/calendar/feeds"],
268 default_scopes=["https://www.googleapis.com/auth/cloud-platform"],
269 )
270
271 assert isinstance(credentials, identity_pool.Credentials)
272 # Since scopes are specified, the project ID can be determined.
273 assert project_id is mock.sentinel.project_id
274 assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
275 assert credentials.default_scopes == [
276 "https://www.googleapis.com/auth/cloud-platform"
277 ]
278
279
280@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
281def test_load_credentials_from_file_external_account_with_quota_project(
282 get_project_id, tmpdir
283):
284 config_file = tmpdir.join("config.json")
285 config_file.write(json.dumps(IDENTITY_POOL_DATA))
286 credentials, project_id = _default.load_credentials_from_file(
287 str(config_file), quota_project_id="project-foo"
288 )
289
290 assert isinstance(credentials, identity_pool.Credentials)
291 # Since no scopes are specified, the project ID cannot be determined.
292 assert project_id is None
293 assert credentials.quota_project_id == "project-foo"
294
295
296def test_load_credentials_from_file_external_account_bad_format(tmpdir):
297 filename = tmpdir.join("external_account_bad.json")
298 filename.write(json.dumps({"type": "external_account"}))
299
300 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
301 _default.load_credentials_from_file(str(filename))
302
303 assert excinfo.match(
304 "Failed to load external account credentials from {}".format(str(filename))
305 )
306
307
308@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
309def test_load_credentials_from_file_external_account_explicit_request(
310 get_project_id, tmpdir
311):
312 config_file = tmpdir.join("config.json")
313 config_file.write(json.dumps(IDENTITY_POOL_DATA))
314 credentials, project_id = _default.load_credentials_from_file(
315 str(config_file),
316 request=mock.sentinel.request,
317 scopes=["https://www.googleapis.com/auth/cloud-platform"],
318 )
319
320 assert isinstance(credentials, identity_pool.Credentials)
321 # Since scopes are specified, the project ID can be determined.
322 assert project_id is mock.sentinel.project_id
323 get_project_id.assert_called_with(credentials, request=mock.sentinel.request)
324
325
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700326@mock.patch.dict(os.environ, {}, clear=True)
327def test__get_explicit_environ_credentials_no_env():
328 assert _default._get_explicit_environ_credentials() == (None, None)
329
330
331@LOAD_FILE_PATCH
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700332def test__get_explicit_environ_credentials(load, monkeypatch):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700333 monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700334
335 credentials, project_id = _default._get_explicit_environ_credentials()
336
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700337 assert credentials is MOCK_CREDENTIALS
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700338 assert project_id is mock.sentinel.project_id
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700339 load.assert_called_with("filename")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700340
341
342@LOAD_FILE_PATCH
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700343def test__get_explicit_environ_credentials_no_project_id(load, monkeypatch):
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700344 load.return_value = MOCK_CREDENTIALS, None
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700345 monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700346
347 credentials, project_id = _default._get_explicit_environ_credentials()
348
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700349 assert credentials is MOCK_CREDENTIALS
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700350 assert project_id is None
351
352
arithmetic1728333cb762021-02-25 15:42:32 -0800353@mock.patch(
354 "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
355)
356@mock.patch("google.auth._default._get_gcloud_sdk_credentials", autospec=True)
357def test__get_explicit_environ_credentials_fallback_to_gcloud(
358 get_gcloud_creds, get_adc_path, monkeypatch
359):
360 # Set explicit credentials path to cloud sdk credentials path.
361 get_adc_path.return_value = "filename"
362 monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
363
364 _default._get_explicit_environ_credentials()
365
366 # Check we fall back to cloud sdk flow since explicit credentials path is
367 # cloud sdk credentials path
368 get_gcloud_creds.assert_called_once()
369
370
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700371@LOAD_FILE_PATCH
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800372@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700373 "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
374)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700375def test__get_gcloud_sdk_credentials(get_adc_path, load):
376 get_adc_path.return_value = SERVICE_ACCOUNT_FILE
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700377
378 credentials, project_id = _default._get_gcloud_sdk_credentials()
379
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700380 assert credentials is MOCK_CREDENTIALS
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700381 assert project_id is mock.sentinel.project_id
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700382 load.assert_called_with(SERVICE_ACCOUNT_FILE)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700383
384
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800385@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700386 "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
387)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700388def test__get_gcloud_sdk_credentials_non_existent(get_adc_path, tmpdir):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700389 non_existent = tmpdir.join("non-existent")
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700390 get_adc_path.return_value = str(non_existent)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700391
392 credentials, project_id = _default._get_gcloud_sdk_credentials()
393
394 assert credentials is None
395 assert project_id is None
396
397
398@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700399 "google.auth._cloud_sdk.get_project_id",
400 return_value=mock.sentinel.project_id,
401 autospec=True,
402)
403@mock.patch("os.path.isfile", return_value=True, autospec=True)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700404@LOAD_FILE_PATCH
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700405def test__get_gcloud_sdk_credentials_project_id(load, unused_isfile, get_project_id):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700406 # Don't return a project ID from load file, make the function check
407 # the Cloud SDK project.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700408 load.return_value = MOCK_CREDENTIALS, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700409
410 credentials, project_id = _default._get_gcloud_sdk_credentials()
411
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700412 assert credentials == MOCK_CREDENTIALS
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700413 assert project_id == mock.sentinel.project_id
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700414 assert get_project_id.called
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700415
416
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700417@mock.patch("google.auth._cloud_sdk.get_project_id", return_value=None, autospec=True)
418@mock.patch("os.path.isfile", return_value=True)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700419@LOAD_FILE_PATCH
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700420def test__get_gcloud_sdk_credentials_no_project_id(load, unused_isfile, get_project_id):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700421 # Don't return a project ID from load file, make the function check
422 # the Cloud SDK project.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700423 load.return_value = MOCK_CREDENTIALS, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700424
425 credentials, project_id = _default._get_gcloud_sdk_credentials()
426
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700427 assert credentials == MOCK_CREDENTIALS
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700428 assert project_id is None
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700429 assert get_project_id.called
430
431
432class _AppIdentityModule(object):
433 """The interface of the App Idenity app engine module.
434 See https://cloud.google.com/appengine/docs/standard/python/refdocs\
435 /google.appengine.api.app_identity.app_identity
436 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700437
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700438 def get_application_id(self):
439 raise NotImplementedError()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700440
441
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700442@pytest.fixture
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700443def app_identity(monkeypatch):
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700444 """Mocks the app_identity module for google.auth.app_engine."""
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700445 app_identity_module = mock.create_autospec(_AppIdentityModule, instance=True)
446 monkeypatch.setattr(app_engine, "app_identity", app_identity_module)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700447 yield app_identity_module
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700448
449
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700450def test__get_gae_credentials(app_identity):
451 app_identity.get_application_id.return_value = mock.sentinel.project
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700452
453 credentials, project_id = _default._get_gae_credentials()
454
455 assert isinstance(credentials, app_engine.Credentials)
456 assert project_id == mock.sentinel.project
457
458
James Wilson6e0781b2018-12-20 20:38:52 -0500459def test__get_gae_credentials_no_app_engine():
460 import sys
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700461
462 with mock.patch.dict("sys.modules"):
463 sys.modules["google.auth.app_engine"] = None
James Wilson6e0781b2018-12-20 20:38:52 -0500464 credentials, project_id = _default._get_gae_credentials()
465 assert credentials is None
466 assert project_id is None
467
468
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700469def test__get_gae_credentials_no_apis():
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700470 assert _default._get_gae_credentials() == (None, None)
471
472
473@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700474 "google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
475)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700476@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700477 "google.auth.compute_engine._metadata.get_project_id",
478 return_value="example-project",
479 autospec=True,
480)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700481def test__get_gce_credentials(unused_get, unused_ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700482 credentials, project_id = _default._get_gce_credentials()
483
484 assert isinstance(credentials, compute_engine.Credentials)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700485 assert project_id == "example-project"
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700486
487
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800488@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700489 "google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
490)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700491def test__get_gce_credentials_no_ping(unused_ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700492 credentials, project_id = _default._get_gce_credentials()
493
494 assert credentials is None
495 assert project_id is None
496
497
498@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700499 "google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
500)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700501@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700502 "google.auth.compute_engine._metadata.get_project_id",
503 side_effect=exceptions.TransportError(),
504 autospec=True,
505)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700506def test__get_gce_credentials_no_project_id(unused_get, unused_ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700507 credentials, project_id = _default._get_gce_credentials()
508
509 assert isinstance(credentials, compute_engine.Credentials)
510 assert project_id is None
511
512
James Wilson6e0781b2018-12-20 20:38:52 -0500513def test__get_gce_credentials_no_compute_engine():
514 import sys
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700515
516 with mock.patch.dict("sys.modules"):
517 sys.modules["google.auth.compute_engine"] = None
James Wilson6e0781b2018-12-20 20:38:52 -0500518 credentials, project_id = _default._get_gce_credentials()
519 assert credentials is None
520 assert project_id is None
521
522
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800523@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700524 "google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
525)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700526def test__get_gce_credentials_explicit_request(ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700527 _default._get_gce_credentials(mock.sentinel.request)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700528 ping.assert_called_with(request=mock.sentinel.request)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700529
530
531@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700532 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700533 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700534 autospec=True,
535)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700536def test_default_early_out(unused_get):
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700537 assert _default.default() == (MOCK_CREDENTIALS, mock.sentinel.project_id)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700538
539
540@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700541 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700542 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700543 autospec=True,
544)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700545def test_default_explict_project_id(unused_get, monkeypatch):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700546 monkeypatch.setenv(environment_vars.PROJECT, "explicit-env")
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700547 assert _default.default() == (MOCK_CREDENTIALS, "explicit-env")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700548
549
550@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700551 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700552 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700553 autospec=True,
554)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700555def test_default_explict_legacy_project_id(unused_get, monkeypatch):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700556 monkeypatch.setenv(environment_vars.LEGACY_PROJECT, "explicit-env")
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700557 assert _default.default() == (MOCK_CREDENTIALS, "explicit-env")
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800558
559
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700560@mock.patch("logging.Logger.warning", autospec=True)
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800561@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700562 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700563 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700564 autospec=True,
565)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600566@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700567 "google.auth._default._get_gcloud_sdk_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700568 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700569 autospec=True,
570)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600571@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700572 "google.auth._default._get_gae_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700573 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700574 autospec=True,
575)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600576@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700577 "google.auth._default._get_gce_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700578 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700579 autospec=True,
580)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600581def test_default_without_project_id(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700582 unused_gce, unused_gae, unused_sdk, unused_explicit, logger_warning
583):
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700584 assert _default.default() == (MOCK_CREDENTIALS, None)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600585 logger_warning.assert_called_with(mock.ANY, mock.ANY, mock.ANY)
586
587
588@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700589 "google.auth._default._get_explicit_environ_credentials",
590 return_value=(None, None),
591 autospec=True,
592)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700593@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700594 "google.auth._default._get_gcloud_sdk_credentials",
595 return_value=(None, None),
596 autospec=True,
597)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700598@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700599 "google.auth._default._get_gae_credentials",
600 return_value=(None, None),
601 autospec=True,
602)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700603@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700604 "google.auth._default._get_gce_credentials",
605 return_value=(None, None),
606 autospec=True,
607)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700608def test_default_fail(unused_gce, unused_gae, unused_sdk, unused_explicit):
609 with pytest.raises(exceptions.DefaultCredentialsError):
610 assert _default.default()
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800611
612
613@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700614 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700615 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700616 autospec=True,
617)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700618@mock.patch(
619 "google.auth.credentials.with_scopes_if_required",
620 return_value=MOCK_CREDENTIALS,
621 autospec=True,
622)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600623def test_default_scoped(with_scopes, unused_get):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700624 scopes = ["one", "two"]
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800625
626 credentials, project_id = _default.default(scopes=scopes)
627
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700628 assert credentials == with_scopes.return_value
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800629 assert project_id == mock.sentinel.project_id
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700630 with_scopes.assert_called_once_with(MOCK_CREDENTIALS, scopes, default_scopes=None)
James Wilson6e0781b2018-12-20 20:38:52 -0500631
632
633@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700634 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700635 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700636 autospec=True,
637)
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700638def test_default_quota_project(with_quota_project):
639 credentials, project_id = _default.default(quota_project_id="project-foo")
640
641 MOCK_CREDENTIALS.with_quota_project.assert_called_once_with("project-foo")
642 assert project_id == mock.sentinel.project_id
643
644
645@mock.patch(
646 "google.auth._default._get_explicit_environ_credentials",
647 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
648 autospec=True,
649)
James Wilson6e0781b2018-12-20 20:38:52 -0500650def test_default_no_app_engine_compute_engine_module(unused_get):
651 """
652 google.auth.compute_engine and google.auth.app_engine are both optional
653 to allow not including them when using this package. This verifies
654 that default fails gracefully if these modules are absent
655 """
656 import sys
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700657
658 with mock.patch.dict("sys.modules"):
659 sys.modules["google.auth.compute_engine"] = None
660 sys.modules["google.auth.app_engine"] = None
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700661 assert _default.default() == (MOCK_CREDENTIALS, mock.sentinel.project_id)
bojeil-googled4d7f382021-02-16 12:33:20 -0800662
663
664@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
665def test_default_environ_external_credentials(get_project_id, monkeypatch, tmpdir):
666 config_file = tmpdir.join("config.json")
667 config_file.write(json.dumps(IDENTITY_POOL_DATA))
668 monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file))
669
670 credentials, project_id = _default.default()
671
672 assert isinstance(credentials, identity_pool.Credentials)
673 # Without scopes, project ID cannot be determined.
674 assert project_id is None
675
676
677@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
678def test_default_environ_external_credentials_with_user_and_default_scopes_and_quota_project_id(
679 get_project_id, monkeypatch, tmpdir
680):
681 config_file = tmpdir.join("config.json")
682 config_file.write(json.dumps(IDENTITY_POOL_DATA))
683 monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file))
684
685 credentials, project_id = _default.default(
686 scopes=["https://www.google.com/calendar/feeds"],
687 default_scopes=["https://www.googleapis.com/auth/cloud-platform"],
688 quota_project_id="project-foo",
689 )
690
691 assert isinstance(credentials, identity_pool.Credentials)
692 assert project_id is mock.sentinel.project_id
693 assert credentials.quota_project_id == "project-foo"
694 assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
695 assert credentials.default_scopes == [
696 "https://www.googleapis.com/auth/cloud-platform"
697 ]
698
699
700@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
701def test_default_environ_external_credentials_explicit_request_with_scopes(
702 get_project_id, monkeypatch, tmpdir
703):
704 config_file = tmpdir.join("config.json")
705 config_file.write(json.dumps(IDENTITY_POOL_DATA))
706 monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file))
707
708 credentials, project_id = _default.default(
709 request=mock.sentinel.request,
710 scopes=["https://www.googleapis.com/auth/cloud-platform"],
711 )
712
713 assert isinstance(credentials, identity_pool.Credentials)
714 assert project_id is mock.sentinel.project_id
715 # default() will initialize new credentials via with_scopes_if_required
716 # and potentially with_quota_project.
717 # As a result the caller of get_project_id() will not match the returned
718 # credentials.
719 get_project_id.assert_called_with(mock.ANY, request=mock.sentinel.request)
720
721
722def test_default_environ_external_credentials_bad_format(monkeypatch, tmpdir):
723 filename = tmpdir.join("external_account_bad.json")
724 filename.write(json.dumps({"type": "external_account"}))
725 monkeypatch.setenv(environment_vars.CREDENTIALS, str(filename))
726
727 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
728 _default.default()
729
730 assert excinfo.match(
731 "Failed to load external account credentials from {}".format(str(filename))
732 )