diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 894fb6bc9..5fc5daa31 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,4 +13,4 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:f62c53736eccb0c4934a3ea9316e0d57696bb49c1a7c86c726e9bb8a2f87dadf + digest: sha256:8555f0e37e6261408f792bfd6635102d2da5ad73f8f09bcb24f25e6afb5fac97 diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in index cbd7e77f4..882178ce6 100644 --- a/.kokoro/requirements.in +++ b/.kokoro/requirements.in @@ -1,5 +1,5 @@ gcp-docuploader -gcp-releasetool +gcp-releasetool>=1.10.5 # required for compatibility with cryptography>=39.x importlib-metadata typing-extensions twine diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 096e4800a..fa99c1290 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -154,9 +154,9 @@ gcp-docuploader==0.6.4 \ --hash=sha256:01486419e24633af78fd0167db74a2763974765ee8078ca6eb6964d0ebd388af \ --hash=sha256:70861190c123d907b3b067da896265ead2eeb9263969d6955c9e0bb091b5ccbf # via -r requirements.in -gcp-releasetool==1.10.0 \ - --hash=sha256:72a38ca91b59c24f7e699e9227c90cbe4dd71b789383cb0164b088abae294c83 \ - --hash=sha256:8c7c99320208383d4bb2b808c6880eb7a81424afe7cdba3c8d84b25f4f0e097d +gcp-releasetool==1.10.5 \ + --hash=sha256:174b7b102d704b254f2a26a3eda2c684fd3543320ec239baf771542a2e58e109 \ + --hash=sha256:e29d29927fe2ca493105a82958c6873bb2b90d503acac56be2c229e74de0eec9 # via -r requirements.in google-api-core==2.10.2 \ --hash=sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320 \ diff --git a/CHANGELOG.md b/CHANGELOG.md index e97486714..171fc3e7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ [1]: https://pypi.org/project/google-auth/#history +## [2.16.2](https://github.com/googleapis/google-auth-library-python/compare/v2.16.1...v2.16.2) (2023-03-02) + + +### Bug Fixes + +* Call gcloud config get project to get project for user cred ([#1243](https://github.com/googleapis/google-auth-library-python/issues/1243)) ([c078a13](https://github.com/googleapis/google-auth-library-python/commit/c078a13f3d7a6bda69efab11f11c1120e20137ef)) +* Do not use hardcoded string 'python', when you mean sys.executable. ([#1233](https://github.com/googleapis/google-auth-library-python/issues/1233)) ([91ac8e6](https://github.com/googleapis/google-auth-library-python/commit/91ac8e66396fd2335f2be6e7b40dc5c4f6e47bc2)) +* Don't retry if error or error_description is not string ([#1241](https://github.com/googleapis/google-auth-library-python/issues/1241)) ([e2d263a](https://github.com/googleapis/google-auth-library-python/commit/e2d263a2e79a35e8cc90aa338780d07c3313dc76)) +* Improve ADC related errors and warnings ([#1237](https://github.com/googleapis/google-auth-library-python/issues/1237)) ([2dfa213](https://github.com/googleapis/google-auth-library-python/commit/2dfa21371185340404d5d739723a8cd434886e02)) + ## [2.16.1](https://github.com/googleapis/google-auth-library-python/compare/v2.16.0...v2.16.1) (2023-02-17) diff --git a/google/auth/_cloud_sdk.py b/google/auth/_cloud_sdk.py index 40e6aec13..36c5b0158 100644 --- a/google/auth/_cloud_sdk.py +++ b/google/auth/_cloud_sdk.py @@ -14,12 +14,12 @@ """Helpers for reading the Google Cloud SDK's configuration.""" -import json import os import subprocess import six +from google.auth import _helpers from google.auth import environment_vars from google.auth import exceptions @@ -35,7 +35,7 @@ _CLOUD_SDK_POSIX_COMMAND = "gcloud" _CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd" # The command to get the Cloud SDK configuration -_CLOUD_SDK_CONFIG_COMMAND = ("config", "config-helper", "--format", "json") +_CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND = ("config", "get", "project") # The command to get google user access token _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND = ("auth", "print-access-token") # Cloud SDK's application-default client ID @@ -105,18 +105,14 @@ def get_project_id(): try: # Ignore the stderr coming from gcloud, so it won't be mixed into the output. # https://github.com/googleapis/google-auth-library-python/issues/673 - output = _run_subprocess_ignore_stderr((command,) + _CLOUD_SDK_CONFIG_COMMAND) - except (subprocess.CalledProcessError, OSError, IOError): - return None - - try: - configuration = json.loads(output.decode("utf-8")) - except ValueError: - return None + project = _run_subprocess_ignore_stderr( + (command,) + _CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND + ) - try: - return configuration["configuration"]["properties"]["core"]["project"] - except KeyError: + # Turn bytes into a string and remove "\n" + project = _helpers.from_bytes(project).strip() + return project if project else None + except (subprocess.CalledProcessError, OSError, IOError): return None diff --git a/google/auth/_default.py b/google/auth/_default.py index 195388c9d..997f2b4e8 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -48,23 +48,18 @@ ) # Help message when no credentials can be found. -_HELP_MESSAGE = """\ -Could not automatically determine credentials. Please set {env} or \ -explicitly create credentials and re-run the application. For more \ -information, please see \ -https://cloud.google.com/docs/authentication/getting-started -""".format( - env=environment_vars.CREDENTIALS -).strip() +_CLOUD_SDK_MISSING_CREDENTIALS = """\ +Your default credentials were not found. To set up Application Default Credentials, \ +see https://cloud.google.com/docs/authentication/external/set-up-adc for more information.\ +""" # Warning when using Cloud SDK user credentials _CLOUD_SDK_CREDENTIALS_WARNING = """\ Your application has authenticated using end user credentials from Google \ Cloud SDK without a quota project. You might receive a "quota exceeded" \ -or "API not enabled" error. We recommend you rerun \ -`gcloud auth application-default login` and make sure a quota project is \ -added. Or you can use service accounts instead. For more information \ -about service accounts, see https://cloud.google.com/docs/authentication/""" +or "API not enabled" error. See the following page for troubleshooting: \ +https://cloud.google.com/docs/authentication/adc-troubleshooting/user-creds. \ +""" # The subject token type used for AWS external_account credentials. _AWS_SUBJECT_TOKEN_TYPE = "urn:ietf:params:aws:token-type:aws4_request" @@ -650,4 +645,4 @@ def default(scopes=None, request=None, quota_project_id=None, default_scopes=Non ) return credentials, effective_project_id - raise exceptions.DefaultCredentialsError(_HELP_MESSAGE) + raise exceptions.DefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS) diff --git a/google/auth/_default_async.py b/google/auth/_default_async.py index 5a41f2a6e..93a570c77 100644 --- a/google/auth/_default_async.py +++ b/google/auth/_default_async.py @@ -281,4 +281,4 @@ def default_async(scopes=None, request=None, quota_project_id=None): ) return credentials, effective_project_id - raise exceptions.DefaultCredentialsError(_default._HELP_MESSAGE) + raise exceptions.DefaultCredentialsError(_default._CLOUD_SDK_MISSING_CREDENTIALS) diff --git a/google/auth/version.py b/google/auth/version.py index a982b4bb6..0b9b05843 100644 --- a/google/auth/version.py +++ b/google/auth/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.16.1" +__version__ = "2.16.2" diff --git a/google/oauth2/_client.py b/google/oauth2/_client.py index c2eb6443f..428993646 100644 --- a/google/oauth2/_client.py +++ b/google/oauth2/_client.py @@ -90,6 +90,11 @@ def _can_retry(status_code, response_data): error_desc = response_data.get("error_description") or "" error_code = response_data.get("error") or "" + if not isinstance(error_code, six.string_types) or not isinstance( + error_desc, six.string_types + ): + return False + # Per Oauth 2.0 RFC https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.2.1 # This is needed because a redirect will not return a 500 status code. retryable_error_descriptions = { diff --git a/samples/cloud-client/snippets/verify_google_idtoken.py b/samples/cloud-client/snippets/verify_google_idtoken.py index 4d2216efd..35b88c99e 100644 --- a/samples/cloud-client/snippets/verify_google_idtoken.py +++ b/samples/cloud-client/snippets/verify_google_idtoken.py @@ -48,7 +48,7 @@ def verify_google_idtoken(idtoken: str, audience="iap.googleapis.com", request = google.auth.transport.requests.Request() # Set the parameters and verify the token. # Setting "certs_url" is optional. When verifying a Google ID token, this is set by default. - result = id_token.verify_token(idtoken, request, audience) + result = id_token.verify_token(idtoken, request, audience, clock_skew_in_seconds=10) # Verify that the token contains subject and email claims. # Get the User id. diff --git a/system_tests/secrets.tar.enc b/system_tests/secrets.tar.enc index 1e7949e8e..522223f61 100644 Binary files a/system_tests/secrets.tar.enc and b/system_tests/secrets.tar.enc differ diff --git a/tests/data/cloud_sdk_config.json b/tests/data/cloud_sdk_config.json deleted file mode 100644 index a5fe4a9a4..000000000 --- a/tests/data/cloud_sdk_config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "configuration": { - "active_configuration": "default", - "properties": { - "core": { - "account": "user@example.com", - "disable_usage_reporting": "False", - "project": "example-project" - } - } - }, - "credential": { - "access_token": "don't use me", - "token_expiry": "2017-03-23T23:09:49Z" - }, - "sentinels": { - "config_sentinel": "/Users/example/.config/gcloud/config_sentinel" - } -} diff --git a/tests/oauth2/test__client.py b/tests/oauth2/test__client.py index b322eefed..ff3096057 100644 --- a/tests/oauth2/test__client.py +++ b/tests/oauth2/test__client.py @@ -94,7 +94,14 @@ def test__can_retry_message(response_data): assert _client._can_retry(http_client.OK, response_data) -@pytest.mark.parametrize("response_data", [{"error": "invalid_scope"}]) +@pytest.mark.parametrize( + "response_data", + [ + {"error": "invalid_scope"}, + {"error": {"foo": "bar"}}, + {"error_description": {"foo", "bar"}}, + ], +) def test__can_retry_no_retry_message(response_data): assert not _client._can_retry(http_client.OK, response_data) diff --git a/tests/test__cloud_sdk.py b/tests/test__cloud_sdk.py index c05c44320..e45c65bd9 100644 --- a/tests/test__cloud_sdk.py +++ b/tests/test__cloud_sdk.py @@ -16,6 +16,7 @@ import json import os import subprocess +import sys import mock import pytest # type: ignore @@ -36,17 +37,10 @@ with io.open(SERVICE_ACCOUNT_FILE, "rb") as fh: SERVICE_ACCOUNT_FILE_DATA = json.load(fh) -with io.open(os.path.join(DATA_DIR, "cloud_sdk_config.json"), "rb") as fh: - CLOUD_SDK_CONFIG_FILE_DATA = fh.read() - @pytest.mark.parametrize( "data, expected_project_id", - [ - (CLOUD_SDK_CONFIG_FILE_DATA, "example-project"), - (b"I am some bad json", None), - (b"{}", None), - ], + [(b"example-project\n", "example-project"), (b"", None)], ) def test_get_project_id(data, expected_project_id): check_output_patch = mock.patch( @@ -73,7 +67,7 @@ def test_get_project_id_call_error(check_output): def test__run_subprocess_ignore_stderr(): command = [ - "python", + sys.executable, "-c", "from __future__ import print_function;" + "import sys;" @@ -93,9 +87,7 @@ def test__run_subprocess_ignore_stderr(): @mock.patch("os.name", new="nt") def test_get_project_id_windows(): check_output_patch = mock.patch( - "subprocess.check_output", - autospec=True, - return_value=CLOUD_SDK_CONFIG_FILE_DATA, + "subprocess.check_output", autospec=True, return_value=b"example-project\n" ) with check_output_patch as check_output: diff --git a/tests/test__default.py b/tests/test__default.py index b10fb192c..ccd4b2b9a 100644 --- a/tests/test__default.py +++ b/tests/test__default.py @@ -916,9 +916,11 @@ def test_default_without_project_id( autospec=True, ) def test_default_fail(unused_gce, unused_gae, unused_sdk, unused_explicit): - with pytest.raises(exceptions.DefaultCredentialsError): + with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: assert _default.default() + assert excinfo.match(_default._CLOUD_SDK_MISSING_CREDENTIALS) + @mock.patch( "google.auth._default._get_explicit_environ_credentials", @@ -1128,7 +1130,7 @@ def test_default_environ_external_credentials_bad_format(monkeypatch, tmpdir): def test_default_warning_without_quota_project_id_for_user_creds(get_adc_path): get_adc_path.return_value = AUTHORIZED_USER_CLOUD_SDK_FILE - with pytest.warns(UserWarning, match="Cloud SDK"): + with pytest.warns(UserWarning, match=_default._CLOUD_SDK_CREDENTIALS_WARNING): credentials, project_id = _default.default(quota_project_id=None)