feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""User errors raised by auth commands."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.core import exceptions
class AuthenticationError(exceptions.Error):
"""Raised for errors reported by Oauth2client library."""
class InvalidCredentialsError(exceptions.Error):
"""Raised if credentials are not usable."""
class WrongAccountError(exceptions.Error):
"""Raised when credential account does not match expected account."""
class GitCredentialHelperError(exceptions.Error):
"""Raised for issues related to passing auth credentials to Git."""
class InvalidIdentityTokenError(exceptions.Error):
"""Raised when identity token of credential is None."""
class WrongAccountTypeError(exceptions.Error):
"""Raised when audiences are specified but account type is not service account."""
class GCEIdentityTokenError(exceptions.Error):
"""Raised when request for GCE ID token is wrong."""

View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Manages logic for external accounts."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import json
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.credentials import creds as c_creds
from googlecloudsdk.core.credentials import introspect as c_introspect
from googlecloudsdk.core.util import files
_EXTERNAL_ACCOUNT_TYPE = 'external_account'
class Error(exceptions.Error):
"""Errors raised by this module."""
class BadCredentialFileException(Error):
"""Raised when file cannot be read."""
class BadCredentialJsonFileException(Error):
"""Raised when the JSON file is in an invalid format."""
def GetExternalAccountCredentialsConfig(filename):
"""Returns the JSON content if the file corresponds to an external account.
This function is useful when the content of a file need to be inspected first
before determining how to handle it. More specifically, it would check a
config file contains an external account cred and return its content which can
then be used with CredentialsFromAdcDictGoogleAuth (if the contents
correspond to an external account cred) to avoid having to open the file
twice.
Args:
filename (str): The filepath to the ADC file representing an external
account credentials.
Returns:
Optional(Mapping): The JSON content if the configuration represents an
external account. Otherwise None is returned.
Raises:
BadCredentialFileException: If JSON parsing of the file fails.
"""
content = files.ReadFileContents(filename)
try:
content_json = json.loads(content)
except ValueError as e:
# File has to be in JSON format.
raise BadCredentialFileException('Could not read json file {0}: {1}'.format(
filename, e))
if IsExternalAccountConfig(content_json):
return content_json
else:
return None
def IsExternalAccountConfig(content_json):
"""Returns whether a JSON content corresponds to an external account cred."""
return (content_json or {}).get('type') == _EXTERNAL_ACCOUNT_TYPE
def CredentialsFromAdcDictGoogleAuth(external_config):
"""Creates external account creds from a dict of application default creds.
Args:
external_config (Mapping): The configuration dictionary representing the
credentials. This is loaded from the ADC file typically.
Returns:
google.auth.external_account.Credentials: The initialized external account
credentials.
Raises:
BadCredentialJsonFileException: If the config format is invalid.
googlecloudsdk.core.credentials.creds.InvalidCredentialsError: If the
provided configuration is invalid or unsupported.
"""
if ('type' not in external_config or
external_config['type'] != _EXTERNAL_ACCOUNT_TYPE):
raise BadCredentialJsonFileException(
'The provided credentials configuration is not in a valid format.')
return c_creds.FromJsonGoogleAuth(json.dumps(external_config))
def GetExternalAccountId(creds):
"""Returns the account identifier corresponding to the external account creds.
Args:
creds (google.auth.credentials.Credentials): The credentials whose account
ID is to be returned.
Returns:
Optional(str): The corresponding account ID, or None if the credentials are
not external_account credentials.
"""
if (c_creds.IsExternalAccountCredentials(creds) or
c_creds.IsExternalAccountUserCredentials(creds) or
c_creds.IsExternalAccountAuthorizedUserCredentials(creds)):
return (getattr(creds, 'service_account_email', None) or
c_introspect.GetExternalAccountId(creds))
return None

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Manages logic for refresh token."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.credentials import store as c_store
from oauth2client import client
from google.auth import exceptions as google_auth_exceptions
class LoadingCredentialsError(exceptions.Error):
"""Reraise on oauth2client and google-auth errors."""
class UnsupportedCredentialsType(exceptions.Error):
"""Raised when credentials do not have refresh token."""
def ActivateCredentials(account, refresh_token):
"""Activates credentials for given account with given refresh token."""
creds = c_store.AcquireFromToken(
refresh_token, use_google_auth=True)
c_store.ActivateCredentials(account, creds)
return creds
def GetForAccount(account=None):
"""Returns refresh token for given account.
Args:
account: str, usually email like string,
if not provided current account is used.
Returns:
str: refresh token
Raises:
UnsupportedCredentialsType: if credentials are not user credentials.
"""
try:
creds = c_store.Load(account, use_google_auth=True)
except (client.Error, google_auth_exceptions.GoogleAuthError):
raise calliope_exceptions.NewErrorFromCurrentException(
LoadingCredentialsError)
refresh_token = getattr(creds, 'refresh_token', None)
if refresh_token is None:
raise UnsupportedCredentialsType(
'Credentials for account {0} do not support refresh tokens.'
.format(account))
return refresh_token

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Manages logic for service accounts."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import json
from googlecloudsdk.core import config
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.credentials import creds as c_creds
from googlecloudsdk.core.credentials import p12_service_account
from googlecloudsdk.core.util import files
from oauth2client import service_account
_SERVICE_ACCOUNT_TYPE = 'service_account'
class Error(exceptions.Error):
"""Errors raised by this module."""
class UnsupportedCredentialsType(Error):
"""Raised when given type credentials cannot be created."""
class BadCredentialFileException(Error):
"""Raised when file cannot be read."""
class BadCredentialJsonFileException(Error):
"""Raised when file cannot be read."""
def IsServiceAccountConfig(content_json):
"""Returns whether a JSON content corresponds to an service account cred."""
return (content_json or {}).get('type') == _SERVICE_ACCOUNT_TYPE
def CredentialsFromAdcFile(filename):
"""Load credentials from given service account json file."""
content = files.ReadFileContents(filename)
try:
json_key = json.loads(content)
return CredentialsFromAdcDict(json_key)
except ValueError as e:
raise BadCredentialFileException('Could not read json file {0}: {1}'.format(
filename, e))
def CredentialsFromAdcDict(json_key):
"""Creates oauth2client creds from a dict of application default creds."""
if 'client_email' not in json_key:
raise BadCredentialJsonFileException(
'The .json key file is not in a valid format.')
json_key['token_uri'] = c_creds.GetEffectiveTokenUriFromCreds(json_key)
creds = service_account.ServiceAccountCredentials.from_json_keyfile_dict(
json_key, scopes=config.CLOUDSDK_SCOPES)
# User agent needs to be set separately, see
# https://github.com/google/oauth2client/issues/445
# pylint: disable=protected-access
creds.user_agent = creds._user_agent = config.CLOUDSDK_USER_AGENT
return creds
def CredentialsFromAdcDictGoogleAuth(json_key):
"""Creates google-auth creds from a dict of application default creds."""
# Import only when necessary to decrease the startup time. Move it to
# global once google-auth is ready to replace oauth2client.
# pylint: disable=g-import-not-at-top
from google.oauth2 import service_account as google_auth_service_account
# pylint: enable=g-import-not-at-top
if 'client_email' not in json_key:
raise BadCredentialJsonFileException(
'The .json key file is not in a valid format.')
# 'token_uri' is required by google-auth credentails construction. However,
# the service account keys generated before 2015 do not provide this field.
# More details in http://shortn/_LtMjDvpgfh.
json_key['token_uri'] = c_creds.GetEffectiveTokenUriFromCreds(json_key)
service_account_credentials = (
google_auth_service_account.Credentials.from_service_account_info)
creds = service_account_credentials(json_key, scopes=config.CLOUDSDK_SCOPES)
# The below additional fields are not natively supported in the google-auth
# library but are needed in gcloud:
# private_key, private_key_id: required by credentials deserialization;
# client_id: to provid backward compatibility for oauth2client. This field is
# used in oauth2client service account credentials.
creds.private_key = json_key.get('private_key')
creds.private_key_id = json_key.get('private_key_id')
creds.client_id = json_key.get('client_id')
return creds
def CredentialsFromP12Key(private_key, account, password=None):
"""Creates credentials object from given p12 private key and account name."""
return p12_service_account.CreateP12ServiceAccount(
private_key,
password,
service_account_email=account,
token_uri=c_creds.GetEffectiveTokenUriFromCreds({}),
scopes=config.CLOUDSDK_SCOPES,
)

View File

@@ -0,0 +1,439 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A library to support auth commands."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import abc
import json
import textwrap
from googlecloudsdk.command_lib.util import check_browser
from googlecloudsdk.core import config
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import yaml
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.credentials import creds
from googlecloudsdk.core.util import files
import six
# Client ID from project "usable-auth-library", configured for
# general purpose API testing
# pylint: disable=g-line-too-long
DEFAULT_CREDENTIALS_DEFAULT_CLIENT_ID = '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com'
DEFAULT_CREDENTIALS_DEFAULT_CLIENT_SECRET = 'd-FL95Q19q7MQmFpd7hHD0Ty'
CLOUD_PLATFORM_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'
SQL_LOGIN_SCOPE = 'https://www.googleapis.com/auth/sqlservice.login'
GOOGLE_DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive'
USER_EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
OPENID = 'openid'
DEFAULT_SCOPES = [
OPENID,
USER_EMAIL_SCOPE,
CLOUD_PLATFORM_SCOPE,
SQL_LOGIN_SCOPE
]
CLIENT_SECRET_INSTALLED_TYPE = 'installed'
class Error(exceptions.Error):
"""A base exception for this class."""
pass
class InvalidClientSecretsError(Error):
"""An error for when we fail to load the client secrets file."""
pass
class BadCredentialFileException(Error):
"""Raised when credentials file cannot be read."""
pass
def GetCredentialsConfigFromFile(filename):
"""Returns the JSON content of a credentials config file.
This function is useful when the content of a file need to be inspected first
before determining how to handle it (how to initialize the underlying
credentials). Only UTF-8 JSON files are supported.
Args:
filename (str): The filepath to the ADC file representing credentials.
Returns:
Optional(Mapping): The JSON content.
Raises:
BadCredentialFileException: If JSON parsing of the file fails.
"""
try:
# YAML is a superset of JSON.
content = yaml.load_path(filename)
except UnicodeDecodeError as e:
raise BadCredentialFileException(
'File {0} is not utf-8 encoded: {1}'.format(filename, e))
except yaml.YAMLParseError as e:
raise BadCredentialFileException('Could not read json file {0}: {1}'.format(
filename, e))
# Require the JSON content to be an object.
# Credentials and configs are always objects.
if not isinstance(content, dict):
raise BadCredentialFileException(
'Could not read json file {0}'.format(filename))
return content
def _HandleFlowError(exc, default_help_msg):
"""Prints help messages when auth flow throws errors."""
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core import context_aware
# pylint: enable=g-import-not-at-top
if context_aware.IsContextAwareAccessDeniedError(exc):
log.error(context_aware.ContextAwareAccessError.Get())
else:
log.error(default_help_msg)
class FlowRunner(six.with_metaclass(abc.ABCMeta, object)):
"""Base auth flow runner class.
Attributes:
_scopes: [str], The list of scopes to authorize.
_client_config: The client configuration in the Google client secrets
format.
"""
_FLOW_ERROR_HELP_MSG = 'There was a problem with web authentication.'
def __init__(self, scopes, client_config, redirect_uri=None):
self._scopes = scopes
self._client_config = client_config
self._redirect_uri = redirect_uri
self._flow = self._CreateFlow()
@abc.abstractmethod
def _CreateFlow(self):
pass
def Run(self, **kwargs):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
try:
return self._flow.Run(**kwargs)
except c_flow.Error as e:
_HandleFlowError(e, self._FLOW_ERROR_HELP_MSG)
raise
class OobFlowRunner(FlowRunner):
"""A flow runner to run OobFlow."""
def _CreateFlow(self):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
return c_flow.OobFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
class NoBrowserFlowRunner(FlowRunner):
"""A flow runner to run NoBrowserFlow."""
def _CreateFlow(self):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
return c_flow.NoBrowserFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
class RemoteLoginWithAuthProxyFlowRunner(FlowRunner):
"""A flow runner to run RemoteLoginWithAuthProxyFlow."""
def _CreateFlow(self):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
return c_flow.RemoteLoginWithAuthProxyFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool(),
redirect_uri=self._redirect_uri)
class NoBrowserHelperRunner(FlowRunner):
"""A flow runner to run NoBrowserHelperFlow."""
def _CreateFlow(self):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
try:
return c_flow.NoBrowserHelperFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
except c_flow.LocalServerCreationError:
log.error('Cannot start a local server to handle authorization '
'redirection. Please run this command on a machine where '
'gcloud can start a local server.')
raise
class BrowserFlowWithOobFallbackRunner(FlowRunner):
"""A flow runner to try normal web flow and fall back to oob flow."""
_FLOW_ERROR_HELP_MSG = ('There was a problem with web authentication. '
'Try running again with --no-launch-browser.')
def _CreateFlow(self):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
try:
return c_flow.FullWebFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
except c_flow.LocalServerCreationError as e:
log.warning(e)
log.warning('Defaulting to URL copy/paste mode.')
return c_flow.OobFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
class BrowserFlowWithNoBrowserFallbackRunner(FlowRunner):
"""A flow runner to try normal web flow and fall back to NoBrowser flow."""
_FLOW_ERROR_HELP_MSG = ('There was a problem with web authentication. '
'Try running again with --no-browser.')
def _CreateFlow(self):
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
try:
return c_flow.FullWebFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
except c_flow.LocalServerCreationError as e:
log.warning(e)
log.warning('Defaulting to --no-browser mode.')
return c_flow.NoBrowserFlow.from_client_config(
self._client_config,
self._scopes,
autogenerate_code_verifier=not properties.VALUES.auth
.disable_code_verifier.GetBool())
def _CreateGoogleAuthClientConfig(client_id_file=None):
"""Creates a client config from a client id file or gcloud's properties."""
if client_id_file:
with files.FileReader(client_id_file) as f:
return json.load(f)
return _CreateGoogleAuthClientConfigFromProperties()
def _CreateGoogleAuthClientConfigFromProperties():
"""Creates a client config from gcloud's properties."""
auth_uri = properties.VALUES.auth.auth_host.Get(required=True)
token_uri = creds.GetDefaultTokenUri()
client_id = properties.VALUES.auth.client_id.Get(required=True)
client_secret = properties.VALUES.auth.client_secret.Get(required=True)
return {
'installed': {
'client_id': client_id,
'client_secret': client_secret,
'auth_uri': auth_uri,
'token_uri': token_uri
}
}
def _IsGoogleOwnedClientID(client_config):
return (client_config['installed']['client_id']
in (config.CLOUDSDK_CLIENT_ID, DEFAULT_CREDENTIALS_DEFAULT_CLIENT_ID))
def DoInstalledAppBrowserFlowGoogleAuth(scopes,
client_id_file=None,
client_config=None,
no_launch_browser=False,
no_browser=False,
remote_bootstrap=None,
query_params=None,
auth_proxy_redirect_uri=None):
"""Launches a 3LO oauth2 flow to get google-auth credentials.
Args:
scopes: [str], The list of scopes to authorize.
client_id_file: str, The path to a file containing the client id and secret
to use for the flow. If None, the default client id for the Cloud SDK is
used.
client_config: Optional[Mapping], the client secrets and urls that should be
used for the OAuth flow.
no_launch_browser: bool, True if users specify --no-launch-browser flag to
use the remote login with auth proxy flow.
no_browser: bool, True if users specify --no-browser flag to ask another
gcloud instance to help with authorization.
remote_bootstrap: str, The auth parameters specified by --remote-bootstrap
flag. Once used, it means the command is to help authorize another
gcloud (i.e. gcloud without access to browser).
query_params: Optional[Mapping], extra params to pass to the flow during
`Run`. These params end up getting used as query
params for authorization_url.
auth_proxy_redirect_uri: str, The uri where OAuth service will redirect the
user to once the authentication is complete for a remote login with auth
proxy flow.
Returns:
core.credentials.google_auth_credentials.Credentials, The credentials
obtained from the flow.
"""
# pylint: disable=g-import-not-at-top
from google.auth import external_account_authorized_user
from google.oauth2 import credentials as oauth2_credentials
from googlecloudsdk.core.credentials import flow as c_flow
# pylint: enable=g-import-not-at-top
if client_id_file:
AssertClientSecretIsInstalledType(client_id_file)
if not client_config:
client_config = _CreateGoogleAuthClientConfig(client_id_file)
if not query_params:
query_params = {}
can_launch_browser = check_browser.ShouldLaunchBrowser(
attempt_launch_browser=True)
if no_browser:
user_creds = NoBrowserFlowRunner(scopes, client_config).Run(**query_params)
elif remote_bootstrap:
if not can_launch_browser:
raise c_flow.WebBrowserInaccessible(
'Cannot launch browser. Please run this command on a machine '
'where gcloud can launch a web browser.')
user_creds = NoBrowserHelperRunner(scopes, client_config).Run(
partial_auth_url=remote_bootstrap, **query_params)
elif no_launch_browser:
user_creds = RemoteLoginWithAuthProxyFlowRunner(
scopes, client_config, auth_proxy_redirect_uri
).Run(**query_params)
elif not can_launch_browser:
# RemoteLoginWithAuthProxyFlowrunner uses redirect_uri for https://sdk.cloud.google.com
# which is intended for google-owned client only.
# Non-google-owned clients can only use NoBrowserFlowRunner.
if client_id_file and not _IsGoogleOwnedClientID(client_config):
user_creds = NoBrowserFlowRunner(scopes, client_config).Run(
**query_params
)
else:
user_creds = RemoteLoginWithAuthProxyFlowRunner(
scopes, client_config, auth_proxy_redirect_uri
).Run(**query_params)
else:
user_creds = BrowserFlowWithNoBrowserFallbackRunner(
scopes, client_config).Run(**query_params)
if user_creds:
if isinstance(user_creds, oauth2_credentials.Credentials):
# c_google_auth.Credentials adds reauth capabilities to oauth2
# credentials, which is needed as they are long-term credentials.
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import google_auth_credentials as c_google_auth
# pylint: enable=g-import-not-at-top
return c_google_auth.Credentials.FromGoogleAuthUserCredentials(user_creds)
if isinstance(user_creds, external_account_authorized_user.Credentials):
return user_creds
def AssertClientSecretIsInstalledType(client_id_file):
"""Assert that the file is a valid json file for installed application."""
actionable_message = (
'To obtain a valid client ID file, create a Desktop App following'
' the steps outlined in'
' https://support.google.com/cloud/answer/6158849?hl=en#zippy=%2Cnative-applications%2Cdesktop-apps.'
)
try:
obj = json.loads(files.ReadFileContents(client_id_file))
except files.Error:
raise InvalidClientSecretsError(f'Cannot read file: "{client_id_file}".')
except json.JSONDecodeError:
raise InvalidClientSecretsError(
f'Client ID file {client_id_file} is not a valid JSON file.'
f' {actionable_message}'
)
if len(obj) != 1:
raise InvalidClientSecretsError(
'Expected a JSON object with a single property for an "installed"'
f' application. {actionable_message}'
)
client_type = tuple(obj)[0]
if client_type != CLIENT_SECRET_INSTALLED_TYPE:
raise InvalidClientSecretsError(
f"Only client IDs of type '{CLIENT_SECRET_INSTALLED_TYPE}' are allowed,"
f" but encountered type '{client_type}'. {actionable_message}"
)
def HandleUniverseDomainConflict(new_universe_domain, account):
"""Prompt the user to update the universe domain if there is conflict.
If the given universe domain is different from the core/universe_domain
property, prompt the user to update the core/universe_domain property.
Args:
new_universe_domain: str, The given new universe domain.
account: str, The account name to use.
"""
current_universe_domain = properties.VALUES.core.universe_domain.Get()
if current_universe_domain == new_universe_domain:
return
message = textwrap.dedent("""\
WARNING: This account [{0}] is from the universe domain [{1}],
which does not match the current core/universe property [{2}].\n
Do you want to set property [core/universe_domain] to [{1}]? [Y/N]
""").format(account, new_universe_domain, current_universe_domain)
should_update_universe_domain = console_io.PromptContinue(message=message)
if should_update_universe_domain:
properties.PersistProperty(
properties.VALUES.core.universe_domain, new_universe_domain
)
log.status.Print('Updated property [core/universe_domain].')