def test_chain_attempts_all_credentials(): expected_token = AccessToken("expected_token", 0) credentials = [ Mock(get_token=Mock(side_effect=CredentialUnavailableError(message=""))), Mock(get_token=Mock(side_effect=CredentialUnavailableError(message=""))), Mock(get_token=Mock(return_value=expected_token)), ] token = ChainedTokenCredential(*credentials).get_token("scope") assert token is expected_token for credential in credentials: assert credential.get_token.call_count == 1
def test_chain_raises_for_unexpected_error(): """the chain should not continue after an unexpected error (i.e. anything but CredentialUnavailableError)""" expected_message = "it can't be done" credentials = [ Mock(get_token=Mock(side_effect=CredentialUnavailableError(message=""))), Mock(get_token=Mock(side_effect=ValueError(expected_message))), Mock(get_token=Mock(return_value=AccessToken("**", 42))), ] with pytest.raises(ClientAuthenticationError) as ex: ChainedTokenCredential(*credentials).get_token("scope") assert expected_message in ex.value.message assert credentials[-1].get_token.call_count == 0
def test_credential_chain_error_message(): first_error = "first_error" first_credential = Mock( spec=ClientSecretCredential, get_token=Mock(side_effect=CredentialUnavailableError(first_error)) ) second_error = "second_error" second_credential = Mock( name="second_credential", get_token=Mock(side_effect=ClientAuthenticationError(second_error)) ) with pytest.raises(ClientAuthenticationError) as ex: ChainedTokenCredential(first_credential, second_credential).get_token("scope") assert "ClientSecretCredential" in ex.value.message assert first_error in ex.value.message assert second_error in ex.value.message
def test_iterates_only_once(): """When a credential succeeds, DefaultAzureCredential should use that credential thereafter, ignoring the others""" unavailable_credential = Mock(get_token=Mock(side_effect=CredentialUnavailableError(message="..."))) successful_credential = Mock(get_token=Mock(return_value=AccessToken("***", 42))) credential = DefaultAzureCredential() credential.credentials = [ unavailable_credential, successful_credential, Mock(get_token=Mock(side_effect=Exception("iteration didn't stop after a credential provided a token"))), ] for n in range(3): credential.get_token("scope") assert unavailable_credential.get_token.call_count == 1 assert successful_credential.get_token.call_count == n + 1
async def test_iterates_only_once(): """When a credential succeeds, AzureApplicationCredential should use that credential thereafter""" expected_token = AccessToken("***", 42) unavailable_credential = Mock(get_token=Mock(side_effect=CredentialUnavailableError(message="..."))) successful_credential = Mock(get_token=Mock(return_value=get_completed_future(expected_token))) credential = AzureApplicationCredential() credential.credentials = [ unavailable_credential, successful_credential, Mock(get_token=Mock(side_effect=Exception("iteration didn't stop after a credential provided a token"))), ] for n in range(3): token = await credential.get_token("scope") assert token.token == expected_token.token assert unavailable_credential.get_token.call_count == 1 assert successful_credential.get_token.call_count == n + 1
async def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument # type: (*str, **Any) -> AccessToken """Request an access token for `scopes`. This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method only returns tokens for the https://quantum.microsoft.com/.default scope. :raises ~azure.identity.CredentialUnavailableError: when failing to get token. The exception has a `message` attribute with the error message. """ if not self.token_file: raise CredentialUnavailableError( message="Token file location not set.") if not os.path.isfile(self.token_file): raise CredentialUnavailableError( message="Token file at {} does not exist.".format( self.token_file)) try: token = await self._parse_token_file(self.token_file) except JSONDecodeError: raise CredentialUnavailableError( message="Failed to parse token file: Invalid JSON.") except KeyError as e: raise CredentialUnavailableError( message="Failed to parse token file: Missing expected value: " + str(e)) except Exception as e: raise CredentialUnavailableError( message="Failed to parse token file: " + str(e)) if token.expires_on <= time.time(): raise CredentialUnavailableError( message="Token already expired at {}".format( time.asctime(time.gmtime(token.expires_on)))) return token
async def credential_unavailable(message="it didn't work"): raise CredentialUnavailableError(message)