def Run(self, args): """Run the helper command.""" cred = c_store.Load(args.account) c_store.Refresh(cred) if args.force_auth_refresh: c_store.Refresh(cred) if not cred.access_token: raise auth_exceptions.InvalidCredentialsError( 'No access token could be obtained from the current credentials.') return cred
def GetCredentials(access_boundary_json): """Get an access token for the user's current credentials. Args: access_boundary_json: JSON string holding the definition of the access boundary to apply to the credentials. Raises: PersonalAuthError: If no access token could be fetched for the user. Returns: An access token for the user. """ cred = c_store.Load(None, allow_account_impersonation=True, use_google_auth=True) c_store.Refresh(cred) if c_creds.IsOauth2ClientCredentials(cred): token = cred.access_token else: token = cred.token if not token: raise exceptions.PersonalAuthError( 'No access token could be obtained from the current credentials.') return _DownscopeCredentials(token, access_boundary_json)
def Run(self, args): """Run the helper command.""" if args.method == DockerHelper.LIST: return { # This tells Docker that the secret will be an access token, not a # username/password. # Docker normally expects a prefixed 'https://' for auth configs. ('https://' + url): 'oauth2accesstoken' for url in constants.ALL_SUPPORTED_REGISTRIES } elif args.method == DockerHelper.GET: cred = c_store.Load() c_store.Refresh(cred) url = sys.stdin.read().strip() if not (url in constants.ALL_SUPPORTED_REGISTRIES or url.lstrip('https://') in constants.ALL_SUPPORTED_REGISTRIES): raise exceptions.Error( 'Repository url [{url}] is not supported'.format(url=url)) # Putting an actual username in the response doesn't work. Docker will # then prompt for a password instead of using the access token. return { 'Secret': cred.access_token, 'Username': '******', } # Don't print anything if we are not supporting the given action. # The credential helper protocol also support 'store' and 'erase' actions # that don't apply here. The full spec can be found here: # https://github.com/docker/docker-credential-helpers#development args.GetDisplayInfo().AddFormat('none') return None
def UpdateDockerCredentials(server): """Updates the docker config to have fresh credentials. This reads the current contents of Docker's keyring, and extends it with a fresh entry for the provided 'server', based on the active gcloud credential. If a credential exists for 'server' this replaces it. Args: server: The hostname of the registry for which we're freshening the credential. Raises: store.Error: There was an error loading the credentials. """ # Loading credentials will ensure that we're logged in. # And prompt/abort to 'run gcloud auth login' otherwise. cred = store.Load() # Ensure our credential has a valid access token, # which has the full duration available. store.Refresh(cred) if not cred.access_token: raise exceptions.Error('No access token could be obtained ' 'from the current credentials.') # Update the docker configuration file passing the access token # as a password, and a benign value as the username. _UpdateDockerConfig(server, _USERNAME, cred.access_token)
def Run(self, args): """Run the helper command.""" if args.method == DockerHelper.LIST: return { # This tells Docker that the secret will be an access token, not a # username/password. # Docker normally expects a prefixed 'https://' for auth configs. ('https://' + url): '_dcgcloud_token' for url in credential_utils.DefaultAuthenticatedRegistries() } elif args.method == DockerHelper.GET: cred = c_store.Load() if (not cred.token_expiry or cred.token_expiry.utcnow() > cred.token_expiry - datetime.timedelta(minutes=55)): c_store.Refresh(cred) url = sys.stdin.read().strip() if (url.replace('https://', '', 1) not in credential_utils.SupportedRegistries()): raise exceptions.Error( 'Repository url [{url}] is not supported'.format(url=url)) # Putting an actual username in the response doesn't work. Docker will # then prompt for a password instead of using the access token. return { 'Secret': cred.access_token, 'Username': '******', } # Don't print anything if we are not supporting the given action. # The credential helper protocol also support 'store' and 'erase' actions # that don't apply here. The full spec can be found here: # https://github.com/docker/docker-credential-helpers#development args.GetDisplayInfo().AddFormat('none') return None
def MakeRequest(url, command_path): """Gets the request object for the given URL. If the URL is for cloud storage and we get a 403, this will try to load the active credentials and use them to authenticate the download. Args: url: str, The URL to download. command_path: the command path to include in the User-Agent header if the URL is HTTP Raises: AuthenticationError: If this download requires authentication and there are no credentials or the credentials do not have access. Returns: urllib2.Request, The request. """ headers = { 'Cache-Control': 'no-cache', 'User-Agent': cli.MakeUserAgentString(command_path) } try: if url.startswith(ComponentInstaller.GCS_BROWSER_DL_URL): url = url.replace(ComponentInstaller.GCS_BROWSER_DL_URL, ComponentInstaller.GCS_API_DL_URL, 1) req = urllib2.Request(url, headers=headers) return urlopen_with_cacerts.urlopen(req, timeout=TIMEOUT_IN_SEC) except urllib2.HTTPError as e: if e.code != 403 or not url.startswith( ComponentInstaller.GCS_API_DL_URL): raise e try: creds = store.Load() store.Refresh(creds) creds.apply(headers) except store.Error as e: # If we fail here, it is because there are no active credentials or the # credentials are bad. raise AuthenticationError( 'This component requires valid credentials to install.', e) try: # Retry the download using the credentials. req = urllib2.Request(url, headers=headers) return urlopen_with_cacerts.urlopen(req, timeout=TIMEOUT_IN_SEC) except urllib2.HTTPError as e: if e.code != 403: raise e # If we fail again with a 403, that means we used the credentials, but # they didn't have access to the resource. raise AuthenticationError( """\ Account [{account}] does not have permission to install this component. Please ensure that this account should have access or run: $ gcloud config set account ``ACCOUNT'' to choose another account.""".format( account=properties.VALUES.core.account.Get()), e)
def GenerateIdToken(): """Generate an expiring Google-signed OAuth2 identity token. Returns: token: str, expiring Google-signed OAuth2 identity token """ # str | None, account is either a user account or google service account. account = None # oauth2client.client.OAuth2Credentials | # core.credentials.google_auth_credentials.Credentials cred = store.Load( # if account is None, implicitly retrieves properties.VALUES.core.account account, allow_account_impersonation=True, use_google_auth=True) # sets token on property of either # credentials.token_response['id_token'] or # credentials.id_tokenb64 store.Refresh(cred) credential = config_helper.Credential(cred) # str, Expiring Google-signed OAuth2 identity token token = credential.id_token return token
def UpdateDockerCredentials(server, refresh=True): """Updates the docker config to have fresh credentials. This reads the current contents of Docker's keyring, and extends it with a fresh entry for the provided 'server', based on the active gcloud credential. If a credential exists for 'server' this replaces it. Args: server: The hostname of the registry for which we're freshening the credential. refresh: Whether to force a token refresh on the active credential. Raises: store.Error: There was an error loading the credentials. """ # Loading credentials will ensure that we're logged in. # And prompt/abort to 'run gcloud auth login' otherwise. # Disable refreshing, since we'll do this ourself. cred = store.Load(prevent_refresh=True) if refresh: # Ensure our credential has a valid access token, # which has the full duration available. store.Refresh(cred) if not cred.access_token: raise exceptions.Error( 'No access token could be obtained from the current credentials.') url = _GetNormalizedURL(server) # Strip the port, if it exists. It's OK to butcher IPv6, this is only an # optimization for hostnames in constants.ALL_SUPPORTED_REGISTRIES. hostname = url.hostname.split(':')[0] third_party_cred_helper = (_CredentialHelperConfigured() and not _GCRCredHelperConfigured()) if (third_party_cred_helper or hostname not in constants.ALL_SUPPORTED_REGISTRIES): # If this is a custom host or if there's a third-party credential helper... try: # Update the credentials stored by docker, passing the access token # as a password, and benign values as the email and username. _DockerLogin(server, _EMAIL, _USERNAME, cred.access_token) except DockerError as e: # Only catch docker-not-found error if str(e) != _DOCKER_NOT_FOUND_ERROR: raise # Fall back to the previous manual .dockercfg manipulation # in order to support gcloud app's docker-binaryless use case. # TODO(user) when app deploy is using Argo to take over builds, # remove this. _UpdateDockerConfig(server, _USERNAME, cred.access_token) log.warn("'docker' was not discovered on the path. Credentials have been " 'stored, but are not guaranteed to work with the Docker client ' ' if an external credential store is configured.') elif not _GCRCredHelperConfigured(): # If this is not a custom host and no third-party cred helper... _UpdateDockerConfig(server, _USERNAME, cred.access_token)
def GetFreshToken() -> str: "Get a fresh Bearer access token, good for about 60 minutes." ucr = _credstore.Load() _credstore.Refresh(ucr) try: return ucr.access_token except AttributeError: return ucr.token
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 Run(self, args): """Run the print_identity_token command.""" cred = c_store.Load() if args.force_auth_refresh: c_store.Refresh(cred) if not cred.id_token64: raise auth_exceptions.InvalidIdentityTokenError( 'No identity token can be obtained from the current credentials.' ) return cred.id_tokenb64
def Run(self, args): """Run the helper command.""" cred = c_store.Load(args.account) c_store.Refresh(cred) if not cred.access_token: raise c_exc.ToolException( 'No access token could be obtained from the current credentials.' ) return cred.access_token
def testRefreshImpersonationAccountImpersonationNotConfigured(self): self.StartObjectPatch(util, 'GenerateAccessToken') credentials = util.ImpersonationCredentials('service-account-id', 'access-token', '2016-01-08T00:00:00Z', config.CLOUDSDK_SCOPES) store.IMPERSONATION_TOKEN_PROVIDER = None with self.assertRaisesRegex( store.AccountImpersonationError, 'gcloud is configured to impersonate a service account ' 'but impersonation support is not available.'): store.Refresh(credentials, is_impersonated_credential=True)
def Run(self, args): """Run the print_identity_token command.""" cred = c_store.Load(args.account) c_store.Refresh(cred) credential = config_helper.Credential(cred) if not credential.id_token: raise auth_exceptions.InvalidIdentityTokenError( 'No identity token can be obtained from the current credentials.' ) return credential
def _AcceptNewConnection(self): """Accept a new socket connection and start a new WebSocket tunnel.""" conn, socket_address = self._server_socket.accept() log.info('New connection from [%r]', socket_address) store.Refresh(self._cred) websocket_conn = self._InitiateWebSocketConnection(conn, self._cred) new_thread = threading.Thread( target=self._RunReceiveLocalData, args=(conn, socket_address, websocket_conn)) new_thread.daemon = True new_thread.start() return conn, websocket_conn
def _GetAccessTokenCallback(credentials): """Callback function to refresh credentials and return access token.""" if not credentials: return None log.debug('credentials type for _GetAccessTokenCallback is [%s].', six.text_type(credentials)) store.Refresh(credentials) if creds.IsGoogleAuthCredentials(credentials): return credentials.token else: return credentials.access_token
def Run(self, args): cred = store.Load() if args.force_auth_refresh: store.Refresh(cred) config_name = named_configs.ConfigurationStore.ActiveConfig().name props = properties.VALUES.AllValues() return config_helper.ConfigHelperResult( credential=cred, active_configuration=config_name, properties=props, )
def SetUp(self): # get some real credentials so we can feed tests something that works. real_creds = c_store.Load() c_store.Refresh(real_creds) self.devshell_proxy = devshell_test_base.AuthReferenceServer( self.GetPort(), response=devshell.CredentialInfoResponse( user_email='*****@*****.**', project_id='fooproj', access_token=real_creds.access_token)) self.devshell_proxy.Start() self._devshell_provider = c_store.DevShellCredentialProvider() self._devshell_provider.Register() properties.VALUES.core.account.Set('*****@*****.**')
def UpdateDockerCredentials(server, refresh=True): """Updates the docker config to have fresh credentials. This reads the current contents of Docker's keyring, and extends it with a fresh entry for the provided 'server', based on the active gcloud credential. If a credential exists for 'server' this replaces it. Args: server: The hostname of the registry for which we're freshening the credential. refresh: Whether to force a token refresh on the active credential. Raises: store.Error: There was an error loading the credentials. """ # Loading credentials will ensure that we're logged in. # And prompt/abort to 'run gcloud auth login' otherwise. # Disable refreshing, since we'll do this ourself. cred = store.Load(prevent_refresh=True) if refresh: # Ensure our credential has a valid access token, # which has the full duration available. store.Refresh(cred) if not cred.access_token: raise exceptions.Error( 'No access token could be obtained from the current credentials.') if _CredentialStoreConfigured(): try: # Update the credentials stored by docker, passing the sentinel username # and access token. DockerLogin(server, _USERNAME, cred.access_token) except client_lib.DockerError as e: # Only catch docker-not-found error if str(e) != client_lib.DOCKER_NOT_FOUND_ERROR: raise # Fall back to the previous manual .dockercfg manipulation # in order to support gcloud app's docker-binaryless use case. _UpdateDockerConfig(server, _USERNAME, cred.access_token) log.warning( "'docker' was not discovered on the path. Credentials have been " 'stored, but are not guaranteed to work with the Docker client ' ' if an external credential store is configured.') else: _UpdateDockerConfig(server, _USERNAME, cred.access_token)
def UpdateDockerCredentials(server): """Updates the docker config to have fresh credentials.""" # Loading credentials will ensure that we're logged in. # And prompt/abort to 'run gcloud auth login' otherwise. cred = c_store.Load() # Ensure our credential has a valid access token, # which has the full duration available. c_store.Refresh(cred) if not cred.access_token: raise exceptions.Error('No access token could be obtained ' 'from the current credentials.') # Update the docker configuration file passing the access token # as a password, and a benign value as the username. _UpdateDockerConfig(server, USERNAME, cred.access_token)
def Run(self, args): """Run the helper command.""" cred = c_store.Load( args.account, allow_account_impersonation=True, use_google_auth=True) c_store.Refresh(cred) if c_creds.IsOauth2ClientCredentials(cred): token = cred.access_token else: token = cred.token if not token: raise auth_exceptions.InvalidCredentialsError( 'No access token could be obtained from the current credentials.') return DummyCredentials(token)
def _Run(args): """Run the print_identity_token command.""" do_impersonation = args.IsSpecified('impersonate_service_account') cred = c_store.Load( args.account, allow_account_impersonation=do_impersonation, use_google_auth=True) is_impersonated_account = auth_util.IsImpersonationCredential(cred) if args.audiences: if not auth_util.ValidIdTokenCredential(cred): raise auth_exceptions.WrongAccountTypeError( 'Invalid account Type for `--audiences`. ' 'Requires valid service account.') target_audiences = ' '.join(args.audiences) # TODO(b/170394261): Avoid changing constant values. config.CLOUDSDK_CLIENT_ID = target_audiences if args.IsSpecified('token_format') or args.IsSpecified('include_license'): if not auth_util.IsGceAccountCredentials(cred): raise auth_exceptions.WrongAccountTypeError( 'Invalid account type for `--token-format` or `--include-license`. ' 'Requires a valid GCE service account.') if args.token_format == 'standard': if args.include_license: raise auth_exceptions.GCEIdentityTokenError( '`--include-license` can only be specified when ' '`--token-format=full`.') if args.IsSpecified('include_email'): if not auth_util.IsImpersonationCredential(cred): raise auth_exceptions.WrongAccountTypeError( 'Invalid account type for `--include-email`. ' 'Requires an impersonate service account.') c_store.Refresh( cred, is_impersonated_credential=is_impersonated_account, include_email=args.include_email, gce_token_format=args.token_format, gce_include_license=args.include_license) credential = config_helper.Credential(cred) if not credential.id_token: raise auth_exceptions.InvalidIdentityTokenError( 'No identity token can be obtained from the current credentials.') return credential
def Run(self, args): cred = store.Load() min_expiry_time_not_met = not cred.token_expiry or ( cred.token_expiry.utcnow() > cred.token_expiry - datetime.timedelta(seconds=args.min_expiry)) if args.force_auth_refresh or min_expiry_time_not_met: store.Refresh(cred) config_name = named_configs.ConfigurationStore.ActiveConfig().name props = properties.VALUES.AllValues() return config_helper.ConfigHelperResult( credential=cred, active_configuration=config_name, properties=props, )
def Run(self, args): cred = store.Load(use_google_auth=True) if args.force_auth_refresh: store.Refresh(cred) else: store.RefreshIfExpireWithinWindow(cred, '{}'.format(args.min_expiry)) config_name = named_configs.ConfigurationStore.ActiveConfig().name props = properties.VALUES.AllValues() return config_helper.ConfigHelperResult( credential=cred, active_configuration=config_name, properties=props, )
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 Run(self, args): """Run the helper command.""" cred = c_store.Load(args.account, allow_account_impersonation=True, use_google_auth=True) if args.scopes: cred_type = c_creds.CredentialTypeGoogleAuth.FromCredentials(cred) if cred_type not in [ c_creds.CredentialTypeGoogleAuth.USER_ACCOUNT, c_creds.CredentialTypeGoogleAuth.SERVICE_ACCOUNT ]: # TODO(b/223649175): Add support for other credential types(e.g GCE). log.warning( '`--scopes` flag may not working as expected and will be ignored ' 'for account type {}.'.format(cred_type.key)) scopes = args.scopes + [ auth_util.OPENID, auth_util.USER_EMAIL_SCOPE ] # non user account credential types if isinstance(cred, credentials.Scoped): cred = cred.with_scopes(scopes) else: requested_scopes = set(args.scopes) trusted_scopes = set(config.CLOUDSDK_SCOPES) if not requested_scopes.issubset(trusted_scopes): raise c_exc.InvalidArgumentException( '--scopes', 'Invalid scopes value. Please make sure the scopes are from [{0}]' .format(config.CLOUDSDK_SCOPES)) # pylint:disable=protected-access cred._scopes = scopes c_store.Refresh(cred) if c_creds.IsOauth2ClientCredentials(cred): token = cred.access_token else: token = cred.token if not token: raise auth_exceptions.InvalidCredentialsError( 'No access token could be obtained from the current credentials.' ) return FakeCredentials(token)
def _Run(args): """Run the print_identity_token command.""" cred = c_store.Load(args.account) is_service_account = auth_util.CheckAccountType(cred) if not is_service_account: raise auth_exceptions.WrongAccountTypeError( '`--audiences` can only be specified for service account.') if args.audiences: target_audiences = ' '.join(args.audiences) config.CLOUDSDK_CLIENT_ID = target_audiences c_store.Refresh(cred) credential = config_helper.Credential(cred) if not credential.id_token: raise auth_exceptions.InvalidIdentityTokenError( 'No identity token can be obtained from the current credentials.') return credential
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 _Run(args): """Run the print_identity_token command.""" do_impersonation = args.IsSpecified('impersonate_service_account') cred = c_store.Load( args.account, allow_account_impersonation=do_impersonation) is_impersonated_account = auth_util.IsImpersonationCredential(cred) if args.audiences: if not auth_util.ValidIdTokenCredential(cred): raise auth_exceptions.WrongAccountTypeError( 'Invalid account Type for `--audiences`. ' 'Requires valid service account.') target_audiences = ' '.join(args.audiences) config.CLOUDSDK_CLIENT_ID = target_audiences c_store.Refresh(cred, is_impersonated_credential=is_impersonated_account) credential = config_helper.Credential(cred) if not credential.id_token: raise auth_exceptions.InvalidIdentityTokenError( 'No identity token can be obtained from the current credentials.') return credential
def Run(self, args): """Run the authentication command.""" token = args.token or console_io.PromptResponse('Refresh token: ') if not token: raise c_exc.InvalidArgumentException('token', 'No value provided.') creds = c_store.AcquireFromToken(token) account = args.account c_store.Refresh(creds) c_store.Store(creds, account) properties.PersistProperty(properties.VALUES.core.account, account) project = args.project if project: properties.PersistProperty(properties.VALUES.core.project, project) return creds