Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: do not use the GAE APIs on gen2+ runtimes #807

Merged
merged 3 commits into from Jul 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions google/auth/_default.py
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions google/auth/environment_vars.py
Expand Up @@ -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
Expand Down
57 changes: 53 additions & 4 deletions tests/test__default.py
Expand Up @@ -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"
zevdg marked this conversation as resolved.
Show resolved Hide resolved
app_identity.get_application_id.return_value = mock.sentinel.project

credentials, project_id = _default._get_gae_credentials()
Expand All @@ -456,18 +458,65 @@ def test__get_gae_credentials(app_identity):
assert project_id == mock.sentinel.project


@mock.patch.dict(os.environ)
def test__get_gae_credentials_gen2():
os.environ["GAE_RUNTIME"] = "python37"
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch.dict(os.environ)
def test__get_gae_credentials_gen2_backwards_compat():
# compat helpers may copy GAE_RUNTIME to APPENGINE_RUNTIME
# for backwards compatibility with code that relies on it
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
assert "GAE_RUNTIME" not in os.environ
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch.dict(os.environ)
def test__get_gae_credentials_no_app_engine():
# test both with and without LEGACY_APPENGINE_RUNTIME setting
assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ

import sys

with mock.patch.dict("sys.modules"):
sys.modules["google.auth.app_engine"] = None
with mock.patch.dict(sys.modules, {"google.auth.app_engine": None}):
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None

os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch.dict(os.environ)
@mock.patch.object(app_engine, "app_identity", new=None)
def test__get_gae_credentials_no_apis():
assert _default._get_gae_credentials() == (None, None)
# test both with and without LEGACY_APPENGINE_RUNTIME setting
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

os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch(
Expand Down
2 changes: 2 additions & 0 deletions tests/test_app_engine.py
Expand Up @@ -52,6 +52,7 @@ def test_get_project_id(app_identity):
assert app_engine.get_project_id() == mock.sentinel.project


@mock.patch.object(app_engine, "app_identity", new=None)
def test_get_project_id_missing_apis():
with pytest.raises(EnvironmentError) as excinfo:
assert app_engine.get_project_id()
Expand Down Expand Up @@ -86,6 +87,7 @@ def test_sign(self, app_identity):


class TestCredentials(object):
@mock.patch.object(app_engine, "app_identity", new=None)
def test_missing_apis(self):
with pytest.raises(EnvironmentError) as excinfo:
app_engine.Credentials()
Expand Down
57 changes: 53 additions & 4 deletions tests_async/test__default_async.py
Expand Up @@ -284,7 +284,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()
Expand All @@ -293,18 +295,65 @@ def test__get_gae_credentials(app_identity):
assert project_id == mock.sentinel.project


@mock.patch.dict(os.environ)
def test__get_gae_credentials_gen2():
os.environ["GAE_RUNTIME"] = "python37"
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch.dict(os.environ)
def test__get_gae_credentials_gen2_backwards_compat():
# compat helpers may copy GAE_RUNTIME to APPENGINE_RUNTIME
# for backwards compatibility with code that relies on it
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
assert "GAE_RUNTIME" not in os.environ
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch.dict(os.environ)
def test__get_gae_credentials_no_app_engine():
# test both with and without LEGACY_APPENGINE_RUNTIME setting
assert environment_vars.LEGACY_APPENGINE_RUNTIME not in os.environ

import sys

with mock.patch.dict("sys.modules"):
sys.modules["google.auth.app_engine"] = None
with mock.patch.dict(sys.modules, {"google.auth.app_engine": None}):
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None

os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch.dict(os.environ)
@mock.patch.object(app_engine, "app_identity", new=None)
def test__get_gae_credentials_no_apis():
assert _default._get_gae_credentials() == (None, None)
# test both with and without LEGACY_APPENGINE_RUNTIME setting
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

os.environ[environment_vars.LEGACY_APPENGINE_RUNTIME] = "python27"
credentials, project_id = _default._get_gae_credentials()
assert credentials is None
assert project_id is None


@mock.patch(
Expand Down