def testServiceAccountImpersonation(self): # This is the logged in credential, but it will not be used to make the call # because of the impersonation. store.Store(self.fake_cred) properties.VALUES.auth.impersonate_service_account.Set('*****@*****.**') # Expect a call to get a temp access token for impersonation. fake_token = 'impersonation-token' self.mock_client.projects_serviceAccounts.GenerateAccessToken.Expect( self.gen_request_msg( name='projects/-/serviceAccounts/[email protected]', generateAccessTokenRequest=self.messages.GenerateAccessTokenRequest( scope=config.CLOUDSDK_SCOPES)), self.messages.GenerateAccessTokenResponse( accessToken=fake_token, expireTime='2016-01-08T00:00:00Z') ) request_mock = self.StartObjectPatch( httplib2.Http, 'request', return_value=(httplib2.Response({'status': 200}), b'')) try: store.IMPERSONATION_TOKEN_PROVIDER = ( util.ImpersonationAccessTokenProvider()) http.Http().request('http://foo.com', 'GET', None, {}) access_token = request_mock.call_args[0][3][b'Authorization'] self.assertEqual(access_token, b'Bearer ' + fake_token.encode('utf8')) finally: store.IMPERSONATION_TOKEN_PROVIDER = None
def SetUp(self): properties.VALUES.core.account.Set('fakeuser') fake_creds = self.MakeUserAccountCredentialsGoogleAuth() fake_creds.token = 'access-token' store.Store(fake_creds) properties.VALUES.auth.impersonate_service_account.Set( '*****@*****.**') self.refresh_mock = self.StartObjectPatch( google_auth_credentials.UserCredWithReauth, 'refresh') self.StartObjectPatch(client.OAuth2Credentials, 'refresh') self.request_mock = self.StartObjectPatch( httplib2.Http, 'request', autospec=True, return_value=(httplib2.Response({'status': 200}), b'')) self.impersonation_token = 'impersonation-token' self.StartObjectPatch(impersonated_credentials, '_make_iam_token_request', return_value=(self.impersonation_token, datetime.datetime( 9999, 2, 3, 14, 15, 16))) store.IMPERSONATION_TOKEN_PROVIDER = ( util.ImpersonationAccessTokenProvider())
def testRefreshImpersonationAccountId(self): # Store test credential store.Store(self.fake_cred) properties.VALUES.auth.impersonate_service_account.Set('*****@*****.**') try: # Set Token Provider store.IMPERSONATION_TOKEN_PROVIDER = ( util.ImpersonationAccessTokenProvider()) # Mock response from util.GenerateAccessToken self.mock_client.projects_serviceAccounts.GenerateAccessToken.Expect( self.gen_request_msg( name='projects/-/serviceAccounts/[email protected]', generateAccessTokenRequest=( self.messages.GenerateAccessTokenRequest( scope=config.CLOUDSDK_SCOPES))), self.messages.GenerateAccessTokenResponse( accessToken='impersonation-token', expireTime='2016-01-08T00:00:00Z')) # Load test impersonation token loaded = store.Load(allow_account_impersonation=True) loaded.token_response = {'id_token': 'old-id-token'} audience = 'https://service-hash-uc.a.run.app' config.CLOUDSDK_CLIENT_ID = audience # Refresh the credential # Mock response from util.GenerateAccessToken (2nd call from Refresh) self.mock_client.projects_serviceAccounts.GenerateAccessToken.Expect( self.gen_request_msg( name='projects/-/serviceAccounts/[email protected]', generateAccessTokenRequest=( self.messages.GenerateAccessTokenRequest( scope=config.CLOUDSDK_SCOPES))), self.messages.GenerateAccessTokenResponse( accessToken='impersonation-token', expireTime='2016-01-08T00:00:00Z')) # Mock response from util.GenerateIdToken new_token_id = 'new-id-token' self.mock_client.projects_serviceAccounts.GenerateIdToken.Expect( self.gen_id_msg( name='projects/-/serviceAccounts/[email protected]', generateIdTokenRequest=self.messages.GenerateIdTokenRequest( audience=audience, includeEmail=False)), self.messages.GenerateIdTokenResponse(token=new_token_id) ) # Load test impersonation token loaded = store.Load(allow_account_impersonation=True) loaded.token_response = {'id_token': 'old-id-token'} audience = 'https://service-hash-uc.a.run.app' config.CLOUDSDK_CLIENT_ID = audience store.Refresh(loaded, is_impersonated_credential=True) self.assertEqual(loaded.token_response['id_token'], new_token_id) finally: # Clean-Up store.IMPERSONATION_TOKEN_PROVIDER = None
def __enter__(self): """Registers sources for credentials and project for use by commands.""" self._credential_providers = self._credential_providers or [ store.GceCredentialProvider(), ] for provider in self._credential_providers: provider.Register() # Register support for service account impersonation. store.IMPERSONATION_TOKEN_PROVIDER = ( iamcred_util.ImpersonationAccessTokenProvider()) return self
def main(gcloud_cli=None, credential_providers=None): if not platforms.PythonVersion().IsCompatible( allow_py3=properties.VALUES.core.allow_py3.GetBool()): sys.exit(1) metrics.Started(START_TIME) # TODO(b/36049857): Put a real version number here metrics.Executions( 'gcloud', local_state.InstallationState.VersionForInstalledComponent('core')) if gcloud_cli is None: gcloud_cli = CreateCLI([]) # Register some other sources for credentials and project. credential_providers = credential_providers or [ creds_store.DevShellCredentialProvider(), creds_store.GceCredentialProvider(), ] for provider in credential_providers: provider.Register() # Register support for service account impersonation. creds_store.IMPERSONATION_TOKEN_PROVIDER = ( iamcred_util.ImpersonationAccessTokenProvider()) try: try: gcloud_cli.Execute() except IOError as err: # We want to ignore EPIPE IOErrors. # By default, Python ignores SIGPIPE (see # http://utcc.utoronto.ca/~cks/space/blog/python/SignalExceptionSurprise). # This means that attempting to write any output to a closed pipe (e.g. in # the case of output piped to `head` or `grep -q`) will result in an # IOError, which gets reported as a gcloud crash. We don't want this # behavior, so we ignore EPIPE (it's not a real error; it's a normal thing # to occur). # Before, we restore the SIGPIPE signal handler, but that caused issues # with scripts/programs that wrapped gcloud. if err.errno != errno.EPIPE: raise except Exception as err: # pylint:disable=broad-except crash_handling.HandleGcloudCrash(err) if properties.VALUES.core.print_unhandled_tracebacks.GetBool(): # We want to see the traceback as normally handled by Python raise else: # This is the case for most non-Cloud SDK developers. They shouldn't see # the full stack trace, but just the nice "gcloud crashed" message. sys.exit(1) finally: for provider in credential_providers: provider.UnRegister()
def testImpersonation(self): self.request_mock.return_value = (httplib2.Response({'status': 200}), b'{"projects": []}') try: store.IMPERSONATION_TOKEN_PROVIDER = ( util.ImpersonationAccessTokenProvider()) self.Run( '--impersonate-service-account [email protected] projects list') access_token = self.request_mock.call_args[1]['headers'][ b'authorization'] # Make sure the request was made with the service account token. self.assertEqual(access_token, b'Bearer impersonation-token') finally: store.IMPERSONATION_TOKEN_PROVIDER = None
def __enter__(self): self._orig_account = properties.VALUES.core.account.Get() self._orig_project = properties.VALUES.core.project.Get() self._orig_impersonate_service_account = ( properties.VALUES.auth.impersonate_service_account.Get()) user_creds = c_store.AcquireFromToken(self._refresh_token) c_store.ActivateCredentials(self._account, user_creds) if self._project_override: properties.VALUES.core.project.Set(self._project_override) properties.VALUES.auth.impersonate_service_account.Set( self._service_account_email) self._orig_impersonate_provider = c_store.IMPERSONATION_TOKEN_PROVIDER c_store.IMPERSONATION_TOKEN_PROVIDER = ( iamcred_util.ImpersonationAccessTokenProvider()) return self
def testRefreshImpersonationAccountImpersonationBadCred(self): self.StartObjectPatch(util, 'GenerateAccessToken') def refresh(fake_client): del fake_client bad_credential = client.OAuth2Credentials(None, None, None, None, None, None, None) bad_credential.refresh = refresh try: store.IMPERSONATION_TOKEN_PROVIDER = ( util.ImpersonationAccessTokenProvider()) with self.assertRaisesRegex(store.AccountImpersonationError, 'Invalid impersonation account for refresh'): store.Refresh(bad_credential, is_impersonated_credential=True) finally: # Clean-Up store.IMPERSONATION_TOKEN_PROVIDER = None
def testImpersonation(self): self.mock_client.projects_serviceAccounts.GenerateAccessToken.Expect( self.gen_request_msg( name='projects/-/serviceAccounts/[email protected]', generateAccessTokenRequest=self.messages. GenerateAccessTokenRequest(scope=config.CLOUDSDK_SCOPES)), self.messages.GenerateAccessTokenResponse( accessToken='impersonation-token', expireTime='2016-01-08T00:00:00Z')) self.request_mock.return_value = (httplib2.Response({'status': 200}), b'{"projects": []}') try: store.IMPERSONATION_TOKEN_PROVIDER = ( util.ImpersonationAccessTokenProvider()) self.Run( 'alpha --impersonate-service-account [email protected] projects list' ) access_token = self.request_mock.call_args[0][4][b'Authorization'] # Make sure the request was made with the service account token. self.assertEqual(access_token, b'Bearer impersonation-token') finally: store.IMPERSONATION_TOKEN_PROVIDER = None
def main(gcloud_cli=None, credential_providers=None): if not platforms.PythonVersion().IsCompatible( allow_py3=properties.VALUES.core.allow_py3.GetBool()): sys.exit(1) metrics.Started(START_TIME) # TODO(b/36049857): Put a real version number here metrics.Executions( 'gcloud', local_state.InstallationState.VersionForInstalledComponent('core')) if gcloud_cli is None: gcloud_cli = CreateCLI([]) # Register some other sources for credentials and project. credential_providers = credential_providers or [ creds_store.DevShellCredentialProvider(), creds_store.GceCredentialProvider(), ] for provider in credential_providers: provider.Register() # Register support for service account impersonation. creds_store.IMPERSONATION_TOKEN_PROVIDER = ( iamcred_util.ImpersonationAccessTokenProvider()) try: try: gcloud_cli.Execute() # Flush stdout so that if we've received a SIGPIPE we handle the broken # pipe within this try block, instead of potentially during interpreter # shutdown. sys.stdout.flush() except IOError as err: # We want to ignore EPIPE IOErrors (as of Python 3.3 these can be caught # specifically with BrokenPipeError, but we do it this way for Python 2 # compatibility). # # By default, Python ignores SIGPIPE (see # http://utcc.utoronto.ca/~cks/space/blog/python/SignalExceptionSurprise). # This means that attempting to write any output to a closed pipe (e.g. in # the case of output piped to `head` or `grep -q`) will result in an # IOError, which gets reported as a gcloud crash. We don't want this # behavior, so we ignore EPIPE (it's not a real error; it's a normal thing # to occur). # # Before, we restored the SIGPIPE signal handler, but that caused issues # with scripts/programs that wrapped gcloud. if err.errno == errno.EPIPE: # At this point we've caught the broken pipe, but since Python flushes # standard streams on exit, it's still possible for a broken pipe error # to happen during interpreter shutdown. The interpreter will catch this # but in Python 3 it still prints a warning to stderr saying that the # exception was ignored (see https://bugs.python.org/issue11380): # # Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' # encoding='UTF-8'> # BrokenPipeError: [Errno 32] Broken pipe # # To prevent this from happening, we redirect any remaining output to # devnull as recommended here: # https://docs.python.org/3/library/signal.html#note-on-sigpipe. devnull = os.open(os.devnull, os.O_WRONLY) os.dup2(devnull, sys.stdout.fileno()) else: raise except Exception as err: # pylint:disable=broad-except crash_handling.HandleGcloudCrash(err) if properties.VALUES.core.print_unhandled_tracebacks.GetBool(): # We want to see the traceback as normally handled by Python raise else: # This is the case for most non-Cloud SDK developers. They shouldn't see # the full stack trace, but just the nice "gcloud crashed" message. sys.exit(1) finally: for provider in credential_providers: provider.UnRegister()