diff --git a/google/auth/_default.py b/google/auth/_default.py index 4dc0725e7..f889e5872 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -230,6 +230,11 @@ def _get_explicit_environ_credentials(): def _get_gae_credentials(): """Gets Google App Engine App Identity credentials and project ID.""" + # If not GAE gen1, prefer the metadata service even if the GAE APIs are + # available as per https://google.aip.dev/auth/4115. + if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27": + return None, None + # While this library is normally bundled with app_engine, there are # some cases where it's not available, so we tolerate ImportError. try: diff --git a/google/auth/environment_vars.py b/google/auth/environment_vars.py index f02774181..d36d6c4af 100644 --- a/google/auth/environment_vars.py +++ b/google/auth/environment_vars.py @@ -60,6 +60,12 @@ The default value is false. Users have to explicitly set this value to true in order to use client certificate to establish a mutual TLS channel.""" +LEGACY_APPENGINE_RUNTIME = "APPENGINE_RUNTIME" +"""Gen1 environment variable defining the App Engine Runtime. + +Used to distinguish between GAE gen1 and GAE gen2+. +""" + # AWS environment variables used with AWS workload identity pools to retrieve # AWS security credentials and the AWS region needed to create a serialized # signed requests to the AWS STS GetCalledIdentity API that can be exchanged diff --git a/tests/test__default.py b/tests/test__default.py index e13689625..94254b8bc 100644 --- a/tests/test__default.py +++ b/tests/test__default.py @@ -447,7 +447,9 @@ def app_identity(monkeypatch): yield app_identity_module -def test__get_gae_credentials(app_identity): +@mock.patch.dict(os.environ) +def test__get_gae_credentials_gen1(app_identity): + os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27" app_identity.get_application_id.return_value = mock.sentinel.project credentials, project_id = _default._get_gae_credentials() @@ -455,6 +457,19 @@ def test__get_gae_credentials(app_identity): assert isinstance(credentials, app_engine.Credentials) assert project_id == mock.sentinel.project +@mock.patch.dict(os.environ) +def test__get_gae_credentials_gen2(): + os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python37" + os.environ["GAE_RUNTIME"] = "python37" + credentials, project_id = _default._get_gae_credentials() + assert credentials is None + assert project_id is None + +def test__get_gae_credentials_env_unset(): + assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ + credentials, project_id = _default._get_gae_credentials() + assert credentials is None + assert project_id is None def test__get_gae_credentials_no_app_engine(): import sys diff --git a/tests_async/test__default_async.py b/tests_async/test__default_async.py index 527a8da45..aedd38a0a 100644 --- a/tests_async/test__default_async.py +++ b/tests_async/test__default_async.py @@ -283,8 +283,9 @@ def app_identity(monkeypatch): monkeypatch.setattr(app_engine, "app_identity", app_identity_module) yield app_identity_module - -def test__get_gae_credentials(app_identity): +@mock.patch.dict(os.environ) +def test__get_gae_credentials_gen1(app_identity): + os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27" app_identity.get_application_id.return_value = mock.sentinel.project credentials, project_id = _default._get_gae_credentials() @@ -292,6 +293,19 @@ def test__get_gae_credentials(app_identity): assert isinstance(credentials, app_engine.Credentials) assert project_id == mock.sentinel.project +@mock.patch.dict(os.environ) +def test__get_gae_credentials_gen2(): + os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python37" + os.environ["GAE_RUNTIME"] = "python37" + credentials, project_id = _default._get_gae_credentials() + assert credentials is None + assert project_id is None + +def test__get_gae_credentials_env_unset(): + assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ + credentials, project_id = _default._get_gae_credentials() + assert credentials is None + assert project_id is None def test__get_gae_credentials_no_app_engine(): import sys