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
Example #5
0
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
Example #6
0
    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
Example #7
0
 async def credential_unavailable(message="it didn't work"):
     raise CredentialUnavailableError(message)