blob: a515f3813395b41f1ba53bfa8462d8e7bb657efc [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
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400450@mock.patch.dict(os.environ)
451def test__get_gae_credentials_gen1(app_identity):
452 os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700453 app_identity.get_application_id.return_value = mock.sentinel.project
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700454
455 credentials, project_id = _default._get_gae_credentials()
456
457 assert isinstance(credentials, app_engine.Credentials)
458 assert project_id == mock.sentinel.project
459
460
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400461@mock.patch.dict(os.environ)
462def test__get_gae_credentials_gen2():
463 os.environ["GAE_RUNTIME"] = "python37"
464 credentials, project_id = _default._get_gae_credentials()
465 assert credentials is None
466 assert project_id is None
467
468
469@mock.patch.dict(os.environ)
470def test__get_gae_credentials_gen2_backwards_compat():
471 # compat helpers may copy GAE_RUNTIME to APPENGINE_RUNTIME
472 # for backwards compatibility with code that relies on it
473 os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python37"
474 os.environ["GAE_RUNTIME"] = "python37"
475 credentials, project_id = _default._get_gae_credentials()
476 assert credentials is None
477 assert project_id is None
478
479
480def test__get_gae_credentials_env_unset():
481 assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ
482 assert "GAE_RUNTIME" not in os.environ
483 credentials, project_id = _default._get_gae_credentials()
484 assert credentials is None
485 assert project_id is None
486
487
488@mock.patch.dict(os.environ)
James Wilson6e0781b2018-12-20 20:38:52 -0500489def test__get_gae_credentials_no_app_engine():
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400490 # test both with and without LEGACY_APPENGINE_RUNTIME setting
491 assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ
492
James Wilson6e0781b2018-12-20 20:38:52 -0500493 import sys
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700494
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400495 with mock.patch.dict(sys.modules, {"google.auth.app_engine": None}):
496 credentials, project_id = _default._get_gae_credentials()
497 assert credentials is None
498 assert project_id is None
499
500 os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
James Wilson6e0781b2018-12-20 20:38:52 -0500501 credentials, project_id = _default._get_gae_credentials()
502 assert credentials is None
503 assert project_id is None
504
505
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400506@mock.patch.dict(os.environ)
507@mock.patch.object(app_engine, "app_identity", new=None)
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700508def test__get_gae_credentials_no_apis():
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400509 # test both with and without LEGACY_APPENGINE_RUNTIME setting
510 assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ
511
512 credentials, project_id = _default._get_gae_credentials()
513 assert credentials is None
514 assert project_id is None
515
516 os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
517 credentials, project_id = _default._get_gae_credentials()
518 assert credentials is None
519 assert project_id is None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700520
521
522@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700523 "google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
524)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700525@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700526 "google.auth.compute_engine._metadata.get_project_id",
527 return_value="example-project",
528 autospec=True,
529)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700530def test__get_gce_credentials(unused_get, unused_ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700531 credentials, project_id = _default._get_gce_credentials()
532
533 assert isinstance(credentials, compute_engine.Credentials)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700534 assert project_id == "example-project"
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700535
536
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800537@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700538 "google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
539)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700540def test__get_gce_credentials_no_ping(unused_ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700541 credentials, project_id = _default._get_gce_credentials()
542
543 assert credentials is None
544 assert project_id is None
545
546
547@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700548 "google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
549)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700550@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700551 "google.auth.compute_engine._metadata.get_project_id",
552 side_effect=exceptions.TransportError(),
553 autospec=True,
554)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700555def test__get_gce_credentials_no_project_id(unused_get, unused_ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700556 credentials, project_id = _default._get_gce_credentials()
557
558 assert isinstance(credentials, compute_engine.Credentials)
559 assert project_id is None
560
561
James Wilson6e0781b2018-12-20 20:38:52 -0500562def test__get_gce_credentials_no_compute_engine():
563 import sys
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700564
565 with mock.patch.dict("sys.modules"):
566 sys.modules["google.auth.compute_engine"] = None
James Wilson6e0781b2018-12-20 20:38:52 -0500567 credentials, project_id = _default._get_gce_credentials()
568 assert credentials is None
569 assert project_id is None
570
571
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800572@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700573 "google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
574)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700575def test__get_gce_credentials_explicit_request(ping):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700576 _default._get_gce_credentials(mock.sentinel.request)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700577 ping.assert_called_with(request=mock.sentinel.request)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700578
579
580@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700581 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700582 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700583 autospec=True,
584)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700585def test_default_early_out(unused_get):
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700586 assert _default.default() == (MOCK_CREDENTIALS, mock.sentinel.project_id)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700587
588
589@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700590 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700591 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700592 autospec=True,
593)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700594def test_default_explict_project_id(unused_get, monkeypatch):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700595 monkeypatch.setenv(environment_vars.PROJECT, "explicit-env")
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700596 assert _default.default() == (MOCK_CREDENTIALS, "explicit-env")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700597
598
599@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700600 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700601 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700602 autospec=True,
603)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700604def test_default_explict_legacy_project_id(unused_get, monkeypatch):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700605 monkeypatch.setenv(environment_vars.LEGACY_PROJECT, "explicit-env")
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700606 assert _default.default() == (MOCK_CREDENTIALS, "explicit-env")
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800607
608
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700609@mock.patch("logging.Logger.warning", autospec=True)
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800610@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700611 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700612 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700613 autospec=True,
614)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600615@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700616 "google.auth._default._get_gcloud_sdk_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700617 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700618 autospec=True,
619)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600620@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700621 "google.auth._default._get_gae_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700622 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700623 autospec=True,
624)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600625@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700626 "google.auth._default._get_gce_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700627 return_value=(MOCK_CREDENTIALS, None),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700628 autospec=True,
629)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600630def test_default_without_project_id(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700631 unused_gce, unused_gae, unused_sdk, unused_explicit, logger_warning
632):
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700633 assert _default.default() == (MOCK_CREDENTIALS, None)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600634 logger_warning.assert_called_with(mock.ANY, mock.ANY, mock.ANY)
635
636
637@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700638 "google.auth._default._get_explicit_environ_credentials",
639 return_value=(None, None),
640 autospec=True,
641)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700642@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700643 "google.auth._default._get_gcloud_sdk_credentials",
644 return_value=(None, None),
645 autospec=True,
646)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700647@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700648 "google.auth._default._get_gae_credentials",
649 return_value=(None, None),
650 autospec=True,
651)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700652@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700653 "google.auth._default._get_gce_credentials",
654 return_value=(None, None),
655 autospec=True,
656)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700657def test_default_fail(unused_gce, unused_gae, unused_sdk, unused_explicit):
658 with pytest.raises(exceptions.DefaultCredentialsError):
659 assert _default.default()
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800660
661
662@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700663 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700664 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700665 autospec=True,
666)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700667@mock.patch(
668 "google.auth.credentials.with_scopes_if_required",
669 return_value=MOCK_CREDENTIALS,
670 autospec=True,
671)
Jacob Hayes15af07b2017-12-13 14:09:47 -0600672def test_default_scoped(with_scopes, unused_get):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700673 scopes = ["one", "two"]
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800674
675 credentials, project_id = _default.default(scopes=scopes)
676
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700677 assert credentials == with_scopes.return_value
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800678 assert project_id == mock.sentinel.project_id
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700679 with_scopes.assert_called_once_with(MOCK_CREDENTIALS, scopes, default_scopes=None)
James Wilson6e0781b2018-12-20 20:38:52 -0500680
681
682@mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700683 "google.auth._default._get_explicit_environ_credentials",
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700684 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700685 autospec=True,
686)
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700687def test_default_quota_project(with_quota_project):
688 credentials, project_id = _default.default(quota_project_id="project-foo")
689
690 MOCK_CREDENTIALS.with_quota_project.assert_called_once_with("project-foo")
691 assert project_id == mock.sentinel.project_id
692
693
694@mock.patch(
695 "google.auth._default._get_explicit_environ_credentials",
696 return_value=(MOCK_CREDENTIALS, mock.sentinel.project_id),
697 autospec=True,
698)
James Wilson6e0781b2018-12-20 20:38:52 -0500699def test_default_no_app_engine_compute_engine_module(unused_get):
700 """
701 google.auth.compute_engine and google.auth.app_engine are both optional
702 to allow not including them when using this package. This verifies
703 that default fails gracefully if these modules are absent
704 """
705 import sys
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700706
707 with mock.patch.dict("sys.modules"):
708 sys.modules["google.auth.compute_engine"] = None
709 sys.modules["google.auth.app_engine"] = None
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700710 assert _default.default() == (MOCK_CREDENTIALS, mock.sentinel.project_id)
bojeil-googled4d7f382021-02-16 12:33:20 -0800711
712
713@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
714def test_default_environ_external_credentials(get_project_id, monkeypatch, tmpdir):
715 config_file = tmpdir.join("config.json")
716 config_file.write(json.dumps(IDENTITY_POOL_DATA))
717 monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file))
718
719 credentials, project_id = _default.default()
720
721 assert isinstance(credentials, identity_pool.Credentials)
722 # Without scopes, project ID cannot be determined.
723 assert project_id is None
724
725
726@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
727def test_default_environ_external_credentials_with_user_and_default_scopes_and_quota_project_id(
728 get_project_id, monkeypatch, tmpdir
729):
730 config_file = tmpdir.join("config.json")
731 config_file.write(json.dumps(IDENTITY_POOL_DATA))
732 monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file))
733
734 credentials, project_id = _default.default(
735 scopes=["https://www.google.com/calendar/feeds"],
736 default_scopes=["https://www.googleapis.com/auth/cloud-platform"],
737 quota_project_id="project-foo",
738 )
739
740 assert isinstance(credentials, identity_pool.Credentials)
741 assert project_id is mock.sentinel.project_id
742 assert credentials.quota_project_id == "project-foo"
743 assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
744 assert credentials.default_scopes == [
745 "https://www.googleapis.com/auth/cloud-platform"
746 ]
747
748
749@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH
750def test_default_environ_external_credentials_explicit_request_with_scopes(
751 get_project_id, monkeypatch, tmpdir
752):
753 config_file = tmpdir.join("config.json")
754 config_file.write(json.dumps(IDENTITY_POOL_DATA))
755 monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file))
756
757 credentials, project_id = _default.default(
758 request=mock.sentinel.request,
759 scopes=["https://www.googleapis.com/auth/cloud-platform"],
760 )
761
762 assert isinstance(credentials, identity_pool.Credentials)
763 assert project_id is mock.sentinel.project_id
764 # default() will initialize new credentials via with_scopes_if_required
765 # and potentially with_quota_project.
766 # As a result the caller of get_project_id() will not match the returned
767 # credentials.
768 get_project_id.assert_called_with(mock.ANY, request=mock.sentinel.request)
769
770
771def test_default_environ_external_credentials_bad_format(monkeypatch, tmpdir):
772 filename = tmpdir.join("external_account_bad.json")
773 filename.write(json.dumps({"type": "external_account"}))
774 monkeypatch.setenv(environment_vars.CREDENTIALS, str(filename))
775
776 with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
777 _default.default()
778
779 assert excinfo.match(
780 "Failed to load external account credentials from {}".format(str(filename))
781 )