Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-google-auth for openSUSE:Factory checked in at 2024-12-08 11:36:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-google-auth (Old) and /work/SRC/openSUSE:Factory/.python-google-auth.new.21547 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-auth" Sun Dec 8 11:36:24 2024 rev:50 rq:1228735 version:2.36.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-google-auth/python-google-auth.changes 2024-10-03 18:00:03.069365643 +0200 +++ /work/SRC/openSUSE:Factory/.python-google-auth.new.21547/python-google-auth.changes 2024-12-08 11:37:32.766657731 +0100 @@ -1,0 +2,16 @@ +Thu Dec 5 11:00:48 UTC 2024 - John Paul Adrian Glaubitz <[email protected]> + +- Update to version 2.36.0 + * IAM signblob retries (#1600) + * Making IAM endpoint universe-aware (#1604) + * Support External Account Authorized User as a Source + Credential for impersonated credentials in ADC (#1608) + * Adding default parameters to updated interfaces (#1622) + * Change universe_domain to universe-domain (#1613) + * Remove base class to avoid type conflict (#1619) + * Revert templates for iam endpoints (#1614) + * Update secret (#1611) + * Update secret (#1617) + * Update secret (#1621) + +------------------------------------------------------------------- Old: ---- google_auth-2.35.0.tar.gz New: ---- google_auth-2.36.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-google-auth.spec ++++++ --- /var/tmp/diff_new_pack.SD3yOx/_old 2024-12-08 11:37:33.266678503 +0100 +++ /var/tmp/diff_new_pack.SD3yOx/_new 2024-12-08 11:37:33.270678670 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-google-auth -Version: 2.35.0 +Version: 2.36.0 Release: 0 Summary: Google Authentication Library License: Apache-2.0 @@ -34,9 +34,9 @@ BuildRequires: %{python_module freezegun} BuildRequires: %{python_module pyOpenSSL >= 22.0.0} BuildRequires: %{python_module pyasn1-modules >= 0.2.1} +BuildRequires: %{python_module pytest-asyncio} BuildRequires: %{python_module pytest-localserver} BuildRequires: %{python_module pytest} -BuildRequires: %{python_module pytest-asyncio} BuildRequires: %{python_module pyu2f >= 0.1.5} BuildRequires: %{python_module requests >= 2.20.0} BuildRequires: %{python_module responses} ++++++ google_auth-2.35.0.tar.gz -> google_auth-2.36.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/PKG-INFO new/google_auth-2.36.0/PKG-INFO --- old/google_auth-2.35.0/PKG-INFO 2024-09-19 20:07:29.919246000 +0200 +++ new/google_auth-2.36.0/PKG-INFO 2024-11-06 19:05:17.942188700 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: google-auth -Version: 2.35.0 +Version: 2.36.0 Summary: Google Authentication Library Home-page: https://github.com/googleapis/google-auth-library-python Author: Google Cloud Platform diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/_default.py new/google_auth-2.36.0/google/auth/_default.py --- old/google_auth-2.35.0/google/auth/_default.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/_default.py 2024-11-06 19:02:16.000000000 +0100 @@ -472,6 +472,10 @@ source_credentials, _ = _get_service_account_credentials( filename, source_credentials_info ) + elif source_credentials_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE: + source_credentials, _ = _get_external_account_authorized_user_credentials( + filename, source_credentials_info + ) else: raise exceptions.InvalidType( "source credential of type {} is not supported.".format( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/compute_engine/_metadata.py new/google_auth-2.36.0/google/auth/compute_engine/_metadata.py --- old/google_auth-2.35.0/google/auth/compute_engine/_metadata.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/compute_engine/_metadata.py 2024-11-06 19:02:16.000000000 +0100 @@ -294,7 +294,7 @@ 404 occurs while retrieving metadata. """ universe_domain = get( - request, "universe/universe_domain", return_none_for_not_found_error=True + request, "universe/universe-domain", return_none_for_not_found_error=True ) if not universe_domain: return "googleapis.com" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/iam.py new/google_auth-2.36.0/google/auth/iam.py --- old/google_auth-2.35.0/google/auth/iam.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/iam.py 2024-11-06 19:02:16.000000000 +0100 @@ -23,10 +23,18 @@ import http.client as http_client import json +from google.auth import _exponential_backoff from google.auth import _helpers +from google.auth import credentials from google.auth import crypt from google.auth import exceptions +IAM_RETRY_CODES = { + http_client.INTERNAL_SERVER_ERROR, + http_client.BAD_GATEWAY, + http_client.SERVICE_UNAVAILABLE, + http_client.GATEWAY_TIMEOUT, +} _IAM_SCOPE = ["https://www.googleapis.com/auth/iam"] @@ -82,21 +90,30 @@ message = _helpers.to_bytes(message) method = "POST" - url = _IAM_SIGN_ENDPOINT.format(self._service_account_email) + url = _IAM_SIGN_ENDPOINT.replace( + credentials.DEFAULT_UNIVERSE_DOMAIN, self._credentials.universe_domain + ).format(self._service_account_email) headers = {"Content-Type": "application/json"} body = json.dumps( {"payload": base64.b64encode(message).decode("utf-8")} ).encode("utf-8") - self._credentials.before_request(self._request, method, url, headers) - response = self._request(url=url, method=method, body=body, headers=headers) + retries = _exponential_backoff.ExponentialBackoff() + for _ in retries: + self._credentials.before_request(self._request, method, url, headers) + + response = self._request(url=url, method=method, body=body, headers=headers) + + if response.status in IAM_RETRY_CODES: + continue + + if response.status != http_client.OK: + raise exceptions.TransportError( + "Error calling the IAM signBlob API: {}".format(response.data) + ) - if response.status != http_client.OK: - raise exceptions.TransportError( - "Error calling the IAM signBlob API: {}".format(response.data) - ) - - return json.loads(response.data.decode("utf-8")) + return json.loads(response.data.decode("utf-8")) + raise exceptions.TransportError("exhausted signBlob endpoint retries") @property def key_id(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/impersonated_credentials.py new/google_auth-2.36.0/google/auth/impersonated_credentials.py --- old/google_auth-2.35.0/google/auth/impersonated_credentials.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/impersonated_credentials.py 2024-11-06 19:02:16.000000000 +0100 @@ -31,6 +31,7 @@ import http.client as http_client import json +from google.auth import _exponential_backoff from google.auth import _helpers from google.auth import credentials from google.auth import exceptions @@ -45,7 +46,12 @@ def _make_iam_token_request( - request, principal, headers, body, iam_endpoint_override=None + request, + principal, + headers, + body, + universe_domain=credentials.DEFAULT_UNIVERSE_DOMAIN, + iam_endpoint_override=None, ): """Makes a request to the Google Cloud IAM service for an access token. Args: @@ -66,7 +72,9 @@ `iamcredentials.googleapis.com` is not enabled or the `Service Account Token Creator` is not assigned """ - iam_endpoint = iam_endpoint_override or iam._IAM_ENDPOINT.format(principal) + iam_endpoint = iam_endpoint_override or iam._IAM_ENDPOINT.replace( + credentials.DEFAULT_UNIVERSE_DOMAIN, universe_domain + ).format(principal) body = json.dumps(body).encode("utf-8") @@ -218,6 +226,8 @@ and self._source_credentials._always_use_jwt_access ): self._source_credentials._create_self_signed_jwt(None) + + self._universe_domain = source_credentials.universe_domain self._target_principal = target_principal self._target_scopes = target_scopes self._delegates = delegates @@ -270,13 +280,16 @@ principal=self._target_principal, headers=headers, body=body, + universe_domain=self.universe_domain, iam_endpoint_override=self._iam_endpoint_override, ) def sign_bytes(self, message): from google.auth.transport.requests import AuthorizedSession - iam_sign_endpoint = iam._IAM_SIGN_ENDPOINT.format(self._target_principal) + iam_sign_endpoint = iam._IAM_SIGN_ENDPOINT.replace( + credentials.DEFAULT_UNIVERSE_DOMAIN, self.universe_domain + ).format(self._target_principal) body = { "payload": base64.b64encode(message).decode("utf-8"), @@ -288,18 +301,22 @@ authed_session = AuthorizedSession(self._source_credentials) try: - response = authed_session.post( - url=iam_sign_endpoint, headers=headers, json=body - ) + retries = _exponential_backoff.ExponentialBackoff() + for _ in retries: + response = authed_session.post( + url=iam_sign_endpoint, headers=headers, json=body + ) + if response.status_code in iam.IAM_RETRY_CODES: + continue + if response.status_code != http_client.OK: + raise exceptions.TransportError( + "Error calling sign_bytes: {}".format(response.json()) + ) + + return base64.b64decode(response.json()["signedBlob"]) finally: authed_session.close() - - if response.status_code != http_client.OK: - raise exceptions.TransportError( - "Error calling sign_bytes: {}".format(response.json()) - ) - - return base64.b64decode(response.json()["signedBlob"]) + raise exceptions.TransportError("exhausted signBlob endpoint retries") @property def signer_email(self): @@ -422,9 +439,10 @@ def refresh(self, request): from google.auth.transport.requests import AuthorizedSession - iam_sign_endpoint = iam._IAM_IDTOKEN_ENDPOINT.format( - self._target_credentials.signer_email - ) + iam_sign_endpoint = iam._IAM_IDTOKEN_ENDPOINT.replace( + credentials.DEFAULT_UNIVERSE_DOMAIN, + self._target_credentials.universe_domain, + ).format(self._target_credentials.signer_email) body = { "audience": self._target_audience, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/transport/_requests_base.py new/google_auth-2.36.0/google/auth/transport/_requests_base.py --- old/google_auth-2.35.0/google/auth/transport/_requests_base.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/transport/_requests_base.py 2024-11-06 19:02:16.000000000 +0100 @@ -13,7 +13,8 @@ # limitations under the License. """Transport adapter for Base Requests.""" - +# NOTE: The coverage for this file is temporarily disabled in `.coveragerc` +# since it is currently unused. import abc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/transport/requests.py new/google_auth-2.36.0/google/auth/transport/requests.py --- old/google_auth-2.35.0/google/auth/transport/requests.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/transport/requests.py 2024-11-06 19:02:16.000000000 +0100 @@ -38,7 +38,6 @@ from google.auth import exceptions from google.auth import transport import google.auth.transport._mtls_helper -from google.auth.transport._requests_base import _BaseAuthorizedSession from google.oauth2 import service_account _LOGGER = logging.getLogger(__name__) @@ -293,7 +292,7 @@ return super(_MutualTlsOffloadAdapter, self).proxy_manager_for(*args, **kwargs) -class AuthorizedSession(requests.Session, _BaseAuthorizedSession): +class AuthorizedSession(requests.Session): """A Requests Session class with credentials. This class is used to perform requests to API endpoints that require @@ -390,7 +389,7 @@ default_host=None, ): super(AuthorizedSession, self).__init__() - _BaseAuthorizedSession.__init__(self, credentials) + self.credentials = credentials self._refresh_status_codes = refresh_status_codes self._max_refresh_attempts = max_refresh_attempts self._refresh_timeout = refresh_timeout diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/auth/version.py new/google_auth-2.36.0/google/auth/version.py --- old/google_auth-2.35.0/google/auth/version.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/auth/version.py 2024-11-06 19:02:16.000000000 +0100 @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.35.0" +__version__ = "2.36.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/oauth2/_client.py new/google_auth-2.36.0/google/oauth2/_client.py --- old/google_auth-2.35.0/google/oauth2/_client.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/oauth2/_client.py 2024-11-06 19:02:16.000000000 +0100 @@ -30,6 +30,7 @@ from google.auth import _exponential_backoff from google.auth import _helpers +from google.auth import credentials from google.auth import exceptions from google.auth import jwt from google.auth import metrics @@ -319,7 +320,12 @@ def call_iam_generate_id_token_endpoint( - request, iam_id_token_endpoint, signer_email, audience, access_token + request, + iam_id_token_endpoint, + signer_email, + audience, + access_token, + universe_domain=credentials.DEFAULT_UNIVERSE_DOMAIN, ): """Call iam.generateIdToken endpoint to get ID token. @@ -339,7 +345,9 @@ response_data = _token_endpoint_request( request, - iam_id_token_endpoint.format(signer_email), + iam_id_token_endpoint.replace( + credentials.DEFAULT_UNIVERSE_DOMAIN, universe_domain + ).format(signer_email), body, access_token=access_token, use_json=True, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google/oauth2/service_account.py new/google_auth-2.36.0/google/oauth2/service_account.py --- old/google_auth-2.35.0/google/oauth2/service_account.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/google/oauth2/service_account.py 2024-11-06 19:02:16.000000000 +0100 @@ -812,6 +812,7 @@ self.signer_email, self._target_audience, jwt_credentials.token.decode(), + self._universe_domain, ) @_helpers.copy_docstring(credentials.Credentials) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google_auth.egg-info/PKG-INFO new/google_auth-2.36.0/google_auth.egg-info/PKG-INFO --- old/google_auth-2.35.0/google_auth.egg-info/PKG-INFO 2024-09-19 20:07:29.000000000 +0200 +++ new/google_auth-2.36.0/google_auth.egg-info/PKG-INFO 2024-11-06 19:05:17.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: google-auth -Version: 2.35.0 +Version: 2.36.0 Summary: Google Authentication Library Home-page: https://github.com/googleapis/google-auth-library-python Author: Google Cloud Platform diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/google_auth.egg-info/SOURCES.txt new/google_auth-2.36.0/google_auth.egg-info/SOURCES.txt --- old/google_auth-2.35.0/google_auth.egg-info/SOURCES.txt 2024-09-19 20:07:29.000000000 +0200 +++ new/google_auth-2.36.0/google_auth.egg-info/SOURCES.txt 2024-11-06 19:05:17.000000000 +0100 @@ -135,6 +135,7 @@ tests/data/external_subject_token.txt tests/data/gdch_service_account.json tests/data/impersonated_service_account_authorized_user_source.json +tests/data/impersonated_service_account_external_account_authorized_user_source.json tests/data/impersonated_service_account_service_account_source.json tests/data/impersonated_service_account_with_quota_project.json tests/data/old_oauth_credentials_py3.pickle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/compute_engine/test__metadata.py new/google_auth-2.36.0/tests/compute_engine/test__metadata.py --- old/google_auth-2.35.0/tests/compute_engine/test__metadata.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/compute_engine/test__metadata.py 2024-11-06 19:02:16.000000000 +0100 @@ -397,7 +397,7 @@ request.assert_called_once_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) assert universe_domain == "fake_universe_domain" @@ -410,7 +410,7 @@ request.assert_called_once_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) assert universe_domain == "googleapis.com" @@ -425,7 +425,7 @@ request.assert_called_once_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) assert universe_domain == "googleapis.com" @@ -445,7 +445,7 @@ request.assert_called_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) assert request.call_count == 5 @@ -487,12 +487,12 @@ request_error.assert_called_once_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) request_ok.assert_called_once_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) @@ -511,7 +511,7 @@ request.assert_called_once_with( method="GET", - url=_metadata._METADATA_ROOT + "universe/universe_domain", + url=_metadata._METADATA_ROOT + "universe/universe-domain", headers=_metadata._METADATA_HEADERS, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/compute_engine/test_credentials.py new/google_auth-2.36.0/tests/compute_engine/test_credentials.py --- old/google_auth-2.35.0/tests/compute_engine/test_credentials.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/compute_engine/test_credentials.py 2024-11-06 19:02:16.000000000 +0100 @@ -487,6 +487,16 @@ }, ) + # mock information about universe_domain + responses.add( + responses.GET, + "http://metadata.google.internal/computeMetadata/v1/universe/" + "universe-domain", + status=200, + content_type="application/json", + json={}, + ) + # mock token for credentials responses.add( responses.GET, @@ -659,6 +669,16 @@ }, ) + # stubby response about universe_domain + responses.add( + responses.GET, + "http://metadata.google.internal/computeMetadata/v1/universe/" + "universe-domain", + status=200, + content_type="application/json", + json={}, + ) + # mock sign blob endpoint signature = base64.b64encode(b"some-signature").decode("utf-8") responses.add( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/data/impersonated_service_account_external_account_authorized_user_source.json new/google_auth-2.36.0/tests/data/impersonated_service_account_external_account_authorized_user_source.json --- old/google_auth-2.35.0/tests/data/impersonated_service_account_external_account_authorized_user_source.json 1970-01-01 01:00:00.000000000 +0100 +++ new/google_auth-2.36.0/tests/data/impersonated_service_account_external_account_authorized_user_source.json 2024-11-06 19:02:16.000000000 +0100 @@ -0,0 +1,16 @@ +{ + "delegates": [ + "[email protected]" + ], + "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/[email protected]:generateAccessToken", + "source_credentials": { + "type": "external_account_authorized_user", + "audience": "//iam.googleapis.com/locations/global/workforcePools/$WORKFORCE_POOL_ID/providers/$PROVIDER_ID", + "refresh_token": "refreshToken", + "token_url": "https://sts.googleapis.com/v1/oauth/token", + "token_info_url": "https://sts.googleapis.com/v1/instrospect", + "client_id": "clientId", + "client_secret": "clientSecret" + }, + "type": "impersonated_service_account" +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/oauth2/test__client.py new/google_auth-2.36.0/tests/oauth2/test__client.py --- old/google_auth-2.35.0/tests/oauth2/test__client.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/oauth2/test__client.py 2024-11-06 19:02:16.000000000 +0100 @@ -324,6 +324,7 @@ "fake_email", "fake_audience", "fake_access_token", + "googleapis.com", ) assert ( @@ -361,6 +362,7 @@ "fake_email", "fake_audience", "fake_access_token", + "googleapis.com", ) assert excinfo.match("No ID token in response") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/oauth2/test_service_account.py new/google_auth-2.36.0/tests/oauth2/test_service_account.py --- old/google_auth-2.35.0/tests/oauth2/test_service_account.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/oauth2/test_service_account.py 2024-11-06 19:02:16.000000000 +0100 @@ -789,7 +789,7 @@ ) request = mock.Mock() credentials.refresh(request) - req, iam_endpoint, signer_email, target_audience, access_token = call_iam_generate_id_token_endpoint.call_args[ + req, iam_endpoint, signer_email, target_audience, access_token, universe_domain = call_iam_generate_id_token_endpoint.call_args[ 0 ] assert req == request @@ -811,7 +811,7 @@ ) request = mock.Mock() credentials.refresh(request) - req, iam_endpoint, signer_email, target_audience, access_token = call_iam_generate_id_token_endpoint.call_args[ + req, iam_endpoint, signer_email, target_audience, access_token, universe_domain = call_iam_generate_id_token_endpoint.call_args[ 0 ] assert req == request diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/test__default.py new/google_auth-2.36.0/tests/test__default.py --- old/google_auth-2.35.0/tests/test__default.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/test__default.py 2024-11-06 19:02:16.000000000 +0100 @@ -153,6 +153,11 @@ DATA_DIR, "impersonated_service_account_service_account_source.json" ) +IMPERSONATED_SERVICE_ACCOUNT_EXTERNAL_ACCOUNT_AUTHORIZED_USER_SOURCE_FILE = os.path.join( + DATA_DIR, + "impersonated_service_account_external_account_authorized_user_source.json", +) + EXTERNAL_ACCOUNT_AUTHORIZED_USER_FILE = os.path.join( DATA_DIR, "external_account_authorized_user.json" ) @@ -365,6 +370,17 @@ assert not credentials._quota_project_id +def test_load_credentials_from_file_impersonated_with_external_account_authorized_user_source(): + credentials, _ = _default.load_credentials_from_file( + IMPERSONATED_SERVICE_ACCOUNT_EXTERNAL_ACCOUNT_AUTHORIZED_USER_SOURCE_FILE + ) + assert isinstance(credentials, impersonated_credentials.Credentials) + assert isinstance( + credentials._source_credentials, external_account_authorized_user.Credentials + ) + assert not credentials._quota_project_id + + def test_load_credentials_from_file_impersonated_passing_quota_project(): credentials, _ = _default.load_credentials_from_file( IMPERSONATED_SERVICE_ACCOUNT_SERVICE_ACCOUNT_SOURCE_FILE, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/test_iam.py new/google_auth-2.36.0/tests/test_iam.py --- old/google_auth-2.35.0/tests/test_iam.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/test_iam.py 2024-11-06 19:02:16.000000000 +0100 @@ -91,6 +91,7 @@ assert returned_signature == signature kwargs = request.call_args[1] assert kwargs["headers"]["Content-Type"] == "application/json" + request.call_count == 1 def test_sign_bytes_failure(self): request = make_request(http_client.UNAUTHORIZED) @@ -100,3 +101,15 @@ with pytest.raises(exceptions.TransportError): signer.sign("123") + request.call_count == 1 + + @mock.patch("time.sleep", return_value=None) + def test_sign_bytes_retryable_failure(self, mock_time): + request = make_request(http_client.INTERNAL_SERVER_ERROR) + credentials = make_credentials() + + signer = iam.Signer(request, credentials, mock.sentinel.service_account_email) + + with pytest.raises(exceptions.TransportError): + signer.sign("123") + request.call_count == 3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google_auth-2.35.0/tests/test_impersonated_credentials.py new/google_auth-2.36.0/tests/test_impersonated_credentials.py --- old/google_auth-2.35.0/tests/test_impersonated_credentials.py 2024-09-19 20:05:39.000000000 +0200 +++ new/google_auth-2.36.0/tests/test_impersonated_credentials.py 2024-11-06 19:02:16.000000000 +0100 @@ -146,6 +146,13 @@ "principal": "[email protected]", } + def test_universe_domain_matching_source(self): + source_credentials = service_account.Credentials( + SIGNER, "[email protected]", TOKEN_URI, universe_domain="foo.bar" + ) + credentials = self.make_credentials(source_credentials=source_credentials) + assert credentials.universe_domain == "foo.bar" + def test__make_copy_get_cred_info(self): credentials = self.make_credentials() credentials._cred_file_path = "/path/to/file" @@ -232,6 +239,38 @@ ) @pytest.mark.parametrize("use_data_bytes", [True, False]) + def test_refresh_success_nonGdu(self, use_data_bytes, mock_donor_credentials): + source_credentials = service_account.Credentials( + SIGNER, "[email protected]", TOKEN_URI, universe_domain="foo.bar" + ) + credentials = self.make_credentials( + lifetime=None, source_credentials=source_credentials + ) + token = "token" + + expire_time = ( + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + response_body = {"accessToken": token, "expireTime": expire_time} + + request = self.make_request( + data=json.dumps(response_body), + status=http_client.OK, + use_data_bytes=use_data_bytes, + ) + + credentials.refresh(request) + + assert credentials.valid + assert not credentials.expired + # Confirm override endpoint used. + request_kwargs = request.call_args[1] + assert ( + request_kwargs["url"] + == "https://iamcredentials.foo.bar/v1/projects/-/serviceAccounts/[email protected]:generateAccessToken" + ) + + @pytest.mark.parametrize("use_data_bytes", [True, False]) def test_refresh_success_iam_endpoint_override( self, use_data_bytes, mock_donor_credentials ): @@ -397,6 +436,38 @@ def test_sign_bytes(self, mock_donor_credentials, mock_authorizedsession_sign): credentials = self.make_credentials(lifetime=None) + expected_url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/[email protected]:signBlob" + self._sign_bytes_helper( + credentials, + mock_donor_credentials, + mock_authorizedsession_sign, + expected_url, + ) + + def test_sign_bytes_nonGdu( + self, mock_donor_credentials, mock_authorizedsession_sign + ): + source_credentials = service_account.Credentials( + SIGNER, "[email protected]", TOKEN_URI, universe_domain="foo.bar" + ) + credentials = self.make_credentials( + lifetime=None, source_credentials=source_credentials + ) + expected_url = "https://iamcredentials.foo.bar/v1/projects/-/serviceAccounts/[email protected]:signBlob" + self._sign_bytes_helper( + credentials, + mock_donor_credentials, + mock_authorizedsession_sign, + expected_url, + ) + + def _sign_bytes_helper( + self, + credentials, + mock_donor_credentials, + mock_authorizedsession_sign, + expected_url, + ): token = "token" expire_time = ( @@ -412,11 +483,19 @@ request.return_value = response credentials.refresh(request) - assert credentials.valid assert not credentials.expired signature = credentials.sign_bytes(b"signed bytes") + mock_authorizedsession_sign.assert_called_with( + mock.ANY, + "POST", + expected_url, + None, + json={"payload": "c2lnbmVkIGJ5dGVz", "delegates": []}, + headers={"Content-Type": "application/json"}, + ) + assert signature == b"signature" def test_sign_bytes_failure(self): @@ -426,12 +505,28 @@ "google.auth.transport.requests.AuthorizedSession.request", autospec=True ) as auth_session: data = {"error": {"code": 403, "message": "unauthorized"}} - auth_session.return_value = MockResponse(data, http_client.FORBIDDEN) + mock_response = MockResponse(data, http_client.UNAUTHORIZED) + auth_session.return_value = mock_response with pytest.raises(exceptions.TransportError) as excinfo: credentials.sign_bytes(b"foo") assert excinfo.match("'code': 403") + @mock.patch("time.sleep", return_value=None) + def test_sign_bytes_retryable_failure(self, mock_time): + credentials = self.make_credentials(lifetime=None) + + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.request", autospec=True + ) as auth_session: + data = {"error": {"code": 500, "message": "internal_failure"}} + mock_response = MockResponse(data, http_client.INTERNAL_SERVER_ERROR) + auth_session.return_value = mock_response + + with pytest.raises(exceptions.TransportError) as excinfo: + credentials.sign_bytes(b"foo") + assert excinfo.match("exhausted signBlob endpoint retries") + def test_with_quota_project(self): credentials = self.make_credentials() @@ -547,6 +642,45 @@ self, mock_donor_credentials, mock_authorizedsession_idtoken ): credentials = self.make_credentials(lifetime=None) + target_credentials = self.make_credentials(lifetime=None) + expected_url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/[email protected]:generateIdToken" + self._test_id_token_helper( + credentials, + target_credentials, + mock_donor_credentials, + mock_authorizedsession_idtoken, + expected_url, + ) + + def test_id_token_from_credential_nonGdu( + self, mock_donor_credentials, mock_authorizedsession_idtoken + ): + source_credentials = service_account.Credentials( + SIGNER, "[email protected]", TOKEN_URI, universe_domain="foo.bar" + ) + credentials = self.make_credentials( + lifetime=None, source_credentials=source_credentials + ) + target_credentials = self.make_credentials( + lifetime=None, source_credentials=source_credentials + ) + expected_url = "https://iamcredentials.foo.bar/v1/projects/-/serviceAccounts/[email protected]:generateIdToken" + self._test_id_token_helper( + credentials, + target_credentials, + mock_donor_credentials, + mock_authorizedsession_idtoken, + expected_url, + ) + + def _test_id_token_helper( + self, + credentials, + target_credentials, + mock_donor_credentials, + mock_authorizedsession_idtoken, + expected_url, + ): token = "token" target_audience = "https://foo.bar" @@ -564,17 +698,19 @@ assert credentials.valid assert not credentials.expired - new_credentials = self.make_credentials(lifetime=None) - id_creds = impersonated_credentials.IDTokenCredentials( credentials, target_audience=target_audience, include_email=True ) - id_creds = id_creds.from_credentials(target_credentials=new_credentials) + id_creds = id_creds.from_credentials(target_credentials=target_credentials) id_creds.refresh(request) + args = mock_authorizedsession_idtoken.call_args.args + + assert args[2] == expected_url + assert id_creds.token == ID_TOKEN_DATA assert id_creds._include_email is True - assert id_creds._target_credentials is new_credentials + assert id_creds._target_credentials is target_credentials def test_id_token_with_target_audience( self, mock_donor_credentials, mock_authorizedsession_idtoken
