Skip to content

Commit fefe1a3

Browse files
authored
[Identity] Update behavior of MI Service Fabric (Azure#39848)
Service Fabric managed identity should not allow a user to pass in a user-assigned identity since Service Fabric will always use the cluster-configured identity. Signed-off-by: Paul Van Eck <[email protected]>
1 parent 339fdc5 commit fefe1a3

File tree

5 files changed

+85
-1
lines changed

5 files changed

+85
-1
lines changed

sdk/identity/azure-identity/CHANGELOG.md

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

77
### Breaking Changes
88

9+
- Previously, if a `client_id` or `identity_config` was specified in `ManagedIdentityCredential` for Service Fabric managed identity, which is not supported, the `client_id` (or `resource_id`/`object_id` specified `identity_config`) would be silently ignored. Now, an exception will be raised during a token request if a `client_id` or `identity_config` is specified for Service Fabric managed identity.
10+
911
### Bugs Fixed
1012

1113
### Other Changes

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,37 @@
66
import os
77
from typing import Dict, Optional, Any
88

9+
from azure.core.credentials import AccessToken, AccessTokenInfo, TokenRequestOptions
10+
from azure.core.exceptions import ClientAuthenticationError
911
from azure.core.pipeline.transport import HttpRequest
1012

1113
from .._constants import EnvironmentVariables
1214
from .._internal.msal_managed_identity_client import MsalManagedIdentityClient
1315

1416

17+
SERVICE_FABRIC_ERROR_MESSAGE = (
18+
"Specifying a client_id or identity_config is not supported by the Service Fabric managed identity environment. "
19+
"The managed identity configuration is determined by the Service Fabric cluster resource configuration. "
20+
"See https://aka.ms/servicefabricmi for more information."
21+
)
22+
23+
1524
class ServiceFabricCredential(MsalManagedIdentityClient):
1625
def get_unavailable_message(self, desc: str = "") -> str:
1726
return f"Service Fabric managed identity configuration not found in environment. {desc}"
1827

28+
def get_token(
29+
self, *scopes: str, claims: Optional[str] = None, tenant_id: Optional[str] = None, **kwargs: Any
30+
) -> AccessToken:
31+
if self._settings.get("client_id") or self._settings.get("identity_config"):
32+
raise ClientAuthenticationError(message=SERVICE_FABRIC_ERROR_MESSAGE)
33+
return super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
34+
35+
def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
36+
if self._settings.get("client_id") or self._settings.get("identity_config"):
37+
raise ClientAuthenticationError(message=SERVICE_FABRIC_ERROR_MESSAGE)
38+
return super().get_token_info(*scopes, options=options)
39+
1940

2041
def _get_client_args(**kwargs: Any) -> Optional[Dict]:
2142
url = os.environ.get(EnvironmentVariables.IDENTITY_ENDPOINT)

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
# ------------------------------------
55
from typing import Optional, Any
66

7+
from azure.core.credentials import AccessToken, AccessTokenInfo, TokenRequestOptions
8+
from azure.core.exceptions import ClientAuthenticationError
9+
710
from .._internal.managed_identity_base import AsyncManagedIdentityBase
811
from .._internal.managed_identity_client import AsyncManagedIdentityClient
9-
from ..._credentials.service_fabric import _get_client_args
12+
from ..._credentials.service_fabric import _get_client_args, SERVICE_FABRIC_ERROR_MESSAGE
1013

1114

1215
class ServiceFabricCredential(AsyncManagedIdentityBase):
@@ -18,3 +21,16 @@ def get_client(self, **kwargs: Any) -> Optional[AsyncManagedIdentityClient]:
1821

1922
def get_unavailable_message(self, desc: str = "") -> str:
2023
return f"Service Fabric managed identity configuration not found in environment. {desc}"
24+
25+
async def get_token(
26+
self, *scopes: str, claims: Optional[str] = None, tenant_id: Optional[str] = None, **kwargs: Any
27+
) -> AccessToken:
28+
if self._client and self._client._identity_config: # pylint:disable=protected-access
29+
raise ClientAuthenticationError(message=SERVICE_FABRIC_ERROR_MESSAGE)
30+
return await super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
31+
32+
async def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
33+
34+
if self._client and self._client._identity_config: # pylint:disable=protected-access
35+
raise ClientAuthenticationError(message=SERVICE_FABRIC_ERROR_MESSAGE)
36+
return await super().get_token_info(*scopes, options=options)

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import logging
88
from unittest import mock
99

10+
from azure.core.exceptions import ClientAuthenticationError
1011
from azure.identity import ManagedIdentityCredential, CredentialUnavailableError
1112
from azure.identity._constants import EnvironmentVariables
1213
from azure.identity._credentials.imds import IMDS_AUTHORITY, IMDS_TOKEN_PATH
@@ -820,6 +821,27 @@ def send(request, **kwargs):
820821
assert abs(token.expires_on - expires_on) <= 1
821822

822823

824+
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
825+
def test_service_fabric_with_client_id_error(get_token_method):
826+
"""ManagedIdentityCredential should raise an error if a user identity is provided."""
827+
endpoint = "http://localhost:42"
828+
with mock.patch(
829+
"os.environ",
830+
{
831+
EnvironmentVariables.IDENTITY_ENDPOINT: endpoint,
832+
EnvironmentVariables.IDENTITY_HEADER: "secret",
833+
EnvironmentVariables.IDENTITY_SERVER_THUMBPRINT: "thumbprint",
834+
},
835+
):
836+
cred = ManagedIdentityCredential(client_id="client_id")
837+
with pytest.raises(ClientAuthenticationError):
838+
getattr(cred, get_token_method)("scope")
839+
840+
cred = ManagedIdentityCredential(identity_config={"resource_id": "resource_id"})
841+
with pytest.raises(ClientAuthenticationError):
842+
getattr(cred, get_token_method)("scope")
843+
844+
823845
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
824846
def test_token_exchange(tmpdir, get_token_method):
825847
exchange_token = "exchange-token"

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,29 @@ async def send(request, **kwargs):
858858
assert token.expires_on == expires_on
859859

860860

861+
@pytest.mark.asyncio
862+
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
863+
async def test_service_fabric_with_client_id_error(get_token_method):
864+
"""ManagedIdentityCredential should raise an error if a user identity is provided."""
865+
endpoint = "http://localhost:42"
866+
with mock.patch(
867+
"os.environ",
868+
{
869+
EnvironmentVariables.IDENTITY_ENDPOINT: endpoint,
870+
EnvironmentVariables.IDENTITY_HEADER: "secret",
871+
EnvironmentVariables.IDENTITY_SERVER_THUMBPRINT: "thumbprint",
872+
},
873+
):
874+
875+
cred = ManagedIdentityCredential(client_id="client_id")
876+
with pytest.raises(ClientAuthenticationError):
877+
await getattr(cred, get_token_method)("scope")
878+
879+
cred = ManagedIdentityCredential(identity_config={"resource_id": "resource_id"})
880+
with pytest.raises(ClientAuthenticationError):
881+
await getattr(cred, get_token_method)("scope")
882+
883+
861884
@pytest.mark.asyncio
862885
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
863886
async def test_azure_arc(tmpdir, get_token_method):

0 commit comments

Comments
 (0)