Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support to validate credentials for configured universe #2362

Merged
merged 21 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
570799d
feat: add support to validate credentials for configured universe
ohmayr Mar 19, 2024
c658590
update test_pickle
ohmayr Mar 19, 2024
f654914
update TODO comment
ohmayr Mar 19, 2024
9761d88
update docstring
ohmayr Mar 19, 2024
360b1e3
conditionally import universe from api_core
ohmayr Mar 19, 2024
c851022
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Mar 19, 2024
7a80f74
this check is not needed
ohmayr Mar 19, 2024
6096489
Merge branch 'validate-credentials-for-universe' of github.com:google…
ohmayr Mar 20, 2024
30caccc
fix whitespace
ohmayr Mar 20, 2024
39eb08f
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Mar 20, 2024
afd9124
Merge branch 'validate-credentials-for-universe' of https://github.co…
gcf-owl-bot[bot] Mar 20, 2024
f51d748
revert setup.py
ohmayr Mar 20, 2024
ebe919f
Merge branch 'validate-credentials-for-universe' of github.com:google…
ohmayr Mar 20, 2024
205a6f2
check that http has credentials
ohmayr Mar 20, 2024
2bf6600
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Mar 20, 2024
832a26c
address PR comments
ohmayr Mar 21, 2024
cac101b
Merge branch 'validate-credentials-for-universe' of github.com:google…
ohmayr Mar 21, 2024
bb758da
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Mar 21, 2024
0e85d3c
add comment for already validated credentials
ohmayr Mar 21, 2024
d9ed8ef
Merge branch 'validate-credentials-for-universe' of github.com:google…
ohmayr Mar 21, 2024
06a383a
Merge branch 'main' into validate-credentials-for-universe
ohmayr Mar 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions googleapiclient/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
except ImportError: # pragma: NO COVER
google_auth_httplib2 = None

try:
from google.api_core import universe as universe
parthea marked this conversation as resolved.
Show resolved Hide resolved

HAS_UNIVERSE = True
except ImportError:
HAS_UNIVERSE = False

# Local imports
from googleapiclient import _auth, mimeparse
from googleapiclient._helpers import _add_query_parameter, positional
Expand Down Expand Up @@ -1352,6 +1359,7 @@ def __init__(
resourceDesc,
rootDesc,
schema,
universe_domain=universe.DEFAULT_UNIVERSE if HAS_UNIVERSE else None,
parthea marked this conversation as resolved.
Show resolved Hide resolved
):
"""Build a Resource from the API description.

Expand All @@ -1369,6 +1377,8 @@ def __init__(
is considered a resource.
rootDesc: object, the entire deserialized discovery document.
schema: object, mapping of schema names to schema descriptions.
universe_domain: string, the universe for the API. The default universe
is "googleapis.com".
"""
self._dynamic_attrs = []

Expand All @@ -1380,6 +1390,8 @@ def __init__(
self._resourceDesc = resourceDesc
self._rootDesc = rootDesc
self._schema = schema
self._universe_domain = universe_domain
self._credentials_validated = False

self._set_service_methods()

Expand Down Expand Up @@ -1546,6 +1558,27 @@ def _add_next_methods(self, resourceDesc, schema):
fixedMethodName, method.__get__(self, self.__class__)
)

def _validate_credentials(self):
"""Validates client's and credentials' universe domains are consistent.

Returns:
bool: True iff the configured universe domain is valid.

Raises:
UniverseMismatchError: If the configured universe domain is not valid.
"""
credentials = getattr(self._http, "credentials", None)

self._credentials_validated = (
(
self._credentials_validated
or universe.compare_domains(self._universe_domain, credentials)
)
if HAS_UNIVERSE
else True
)
return self._credentials_validated


def _findPageTokenName(fields):
"""Search field names for one like a page token.
Expand Down
133 changes: 133 additions & 0 deletions tests/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
except ImportError:
HAS_OAUTH2CLIENT = False

try:
from google.api_core import universe as universe
parthea marked this conversation as resolved.
Show resolved Hide resolved

HAS_UNIVERSE = True
except ImportError:
HAS_UNIVERSE = False

from googleapiclient import _helpers as util
from googleapiclient.discovery import (
DISCOVERY_URI,
Expand Down Expand Up @@ -2118,6 +2125,7 @@ def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
def test_pickle(self):
sorted_resource_keys = [
"_baseUrl",
"_credentials_validated",
"_developerKey",
"_dynamic_attrs",
"_http",
Expand All @@ -2126,6 +2134,7 @@ def test_pickle(self):
"_resourceDesc",
"_rootDesc",
"_schema",
"_universe_domain",
"animals",
"global_",
"load",
Expand Down Expand Up @@ -2331,5 +2340,129 @@ def test_get_media(self):
self.assertEqual(b"standing in for media", response)


if HAS_UNIVERSE:

class Universe(unittest.TestCase):
def test_validate_credentials_with_no_universe(self):
fake_universe = "foo.com"

http = google_auth_httplib2.AuthorizedHttp(
credentials=None, http=build_http()
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=universe.DEFAULT_UNIVERSE
),
)

assert service._validate_credentials()

# TODO(omairn): uncomment when universe configured universe is leveraged.
# http = google_auth_httplib2.AuthorizedHttp(credentials=None, http=build_http())
# discovery = read_datafile("zoo.json")
# service = build_from_document(discovery, http=http, client_options=google.api_core.client_options.ClientOptions(universe_domain=fake_universe))

# with self.assertRaises(universe.UniverseMismatchError):
# service._validate_credentials()

# TODO(omairn): Add test case for not specifying universe domain via client option.
parthea marked this conversation as resolved.
Show resolved Hide resolved

def test_validate_credentials_with_default_universe(self):
fake_universe = "foo.com"

http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE),
http=build_http(),
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=universe.DEFAULT_UNIVERSE
),
)

assert service._validate_credentials()

# TODO(omairn): uncomment when universe configured universe is leveraged.
# http = google_auth_httplib2.AuthorizedHttp(credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE), http=build_http())
# discovery = read_datafile("zoo.json")
# service = build_from_document(discovery, http=http, client_options=google.api_core.client_options.ClientOptions(universe_domain=fake_universe))

# with self.assertRaises(universe.UniverseMismatchError):
# service._validate_credentials()

def test_validate_credentials_with_a_different_universe(self):
fake_universe = "foo.com"

# TODO(omairn): uncomment when universe configured universe is leveraged.
# http = google_auth_httplib2.AuthorizedHttp(credentials=mock.Mock(universe_domain=fake_universe), http=build_http())
# discovery = read_datafile("zoo.json")
# service = build_from_document(discovery, http=http, client_options=google.api_core.client_options.ClientOptions(universe_domain=fake_universe))
# assert service._validate_credentials()

http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=fake_universe), http=build_http()
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=universe.DEFAULT_UNIVERSE
),
)

with self.assertRaises(universe.UniverseMismatchError):
service._validate_credentials()

def test_validate_credentials_with_already_validated_credentials(self):
fake_universe = "foo.com"

http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE),
http=build_http(),
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=universe.DEFAULT_UNIVERSE
),
)

assert service._validate_credentials()
assert service._credentials_validated
assert service._validate_credentials()
parthea marked this conversation as resolved.
Show resolved Hide resolved

# TODO(omairn): uncomment when universe configured universe is leveraged.
# http = google_auth_httplib2.AuthorizedHttp(credentials=mock.Mock(universe_domain=fake_universe), http=build_http())
# discovery = read_datafile("zoo.json")
# service = build_from_document(discovery, http=http, client_options=google.api_core.client_options.ClientOptions(universe_domain=fake_universe))

# assert service._validate_credentials()
# assert service._credentials_validated
# assert service._validate_credentials()

# TODO(omairn):
# Once _validate_universe() is added to the API call, verify that:
# the API call succeeds when the credentials match.
# the API call fails when there is a mismatch.
# the credentials are not verified twice for an already verified Resource.

# Add test case once universe domain is supported via environment variable.

# Once universe.determine_domain() is leveraged:
# add test case for empty universe.
# add test cases when universe domain is provided via both client option and env var.

# Add test case when credentials are passed instead of http.


if __name__ == "__main__":
unittest.main()