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: disable warning if quota project id provided to auth.default() #856

Merged
merged 3 commits into from
Sep 7, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions google/auth/_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def load_credentials_from_file(
)


def _get_gcloud_sdk_credentials():
def _get_gcloud_sdk_credentials(quota_project_id=None):
"""Gets the credentials and project ID from the Cloud SDK."""
from google.auth import _cloud_sdk

Expand All @@ -185,15 +185,17 @@ def _get_gcloud_sdk_credentials():
_LOGGER.debug("Cloud SDK credentials not found on disk; not using them")
return None, None

credentials, project_id = load_credentials_from_file(credentials_filename)
credentials, project_id = load_credentials_from_file(
credentials_filename, quota_project_id=quota_project_id
)

if not project_id:
project_id = _cloud_sdk.get_project_id()

return credentials, project_id


def _get_explicit_environ_credentials():
def _get_explicit_environ_credentials(quota_project_id=None):
"""Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
variable."""
from google.auth import _cloud_sdk
Expand All @@ -213,11 +215,11 @@ def _get_explicit_environ_credentials():
"Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
explicit_file,
)
return _get_gcloud_sdk_credentials()
return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)

if explicit_file is not None:
credentials, project_id = load_credentials_from_file(
os.environ[environment_vars.CREDENTIALS]
os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
)

return credentials, project_id
Expand Down Expand Up @@ -447,8 +449,8 @@ def default(scopes=None, request=None, quota_project_id=None, default_scopes=Non
# with_scopes_if_required() below will ensure scopes/default scopes are
# safely set on the returned credentials since requires_scopes will
# guard against setting scopes on user credentials.
_get_explicit_environ_credentials,
_get_gcloud_sdk_credentials,
lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),
lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),
_get_gae_credentials,
lambda: _get_gce_credentials(request),
)
Expand Down
20 changes: 12 additions & 8 deletions google/auth/_default_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,13 @@ def load_credentials_from_file(filename, scopes=None, quota_project_id=None):
try:
credentials = credentials.Credentials.from_authorized_user_info(
info, scopes=scopes
).with_quota_project(quota_project_id)
)
except ValueError as caught_exc:
msg = "Failed to load authorized user credentials from {}".format(filename)
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
raise new_exc from caught_exc
if quota_project_id:
credentials = credentials.with_quota_project(quota_project_id)
if not credentials.quota_project_id:
_default._warn_about_problematic_credentials(credentials)
return credentials, None
Expand All @@ -104,7 +106,7 @@ def load_credentials_from_file(filename, scopes=None, quota_project_id=None):
)


def _get_gcloud_sdk_credentials():
def _get_gcloud_sdk_credentials(quota_project_id=None):
"""Gets the credentials and project ID from the Cloud SDK."""
from google.auth import _cloud_sdk

Expand All @@ -114,15 +116,17 @@ def _get_gcloud_sdk_credentials():
if not os.path.isfile(credentials_filename):
return None, None

credentials, project_id = load_credentials_from_file(credentials_filename)
credentials, project_id = load_credentials_from_file(
credentials_filename, quota_project_id=quota_project_id
)

if not project_id:
project_id = _cloud_sdk.get_project_id()

return credentials, project_id


def _get_explicit_environ_credentials():
def _get_explicit_environ_credentials(quota_project_id=None):
"""Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
variable."""
from google.auth import _cloud_sdk
Expand All @@ -134,11 +138,11 @@ def _get_explicit_environ_credentials():
# Cloud sdk flow calls gcloud to fetch project id, so if the explicit
# file path is cloud sdk credentials path, then we should fall back
# to cloud sdk flow, otherwise project id cannot be obtained.
return _get_gcloud_sdk_credentials()
return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)

if explicit_file is not None:
credentials, project_id = load_credentials_from_file(
os.environ[environment_vars.CREDENTIALS]
os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
)

return credentials, project_id
Expand Down Expand Up @@ -250,8 +254,8 @@ def default_async(scopes=None, request=None, quota_project_id=None):
)

checkers = (
_get_explicit_environ_credentials,
_get_gcloud_sdk_credentials,
lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),
lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),
_get_gae_credentials,
lambda: _get_gce_credentials(request),
)
Expand Down
44 changes: 35 additions & 9 deletions tests/test__default.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,15 +328,18 @@ def test__get_explicit_environ_credentials_no_env():
assert _default._get_explicit_environ_credentials() == (None, None)


@pytest.mark.parametrize("quota_project_id", [None, "project-foo"])
@LOAD_FILE_PATCH
def test__get_explicit_environ_credentials(load, monkeypatch):
def test__get_explicit_environ_credentials(load, quota_project_id, monkeypatch):
monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")

credentials, project_id = _default._get_explicit_environ_credentials()
credentials, project_id = _default._get_explicit_environ_credentials(
quota_project_id=quota_project_id
)

assert credentials is MOCK_CREDENTIALS
assert project_id is mock.sentinel.project_id
load.assert_called_with("filename")
load.assert_called_with("filename", quota_project_id=quota_project_id)


@LOAD_FILE_PATCH
Expand All @@ -350,36 +353,40 @@ def test__get_explicit_environ_credentials_no_project_id(load, monkeypatch):
assert project_id is None


@pytest.mark.parametrize("quota_project_id", [None, "project-foo"])
@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
@mock.patch("google.auth._default._get_gcloud_sdk_credentials", autospec=True)
def test__get_explicit_environ_credentials_fallback_to_gcloud(
get_gcloud_creds, get_adc_path, monkeypatch
get_gcloud_creds, get_adc_path, quota_project_id, monkeypatch
):
# Set explicit credentials path to cloud sdk credentials path.
get_adc_path.return_value = "filename"
monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")

_default._get_explicit_environ_credentials()
_default._get_explicit_environ_credentials(quota_project_id=quota_project_id)

# Check we fall back to cloud sdk flow since explicit credentials path is
# cloud sdk credentials path
get_gcloud_creds.assert_called_once()
get_gcloud_creds.assert_called_with(quota_project_id=quota_project_id)


@pytest.mark.parametrize("quota_project_id", [None, "project-foo"])
@LOAD_FILE_PATCH
@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
def test__get_gcloud_sdk_credentials(get_adc_path, load):
def test__get_gcloud_sdk_credentials(get_adc_path, load, quota_project_id):
get_adc_path.return_value = SERVICE_ACCOUNT_FILE

credentials, project_id = _default._get_gcloud_sdk_credentials()
credentials, project_id = _default._get_gcloud_sdk_credentials(
quota_project_id=quota_project_id
)

assert credentials is MOCK_CREDENTIALS
assert project_id is mock.sentinel.project_id
load.assert_called_with(SERVICE_ACCOUNT_FILE)
load.assert_called_with(SERVICE_ACCOUNT_FILE, quota_project_id=quota_project_id)


@mock.patch(
Expand Down Expand Up @@ -779,3 +786,22 @@ def test_default_environ_external_credentials_bad_format(monkeypatch, tmpdir):
assert excinfo.match(
"Failed to load external account credentials from {}".format(str(filename))
)


@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
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"):
credentials, project_id = _default.default(quota_project_id=None)


@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
def test_default_no_warning_with_quota_project_id_for_user_creds(get_adc_path):
get_adc_path.return_value = AUTHORIZED_USER_CLOUD_SDK_FILE

credentials, project_id = _default.default(quota_project_id="project-foo")
46 changes: 37 additions & 9 deletions tests_async/test__default_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,18 @@ def test__get_explicit_environ_credentials_no_env():
assert _default._get_explicit_environ_credentials() == (None, None)


@pytest.mark.parametrize("quota_project_id", [None, "project-foo"])
@LOAD_FILE_PATCH
def test__get_explicit_environ_credentials(load, monkeypatch):
def test__get_explicit_environ_credentials(load, quota_project_id, monkeypatch):
monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")

credentials, project_id = _default._get_explicit_environ_credentials()
credentials, project_id = _default._get_explicit_environ_credentials(
quota_project_id=quota_project_id
)

assert credentials is MOCK_CREDENTIALS
assert project_id is mock.sentinel.project_id
load.assert_called_with("filename")
load.assert_called_with("filename", quota_project_id=quota_project_id)


@LOAD_FILE_PATCH
Expand All @@ -187,36 +190,42 @@ def test__get_explicit_environ_credentials_no_project_id(load, monkeypatch):
assert project_id is None


@pytest.mark.parametrize("quota_project_id", [None, "project-foo"])
@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
@mock.patch("google.auth._default_async._get_gcloud_sdk_credentials", autospec=True)
def test__get_explicit_environ_credentials_fallback_to_gcloud(
get_gcloud_creds, get_adc_path, monkeypatch
get_gcloud_creds, get_adc_path, quota_project_id, monkeypatch
):
# Set explicit credentials path to cloud sdk credentials path.
get_adc_path.return_value = "filename"
monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")

_default._get_explicit_environ_credentials()
_default._get_explicit_environ_credentials(quota_project_id=quota_project_id)

# Check we fall back to cloud sdk flow since explicit credentials path is
# cloud sdk credentials path
get_gcloud_creds.assert_called_once()
get_gcloud_creds.assert_called_with(quota_project_id=quota_project_id)


@pytest.mark.parametrize("quota_project_id", [None, "project-foo"])
@LOAD_FILE_PATCH
@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
def test__get_gcloud_sdk_credentials(get_adc_path, load):
def test__get_gcloud_sdk_credentials(get_adc_path, load, quota_project_id):
get_adc_path.return_value = test_default.SERVICE_ACCOUNT_FILE

credentials, project_id = _default._get_gcloud_sdk_credentials()
credentials, project_id = _default._get_gcloud_sdk_credentials(
quota_project_id=quota_project_id
)

assert credentials is MOCK_CREDENTIALS
assert project_id is mock.sentinel.project_id
load.assert_called_with(test_default.SERVICE_ACCOUNT_FILE)
load.assert_called_with(
test_default.SERVICE_ACCOUNT_FILE, quota_project_id=quota_project_id
)


@mock.patch(
Expand Down Expand Up @@ -533,3 +542,22 @@ def test_default_no_app_engine_compute_engine_module(unused_get):
sys.modules["google.auth.compute_engine"] = None
sys.modules["google.auth.app_engine"] = None
assert _default.default_async() == (MOCK_CREDENTIALS, mock.sentinel.project_id)


@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
def test_default_warning_without_quota_project_id_for_user_creds(get_adc_path):
get_adc_path.return_value = test_default.AUTHORIZED_USER_CLOUD_SDK_FILE

with pytest.warns(UserWarning, match="Cloud SDK"):
credentials, project_id = _default.default_async(quota_project_id=None)


@mock.patch(
"google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
)
def test_default_no_warning_with_quota_project_id_for_user_creds(get_adc_path):
get_adc_path.return_value = test_default.AUTHORIZED_USER_CLOUD_SDK_FILE

credentials, project_id = _default.default_async(quota_project_id="project-foo")