Skip to content

Commit d97ff44

Browse files
xiangyan99pvaneck
andauthored
Identity credential unavailable error non json imds (#36016)
* Raise CredentialUnavailableError if the response is not json * update changelog * update * update * Update sdk/identity/azure-identity/CHANGELOG.md Co-authored-by: Paul Van Eck <[email protected]> --------- Co-authored-by: Paul Van Eck <[email protected]>
1 parent b052da8 commit d97ff44

File tree

6 files changed

+50
-9
lines changed

6 files changed

+50
-9
lines changed

sdk/identity/azure-identity/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
### Bugs Fixed
1010

11+
- Allow credential chains to continue when an IMDS probe request returns a non-JSON response in `ManagedIdentityCredential`. ([#36016](https://github.com/Azure/azure-sdk-for-python/pull/36016))
12+
1113
### Other Changes
1214

1315
## 1.17.0b2 (2024-06-11)

sdk/identity/azure-identity/azure/identity/_credentials/imds.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ def _request_token(self, *scopes: str, **kwargs: Any) -> AccessToken:
8787
try:
8888
self._client.request_token(*scopes, connection_timeout=1, retry_total=0)
8989
self._endpoint_available = True
90+
except CredentialUnavailableError:
91+
# Response is not json, skip the IMDS credential
92+
raise
9093
except HttpResponseError as ex:
9194
# IMDS responded
9295
_check_forbidden_response(ex)

sdk/identity/azure-identity/azure/identity/_internal/managed_identity_client.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from azure.core.pipeline.policies import ContentDecodePolicy
1414
from azure.core.pipeline import PipelineResponse
1515
from azure.core.pipeline.transport import HttpRequest
16+
from .. import CredentialUnavailableError
1617
from .._internal import _scopes_to_resource
1718
from .._internal.pipeline import build_pipeline
1819

@@ -49,9 +50,9 @@ def _process_response(self, response: PipelineResponse, request_time: int) -> Ac
4950
except DecodeError as ex:
5051
if response.http_response.content_type.startswith("application/json"):
5152
message = "Failed to deserialize JSON from response"
52-
else:
53-
message = 'Unexpected content type "{}"'.format(response.http_response.content_type)
54-
raise ClientAuthenticationError(message=message, response=response.http_response) from ex
53+
raise ClientAuthenticationError(message=message, response=response.http_response) from ex
54+
message = 'Unexpected content type "{}"'.format(response.http_response.content_type)
55+
raise CredentialUnavailableError(message=message, response=response.http_response) from ex
5556

5657
if not content:
5758
raise ClientAuthenticationError(message="No token received.", response=response.http_response)

sdk/identity/azure-identity/azure/identity/aio/_credentials/imds.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ async def _request_token(self, *scopes: str, **kwargs: Any) -> AccessToken: # p
4545
try:
4646
await self._client.request_token(*scopes, connection_timeout=1, retry_total=0)
4747
self._endpoint_available = True
48+
except CredentialUnavailableError:
49+
# Response is not json, skip the IMDS credential
50+
raise
4851
except HttpResponseError as ex:
4952
# IMDS responded
5053
_check_forbidden_response(ex)

sdk/identity/azure-identity/tests/test_managed_identity.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55
import os
66
import sys
77
import time
8-
9-
try:
10-
from unittest import mock
11-
except ImportError: # python < 3.3
12-
import mock # type: ignore
8+
from unittest import mock
139

1410
from azure.core.credentials import AccessToken
1511
from azure.core.exceptions import ClientAuthenticationError
16-
from azure.identity import ManagedIdentityCredential
12+
from azure.identity import ManagedIdentityCredential, CredentialUnavailableError
1713
from azure.identity._constants import EnvironmentVariables
1814
from azure.identity._credentials.imds import IMDS_AUTHORITY, IMDS_TOKEN_PATH
1915
from azure.identity._internal.user_agent import USER_AGENT
16+
from azure.identity._internal import within_credential_chain
2017
import pytest
2118

2219
from helpers import build_aad_response, validating_transport, mock_response, Request
@@ -686,6 +683,21 @@ def test_imds_tenant_id():
686683
assert token == expected_token
687684

688685

686+
def test_imds_text_response():
687+
within_credential_chain.set(True)
688+
response = mock.Mock(
689+
text=lambda encoding=None: b"{This is a text response}",
690+
headers={"content-type": "text/html; charset=UTF-8"},
691+
content_type="text/html; charset=UTF-8",
692+
status_code=200,
693+
)
694+
mock_send = mock.Mock(return_value=response)
695+
credential = ManagedIdentityCredential(transport=mock.Mock(send=mock_send))
696+
with pytest.raises(CredentialUnavailableError):
697+
token = credential.get_token("")
698+
within_credential_chain.set(False)
699+
700+
689701
def test_client_id_none():
690702
"""the credential should ignore client_id=None"""
691703

sdk/identity/azure-identity/tests/test_managed_identity_async.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
from azure.core.credentials import AccessToken
1010
from azure.core.exceptions import ClientAuthenticationError
11+
from azure.identity import CredentialUnavailableError
1112
from azure.identity.aio import ManagedIdentityCredential
1213
from azure.identity._credentials.imds import IMDS_AUTHORITY, IMDS_TOKEN_PATH
1314
from azure.identity._constants import EnvironmentVariables
1415
from azure.identity._internal.user_agent import USER_AGENT
16+
from azure.identity._internal import within_credential_chain
1517

1618
import pytest
1719

@@ -716,6 +718,24 @@ async def test_imds_user_assigned_identity():
716718
assert token == expected_token
717719

718720

721+
@pytest.mark.asyncio
722+
async def test_imds_text_response():
723+
async def send(request, **kwargs):
724+
response = mock.Mock(
725+
text=lambda encoding=None: b"{This is a text response}",
726+
headers={"content-type": "text/html; charset=UTF-8"},
727+
content_type="text/html; charset=UTF-8",
728+
status_code=200,
729+
)
730+
return response
731+
732+
within_credential_chain.set(True)
733+
credential = ManagedIdentityCredential(transport=mock.Mock(send=send))
734+
with pytest.raises(CredentialUnavailableError):
735+
token = await credential.get_token("")
736+
within_credential_chain.set(False)
737+
738+
719739
@pytest.mark.asyncio
720740
async def test_service_fabric():
721741
"""Service Fabric 2019-07-01-preview"""

0 commit comments

Comments
 (0)