Skip to content

Commit 51638fe

Browse files
feat: add support to validate credentials for configured universe (#2362)
* feat: add support to validate credentials for configured universe * update test_pickle * update TODO comment * update docstring * conditionally import universe from api_core * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * this check is not needed * fix whitespace * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * revert setup.py * check that http has credentials * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * address PR comments * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * add comment for already validated credentials --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent e035336 commit 51638fe

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

googleapiclient/discovery.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@
5353
except ImportError: # pragma: NO COVER
5454
google_auth_httplib2 = None
5555

56+
try:
57+
from google.api_core import universe
58+
59+
HAS_UNIVERSE = True
60+
except ImportError:
61+
HAS_UNIVERSE = False
62+
5663
# Local imports
5764
from googleapiclient import _auth, mimeparse
5865
from googleapiclient._helpers import _add_query_parameter, positional
@@ -1352,6 +1359,7 @@ def __init__(
13521359
resourceDesc,
13531360
rootDesc,
13541361
schema,
1362+
universe_domain=universe.DEFAULT_UNIVERSE if HAS_UNIVERSE else "",
13551363
):
13561364
"""Build a Resource from the API description.
13571365
@@ -1369,6 +1377,8 @@ def __init__(
13691377
is considered a resource.
13701378
rootDesc: object, the entire deserialized discovery document.
13711379
schema: object, mapping of schema names to schema descriptions.
1380+
universe_domain: string, the universe for the API. The default universe
1381+
is "googleapis.com".
13721382
"""
13731383
self._dynamic_attrs = []
13741384

@@ -1380,6 +1390,8 @@ def __init__(
13801390
self._resourceDesc = resourceDesc
13811391
self._rootDesc = rootDesc
13821392
self._schema = schema
1393+
self._universe_domain = universe_domain
1394+
self._credentials_validated = False
13831395

13841396
self._set_service_methods()
13851397

@@ -1546,6 +1558,27 @@ def _add_next_methods(self, resourceDesc, schema):
15461558
fixedMethodName, method.__get__(self, self.__class__)
15471559
)
15481560

1561+
def _validate_credentials(self):
1562+
"""Validates client's and credentials' universe domains are consistent.
1563+
1564+
Returns:
1565+
bool: True iff the configured universe domain is valid.
1566+
1567+
Raises:
1568+
UniverseMismatchError: If the configured universe domain is not valid.
1569+
"""
1570+
credentials = getattr(self._http, "credentials", None)
1571+
1572+
self._credentials_validated = (
1573+
(
1574+
self._credentials_validated
1575+
or universe.compare_domains(self._universe_domain, credentials)
1576+
)
1577+
if HAS_UNIVERSE
1578+
else True
1579+
)
1580+
return self._credentials_validated
1581+
15491582

15501583
def _findPageTokenName(fields):
15511584
"""Search field names for one like a page token.

tests/test_discovery.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@
5454
except ImportError:
5555
HAS_OAUTH2CLIENT = False
5656

57+
try:
58+
from google.api_core import universe
59+
60+
HAS_UNIVERSE = True
61+
except ImportError:
62+
HAS_UNIVERSE = False
63+
5764
from googleapiclient import _helpers as util
5865
from googleapiclient.discovery import (
5966
DISCOVERY_URI,
@@ -2118,6 +2125,7 @@ def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
21182125
def test_pickle(self):
21192126
sorted_resource_keys = [
21202127
"_baseUrl",
2128+
"_credentials_validated",
21212129
"_developerKey",
21222130
"_dynamic_attrs",
21232131
"_http",
@@ -2126,6 +2134,7 @@ def test_pickle(self):
21262134
"_resourceDesc",
21272135
"_rootDesc",
21282136
"_schema",
2137+
"_universe_domain",
21292138
"animals",
21302139
"global_",
21312140
"load",
@@ -2331,5 +2340,94 @@ def test_get_media(self):
23312340
self.assertEqual(b"standing in for media", response)
23322341

23332342

2343+
if HAS_UNIVERSE:
2344+
2345+
class Universe(unittest.TestCase):
2346+
def test_validate_credentials_with_no_universe(self):
2347+
fake_universe = "foo.com"
2348+
2349+
http = google_auth_httplib2.AuthorizedHttp(
2350+
credentials=None, http=build_http()
2351+
)
2352+
discovery = read_datafile("zoo.json")
2353+
service = build_from_document(
2354+
discovery,
2355+
http=http,
2356+
client_options=google.api_core.client_options.ClientOptions(
2357+
universe_domain=universe.DEFAULT_UNIVERSE
2358+
),
2359+
)
2360+
2361+
assert service._validate_credentials()
2362+
2363+
# TODO(google-api-python-client/issues/2365): Add test case for no configured universe and fake credentials' universe.
2364+
2365+
# TODO(google-api-python-client/issues/2365): Add test case for not specifying universe domain via client option.
2366+
2367+
def test_validate_credentials_with_default_universe(self):
2368+
fake_universe = "foo.com"
2369+
2370+
http = google_auth_httplib2.AuthorizedHttp(
2371+
credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE),
2372+
http=build_http(),
2373+
)
2374+
discovery = read_datafile("zoo.json")
2375+
service = build_from_document(
2376+
discovery,
2377+
http=http,
2378+
client_options=google.api_core.client_options.ClientOptions(
2379+
universe_domain=universe.DEFAULT_UNIVERSE
2380+
),
2381+
)
2382+
2383+
assert service._validate_credentials()
2384+
2385+
# TODO(google-api-python-client/issues/2365): # Add test case for "googleapis.com" configured universe and fake credentials' universe.
2386+
2387+
def test_validate_credentials_with_a_different_universe(self):
2388+
fake_universe = "foo.com"
2389+
2390+
# TODO(google-api-python-client/issues/2365): Add test case for fake configured universe and fake credentials' universe.
2391+
2392+
http = google_auth_httplib2.AuthorizedHttp(
2393+
credentials=mock.Mock(universe_domain=fake_universe), http=build_http()
2394+
)
2395+
discovery = read_datafile("zoo.json")
2396+
service = build_from_document(
2397+
discovery,
2398+
http=http,
2399+
client_options=google.api_core.client_options.ClientOptions(
2400+
universe_domain=universe.DEFAULT_UNIVERSE
2401+
),
2402+
)
2403+
2404+
with self.assertRaises(universe.UniverseMismatchError):
2405+
service._validate_credentials()
2406+
2407+
def test_validate_credentials_with_already_validated_credentials(self):
2408+
fake_universe = "foo.com"
2409+
2410+
http = google_auth_httplib2.AuthorizedHttp(
2411+
credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE),
2412+
http=build_http(),
2413+
)
2414+
discovery = read_datafile("zoo.json")
2415+
service = build_from_document(
2416+
discovery,
2417+
http=http,
2418+
client_options=google.api_core.client_options.ClientOptions(
2419+
universe_domain=universe.DEFAULT_UNIVERSE
2420+
),
2421+
)
2422+
2423+
assert service._validate_credentials()
2424+
assert service._credentials_validated
2425+
2426+
# Calling service._validate_credentials() again returns service.credentials_validated.
2427+
assert service._validate_credentials()
2428+
2429+
# TODO(google-api-python-client/issues/2365): Add test case for fake configured universe and fake credentials' universe.
2430+
2431+
23342432
if __name__ == "__main__":
23352433
unittest.main()

0 commit comments

Comments
 (0)