Hello community, here is the log from the commit of package python-google-auth for openSUSE:Factory checked in at 2020-06-21 18:52:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-google-auth (Old) and /work/SRC/openSUSE:Factory/.python-google-auth.new.3606 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-auth" Sun Jun 21 18:52:00 2020 rev:10 rq:815724 version:1.17.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-google-auth/python-google-auth.changes 2020-06-05 19:58:46.487639948 +0200 +++ /work/SRC/openSUSE:Factory/.python-google-auth.new.3606/python-google-auth.changes 2020-06-21 19:04:47.180691374 +0200 @@ -1,0 +2,19 @@ +Thu Jun 18 10:30:04 UTC 2020 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 1.17.2 + + Bug Fixes + * narrow acceptable RSA versions to maintain Python 2 compatability (#528) +- from version 1.17.1 + + Features + * add quota_project_id to service accounts; add with_quota_project methods (#519) +- from version 1.16.1 + + Bug Fixes + * fix impersonated cred exception doc (#521) + * replace environment variable GCE_METADATA_ROOT with GCE_METADATA_HOST (#433) +- from version 1.16.0 + + Features + * add helper func to for default encrypted cert (#514) + + Bug Fixes + * fix impersonated cred for gcloud (#516) + +------------------------------------------------------------------- Old: ---- google-auth-1.15.0.tar.gz New: ---- google-auth-1.17.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-google-auth.spec ++++++ --- /var/tmp/diff_new_pack.R4k2Ng/_old 2020-06-21 19:04:47.548692569 +0200 +++ /var/tmp/diff_new_pack.R4k2Ng/_new 2020-06-21 19:04:47.552692582 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-google-auth -Version: 1.15.0 +Version: 1.17.2 Release: 0 Summary: Google Authentication Library License: Apache-2.0 ++++++ google-auth-1.15.0.tar.gz -> google-auth-1.17.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/PKG-INFO new/google-auth-1.17.2/PKG-INFO --- old/google-auth-1.15.0/PKG-INFO 2020-05-18 19:28:26.764173000 +0200 +++ new/google-auth-1.17.2/PKG-INFO 2020-06-12 21:22:19.426281200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: google-auth -Version: 1.15.0 +Version: 1.17.2 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-1.15.0/google/auth/compute_engine/_metadata.py new/google-auth-1.17.2/google/auth/compute_engine/_metadata.py --- old/google-auth-1.15.0/google/auth/compute_engine/_metadata.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/google/auth/compute_engine/_metadata.py 2020-06-12 21:20:44.000000000 +0200 @@ -32,9 +32,16 @@ _LOGGER = logging.getLogger(__name__) -_METADATA_ROOT = "http://{}/computeMetadata/v1/".format( - os.getenv(environment_vars.GCE_METADATA_ROOT, "metadata.google.internal") -) +# Environment variable GCE_METADATA_HOST is originally named +# GCE_METADATA_ROOT. For compatiblity reasons, here it checks +# the new variable first; if not set, the system falls back +# to the old variable. +_GCE_METADATA_HOST = os.getenv(environment_vars.GCE_METADATA_HOST, None) +if not _GCE_METADATA_HOST: + _GCE_METADATA_HOST = os.getenv( + environment_vars.GCE_METADATA_ROOT, "metadata.google.internal" + ) +_METADATA_ROOT = "http://{}/computeMetadata/v1/".format(_GCE_METADATA_HOST) # This is used to ping the metadata server, it avoids the cost of a DNS # lookup. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/google/auth/environment_vars.py new/google-auth-1.17.2/google/auth/environment_vars.py --- old/google-auth-1.15.0/google/auth/environment_vars.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/google/auth/environment_vars.py 2020-06-12 21:20:44.000000000 +0200 @@ -40,9 +40,15 @@ # These two variables allow for customization of the addresses used when # contacting the GCE metadata service. +GCE_METADATA_HOST = "GCE_METADATA_HOST" GCE_METADATA_ROOT = "GCE_METADATA_ROOT" """Environment variable providing an alternate hostname or host:port to be -used for GCE metadata requests.""" +used for GCE metadata requests. + +This environment variable is originally named GCE_METADATA_ROOT. System will +check the new variable first; should there be no value present, +the system falls back to the old variable. +""" GCE_METADATA_IP = "GCE_METADATA_IP" """Environment variable providing an alternate ip:port to be used for ip-only diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/google/auth/impersonated_credentials.py new/google-auth-1.17.2/google/auth/impersonated_credentials.py --- old/google-auth-1.15.0/google/auth/impersonated_credentials.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/google/auth/impersonated_credentials.py 2020-06-12 21:20:44.000000000 +0200 @@ -75,12 +75,12 @@ API call. Raises: - TransportError: Raised if there is an underlying HTTP connection - Error - DefaultCredentialsError: Raised if the impersonated credentials - are not available. Common reasons are - `iamcredentials.googleapis.com` is not enabled or the - `Service Account Token Creator` is not assigned + google.auth.exceptions.TransportError: Raised if there is an underlying + HTTP connection error + google.auth.exceptions.RefreshError: Raised if the impersonated + credentials are not available. Common reasons are + `iamcredentials.googleapis.com` is not enabled or the + `Service Account Token Creator` is not assigned """ iam_endpoint = _IAM_ENDPOINT.format(principal) @@ -226,10 +226,6 @@ def refresh(self, request): self._update_token(request) - @property - def expired(self): - return _helpers.utcnow() >= self.expiry - def _update_token(self, request): """Updates credentials with a new access_token representing the impersonated account. @@ -239,8 +235,9 @@ to use for refreshing credentials. """ - # Refresh our source credentials. - self._source_credentials.refresh(request) + # Refresh our source credentials if it is not valid. + if not self._source_credentials.valid: + self._source_credentials.refresh(request) body = { "delegates": self._delegates, @@ -347,7 +344,9 @@ headers = {"Content-Type": "application/json"} - authed_session = AuthorizedSession(self._target_credentials._source_credentials) + authed_session = AuthorizedSession( + self._target_credentials._source_credentials, auth_request=request + ) response = authed_session.post( url=iam_sign_endpoint, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/google/auth/transport/mtls.py new/google-auth-1.17.2/google/auth/transport/mtls.py --- old/google-auth-1.15.0/google/auth/transport/mtls.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/google/auth/transport/mtls.py 2020-06-12 21:20:44.000000000 +0200 @@ -58,3 +58,45 @@ return cert_bytes, key_bytes return callback + + +def default_client_encrypted_cert_source(cert_path, key_path): + """Get a callback which returns the default encrpyted client SSL credentials. + + Args: + cert_path (str): The cert file path. The default client certificate will + be written to this file when the returned callback is called. + key_path (str): The key file path. The default encrypted client key will + be written to this file when the returned callback is called. + + Returns: + Callable[[], [str, str, bytes]]: A callback which generates the default + client certificate, encrpyted private key and passphrase. It writes + the certificate and private key into the cert_path and key_path, and + returns the cert_path, key_path and passphrase bytes. + + Raises: + google.auth.exceptions.DefaultClientCertSourceError: If any problem + occurs when loading or saving the client certificate and key. + """ + if not has_default_client_cert_source(): + raise exceptions.MutualTLSChannelError( + "Default client encrypted cert source doesn't exist" + ) + + def callback(): + try: + _, cert_bytes, key_bytes, passphrase_bytes = _mtls_helper.get_client_ssl_credentials( + generate_encrypted_key=True + ) + with open(cert_path, "wb") as cert_file: + cert_file.write(cert_bytes) + with open(key_path, "wb") as key_file: + key_file.write(key_bytes) + except (exceptions.ClientCertError, OSError) as caught_exc: + new_exc = exceptions.MutualTLSChannelError(caught_exc) + six.raise_from(new_exc, caught_exc) + + return cert_path, key_path, passphrase_bytes + + return callback diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/google/oauth2/credentials.py new/google-auth-1.17.2/google/oauth2/credentials.py --- old/google-auth-1.15.0/google/oauth2/credentials.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/google/oauth2/credentials.py 2020-06-12 21:20:44.000000000 +0200 @@ -48,7 +48,13 @@ class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): - """Credentials using OAuth 2.0 access and refresh tokens.""" + """Credentials using OAuth 2.0 access and refresh tokens. + + The credentials are considered immutable. If you want to modify the + quota project, use :meth:`with_quota_project` or :: + + credentials = credentials.with_quota_project('myproject-123) + """ def __init__( self, @@ -160,6 +166,27 @@ the initial token is requested and can not be changed.""" return False + def with_quota_project(self, quota_project_id): + """Returns a copy of these credentials with a modified quota project + + Args: + quota_project_id (str): The project to use for quota and + billing purposes + + Returns: + google.oauth2.credentials.Credentials: A new credentials instance. + """ + return self.__class__( + self.token, + refresh_token=self.refresh_token, + id_token=self.id_token, + token_uri=self.token_uri, + client_id=self.client_id, + client_secret=self.client_secret, + scopes=self.scopes, + quota_project_id=quota_project_id, + ) + @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): if ( @@ -273,8 +300,9 @@ generated JSON. Returns: - str: A JSON representation of this instance, suitable to pass to - from_json(). + str: A JSON representation of this instance. When converted into + a dictionary, it can be passed to from_authorized_user_info() + to create a new credential instance. """ prep = { "token": self.token, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/google/oauth2/service_account.py new/google-auth-1.17.2/google/oauth2/service_account.py --- old/google-auth-1.15.0/google/oauth2/service_account.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/google/oauth2/service_account.py 2020-06-12 21:20:44.000000000 +0200 @@ -112,6 +112,10 @@ scoped_credentials = credentials.with_scopes(['email']) delegated_credentials = credentials.with_subject(subject) + + To add a quota project, use :meth:`with_quota_project`:: + + credentials = credentials.with_quota_project('myproject-123') """ def __init__( @@ -122,6 +126,7 @@ scopes=None, subject=None, project_id=None, + quota_project_id=None, additional_claims=None, ): """ @@ -135,6 +140,8 @@ user to for which to request delegated access. project_id (str): Project ID associated with the service account credential. + quota_project_id (Optional[str]): The project ID used for quota and + billing. additional_claims (Mapping[str, str]): Any additional claims for the JWT assertion used in the authorization grant. @@ -150,6 +157,7 @@ self._service_account_email = service_account_email self._subject = subject self._project_id = project_id + self._quota_project_id = quota_project_id self._token_uri = token_uri if additional_claims is not None: @@ -230,6 +238,11 @@ return self._project_id @property + def quota_project_id(self): + """Project ID to use for quota and billing purposes.""" + return self._quota_project_id + + @property def requires_scopes(self): """Checks if the credentials requires scopes. @@ -247,6 +260,7 @@ token_uri=self._token_uri, subject=self._subject, project_id=self._project_id, + quota_project_id=self._quota_project_id, additional_claims=self._additional_claims.copy(), ) @@ -267,6 +281,7 @@ token_uri=self._token_uri, subject=subject, project_id=self._project_id, + quota_project_id=self._quota_project_id, additional_claims=self._additional_claims.copy(), ) @@ -292,9 +307,32 @@ token_uri=self._token_uri, subject=self._subject, project_id=self._project_id, + quota_project_id=self._quota_project_id, additional_claims=new_additional_claims, ) + def with_quota_project(self, quota_project_id): + """Returns a copy of these credentials with a modified quota project. + + Args: + quota_project_id (str): The project to use for quota and + billing purposes + + Returns: + google.auth.service_account.Credentials: A new credentials + instance. + """ + return self.__class__( + self._signer, + service_account_email=self._service_account_email, + scopes=self._scopes, + token_uri=self._token_uri, + subject=self._subject, + project_id=self._project_id, + quota_project_id=quota_project_id, + additional_claims=self._additional_claims.copy(), + ) + def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. @@ -335,6 +373,12 @@ self.token = access_token self.expiry = expiry + @_helpers.copy_docstring(credentials.Credentials) + def apply(self, headers, token=None): + super(Credentials, self).apply(headers, token=token) + if self.quota_project_id is not None: + headers["x-goog-user-project"] = self.quota_project_id + @_helpers.copy_docstring(credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/google_auth.egg-info/PKG-INFO new/google-auth-1.17.2/google_auth.egg-info/PKG-INFO --- old/google-auth-1.15.0/google_auth.egg-info/PKG-INFO 2020-05-18 19:28:26.000000000 +0200 +++ new/google-auth-1.17.2/google_auth.egg-info/PKG-INFO 2020-06-12 21:22:19.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: google-auth -Version: 1.15.0 +Version: 1.17.2 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-1.15.0/google_auth.egg-info/requires.txt new/google-auth-1.17.2/google_auth.egg-info/requires.txt --- old/google-auth-1.15.0/google_auth.egg-info/requires.txt 2020-05-18 19:28:26.000000000 +0200 +++ new/google-auth-1.17.2/google_auth.egg-info/requires.txt 2020-06-12 21:22:19.000000000 +0200 @@ -1,5 +1,10 @@ cachetools<5.0,>=2.0.0 pyasn1-modules>=0.2.1 -rsa<4.1,>=3.1.4 setuptools>=40.3.0 six>=1.9.0 + +[:python_version < "3"] +rsa<4.1 + +[:python_version >= "3"] +rsa<5,>=3.1.4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/setup.py new/google-auth-1.17.2/setup.py --- old/google-auth-1.15.0/setup.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/setup.py 2020-06-12 21:20:44.000000000 +0200 @@ -21,7 +21,9 @@ DEPENDENCIES = ( "cachetools>=2.0.0,<5.0", "pyasn1-modules>=0.2.1", - "rsa>=3.1.4,<4.1", + # rsa >= 4.1 no longer supports python 2 https://github.com/sybrenstuvel/python-rsa/issues/152 + 'rsa<4.1; python_version < "3"', + 'rsa>=3.1.4,<5; python_version >= "3"', "setuptools>=40.3.0", "six>=1.9.0", ) @@ -30,7 +32,7 @@ with io.open("README.rst", "r") as fh: long_description = fh.read() -version = "1.15.0" +version = "1.17.2" setup( name="google-auth", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/tests/compute_engine/test__metadata.py new/google-auth-1.17.2/tests/compute_engine/test__metadata.py --- old/google-auth-1.15.0/tests/compute_engine/test__metadata.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/tests/compute_engine/test__metadata.py 2020-06-12 21:20:44.000000000 +0200 @@ -155,7 +155,27 @@ assert result == data -def test_get_success_custom_root(): +def test_get_success_custom_root_new_variable(): + request = make_request("{}", headers={"content-type": "application/json"}) + + fake_root = "another.metadata.service" + os.environ[environment_vars.GCE_METADATA_HOST] = fake_root + reload_module(_metadata) + + try: + _metadata.get(request, PATH) + finally: + del os.environ[environment_vars.GCE_METADATA_HOST] + reload_module(_metadata) + + request.assert_called_once_with( + method="GET", + url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH), + headers=_metadata._METADATA_HEADERS, + ) + + +def test_get_success_custom_root_old_variable(): request = make_request("{}", headers={"content-type": "application/json"}) fake_root = "another.metadata.service" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/tests/oauth2/test_credentials.py new/google-auth-1.17.2/tests/oauth2/test_credentials.py --- old/google-auth-1.15.0/tests/oauth2/test_credentials.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/tests/oauth2/test_credentials.py 2020-06-12 21:20:44.000000000 +0200 @@ -323,6 +323,22 @@ creds.apply(headers) assert "x-goog-user-project" not in headers + def test_with_quota_project(self): + creds = credentials.Credentials( + token="token", + refresh_token=self.REFRESH_TOKEN, + token_uri=self.TOKEN_URI, + client_id=self.CLIENT_ID, + client_secret=self.CLIENT_SECRET, + quota_project_id="quota-project-123", + ) + + new_creds = creds.with_quota_project("new-project-456") + assert new_creds.quota_project_id == "new-project-456" + headers = {} + creds.apply(headers) + assert "x-goog-user-project" in headers + def test_from_authorized_user_info(self): info = AUTH_USER_INFO.copy() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/tests/oauth2/test_service_account.py new/google-auth-1.17.2/tests/oauth2/test_service_account.py --- old/google-auth-1.15.0/tests/oauth2/test_service_account.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/tests/oauth2/test_service_account.py 2020-06-12 21:20:44.000000000 +0200 @@ -147,6 +147,14 @@ new_credentials = credentials.with_claims({"meep": "moop"}) assert new_credentials._additional_claims == {"meep": "moop"} + def test_with_quota_project(self): + credentials = self.make_credentials() + new_credentials = credentials.with_quota_project("new-project-456") + assert new_credentials.quota_project_id == "new-project-456" + hdrs = {} + new_credentials.apply(hdrs, token="tok") + assert "x-goog-user-project" in hdrs + def test__make_authorization_grant_assertion(self): credentials = self.make_credentials() token = credentials._make_authorization_grant_assertion() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/tests/test_impersonated_credentials.py new/google-auth-1.17.2/tests/test_impersonated_credentials.py --- old/google-auth-1.15.0/tests/test_impersonated_credentials.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/tests/test_impersonated_credentials.py 2020-06-12 21:20:44.000000000 +0200 @@ -172,6 +172,44 @@ assert credentials.valid assert not credentials.expired + @pytest.mark.parametrize("time_skew", [100, -100]) + def test_refresh_source_credentials(self, time_skew): + credentials = self.make_credentials(lifetime=None) + + # Source credentials is refreshed only if it is expired within + # _helpers.CLOCK_SKEW from now. We add a time_skew to the expiry, so + # source credentials is refreshed only if time_skew <= 0. + credentials._source_credentials.expiry = ( + _helpers.utcnow() + + _helpers.CLOCK_SKEW + + datetime.timedelta(seconds=time_skew) + ) + credentials._source_credentials.token = "Token" + + with mock.patch( + "google.oauth2.service_account.Credentials.refresh", autospec=True + ) as source_cred_refresh: + 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 + ) + + credentials.refresh(request) + + assert credentials.valid + assert not credentials.expired + + # Source credentials is refreshed only if it is expired within + # _helpers.CLOCK_SKEW + if time_skew > 0: + source_cred_refresh.assert_not_called() + else: + source_cred_refresh.assert_called_once() + def test_refresh_failure_malformed_expire_time(self, mock_donor_credentials): credentials = self.make_credentials(lifetime=None) token = "token" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-auth-1.15.0/tests/transport/test_mtls.py new/google-auth-1.17.2/tests/transport/test_mtls.py --- old/google-auth-1.15.0/tests/transport/test_mtls.py 2020-05-18 19:26:54.000000000 +0200 +++ new/google-auth-1.17.2/tests/transport/test_mtls.py 2020-06-12 21:20:44.000000000 +0200 @@ -53,3 +53,31 @@ callback = mtls.default_client_cert_source() with pytest.raises(exceptions.MutualTLSChannelError): callback() + + [email protected]( + "google.auth.transport._mtls_helper.get_client_ssl_credentials", autospec=True +) [email protected]("google.auth.transport.mtls.has_default_client_cert_source", autospec=True) +def test_default_client_encrypted_cert_source( + has_default_client_cert_source, get_client_ssl_credentials +): + # Test default client cert source doesn't exist. + has_default_client_cert_source.return_value = False + with pytest.raises(exceptions.MutualTLSChannelError): + mtls.default_client_encrypted_cert_source("cert_path", "key_path") + + # The following tests will assume default client cert source exists. + has_default_client_cert_source.return_value = True + + # Test good callback. + get_client_ssl_credentials.return_value = (True, b"cert", b"key", b"passphrase") + callback = mtls.default_client_encrypted_cert_source("cert_path", "key_path") + with mock.patch("{}.open".format(__name__), return_value=mock.MagicMock()): + assert callback() == ("cert_path", "key_path", b"passphrase") + + # Test bad callback which throws exception. + get_client_ssl_credentials.side_effect = exceptions.ClientCertError() + callback = mtls.default_client_encrypted_cert_source("cert_path", "key_path") + with pytest.raises(exceptions.MutualTLSChannelError): + callback()
