diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e5950234..3a06300cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ [1]: https://pypi.org/project/google-auth/#history +## [1.24.0](https://www.github.com/googleapis/google-auth-library-python/compare/v1.23.0...v1.24.0) (2020-12-11) + + +### Features + +* add Python 3.9 support, drop Python 3.5 support ([#655](https://www.github.com/googleapis/google-auth-library-python/issues/655)) ([6de753d](https://www.github.com/googleapis/google-auth-library-python/commit/6de753d585254c813b3e6cbde27bf5466261ba10)), closes [#654](https://www.github.com/googleapis/google-auth-library-python/issues/654) + + +### Bug Fixes + +* avoid losing the original '_include_email' parameter in impersonated credentials ([#626](https://www.github.com/googleapis/google-auth-library-python/issues/626)) ([fd9b5b1](https://www.github.com/googleapis/google-auth-library-python/commit/fd9b5b10c80950784bd37ee56e32c505acb5078d)) + + +### Documentation + +* fix typo in import ([#651](https://www.github.com/googleapis/google-auth-library-python/issues/651)) ([3319ea8](https://www.github.com/googleapis/google-auth-library-python/commit/3319ea8ae876c73a94f51237b3bbb3f5df2aef89)), closes [#650](https://www.github.com/googleapis/google-auth-library-python/issues/650) + ## [1.23.0](https://www.github.com/googleapis/google-auth-library-python/compare/v1.22.1...v1.23.0) (2020-10-29) diff --git a/google/auth/aws.py b/google/auth/aws.py index 546e82016..42e21df70 100644 --- a/google/auth/aws.py +++ b/google/auth/aws.py @@ -109,6 +109,10 @@ def get_request_options( additional_headers = additional_headers or {} uri = urllib.parse.urlparse(url) + # Validate provided URL. + if not uri.hostname or uri.scheme != "https": + raise ValueError("Invalid AWS service URL") + header_map = _generate_authentication_header_map( host=uri.hostname, canonical_uri=os.path.normpath(uri.path or "/"), diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py index 4ac6c8c2c..29063103a 100644 --- a/google/auth/compute_engine/credentials.py +++ b/google/auth/compute_engine/credentials.py @@ -14,8 +14,8 @@ """Google Compute Engine credentials. -This module provides authentication for application running on Google Compute -Engine using the Compute Engine metadata server. +This module provides authentication for an application running on Google +Compute Engine using the Compute Engine metadata server. """ diff --git a/google/auth/credentials.py b/google/auth/credentials.py index bc42546b9..02082cad9 100644 --- a/google/auth/credentials.py +++ b/google/auth/credentials.py @@ -63,7 +63,7 @@ def expired(self): if not self.expiry: return False - # Remove 5 minutes from expiry to err on the side of reporting + # Remove 10 seconds from expiry to err on the side of reporting # expiration early so that we avoid the 401-refresh-retry loop. skewed_expiry = self.expiry - _helpers.CLOCK_SKEW return _helpers.utcnow() >= skewed_expiry diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py index e2d0b3a82..b8a6c49a1 100644 --- a/google/auth/impersonated_credentials.py +++ b/google/auth/impersonated_credentials.py @@ -153,7 +153,7 @@ class Credentials(credentials.CredentialsWithQuotaProject, credentials.Signing): Initialize a source credential which does not have access to list bucket:: - from google.oauth2 import service_acccount + from google.oauth2 import service_account target_scopes = [ 'https://www.googleapis.com/auth/devstorage.read_only'] @@ -353,6 +353,7 @@ def from_credentials(self, target_credentials, target_audience=None): return self.__class__( target_credentials=self._target_credentials, target_audience=target_audience, + include_email=self._include_email, quota_project_id=self._quota_project_id, ) @@ -360,6 +361,7 @@ def with_target_audience(self, target_audience): return self.__class__( target_credentials=self._target_credentials, target_audience=target_audience, + include_email=self._include_email, quota_project_id=self._quota_project_id, ) diff --git a/noxfile.py b/noxfile.py index 79a09e4da..fa88d24b2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -73,7 +73,7 @@ def blacken(session): session.run("black", *BLACK_PATHS) -@nox.session(python=["3.6", "3.7", "3.8"]) +@nox.session(python=["3.6", "3.7", "3.8", "3.9"]) def unit(session): session.install(*TEST_DEPENDENCIES) session.install(*(ASYNC_DEPENDENCIES)) @@ -88,7 +88,7 @@ def unit(session): ) -@nox.session(python=["2.7", "3.5"]) +@nox.session(python=["2.7"]) def unit_prev_versions(session): session.install(*TEST_DEPENDENCIES) session.install(".") diff --git a/setup.py b/setup.py index d599ecc17..3006d9ace 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,8 @@ "pyasn1-modules>=0.2.1", # rsa==4.5 is the last version to support 2.7 # https://github.com/sybrenstuvel/python-rsa/issues/152#issuecomment-643470233 - 'rsa<4.6; python_version < "3.5"', - 'rsa>=3.1.4,<5; python_version >= "3.5"', + 'rsa<4.6; python_version < "3.6"', + 'rsa>=3.1.4,<5; python_version >= "3.6"', "setuptools>=40.3.0", "six>=1.9.0", ) @@ -34,7 +34,7 @@ with io.open("README.rst", "r") as fh: long_description = fh.read() -version = "1.23.0" +version = "1.24.0" setup( name="google-auth", @@ -48,17 +48,17 @@ namespace_packages=("google",), install_requires=DEPENDENCIES, extras_require=extras, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*", license="Apache 2.0", keywords="google auth oauth client", classifiers=[ "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", diff --git a/system_tests/noxfile.py b/system_tests/noxfile.py index 3c3617ebd..dcfe8ee81 100644 --- a/system_tests/noxfile.py +++ b/system_tests/noxfile.py @@ -168,11 +168,7 @@ def configure_cloud_sdk(session, application_default_credentials, project=False) # Test sesssions -<<<<<<< HEAD TEST_DEPENDENCIES_ASYNC = ["aiohttp", "pytest-asyncio", "nest-asyncio"] -======= -TEST_DEPENDENCIES_ASYNC = ["aiohttp < 3.7.0dev", "pytest-asyncio", "nest-asyncio"] ->>>>>>> upstream/byoid TEST_DEPENDENCIES_SYNC = ["pytest", "requests"] PYTHON_VERSIONS_ASYNC = ["3.7"] PYTHON_VERSIONS_SYNC = ["2.7", "3.7"] @@ -319,11 +315,7 @@ def default_explicit_service_account_async(session): session.env[EXPECT_PROJECT_ENV] = "1" session.install(*(TEST_DEPENDENCIES_SYNC + TEST_DEPENDENCIES_ASYNC)) session.install(LIBRARY_DIR) -<<<<<<< HEAD session.run("pytest", "system_tests_async/test_default.py", -======= - session.run("pytest", "system_tests_async/test_default.py", ->>>>>>> upstream/byoid "system_tests_async/test_id_token.py") diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt new file mode 100644 index 000000000..e69de29bb diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt new file mode 100644 index 000000000..e69de29bb diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt new file mode 100644 index 000000000..ff7f099d4 --- /dev/null +++ b/testing/constraints-3.6.txt @@ -0,0 +1,14 @@ +# This constraints file is used to check that lower bounds +# are correct in setup.py +# List *all* library dependencies and extras in this file. +# Pin the version to the lower bound. +# +# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", +# Then this file should have foo==1.14.0 +cachetools==2.0.0 +pyasn1-modules==0.2.1 +setuptools==40.3.0 +six==1.9.0 +rsa==4.6 +rsa==3.1.4 +aiohttp==3.6.2 \ No newline at end of file diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt new file mode 100644 index 000000000..e69de29bb diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt new file mode 100644 index 000000000..e69de29bb diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_aws.py b/tests/test_aws.py index 5d18f7ce1..07748f99f 100644 --- a/tests/test_aws.py +++ b/tests/test_aws.py @@ -524,6 +524,51 @@ def test_get_request_options( assert actual_signed_request == signed_request + def test_get_request_options_with_missing_scheme_url(self): + request_signer = aws.RequestSigner("us-east-2") + + with pytest.raises(ValueError) as excinfo: + request_signer.get_request_options( + { + "access_key_id": ACCESS_KEY_ID, + "secret_access_key": SECRET_ACCESS_KEY, + }, + "invalid", + "POST", + ) + + assert excinfo.match(r"Invalid AWS service URL") + + def test_get_request_options_with_invalid_scheme_url(self): + request_signer = aws.RequestSigner("us-east-2") + + with pytest.raises(ValueError) as excinfo: + request_signer.get_request_options( + { + "access_key_id": ACCESS_KEY_ID, + "secret_access_key": SECRET_ACCESS_KEY, + }, + "http://invalid", + "POST", + ) + + assert excinfo.match(r"Invalid AWS service URL") + + def test_get_request_options_with_missing_hostname_url(self): + request_signer = aws.RequestSigner("us-east-2") + + with pytest.raises(ValueError) as excinfo: + request_signer.get_request_options( + { + "access_key_id": ACCESS_KEY_ID, + "secret_access_key": SECRET_ACCESS_KEY, + }, + "https://", + "POST", + ) + + assert excinfo.match(r"Invalid AWS service URL") + class TestCredentials(object): AWS_REGION = "us-east-2" diff --git a/tests/test_impersonated_credentials.py b/tests/test_impersonated_credentials.py index 10b6c55c0..430c770d3 100644 --- a/tests/test_impersonated_credentials.py +++ b/tests/test_impersonated_credentials.py @@ -432,12 +432,13 @@ def test_id_token_from_credential( assert not credentials.expired id_creds = impersonated_credentials.IDTokenCredentials( - credentials, target_audience=target_audience + credentials, target_audience=target_audience, include_email=True ) id_creds = id_creds.from_credentials(target_credentials=credentials) id_creds.refresh(request) assert id_creds.token == ID_TOKEN_DATA + assert id_creds._include_email is True def test_id_token_with_target_audience( self, mock_donor_credentials, mock_authorizedsession_idtoken @@ -460,12 +461,15 @@ def test_id_token_with_target_audience( assert credentials.valid assert not credentials.expired - id_creds = impersonated_credentials.IDTokenCredentials(credentials) + id_creds = impersonated_credentials.IDTokenCredentials( + credentials, include_email=True + ) id_creds = id_creds.with_target_audience(target_audience=target_audience) id_creds.refresh(request) assert id_creds.token == ID_TOKEN_DATA assert id_creds.expiry == datetime.datetime.fromtimestamp(ID_TOKEN_EXPIRY) + assert id_creds._include_email is True def test_id_token_invalid_cred( self, mock_donor_credentials, mock_authorizedsession_idtoken