def QuotaProject(self, enable_resource_quota, force_resource_quota, allow_account_impersonation, use_google_auth): if not (enable_resource_quota or force_resource_quota): return None creds = store.LoadIfEnabled(allow_account_impersonation, use_google_auth) return core_creds.GetQuotaProject(creds, force_resource_quota)
def _RunReceiveLocalData(self, conn, socket_address): """Receive data from provided local connection and send over WebSocket.""" websocket_conn = None try: websocket_conn = self._InitiateWebSocketConnection( conn, functools.partial(_GetAccessTokenCallback, store.LoadIfEnabled())) while not self._shutdown: data = conn.recv(utils.SUBPROTOCOL_MAX_DATA_FRAME_SIZE) if not data: break websocket_conn.Send(data) finally: if self._shutdown: log.info('Terminating connection to [%r].', socket_address) else: log.info('Client closed connection from [%r].', socket_address) try: conn.close() except EnvironmentError: pass try: if websocket_conn: websocket_conn.Close() except (EnvironmentError, exceptions.Error): pass
def Http(timeout='unset'): """Get an httplib2.Http client for working with the Google API. Args: timeout: double, The timeout in seconds to pass to httplib2. This is the socket level timeout. If timeout is None, timeout is infinite. If default argument 'unset' is given, a sensible default is selected. Returns: An authorized httplib2.Http client object, or a regular httplib2.Http object if no credentials are available. Raises: c_store.Error: If an error loading the credentials occurs. """ http_client = http.Http(timeout=timeout) authority_selector = properties.VALUES.auth.authority_selector.Get() authorization_token_file = ( properties.VALUES.auth.authorization_token_file.Get()) if authority_selector or authorization_token_file: http_client = _WrapRequestForIAMAuth(http_client, authority_selector, authorization_token_file) creds = store.LoadIfEnabled() if creds: http_client = creds.authorize(http_client) # Wrap the request method to put in our own error handling. http_client = http.Modifiers.WrapRequest( http_client, [], _HandleAuthError, client.AccessTokenRefreshError) return http_client
def _TestConnection(self): log.status.Print('Testing if tunnel connection works.') websocket_conn = self._InitiateWebSocketConnection( None, functools.partial(_GetAccessTokenCallback, store.LoadIfEnabled(use_google_auth=True))) websocket_conn.Close()
def _getCreds(): if not GOOGLE_CLOUD_SDK_ROOT: raise Exception("Unable to find the Google Cloud SDK - make sure it's installed, " "gcloud utility is in the PATH and configured properly") global GOOGLE_CLOUD_SDK_CREDS if not GOOGLE_CLOUD_SDK_CREDS: from googlecloudsdk.core.credentials import store store.DevShellCredentialProvider().Register() store.GceCredentialProvider().Register() GOOGLE_CLOUD_SDK_CREDS = store.LoadIfEnabled() elif GOOGLE_CLOUD_SDK_CREDS.access_token_expired: print('DEBUG: Updating credentials token') from googlecloudsdk.core.credentials import store GOOGLE_CLOUD_SDK_CREDS = store.LoadIfEnabled() return GOOGLE_CLOUD_SDK_CREDS
def Http(timeout='unset', enable_resource_quota=True, force_resource_quota=False, response_encoding=None, ca_certs=None, allow_account_impersonation=True): """Get an httplib2.Http client for working with the Google API. Args: timeout: double, The timeout in seconds to pass to httplib2. This is the socket level timeout. If timeout is None, timeout is infinite. If default argument 'unset' is given, a sensible default is selected. enable_resource_quota: bool, By default, we are going to tell APIs to use the quota of the project being operated on. For some APIs we want to use gcloud's quota, so you can explicitly disable that behavior by passing False here. force_resource_quota: bool, If true resource project quota will be used by this client regardless of the settings in gcloud. This should be used for newer APIs that cannot work with legacy project quota. response_encoding: str, the encoding to use to decode the response. ca_certs: str, absolute filename of a ca_certs file that overrides the default allow_account_impersonation: bool, True to allow use of impersonated service account credentials for calls made with this client. If False, the active user credentials will always be used. Returns: An authorized httplib2.Http client object, or a regular httplib2.Http object if no credentials are available. Raises: c_store.Error: If an error loading the credentials occurs. """ http_client = http.Http(timeout=timeout, response_encoding=response_encoding, ca_certs=ca_certs) # Wrappers for IAM header injection. authority_selector = properties.VALUES.auth.authority_selector.Get() authorization_token_file = ( properties.VALUES.auth.authorization_token_file.Get()) handlers = _GetIAMAuthHandlers(authority_selector, authorization_token_file) creds = store.LoadIfEnabled( allow_account_impersonation=allow_account_impersonation) if creds: # Inject the resource project header for quota unless explicitly disabled. if enable_resource_quota or force_resource_quota: quota_project = _GetQuotaProject(creds, force_resource_quota) if quota_project: handlers.append(http.Modifiers.Handler( http.Modifiers.SetHeader('X-Goog-User-Project', quota_project))) http_client = creds.authorize(http_client) # Wrap the request method to put in our own error handling. http_client = http.Modifiers.WrapRequest( http_client, handlers, _HandleAuthError, client.AccessTokenRefreshError) return http_client
def PossiblyEnableFlex(project): """Attempts to enable the Flexible Environment API on the project. Possible scenarios: -If Flexible Environment is already enabled, success. -If Flexible Environment API is not yet enabled, attempts to enable it. If that succeeds, success. -If the account doesn't have permissions to confirm that the Flexible Environment API is or isn't enabled on this project, succeeds with a warning. -If the account is a service account, adds an additional warning that the Service Management API may need to be enabled. -If the Flexible Environment API is not enabled on the project and the attempt to enable it fails, raises PrepareFailureError. Args: project: str, the project ID. Raises: PrepareFailureError: if enabling the API fails with a 403 or 404 error code. googlecloudsdk.api_lib.util.exceptions.HttpException: miscellaneous errors returned by server. """ try: log.warn( 'Checking the status of the Appengine Flexible Environment API ' 'during Appengine Flexible deployments is currently in beta.') enable_api.EnableServiceIfDisabled(project, 'appengineflex.googleapis.com') except sm_exceptions.ListServicesPermissionDeniedException: # If we can't find out whether the Flexible API is enabled, proceed with # a warning. warning = FLEXIBLE_SERVICE_VERIFY_WARNING.format(project) # If user is using a service account, add more info about what might # have gone wrong. credential = c_store.LoadIfEnabled() if credential: account_type = creds.CredentialType.FromCredentials(credential) if account_type in (creds.CredentialType.SERVICE_ACCOUNT, creds.CredentialType.P12_SERVICE_ACCOUNT): warning += '\n\n{}'.format( FLEXIBLE_SERVICE_VERIFY_WITH_SERVICE_ACCOUNT) log.warn(warning) except sm_exceptions.EnableServicePermissionDeniedException: # If enabling the Flexible API fails due to a permissions error, the # deployment fails. raise PrepareFailureError(PREPARE_FAILURE_MSG.format(project)) except api_lib_exceptions.HttpException as err: # The deployment should also fail if there are unforeseen errors in # enabling the Flexible API. If so, display detailed information. err.error_format = ('Error [{status_code}] {status_message}' '{error.details?' '\nDetailed error information:\n{?}}') raise err
def AuthorizeEnvironment(): client = apis.GetClientInstance('cloudshell', 'v1alpha1') messages = apis.GetMessagesModule('cloudshell', 'v1alpha1') creds = store.LoadIfEnabled() access_token = None if creds is not None: access_token = creds.get_access_token().access_token client.users_environments.Authorize( messages.CloudshellUsersEnvironmentsAuthorizeRequest( name=DEFAULT_ENVIRONMENT_NAME, authorizeEnvironmentRequest=messages. AuthorizeEnvironmentRequest(accessToken=access_token)))
def Http(timeout='unset', enable_resource_quota=True): """Get an httplib2.Http client for working with the Google API. Args: timeout: double, The timeout in seconds to pass to httplib2. This is the socket level timeout. If timeout is None, timeout is infinite. If default argument 'unset' is given, a sensible default is selected. enable_resource_quota: bool, By default, we are going to tell APIs to use the quota of the project being operated on. For some APIs we want to use gcloud's quota, so you can explicitly disable that behavior by passing False here. Returns: An authorized httplib2.Http client object, or a regular httplib2.Http object if no credentials are available. Raises: c_store.Error: If an error loading the credentials occurs. """ http_client = http.Http(timeout=timeout) # Wrappers for IAM header injection. authority_selector = properties.VALUES.auth.authority_selector.Get() authorization_token_file = ( properties.VALUES.auth.authorization_token_file.Get()) handlers = _GetIAMAuthHandlers(authority_selector, authorization_token_file) # Inject the resource project header for quota unless explicitly disabled. if enable_resource_quota: quota_project = properties.VALUES.billing.quota_project.Get() if quota_project == properties.VALUES.billing.LEGACY: quota_project = None elif quota_project == properties.VALUES.billing.CURRENT_PROJECT: quota_project = properties.VALUES.core.project.Get() if quota_project: handlers.append( http.Modifiers.Handler( http.Modifiers.SetHeader('X-Goog-User-Project', quota_project))) creds = store.LoadIfEnabled() if creds: http_client = creds.authorize(http_client) # Wrap the request method to put in our own error handling. http_client = http.Modifiers.WrapRequest( http_client, handlers, _HandleAuthError, client.AccessTokenRefreshError) return http_client
def GetGapicCredentials(enable_resource_quota=True, allow_account_impersonation=True): """Returns a credential object for use by gapic client libraries. Currently, we set _quota_project on the credentials, unlike for http requests, which add quota project through request wrapping to implement go/gcloud-quota-model-v2. Additionally, we wrap the refresh method and plug in our own google.auth.transport.Request object that uses our transport. Args: enable_resource_quota: bool, By default, we are going to tell APIs to use the quota of the project being operated on. For some APIs we want to use gcloud's quota, so you can explicitly disable that behavior by passing False here. allow_account_impersonation: bool, True to allow use of impersonated service account credentials for calls made with this client. If False, the active user credentials will always be used. Returns: A google auth credentials.Credentials object. Raises: MissingStoredCredentialsError: If a google-auth credential cannot be loaded. """ credentials = store.LoadIfEnabled( allow_account_impersonation=allow_account_impersonation, use_google_auth=True) if not creds.IsGoogleAuthCredentials(credentials): raise MissingStoredCredentialsError('Unable to load credentials') if enable_resource_quota: # pylint: disable=protected-access credentials._quota_project_id = creds.GetQuotaProject(credentials) # In order to ensure that credentials.Credentials:refresh is called with a # google.auth.transport.Request that uses our transport, we ignore the request # argument that is passed in and plug in our own. original_refresh = credentials.refresh def WrappedRefresh(request): del request # unused return original_refresh(requests.GoogleAuthRequest()) credentials.refresh = WrappedRefresh return credentials
def WrapCredentials(self, http_client, allow_account_impersonation=True, use_google_auth=None): """Get an http client for working with Google APIs. Args: http_client: The http client to be wrapped. allow_account_impersonation: bool, True to allow use of impersonated service account credentials for calls made with this client. If False, the active user credentials will always be used. use_google_auth: bool, True if the calling command indicates to use google-auth library for authentication. If False, authentication will fallback to using the oauth2client library. If None, set the value based the configuration. Returns: An authorized http client with exception handling. Raises: c_store.Error: If an error loading the credentials occurs. """ # Wrappers for IAM header injection. authority_selector = properties.VALUES.auth.authority_selector.Get() authorization_token_file = ( properties.VALUES.auth.authorization_token_file.Get()) handlers = _GetIAMAuthHandlers(authority_selector, authorization_token_file) if use_google_auth is None: use_google_auth = base.UseGoogleAuth() creds = store.LoadIfEnabled(allow_account_impersonation, use_google_auth) if creds: http_client = self.AuthorizeClient(http_client, creds) # Set this attribute so we can access it later, even after the http_client # request method has been wrapped setattr(http_client, '_googlecloudsdk_credentials', creds) self.WrapRequest(http_client, handlers, _HandleAuthError, (client.AccessTokenRefreshError, google_auth_exceptions.RefreshError)) return http_client
def __init__(self, enable_resource_quota=True, force_resource_quota=False, allow_account_impersonation=True): super(StoredCredentials, self).__init__() self.stored_credentials = store.LoadIfEnabled( allow_account_impersonation=allow_account_impersonation, use_google_auth=True) if self.stored_credentials is None: raise MissingStoredCredentialsError() if creds.IsOauth2ClientCredentials(self.stored_credentials): self.token = self.stored_credentials.access_token else: self.token = self.stored_credentials.token if enable_resource_quota or force_resource_quota: self.quota_project_id = creds.GetQuotaProject( self.stored_credentials, force_resource_quota) else: self.quota_project_id = None
def StartProxyServer(self): """Start accepting connections.""" self._cred = store.LoadIfEnabled() self._TestConnection() self._server_shutdown = False self._server_socket = self._OpenLocalTcpSocket() self._connections = [] try: with execution_utils.RaisesKeyboardInterrupt(): while True: self._connections.append(self._AcceptNewConnection()) except KeyboardInterrupt: log.info('Keyboard interrupt received.') finally: self._CloseServerSocket() self._server_shutdown = True self._CloseClientConnections() log.Print('Server shutdown complete.')
def AuthorizeEnvironment(): """Pushes gcloud credentials to the user's environment.""" client = apis.GetClientInstance('cloudshell', 'v1alpha1') messages = apis.GetMessagesModule('cloudshell', 'v1alpha1') # Load creds and refresh them if they're about to expire creds = store.LoadIfEnabled() if creds is not None and creds.token_expiry - creds.token_expiry.utcnow( ) < MIN_CREDS_EXPIRY: store.Refresh(creds) access_token = None if creds is not None: access_token = creds.get_access_token().access_token client.users_environments.Authorize( messages.CloudshellUsersEnvironmentsAuthorizeRequest( name=DEFAULT_ENVIRONMENT_NAME, authorizeEnvironmentRequest=messages. AuthorizeEnvironmentRequest(accessToken=access_token)))
def _RunReceiveLocalData(self, conn, socket_address, user_agent): """Receive data from provided local connection and send over WebSocket. Args: conn: A socket or _StdinSocket representing the local connection. socket_address: A verbose loggable string describing where conn is connected to. user_agent: The user_agent of this connection """ websocket_conn = None try: websocket_conn = self._InitiateWebSocketConnection( conn, functools.partial(_GetAccessTokenCallback, store.LoadIfEnabled(use_google_auth=True)), user_agent) while not self._shutdown: data = conn.recv(utils.SUBPROTOCOL_MAX_DATA_FRAME_SIZE) if not data: # When we recv an EOF, we notify the websocket_conn of it, then we # wait for all data to send before returning. websocket_conn.LocalEOF() if not websocket_conn.WaitForAllSent(): log.warning('Failed to send all data from [%s].', socket_address) break websocket_conn.Send(data) finally: if self._shutdown: log.info('Terminating connection to [%s].', socket_address) else: log.info('Client closed connection from [%s].', socket_address) try: conn.close() except EnvironmentError: pass try: if websocket_conn: websocket_conn.Close() except (EnvironmentError, exceptions.Error): pass
def __init__(self, server=None, ignore_bad_certs=False): self.server = server or 'appengine.google.com' self.project = properties.VALUES.core.project.Get(required=True) self.ignore_bad_certs = ignore_bad_certs # Auth related options self.oauth2_access_token = None self.oauth2_refresh_token = None self.oauth_scopes = APPCFG_SCOPES self.authenticate_service_account = False self.client_id = None self.client_secret = None credentials = c_store.LoadIfEnabled() if credentials: if isinstance(credentials, service_account.ServiceAccountCredentials): self.oauth2_access_token = credentials.access_token self.client_id = credentials.client_id self.client_secret = credentials.client_secret elif isinstance(credentials, c_devshell.DevshellCredentials): # TODO(b/36057357): This passes the access token to use for API calls to # appcfg which means that commands that are longer than the lifetime # of the access token may fail - e.g. some long deployments. The proper # solution is to integrate appcfg closer with the Cloud SDK libraries, # this code will go away then and the standard credentials flow will be # used. self.oauth2_access_token = credentials.access_token self.client_id = None self.client_secret = None elif isinstance(credentials, oauth2client_gce.AppAssertionCredentials): # If we are on GCE, use the service account self.authenticate_service_account = True self.client_id = None self.client_secret = None else: # Otherwise use a stored refresh token self.oauth2_refresh_token = credentials.refresh_token self.client_id = credentials.client_id self.client_secret = credentials.client_secret
def PrepareV1Environment(args): """Ensures that the user's environment is ready to accept SSH connections.""" # Load Cloud Shell API. client = apis.GetClientInstance('cloudshell', 'v1') messages = apis.GetMessagesModule('cloudshell', 'v1') operations_client = apis.GetClientInstance('cloudshell', 'v1') # Ensure we have a key pair on the local machine. ssh_env = ssh.Environment.Current() ssh_env.RequireSSH() keys = ssh.Keys.FromFilename(filename=args.ssh_key_file) keys.EnsureKeysExist(overwrite=args.force_key_file_overwrite) # Look up the Cloud Shell environment. environment = client.users_environments.Get( messages.CloudshellUsersEnvironmentsGetRequest( name=DEFAULT_ENVIRONMENT_NAME)) # If the environment doesn't have the public key, push it. key = keys.GetPublicKey().ToEntry() has_key = False for candidate in environment.publicKeys: if key == candidate: has_key = True break if not has_key: add_public_key_operation = client.users_environments.AddPublicKey( messages.CloudshellUsersEnvironmentsAddPublicKeyRequest( environment=DEFAULT_ENVIRONMENT_NAME, addPublicKeyRequest=messages.AddPublicKeyRequest(key=key), )) environment = waiter.WaitFor(EnvironmentPoller( client.users_environments, operations_client.operations), add_public_key_operation, 'Pushing your public key to Cloud Shell', sleep_ms=500, max_wait_ms=None) # If the environment isn't running, start it. if environment.state != messages.Environment.StateValueValuesEnum.RUNNING: log.Print('Starting your Cloud Shell machine...') access_token = None if args.authorize_session: creds = store.LoadIfEnabled() if creds is not None and creds.token_expiry - creds.token_expiry.utcnow( ) < MIN_CREDS_EXPIRY: store.Refresh(creds) if creds is not None: access_token = creds.get_access_token().access_token start_operation = client.users_environments.Start( messages.CloudshellUsersEnvironmentsStartRequest( name=DEFAULT_ENVIRONMENT_NAME, startEnvironmentRequest=messages.StartEnvironmentRequest( accessToken=access_token))) environment = waiter.WaitFor( EnvironmentPoller(client.users_environments, operations_client.operations), start_operation, 'Waiting for your Cloud Shell machine to start', sleep_ms=500, max_wait_ms=None) return ConnectionInfo( ssh_env=ssh_env, user=environment.sshUsername, host=environment.sshHost, port=environment.sshPort, key=keys.key_file, )
def _TestConnection(self): log.Print('Testing if can connect.') websocket_conn = self._InitiateWebSocketConnection( None, functools.partial(_GetAccessTokenCallback, store.LoadIfEnabled())) websocket_conn.Close()
def PrepareEnvironment(args): """Ensures that the user's environment is ready to accept SSH connections.""" # Load Cloud Shell API client = apis.GetClientInstance('cloudshell', 'v1alpha1') messages = apis.GetMessagesModule('cloudshell', 'v1alpha1') operations_client = apis.GetClientInstance('cloudshell', 'v1') # Ensure we have a key pair on the local machine ssh_env = ssh.Environment.Current() ssh_env.RequireSSH() keys = ssh.Keys.FromFilename(filename=args.ssh_key_file) keys.EnsureKeysExist(overwrite=args.force_key_file_overwrite) # Look up the Cloud Shell environment environment = client.users_environments.Get( messages.CloudshellUsersEnvironmentsGetRequest( name=DEFAULT_ENVIRONMENT_NAME)) if args.boosted and environment.size != messages.Environment.SizeValueValuesEnum.BOOSTED: boosted_environment = messages.Environment( size=messages.Environment.SizeValueValuesEnum.BOOSTED) client.users_environments.Patch( messages.CloudshellUsersEnvironmentsPatchRequest( name=DEFAULT_ENVIRONMENT_NAME, updateMask='size', environment=boosted_environment)) # If the environment doesn't have the public key, push it key_parts = keys.GetPublicKey().ToEntry().split(' ') key_format = ValidateKeyType(key_parts[0].replace('-', '_').upper(), messages) key = messages.PublicKey( format=key_format, key=base64.b64decode(key_parts[1]), ) has_key = False for candidate in environment.publicKeys: if key.format == candidate.format and key.key == candidate.key: has_key = True break if not has_key: log.Print('Pushing your public key to Cloud Shell...') client.users_environments_publicKeys.Create( messages.CloudshellUsersEnvironmentsPublicKeysCreateRequest( parent=DEFAULT_ENVIRONMENT_NAME, createPublicKeyRequest=messages.CreatePublicKeyRequest( key=key), )) # If the environment isn't running, start it if environment.state != messages.Environment.StateValueValuesEnum.RUNNING: log.Print('Starting your Cloud Shell machine...') creds = store.LoadIfEnabled() if creds is not None and creds.token_expiry - creds.token_expiry.utcnow( ) < MIN_CREDS_EXPIRY: store.Refresh(creds) access_token = None if creds is not None: access_token = creds.get_access_token().access_token start_operation = client.users_environments.Start( messages.CloudshellUsersEnvironmentsStartRequest( name=DEFAULT_ENVIRONMENT_NAME, startEnvironmentRequest=messages.StartEnvironmentRequest( accessToken=access_token))) environment = waiter.WaitFor( StartEnvironmentPoller(client.users_environments, operations_client.operations), start_operation, 'Waiting for your Cloud Shell machine to start', sleep_ms=500, max_wait_ms=None) return ConnectionInfo( ssh_env=ssh_env, user=environment.sshUsername, host=environment.sshHost, port=environment.sshPort, key=keys.key_file, )
def password(self): cred = c_store.LoadIfEnabled() return cred.access_token if cred else None