From 65449efb37a43c2f8e0e1faaa78617ef79a08b76 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 08:52:42 -0700 Subject: [PATCH 01/11] Update Multi-Tenant support to incorporate Arch Board Feedback --- sdk/identity/azure-identity/CHANGELOG.md | 5 + .../azure-identity/azure/identity/__init__.py | 2 - .../azure/identity/_constants.py | 2 +- .../identity/_credentials/application.py | 4 - .../_credentials/authorization_code.py | 6 +- .../azure/identity/_credentials/azure_cli.py | 11 +- .../identity/_credentials/azure_powershell.py | 12 +- .../azure/identity/_credentials/browser.py | 3 - .../identity/_credentials/certificate.py | 8 +- .../identity/_credentials/client_secret.py | 8 +- .../azure/identity/_credentials/default.py | 6 +- .../identity/_credentials/device_code.py | 3 - .../identity/_credentials/environment.py | 7 +- .../identity/_credentials/on_behalf_of.py | 3 - .../identity/_credentials/shared_cache.py | 4 - .../azure/identity/_credentials/silent.py | 7 +- .../identity/_credentials/user_password.py | 3 - .../azure/identity/_credentials/vscode.py | 3 - .../azure/identity/_internal/__init__.py | 10 +- .../identity/_internal/aad_client_base.py | 4 +- .../identity/_internal/get_token_mixin.py | 3 +- .../azure/identity/_internal/interactive.py | 3 +- .../identity/_internal/msal_credentials.py | 7 +- .../identity/aio/_credentials/application.py | 4 - .../aio/_credentials/authorization_code.py | 6 +- .../identity/aio/_credentials/azure_cli.py | 11 +- .../aio/_credentials/azure_powershell.py | 12 +- .../identity/aio/_credentials/certificate.py | 3 - .../aio/_credentials/client_secret.py | 3 - .../identity/aio/_credentials/default.py | 6 +- .../identity/aio/_credentials/environment.py | 7 +- .../identity/aio/_credentials/on_behalf_of.py | 5 +- .../identity/aio/_credentials/shared_cache.py | 6 +- .../azure/identity/aio/_credentials/vscode.py | 6 +- .../identity/aio/_internal/get_token_mixin.py | 3 +- .../azure-identity/tests/test_aad_client.py | 7 +- .../tests/test_aad_client_async.py | 7 +- .../azure-identity/tests/test_auth_code.py | 17 +-- .../tests/test_auth_code_async.py | 18 +-- .../tests/test_certificate_credential.py | 19 ++- .../test_certificate_credential_async.py | 17 +-- .../tests/test_cli_credential.py | 42 +------ .../tests/test_cli_credential_async.py | 41 +------ .../tests/test_client_secret_credential.py | 21 ++-- .../test_client_secret_credential_async.py | 18 +-- .../azure-identity/tests/test_default.py | 30 ----- .../tests/test_default_async.py | 27 ----- .../tests/test_interactive_credential.py | 17 +-- sdk/identity/azure-identity/tests/test_obo.py | 4 +- .../azure-identity/tests/test_obo_async.py | 4 +- .../tests/test_powershell_credential.py | 39 +----- .../tests/test_powershell_credential_async.py | 39 +----- .../tests/test_shared_cache_credential.py | 112 +----------------- .../test_shared_cache_credential_async.py | 47 +------- .../tests/test_vscode_credential.py | 18 +-- .../tests/test_vscode_credential_async.py | 18 +-- 56 files changed, 129 insertions(+), 629 deletions(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 49c726f3f0a2..4905d2b848fa 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -4,6 +4,11 @@ ### Features Added +- `allow_multitenant_authentication` argument is deprecated and the setting is always on. To + disable it, set environment variable + AZURE_IDENTITY_DISABLE_MULTITENANTAUTH to "True". +- `azure.identity.RegionalAuthority` is removed. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/identity/azure-identity/azure/identity/__init__.py b/sdk/identity/azure-identity/azure/identity/__init__.py index 4d7dff365695..0969ad2e0504 100644 --- a/sdk/identity/azure-identity/azure/identity/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/__init__.py @@ -5,7 +5,6 @@ """Credentials for Azure SDK clients.""" from ._auth_record import AuthenticationRecord -from ._enums import RegionalAuthority from ._exceptions import AuthenticationRequiredError, CredentialUnavailableError from ._constants import AzureAuthorityHosts, KnownAuthorities from ._credentials import ( @@ -47,7 +46,6 @@ "InteractiveBrowserCredential", "KnownAuthorities", "OnBehalfOfCredential", - "RegionalAuthority", "ManagedIdentityCredential", "SharedTokenCacheCredential", "TokenCachePersistenceOptions", diff --git a/sdk/identity/azure-identity/azure/identity/_constants.py b/sdk/identity/azure-identity/azure/identity/_constants.py index 878d7f6bce7f..4cf9fb2d9287 100644 --- a/sdk/identity/azure-identity/azure/identity/_constants.py +++ b/sdk/identity/azure-identity/azure/identity/_constants.py @@ -44,7 +44,7 @@ class EnvironmentVariables: MSI_SECRET = "MSI_SECRET" AZURE_AUTHORITY_HOST = "AZURE_AUTHORITY_HOST" - AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION = "AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION" + AZURE_IDENTITY_DISABLE_MULTITENANTAUTH = "AZURE_IDENTITY_DISABLE_MULTITENANTAUTH" AZURE_REGIONAL_AUTHORITY_NAME = "AZURE_REGIONAL_AUTHORITY_NAME" AZURE_FEDERATED_TOKEN_FILE = "AZURE_FEDERATED_TOKEN_FILE" diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/application.py b/sdk/identity/azure-identity/azure/identity/_credentials/application.py index 46a84e13eb7b..abb22fbaabac 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/application.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/application.py @@ -48,10 +48,6 @@ class AzureApplicationCredential(ChainedTokenCredential): `_ for an overview of managed identities. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application or user is registered in. When False, which is the default, the credential will acquire tokens - only from the tenant specified by **AZURE_TENANT_ID**. This argument doesn't apply to managed identity - authentication. :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", the authority for Azure Public Cloud, which is the default when no value is given for this keyword argument or environment variable AZURE_AUTHORITY_HOST. :class:`~azure.identity.AzureAuthorityHosts` defines authorities for diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/authorization_code.py b/sdk/identity/azure-identity/azure/identity/_credentials/authorization_code.py index 587547640744..7eae087b837c 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/authorization_code.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/authorization_code.py @@ -30,9 +30,6 @@ class AuthorizationCodeCredential(GetTokenMixin): the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. :keyword str client_secret: One of the application's client secrets. Required only for web apps and web APIs. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant specified by **tenant_id**. """ def __init__(self, tenant_id, client_id, authorization_code, redirect_uri, **kwargs): @@ -67,8 +64,7 @@ def get_token(self, *scopes, **kwargs): redeeming the authorization code. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` :raises ~azure.core.exceptions.ClientAuthenticationError: authentication failed. The error's ``message`` diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index d535a286adb7..a7cf596b9687 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -20,6 +20,7 @@ from .. import CredentialUnavailableError from .._internal import _scopes_to_resource, resolve_tenant from .._internal.decorators import log_get_token +from .._constants import EnvironmentVariables if TYPE_CHECKING: # pylint:disable=ungrouped-imports @@ -35,14 +36,11 @@ class AzureCliCredential(object): """Authenticates by requesting a token from the Azure CLI. This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. - - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the identity logged in to the Azure CLI is registered in. When False, which is the default, the credential will - acquire tokens only from the tenant of the Azure CLI's active subscription. """ def __init__(self, **kwargs): - self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False) + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + self._allow_multitenant = not disable_multitenant def __enter__(self): return self @@ -63,8 +61,7 @@ def get_token(self, *scopes, **kwargs): also handle token caching because this credential doesn't cache the tokens it acquires. :param str scopes: desired scope for the access token. This credential allows only one scope per request. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py index 17869fbde253..1bcde2850a9c 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py @@ -4,6 +4,7 @@ # ------------------------------------ import base64 import logging +import os import platform import subprocess import sys @@ -18,6 +19,7 @@ from .. import CredentialUnavailableError from .._internal import _scopes_to_resource, resolve_tenant from .._internal.decorators import log_get_token +from .._constants import EnvironmentVariables if TYPE_CHECKING: # pylint:disable=ungrouped-imports @@ -51,15 +53,12 @@ class AzurePowerShellCredential(object): """Authenticates by requesting a token from Azure PowerShell. This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. - - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the identity logged in to Azure PowerShell is registered in. When False, which is the default, the credential - will acquire tokens only from the tenant of Azure PowerShell's active subscription. """ def __init__(self, **kwargs): # type: (**Any) -> None - self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False) + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + self._allow_multitenant = not disable_multitenant def __enter__(self): return self @@ -80,8 +79,7 @@ def get_token(self, *scopes, **kwargs): also handle token caching because this credential doesn't cache the tokens it acquires. :param str scopes: desired scope for the access token. This credential allows only one scope per request. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/browser.py b/sdk/identity/azure-identity/azure/identity/_credentials/browser.py index 6aead5b26f47..5b624046a7ac 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/browser.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/browser.py @@ -51,9 +51,6 @@ class InteractiveBrowserCredential(InteractiveCredential): will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions :keyword int timeout: seconds to wait for the user to complete authentication. Defaults to 300 (5 minutes). - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant specified by **tenant_id**. :raises ValueError: invalid **redirect_uri** """ diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py b/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py index cdb999d41898..f8cd629a9ff0 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py @@ -39,18 +39,14 @@ class CertificateCredential(ClientCredentialBase): :keyword password: The certificate's password. If a unicode string, it will be encoded as UTF-8. If the certificate requires a different encoding, pass appropriately encoded bytes instead. :paramtype password: str or bytes - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only from - the tenant specified by **tenant_id**. :keyword bool send_certificate_chain: if True, the credential will send the public certificate chain in the x5c header of each token request's JWT. This is required for Subject Name/Issuer (SNI) authentication. Defaults to False. :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword ~azure.identity.RegionalAuthority regional_authority: a :class:`~azure.identity.RegionalAuthority` to - which the credential will authenticate. This argument should be used only by applications deployed to Azure - VMs. + :keyword str regional_authority: to which the credential will authenticate. + This argument should be used only by applications deployed to Azure VMs. """ def __init__(self, tenant_id, client_id, certificate_path=None, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py b/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py index 9623b0ef8b1d..6d4d3acb39eb 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py @@ -21,15 +21,11 @@ class ClientSecretCredential(ClientCredentialBase): :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only from - the tenant specified by **tenant_id**. :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword ~azure.identity.RegionalAuthority regional_authority: a :class:`~azure.identity.RegionalAuthority` to - which the credential will authenticate. This argument should be used only by applications deployed to Azure - VMs. + :keyword str regional_authority: to which the credential will authenticate. + This argument should be used only by applications deployed to Azure VMs. """ def __init__(self, tenant_id, client_id, client_secret, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index 90ddc1b39b53..f62e1eb899fe 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -47,9 +47,6 @@ class DefaultAzureCredential(ChainedTokenCredential): This default behavior is configurable with keyword arguments. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only from - its configured tenant. This argument doesn't apply to managed identity authentication. :keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com', the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. Managed identities ignore this because they reside in a single cloud. @@ -158,8 +155,7 @@ def get_token(self, *scopes, **kwargs): This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/device_code.py b/sdk/identity/azure-identity/azure/identity/_credentials/device_code.py index 657bd5eb5568..e5af7b89c8d1 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/device_code.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/device_code.py @@ -55,9 +55,6 @@ class DeviceCodeCredential(InteractiveCredential): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant specified by **tenant_id**. """ def __init__(self, client_id=DEVELOPER_SIGN_ON_CLIENT_ID, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/environment.py b/sdk/identity/azure-identity/azure/identity/_credentials/environment.py index 8d0e7401d8b2..d2b134d11e9e 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/environment.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/environment.py @@ -52,10 +52,6 @@ class EnvironmentCredential(object): - **AZURE_TENANT_ID**: (optional) ID of the service principal's tenant. Also called its 'directory' ID. If not provided, defaults to the 'organizations' tenant, which supports only Azure Active Directory work or school accounts. - - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application or user is registered in. When False, which is the default, the credential will acquire tokens - only from the tenant specified by **AZURE_TENANT_ID**. """ def __init__(self, **kwargs): @@ -123,8 +119,7 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/on_behalf_of.py b/sdk/identity/azure-identity/azure/identity/_credentials/on_behalf_of.py index bc39c7a475e1..70f3c407ca16 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/on_behalf_of.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/on_behalf_of.py @@ -39,9 +39,6 @@ class OnBehalfOfCredential(MsalCredential, GetTokenMixin): :param str user_assertion: the access token the credential will use as the user assertion when requesting on-behalf-of tokens - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only - from the tenant specified by **tenant_id**. :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/shared_cache.py b/sdk/identity/azure-identity/azure/identity/_credentials/shared_cache.py index 906aaad174fc..3cef36e80507 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/shared_cache.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/shared_cache.py @@ -34,10 +34,6 @@ class SharedTokenCacheCredential(object): :keyword cache_persistence_options: configuration for persistent token caching. If not provided, the credential will use the persistent cache shared by Microsoft development applications :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or, if a value was given for **authentication_record**, the tenant specified by the - :class:`AuthenticationRecord`. """ def __init__(self, username=None, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/silent.py b/sdk/identity/azure-identity/azure/identity/_credentials/silent.py index b1aa1ade8c46..27440319944b 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/silent.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/silent.py @@ -18,6 +18,7 @@ from .._internal.msal_client import MsalClient from .._internal.shared_token_cache import NO_TOKEN from .._persistent_cache import _load_persistent_cache, TokenCachePersistenceOptions +from .._constants import EnvironmentVariables if TYPE_CHECKING: # pylint:disable=unused-import,ungrouped-imports @@ -35,7 +36,11 @@ def __init__(self, authentication_record, **kwargs): # authenticate in the tenant that produced the record unless "tenant_id" specifies another self._tenant_id = kwargs.pop("tenant_id", None) or self._auth_record.tenant_id validate_tenant_id(self._tenant_id) - self._allow_multitenant = kwargs.pop("allow_multitenant_authentication", False) + if self._tenant_id == "adfs": + self._allow_multitenant = False + else: + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + self._allow_multitenant = not disable_multitenant self._cache = kwargs.pop("_cache", None) self._client_applications = {} # type: Dict[str, PublicClientApplication] self._client = MsalClient(**kwargs) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/user_password.py b/sdk/identity/azure-identity/azure/identity/_credentials/user_password.py index 77281a185e6e..0521e8fa42d6 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/user_password.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/user_password.py @@ -37,9 +37,6 @@ class UsernamePasswordCredential(InteractiveCredential): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant specified by **tenant_id**. """ def __init__(self, client_id, username, password, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/vscode.py b/sdk/identity/azure-identity/azure/identity/_credentials/vscode.py index cd6866f319da..a66dc9d234a9 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/vscode.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/vscode.py @@ -120,9 +120,6 @@ class VisualStudioCodeCredential(_VSCodeCredentialBase, GetTokenMixin): :keyword str tenant_id: ID of the tenant the credential should authenticate in. Defaults to the "Azure: Tenant" setting in VS Code's user settings or, when that setting has no value, the "organizations" tenant, which supports only Azure Active Directory work or school accounts. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant configured by **tenant_id** or VS Code's user settings. """ def __enter__(self): diff --git a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py index a1799b0679a8..e31a88aa1bc7 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py @@ -66,20 +66,20 @@ def validate_tenant_id(tenant_id): ) -def resolve_tenant(default_tenant, allow_multitenant, tenant_id=None, **_): - # type: (str, bool, Optional[str], **Any) -> str +def resolve_tenant(default_tenant, allow_multitenant=True, tenant_id=None, **_): + # type: (str, Optional[bool], Optional[str], **Any) -> str """Returns the correct tenant for a token request given a credential's configuration""" if ( tenant_id is None or tenant_id == default_tenant - or os.environ.get(EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION) ): return default_tenant - if not allow_multitenant: + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + if disable_multitenant or not allow_multitenant: raise ClientAuthenticationError( 'The specified tenant for this token request, "{}", does not match'.format(tenant_id) - + ' the configured tenant, and "allow_multitenant_authentication" is False.' + + ' the configured tenant, and "multitenant_authentication" is disabled.' ) return tenant_id diff --git a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py index b6a44e8d5681..519ed0a20164 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py @@ -47,13 +47,13 @@ class AadClientBase(ABC): _POST = ["POST"] def __init__( - self, tenant_id, client_id, authority=None, cache=None, allow_multitenant_authentication=False, **kwargs + self, tenant_id, client_id, authority=None, cache=None, allow_multitenant=True, **kwargs ): # type: (str, str, Optional[str], Optional[TokenCache], bool, **Any) -> None self._authority = normalize_authority(authority) if authority else get_default_authority() self._tenant_id = tenant_id - self._allow_multitenant = allow_multitenant_authentication + self._allow_multitenant = allow_multitenant self._cache = cache or TokenCache() self._client_id = client_id diff --git a/sdk/identity/azure-identity/azure/identity/_internal/get_token_mixin.py b/sdk/identity/azure-identity/azure/identity/_internal/get_token_mixin.py index d8b683e984bd..29f30bb1fb8a 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/get_token_mixin.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/get_token_mixin.py @@ -57,8 +57,7 @@ def get_token(self, *scopes, **kwargs): This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/_internal/interactive.py b/sdk/identity/azure-identity/azure/identity/_internal/interactive.py index a095c783c595..e448e6d90a9c 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/interactive.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/interactive.py @@ -109,8 +109,7 @@ def get_token(self, *scopes, **kwargs): :param str scopes: desired scopes for the access token. This method requires at least one scope. :keyword str claims: additional claims required in the token, such as those returned in a resource provider's claims challenge following an authorization failure - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py index 8ac10bbd687d..5089fb1ba246 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py @@ -10,6 +10,7 @@ from .._constants import EnvironmentVariables from .._internal import get_default_authority, normalize_authority, resolve_tenant, validate_tenant_id from .._persistent_cache import _load_persistent_cache +from .._constants import EnvironmentVariables try: from typing import TYPE_CHECKING @@ -33,7 +34,11 @@ def __init__(self, client_id, client_credential=None, **kwargs): ) self._tenant_id = kwargs.pop("tenant_id", None) or "organizations" validate_tenant_id(self._tenant_id) - self._allow_multitenant = kwargs.pop("allow_multitenant_authentication", False) + if self._tenant_id == "adfs": + self._allow_multitenant = False + else: + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + self._allow_multitenant = not disable_multitenant self._client = MsalClient(**kwargs) self._client_applications = {} # type: Dict[str, msal.ClientApplication] diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/application.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/application.py index 7e63bc9b78db..c7812ea69587 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/application.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/application.py @@ -48,10 +48,6 @@ class AzureApplicationCredential(ChainedTokenCredential): `_ for an overview of managed identities. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application or user is registered in. When False, which is the default, the credential will acquire tokens - only from the tenant specified by **AZURE_TENANT_ID**. This argument doesn't apply to managed identity - authentication. :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", the authority for Azure Public Cloud, which is the default when no value is given for this keyword argument or environment variable AZURE_AUTHORITY_HOST. :class:`~azure.identity.AzureAuthorityHosts` defines authorities for diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/authorization_code.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/authorization_code.py index 225fbe434d94..4befed6e9eeb 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/authorization_code.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/authorization_code.py @@ -30,9 +30,6 @@ class AuthorizationCodeCredential(AsyncContextManager, GetTokenMixin): the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. :keyword str client_secret: One of the application's client secrets. Required only for web apps and web APIs. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant specified by **tenant_id**. """ async def __aenter__(self): @@ -66,8 +63,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": redeeming the authorization code. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index 944a2211d023..b398ba2e88de 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -10,6 +10,7 @@ from azure.core.exceptions import ClientAuthenticationError from .._internal import AsyncContextManager from .._internal.decorators import log_get_token_async +from ..._constants import EnvironmentVariables from ... import CredentialUnavailableError from ..._credentials.azure_cli import ( AzureCliCredential as _SyncAzureCliCredential, @@ -31,14 +32,11 @@ class AzureCliCredential(AsyncContextManager): """Authenticates by requesting a token from the Azure CLI. This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. - - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the identity logged in to the Azure CLI is registered in. When False, which is the default, the credential will - acquire tokens only from the tenant of the Azure CLI's active subscription. """ def __init__(self, **kwargs: "Any") -> None: - self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False) + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + self._allow_multitenant = not disable_multitenant @log_get_token_async async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": @@ -48,8 +46,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": also handle token caching because this credential doesn't cache the tokens it acquires. :param str scopes: desired scope for the access token. This credential allows only one scope per request. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py index cfb3cd4331a1..07a384c6295f 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py @@ -3,11 +3,13 @@ # Licensed under the MIT License. # ------------------------------------ import asyncio +import os import sys from typing import cast, TYPE_CHECKING from .._internal import AsyncContextManager from .._internal.decorators import log_get_token_async +from ..._constants import EnvironmentVariables from ... import CredentialUnavailableError from ..._credentials.azure_powershell import ( AzurePowerShellCredential as _SyncCredential, @@ -28,14 +30,11 @@ class AzurePowerShellCredential(AsyncContextManager): """Authenticates by requesting a token from Azure PowerShell. This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. - - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the identity logged in to Azure PowerShell is registered in. When False, which is the default, the credential - will acquire tokens only from the tenant of Azure PowerShell's active subscription. """ def __init__(self, **kwargs: "Any") -> None: - self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False) + disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) + self._allow_multitenant = not disable_multitenant @log_get_token_async async def get_token( @@ -47,8 +46,7 @@ async def get_token( also handle token caching because this credential doesn't cache the tokens it acquires. :param str scopes: desired scope for the access token. This credential allows only one scope per request. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/certificate.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/certificate.py index a78b9b790eac..0957e6a151c6 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/certificate.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/certificate.py @@ -40,9 +40,6 @@ class CertificateCredential(AsyncContextManager, GetTokenMixin): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only from - the tenant specified by **tenant_id**. """ def __init__(self, tenant_id, client_id, certificate_path=None, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/client_secret.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/client_secret.py index 676e0b15e790..4bcfa49cbd19 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/client_secret.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/client_secret.py @@ -29,9 +29,6 @@ class ClientSecretCredential(AsyncContextManager, GetTokenMixin): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only from - the tenant specified by **tenant_id**. """ def __init__(self, tenant_id: str, client_id: str, client_secret: str, **kwargs: "Any") -> None: diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 8888e5d28874..b8c690b21aba 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -42,9 +42,6 @@ class DefaultAzureCredential(ChainedTokenCredential): This default behavior is configurable with keyword arguments. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only from - its configured tenant. This argument doesn't apply to managed identity authentication. :keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com', the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. Managed identities ignore this because they reside in a single cloud. @@ -133,8 +130,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/environment.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/environment.py index d4c0bdff2047..944add051c94 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/environment.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/environment.py @@ -37,10 +37,6 @@ class EnvironmentCredential(AsyncContextManager): - **AZURE_CLIENT_ID**: the service principal's client ID - **AZURE_CLIENT_CERTIFICATE_PATH**: path to a PEM or PKCS12 certificate file including the private key. The certificate must not be password-protected. - - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application or user is registered in. When False, which is the default, the credential will acquire tokens - only from the tenant specified by **AZURE_TENANT_ID**. """ def __init__(self, **kwargs: "Any") -> None: @@ -91,8 +87,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/on_behalf_of.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/on_behalf_of.py index bc7c75f2123a..202de061d3ae 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/on_behalf_of.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/on_behalf_of.py @@ -36,9 +36,6 @@ class OnBehalfOfCredential(AsyncContextManager, GetTokenMixin): :param str user_assertion: the access token the credential will use as the user assertion when requesting on-behalf-of tokens - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the application is registered in. When False, which is the default, the credential will acquire tokens only - from the tenant specified by **tenant_id**. :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` defines authorities for other clouds. @@ -74,7 +71,7 @@ def __init__( else: self._client_credential = client_credential - # note AadClient handles "allow_multitenant_authentication", "authority", and any pipeline kwargs + # note AadClient handles "authority" and any pipeline kwargs self._client = AadClient(tenant_id, client_id, **kwargs) self._assertion = user_assertion diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/shared_cache.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/shared_cache.py index b663f16623af..1852b8523586 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/shared_cache.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/shared_cache.py @@ -32,9 +32,6 @@ class SharedTokenCacheCredential(SharedTokenCacheBase, AsyncContextManager): :keyword cache_persistence_options: configuration for persistent token caching. If not provided, the credential will use the persistent cache shared by Microsoft development applications :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant. """ async def __aenter__(self): @@ -57,8 +54,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": # py This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/vscode.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/vscode.py index 586354f8ff30..c66e1e1c2611 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/vscode.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/vscode.py @@ -26,9 +26,6 @@ class VisualStudioCodeCredential(_VSCodeCredentialBase, AsyncContextManager, Get :keyword str tenant_id: ID of the tenant the credential should authenticate in. Defaults to the "Azure: Tenant" setting in VS Code's user settings or, when that setting has no value, the "organizations" tenant, which supports only Azure Active Directory work or school accounts. - :keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant - the user is registered in. When False, which is the default, the credential will acquire tokens only from the - user's home tenant or the tenant configured by **tenant_id** or VS Code's user settings. """ async def __aenter__(self) -> "VisualStudioCodeCredential": @@ -48,8 +45,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/azure/identity/aio/_internal/get_token_mixin.py b/sdk/identity/azure-identity/azure/identity/aio/_internal/get_token_mixin.py index 17b8d225b55d..f41db52d4132 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_internal/get_token_mixin.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_internal/get_token_mixin.py @@ -47,8 +47,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": This method is called automatically by Azure SDK clients. :param str scopes: desired scopes for the access token. This method requires at least one scope. - :keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication** - is False, specifying a tenant with this argument may raise an exception. + :keyword str tenant_id: optional tenant to include in the token request. :rtype: :class:`azure.core.credentials.AccessToken` diff --git a/sdk/identity/azure-identity/tests/test_aad_client.py b/sdk/identity/azure-identity/tests/test_aad_client.py index 68a452c1e287..bd8f1dc08f8c 100644 --- a/sdk/identity/azure-identity/tests/test_aad_client.py +++ b/sdk/identity/azure-identity/tests/test_aad_client.py @@ -304,11 +304,12 @@ def test_multitenant_cache(): assert client_b.get_cached_access_token([scope]) is None # but C allows multitenant auth and should therefore return the token from tenant_a when appropriate - client_c = AadClient(tenant_id=tenant_c, allow_multitenant_authentication=True, **common_args) + client_c = AadClient(tenant_id=tenant_c, **common_args) assert client_c.get_cached_access_token([scope]) is None token = client_c.get_cached_access_token([scope], tenant_id=tenant_a) assert token.token == expected_token with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}, clear=True + "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True ): - assert client_c.get_cached_access_token([scope], tenant_id=tenant_a) is None + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + client_c.get_cached_access_token([scope], tenant_id=tenant_a) diff --git a/sdk/identity/azure-identity/tests/test_aad_client_async.py b/sdk/identity/azure-identity/tests/test_aad_client_async.py index dba17bc11cf6..59e2b7783c1b 100644 --- a/sdk/identity/azure-identity/tests/test_aad_client_async.py +++ b/sdk/identity/azure-identity/tests/test_aad_client_async.py @@ -308,11 +308,12 @@ async def test_multitenant_cache(): assert client_b.get_cached_access_token([scope]) is None # but C allows multitenant auth and should therefore return the token from tenant_a when appropriate - client_c = AadClient(tenant_id=tenant_c, allow_multitenant_authentication=True, **common_args) + client_c = AadClient(tenant_id=tenant_c, **common_args) assert client_c.get_cached_access_token([scope]) is None token = client_c.get_cached_access_token([scope], tenant_id=tenant_a) assert token.token == expected_token with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}, clear=True + "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True ): - assert client_c.get_cached_access_token([scope], tenant_id=tenant_a) is None + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = client_c.get_cached_access_token([scope], tenant_id=tenant_a) diff --git a/sdk/identity/azure-identity/tests/test_auth_code.py b/sdk/identity/azure-identity/tests/test_auth_code.py index 29ab3733a633..9af2668d463c 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code.py +++ b/sdk/identity/azure-identity/tests/test_auth_code.py @@ -119,8 +119,6 @@ def test_auth_code_credential(): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -138,7 +136,6 @@ def send(request, **_): "client-id", "authcode", "https://localhost", - allow_multitenant_authentication=True, transport=Mock(send=send), ) token = credential.get_token("scope") @@ -156,8 +153,6 @@ def send(request, **_): def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -178,11 +173,9 @@ def send(request, **_): token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="un" + expected_tenant) + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_auth_code_async.py b/sdk/identity/azure-identity/tests/test_auth_code_async.py index 5eb55e6515cc..f948efc6de19 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code_async.py +++ b/sdk/identity/azure-identity/tests/test_auth_code_async.py @@ -143,8 +143,6 @@ async def test_auth_code_credential(): async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -162,7 +160,6 @@ async def send(request, **_): "client-id", "authcode", "https://localhost", - allow_multitenant_authentication=True, transport=Mock(send=send), ) token = await credential.get_token("scope") @@ -180,8 +177,6 @@ async def send(request, **_): async def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -202,11 +197,10 @@ async def send(request, **_): token = await credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="un" + expected_tenant) + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + # but fail if multi tenant is disabled + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + await credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_certificate_credential.py b/sdk/identity/azure-identity/tests/test_certificate_credential.py index 389bf336297c..b8f6d8d5886b 100644 --- a/sdk/identity/azure-identity/tests/test_certificate_credential.py +++ b/sdk/identity/azure-identity/tests/test_certificate_credential.py @@ -7,7 +7,8 @@ from azure.core.exceptions import ClientAuthenticationError from azure.core.pipeline.policies import ContentDecodePolicy, SansIOHTTPPolicy -from azure.identity import CertificateCredential, RegionalAuthority, TokenCachePersistenceOptions +from azure.identity import CertificateCredential, TokenCachePersistenceOptions +from azure.identity._enums import RegionalAuthority from azure.identity._constants import EnvironmentVariables from azure.identity._credentials.certificate import load_pkcs12_certificate from azure.identity._internal.user_agent import USER_AGENT @@ -360,8 +361,6 @@ def test_certificate_arguments(): @pytest.mark.parametrize("cert_path,cert_password", ALL_CERTS) def test_allow_multitenant_authentication(cert_path, cert_password): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -382,7 +381,6 @@ def send(request, **_): "client-id", cert_path, password=cert_password, - allow_multitenant_authentication=True, transport=Mock(send=send), ) token = credential.get_token("scope") @@ -401,8 +399,6 @@ def send(request, **_): @pytest.mark.parametrize("cert_path,cert_password", ALL_CERTS) def test_multitenant_authentication_backcompat(cert_path, cert_password): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -426,13 +422,12 @@ def send(request, **_): token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="un" + expected_tenant) + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token # ...unless the compat switch is enabled with patch.dict( - os.environ, {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}, clear=True + os.environ, {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True ): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_certificate_credential_async.py b/sdk/identity/azure-identity/tests/test_certificate_credential_async.py index a2338e78a910..5ee6045bdd0c 100644 --- a/sdk/identity/azure-identity/tests/test_certificate_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_certificate_credential_async.py @@ -271,8 +271,6 @@ def test_certificate_arguments(): @pytest.mark.asyncio @pytest.mark.parametrize("cert_path,cert_password", ALL_CERTS) async def test_allow_multitenant_authentication(cert_path, cert_password): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -290,7 +288,6 @@ async def send(request, **_): "client-id", cert_path, password=cert_password, - allow_multitenant_authentication=True, transport=Mock(send=send), ) token = await credential.get_token("scope") @@ -310,8 +307,6 @@ async def send(request, **_): @pytest.mark.asyncio @pytest.mark.parametrize("cert_path,cert_password", ALL_CERTS) async def test_multitenant_authentication_backcompat(cert_path, cert_password): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -332,13 +327,11 @@ async def send(request, **_): token = await credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="un" + expected_tenant) + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 - # ...unless the compat switch is enabled with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}, clear=True + "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True ): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_cli_credential.py b/sdk/identity/azure-identity/tests/test_cli_credential.py index bac97fd4ac7c..3bc757ac32f3 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential.py @@ -153,8 +153,6 @@ def test_timeout(): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - default_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -174,7 +172,7 @@ def fake_check_output(command_line, **_): } ) - credential = AzureCliCredential(allow_multitenant_authentication=True) + credential = AzureCliCredential() with mock.patch(CHECK_OUTPUT, fake_check_output): token = credential.get_token("scope") assert token.token == first_token @@ -188,41 +186,3 @@ def fake_check_output(command_line, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token - - -def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - expected_tenant = "expected-tenant" - expected_token = "***" - - def fake_check_output(command_line, **_): - match = re.search("--tenant (.*)", command_line[-1]) - assert match is None or match[1] == expected_tenant - return json.dumps( - { - "expiresOn": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"), - "accessToken": expected_token, - "subscription": "some-guid", - "tenant": expected_token, - "tokenType": "Bearer", - } - ) - - credential = AzureCliCredential() - with mock.patch(CHECK_OUTPUT, fake_check_output): - token = credential.get_token("scope") - assert token.token == expected_token - - # specifying a tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="un" + expected_tenant) - - # ...unless the compat switch is enabled - with mock.patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"} - ): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert ( - token.token == expected_token - ), "credential should ignore tenant_id kwarg when the compat switch is enabled" diff --git a/sdk/identity/azure-identity/tests/test_cli_credential_async.py b/sdk/identity/azure-identity/tests/test_cli_credential_async.py index d5f5885f5d1f..29b3695f955b 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential_async.py @@ -186,8 +186,6 @@ async def test_timeout(): async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - default_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -208,7 +206,7 @@ async def fake_exec(*args, **_): ).encode() return mock.Mock(communicate=mock.Mock(return_value=get_completed_future((output, b""))), returncode=0) - credential = AzureCliCredential(allow_multitenant_authentication=True) + credential = AzureCliCredential() with mock.patch(SUBPROCESS_EXEC, fake_exec): token = await credential.get_token("scope") assert token.token == first_token @@ -222,40 +220,3 @@ async def fake_exec(*args, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token - - -async def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - expected_tenant = "expected-tenant" - expected_token = "***" - - async def fake_exec(*args, **_): - match = re.search("--tenant (.*)", args[-1]) - assert match is None or match[1] == expected_tenant - output = json.dumps( - { - "expiresOn": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"), - "accessToken": expected_token, - "subscription": "some-guid", - "tenant": expected_token, - "tokenType": "Bearer", - } - ).encode() - return mock.Mock(communicate=mock.Mock(return_value=get_completed_future((output, b""))), returncode=0) - - credential = AzureCliCredential() - with mock.patch(SUBPROCESS_EXEC, fake_exec): - token = await credential.get_token("scope") - assert token.token == expected_token - - # specifying a tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="un" + expected_tenant) - - # ...unless the compat switch is enabled - with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert ( - token.token == expected_token - ), "credential should ignore tenant_id kwarg when the compat switch is enabled" diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential.py b/sdk/identity/azure-identity/tests/test_client_secret_credential.py index 7c694c617c60..f3bada708850 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential.py @@ -4,7 +4,8 @@ # ------------------------------------ from azure.core.exceptions import ClientAuthenticationError from azure.core.pipeline.policies import ContentDecodePolicy, SansIOHTTPPolicy -from azure.identity import ClientSecretCredential, RegionalAuthority, TokenCachePersistenceOptions +from azure.identity import ClientSecretCredential, TokenCachePersistenceOptions +from azure.identity._enums import RegionalAuthority from azure.identity._constants import EnvironmentVariables from azure.identity._internal.user_agent import USER_AGENT from msal import TokenCache @@ -212,8 +213,6 @@ def test_cache_multiple_clients(): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -230,7 +229,7 @@ def send(request, **_): return mock_response(json_payload=build_aad_response(access_token=token)) credential = ClientSecretCredential( - first_tenant, "client-id", "secret", allow_multitenant_authentication=True, transport=Mock(send=send) + first_tenant, "client-id", "secret", transport=Mock(send=send) ) token = credential.get_token("scope") assert token.token == first_token @@ -247,8 +246,6 @@ def send(request, **_): def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -270,11 +267,9 @@ def send(request, **_): token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="un" + expected_tenant) + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py index 60554ce9c90b..cfade2b28c59 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py @@ -252,8 +252,6 @@ async def test_cache_multiple_clients(): @pytest.mark.asyncio async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -267,7 +265,7 @@ async def send(request, **_): return mock_response(json_payload=build_aad_response(access_token=token)) credential = ClientSecretCredential( - first_tenant, "client-id", "secret", allow_multitenant_authentication=True, transport=Mock(send=send) + first_tenant, "client-id", "secret", transport=Mock(send=send) ) token = await credential.get_token("scope") assert token.token == first_token @@ -285,8 +283,6 @@ async def send(request, **_): @pytest.mark.asyncio async def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -305,11 +301,9 @@ async def send(request, **_): token = await credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="un" + expected_tenant) + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_default.py b/sdk/identity/azure-identity/tests/test_default.py index 8c8189996861..1c9120a9942a 100644 --- a/sdk/identity/azure-identity/tests/test_default.py +++ b/sdk/identity/azure-identity/tests/test_default.py @@ -402,36 +402,6 @@ def validate_client_id(credential): validate_client_id(mock_credential) -@pytest.mark.parametrize("expected_value", (True, False)) -def test_allow_multitenant_authentication(expected_value): - """the credential should pass "allow_multitenant_authentication" to the inner credentials which support it""" - - inner_credentials = { - credential: Mock() - for credential in ( - "AzureCliCredential", - "AzurePowerShellCredential", - "EnvironmentCredential", - "InteractiveBrowserCredential", - "ManagedIdentityCredential", # will ignore the argument - "SharedTokenCacheCredential", - ) - } - with patch.multiple(DefaultAzureCredential.__module__, **inner_credentials): - DefaultAzureCredential( - allow_multitenant_authentication=expected_value, exclude_interactive_browser_credential=False - ) - - for credential_name, mock_credential in inner_credentials.items(): - assert mock_credential.call_count == 1 - _, kwargs = mock_credential.call_args - - assert "allow_multitenant_authentication" in kwargs, ( - '"allow_multitenant_authentication" was not passed to ' + credential_name - ) - assert kwargs["allow_multitenant_authentication"] == expected_value - - def test_unexpected_kwarg(): """the credential shouldn't raise when given an unexpected keyword argument""" DefaultAzureCredential(foo=42) diff --git a/sdk/identity/azure-identity/tests/test_default_async.py b/sdk/identity/azure-identity/tests/test_default_async.py index 0f144350640c..e4ff1a9fcf11 100644 --- a/sdk/identity/azure-identity/tests/test_default_async.py +++ b/sdk/identity/azure-identity/tests/test_default_async.py @@ -312,33 +312,6 @@ def get_credential_for_shared_cache_test(expected_refresh_token, expected_access return DefaultAzureCredential(_cache=cache, transport=transport, **exclude_other_credentials, **kwargs) -@pytest.mark.parametrize("expected_value", (True, False)) -def test_allow_multitenant_authentication(expected_value): - """the credential should pass "allow_multitenant_authentication" to the inner credentials which support it""" - - inner_credentials = { - credential: Mock() - for credential in ( - "AzureCliCredential", - "AzurePowerShellCredential", - "EnvironmentCredential", - "ManagedIdentityCredential", # will ignore the argument - "SharedTokenCacheCredential", - ) - } - with patch.multiple(DefaultAzureCredential.__module__, **inner_credentials): - DefaultAzureCredential(allow_multitenant_authentication=expected_value) - - for credential_name, mock_credential in inner_credentials.items(): - assert mock_credential.call_count == 1 - _, kwargs = mock_credential.call_args - - assert "allow_multitenant_authentication" in kwargs, ( - '"allow_multitenant_authentication" was not passed to ' + credential_name - ) - assert kwargs["allow_multitenant_authentication"] == expected_value - - def test_unexpected_kwarg(): """the credential shouldn't raise when given an unexpected keyword argument""" DefaultAzureCredential(foo=42) diff --git a/sdk/identity/azure-identity/tests/test_interactive_credential.py b/sdk/identity/azure-identity/tests/test_interactive_credential.py index e82f76f6c5ba..8676e257f72d 100644 --- a/sdk/identity/azure-identity/tests/test_interactive_credential.py +++ b/sdk/identity/azure-identity/tests/test_interactive_credential.py @@ -283,8 +283,6 @@ def _request_token(self, *_, **__): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -312,7 +310,6 @@ def send(request, **_): credential = MockCredential( tenant_id=first_tenant, - allow_multitenant_authentication=True, request_token=request_token, transport=Mock(send=send), ) @@ -331,8 +328,6 @@ def send(request, **_): def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -364,11 +359,9 @@ def send(request, **_): token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="un" + expected_tenant) + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_obo.py b/sdk/identity/azure-identity/tests/test_obo.py index 7cd402ee8c36..393b379c942b 100644 --- a/sdk/identity/azure-identity/tests/test_obo.py +++ b/sdk/identity/azure-identity/tests/test_obo.py @@ -93,8 +93,6 @@ def test_obo_cert(self): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -113,7 +111,7 @@ def send(request, **_): transport = Mock(send=Mock(wraps=send)) credential = OnBehalfOfCredential( - first_tenant, "client-id", "secret", "assertion", allow_multitenant_authentication=True, transport=transport + first_tenant, "client-id", "secret", "assertion", transport=transport ) token = credential.get_token("scope") assert token.token == first_token diff --git a/sdk/identity/azure-identity/tests/test_obo_async.py b/sdk/identity/azure-identity/tests/test_obo_async.py index 0bbaecb79150..3b745799b0a0 100644 --- a/sdk/identity/azure-identity/tests/test_obo_async.py +++ b/sdk/identity/azure-identity/tests/test_obo_async.py @@ -70,8 +70,6 @@ async def test_context_manager(): @pytest.mark.asyncio async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -87,7 +85,7 @@ async def send(request, **_): transport = Mock(send=Mock(wraps=send)) credential = OnBehalfOfCredential( - first_tenant, "client-id", "secret", "assertion", allow_multitenant_authentication=True, transport=transport + first_tenant, "client-id", "secret", "assertion", transport=transport ) token = await credential.get_token("scope") assert token.token == first_token diff --git a/sdk/identity/azure-identity/tests/test_powershell_credential.py b/sdk/identity/azure-identity/tests/test_powershell_credential.py index cdf83543f220..da56ccefce60 100644 --- a/sdk/identity/azure-identity/tests/test_powershell_credential.py +++ b/sdk/identity/azure-identity/tests/test_powershell_credential.py @@ -244,8 +244,6 @@ def Popen(args, **kwargs): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 @@ -264,7 +262,7 @@ def fake_Popen(command, **_): communicate = Mock(return_value=(stdout, "")) return Mock(communicate=communicate, returncode=0) - credential = AzurePowerShellCredential(allow_multitenant_authentication=True) + credential = AzurePowerShellCredential() with patch(POPEN, fake_Popen): token = credential.get_token("scope") assert token.token == first_token @@ -275,38 +273,3 @@ def fake_Popen(command, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token - - -def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - expected_token = "***" - - def fake_Popen(command, **_): - assert command[-1].startswith("pwsh -NonInteractive -EncodedCommand ") - encoded_script = command[-1].split()[-1] - decoded_script = base64.b64decode(encoded_script).decode("utf-16-le") - match = re.search(r"Get-AzAccessToken -ResourceUrl '(\S+)'(?: -TenantId (\S+))?", decoded_script) - tenant = match.groups()[1] - - assert tenant is None, "credential shouldn't accept an explicit tenant ID" - stdout = "azsdk%{}%{}".format(expected_token, int(time.time()) + 3600) - - communicate = Mock(return_value=(stdout, "")) - return Mock(communicate=communicate, returncode=0) - - credential = AzurePowerShellCredential() - with patch(POPEN, fake_Popen): - token = credential.get_token("scope") - assert token.token == expected_token - - # specifying a tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="some tenant") - - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = credential.get_token("scope", tenant_id="some tenant") - assert ( - token.token == expected_token - ), "credential should ignore tenant_id kwarg when the compat switch is enabled" diff --git a/sdk/identity/azure-identity/tests/test_powershell_credential_async.py b/sdk/identity/azure-identity/tests/test_powershell_credential_async.py index 2e67d6c19906..b87ccfbe978d 100644 --- a/sdk/identity/azure-identity/tests/test_powershell_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_powershell_credential_async.py @@ -245,8 +245,6 @@ async def mock_exec(*args, **kwargs): async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 @@ -266,7 +264,7 @@ async def fake_exec(*args, **_): communicate = Mock(return_value=get_completed_future((stdout.encode(), b""))) return Mock(communicate=communicate, returncode=0) - credential = AzurePowerShellCredential(allow_multitenant_authentication=True) + credential = AzurePowerShellCredential() with patch(CREATE_SUBPROCESS_EXEC, fake_exec): token = await credential.get_token("scope") assert token.token == first_token @@ -277,38 +275,3 @@ async def fake_exec(*args, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token - - -async def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - expected_token = "***" - - async def fake_exec(*args, **_): - command = args[2] - assert command.startswith("pwsh -NonInteractive -EncodedCommand ") - encoded_script = command.split()[-1] - decoded_script = base64.b64decode(encoded_script).decode("utf-16-le") - match = re.search(r"Get-AzAccessToken -ResourceUrl '(\S+)'(?: -TenantId (\S+))?", decoded_script) - tenant = match[2] - - assert tenant is None, "credential shouldn't accept an explicit tenant ID" - stdout = "azsdk%{}%{}".format(expected_token, int(time.time()) + 3600) - communicate = Mock(return_value=get_completed_future((stdout.encode(), b""))) - return Mock(communicate=communicate, returncode=0) - - credential = AzurePowerShellCredential() - with patch(CREATE_SUBPROCESS_EXEC, fake_exec): - token = await credential.get_token("scope") - assert token.token == expected_token - - # specifying a tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="some tenant") - - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = await credential.get_token("scope", tenant_id="some tenant") - assert ( - token.token == expected_token - ), "credential should ignore tenant_id kwarg when the compat switch is enabled" diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py index 5081825cebb5..036b99cccd2c 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py @@ -826,8 +826,6 @@ def test_claims_challenge(): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - default_tenant = "organizations" first_token = "***" second_tenant = "second-tenant" @@ -851,7 +849,7 @@ def send(request, **_): cache = populated_cache(expected_account) credential = SharedTokenCacheCredential( - allow_multitenant_authentication=True, authority=authority, transport=Mock(send=send), _cache=cache + authority=authority, transport=Mock(send=send), _cache=cache ) token = credential.get_token("scope") assert token.token == first_token @@ -867,56 +865,7 @@ def send(request, **_): assert token.token == first_token -def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - default_tenant = "organizations" - expected_token = "***" - - def send(request, **_): - parsed = urlparse(request.url) - tenant_id = parsed.path.split("/")[1] - assert tenant_id == default_tenant - return mock_response( - json_payload=build_aad_response( - access_token=expected_token, - id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), - ) - ) - - tenant_id = "tenant-id" - client_id = "client-id" - authority = "localhost" - object_id = "object-id" - username = "me" - - expected_account = get_account_event( - username, object_id, tenant_id, authority=authority, client_id=client_id, refresh_token="**" - ) - cache = populated_cache(expected_account) - - credential = SharedTokenCacheCredential(authority=authority, transport=Mock(send=send), _cache=cache) - - token = credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = credential.get_token("scope", tenant_id=default_tenant) - assert token.token == expected_token - - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="some tenant") - - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = credential.get_token("scope", tenant_id="some tenant") - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" - - def test_allow_multitenant_authentication_auth_record(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - default_tenant = "organizations" first_token = "***" second_tenant = "second-tenant" @@ -947,7 +896,6 @@ def send(request, **_): cache = populated_cache(expected_account) credential = SharedTokenCacheCredential( - allow_multitenant_authentication=True, authority=authority, transport=Mock(send=send), authentication_record=record, @@ -967,64 +915,6 @@ def send(request, **_): assert token.token == first_token -def test_multitenant_authentication_not_allowed_authentication_record(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - default_tenant = "organizations" - expected_token = "***" - - authority = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD - object_id = "object-id" - home_account_id = object_id + "." + default_tenant - record = AuthenticationRecord(default_tenant, "client-id", authority, home_account_id, "user") - - def send(request, **_): - parsed = urlparse(request.url) - tenant_id = parsed.path.split("/")[1] - if "/oauth2/v2.0/token" not in request.url: - return get_discovery_response("https://{}/{}".format(parsed.netloc, tenant_id)) - - assert tenant_id == default_tenant - return mock_response( - json_payload=build_aad_response( - access_token=expected_token, - id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), - ) - ) - - expected_account = get_account_event( - record.username, - object_id, - record.tenant_id, - authority=record.authority, - client_id=record.client_id, - refresh_token="**", - ) - cache = populated_cache(expected_account) - - credential = SharedTokenCacheCredential( - authority=authority, transport=Mock(send=send), authentication_record=record, _cache=cache - ) - - token = credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = credential.get_token("scope", tenant_id=default_tenant) - assert token.token == expected_token - - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="some tenant") - - # ...unless the compat switch is enabled - with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}, clear=True - ): - token = credential.get_token("scope", tenant_id="some tenant") - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" - - def get_account_event( username, uid, utid, authority=None, client_id="client-id", refresh_token="refresh-token", scopes=None, **kwargs ): diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py index a6d7f0d67d60..897e22fdb00a 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py @@ -607,8 +607,6 @@ async def test_initialization(): @pytest.mark.asyncio async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 @@ -630,7 +628,7 @@ async def send(request, **_): cache = populated_cache(expected_account) credential = SharedTokenCacheCredential( - allow_multitenant_authentication=True, authority=authority, transport=Mock(send=send), _cache=cache + authority=authority, transport=Mock(send=send), _cache=cache ) token = await credential.get_token("scope") assert token.token == first_token @@ -644,46 +642,3 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token - - -@pytest.mark.asyncio -async def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - - default_tenant = "organizations" - expected_token = "***" - - async def send(request, **_): - parsed = urlparse(request.url) - tenant_id = parsed.path.split("/")[1] - assert tenant_id == default_tenant - return mock_response( - json_payload=build_aad_response( - access_token=expected_token, - id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), - ) - ) - - authority = "localhost" - expected_account = get_account_event( - "user", "object-id", "tenant-id", authority=authority, client_id="client-id", refresh_token="**" - ) - cache = populated_cache(expected_account) - - credential = SharedTokenCacheCredential(authority=authority, transport=Mock(send=send), _cache=cache) - - token = await credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = await credential.get_token("scope", tenant_id=default_tenant) - assert token.token == expected_token - - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="some tenant") - - # ...unless the compat switch is enabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = await credential.get_token("scope", tenant_id="some tenant") - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential.py b/sdk/identity/azure-identity/tests/test_vscode_credential.py index e6db05f56e5f..c2d5a97036fd 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential.py @@ -278,8 +278,6 @@ def test_no_user_settings(): def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -293,7 +291,7 @@ def send(request, **_): return mock_response(json_payload=build_aad_response(access_token=token)) credential = get_credential( - tenant_id=first_tenant, allow_multitenant_authentication=True, transport=mock.Mock(send=send) + tenant_id=first_tenant, transport=mock.Mock(send=send) ) with mock.patch(GET_REFRESH_TOKEN, lambda _: "**"): token = credential.get_token("scope") @@ -312,8 +310,6 @@ def send(request, **_): def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -333,11 +329,9 @@ def send(request, **_): token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - credential.get_token("scope", tenant_id="un" + expected_tenant) + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 - # ...unless the compat switch is enabled - with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py index 40c996e39957..6c98036e0c2e 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py @@ -269,8 +269,6 @@ async def test_no_user_settings(): @pytest.mark.asyncio async def test_allow_multitenant_authentication(): - """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)""" - first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -284,7 +282,7 @@ async def send(request, **_): return mock_response(json_payload=build_aad_response(access_token=token)) credential = get_credential( - tenant_id=first_tenant, allow_multitenant_authentication=True, transport=mock.Mock(send=send) + tenant_id=first_tenant, transport=mock.Mock(send=send) ) with mock.patch(GET_REFRESH_TOKEN, lambda _: "**"): token = await credential.get_token("scope") @@ -304,8 +302,6 @@ async def send(request, **_): @pytest.mark.asyncio async def test_multitenant_authentication_not_allowed(): - """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" - expected_tenant = "expected-tenant" expected_token = "***" @@ -325,11 +321,9 @@ async def send(request, **_): token = await credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token - # but any other tenant should get an error - with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): - await credential.get_token("scope", tenant_id="un" + expected_tenant) + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 - # ...unless the compat switch is enabled - with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true"}): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled" + with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) From 2a7260886294780d3fef7f891eb48b1dba760bf4 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 10:33:16 -0700 Subject: [PATCH 02/11] update --- .../azure-identity/azure/identity/_credentials/azure_cli.py | 2 +- .../azure/identity/_credentials/azure_powershell.py | 2 +- .../azure-identity/azure/identity/_credentials/certificate.py | 2 +- .../azure-identity/azure/identity/_internal/msal_credentials.py | 1 - .../azure-identity/azure/identity/aio/_credentials/azure_cli.py | 2 +- .../azure/identity/aio/_credentials/azure_powershell.py | 2 +- sdk/identity/azure-identity/tests/test_auth_code.py | 2 +- sdk/identity/azure-identity/tests/test_auth_code_async.py | 2 +- sdk/identity/azure-identity/tests/test_cli_credential.py | 2 +- sdk/identity/azure-identity/tests/test_cli_credential_async.py | 2 +- .../azure-identity/tests/test_client_secret_credential.py | 2 +- .../azure-identity/tests/test_client_secret_credential_async.py | 2 +- .../azure-identity/tests/test_interactive_credential.py | 2 +- sdk/identity/azure-identity/tests/test_obo.py | 2 +- sdk/identity/azure-identity/tests/test_obo_async.py | 2 +- sdk/identity/azure-identity/tests/test_powershell_credential.py | 2 +- .../azure-identity/tests/test_powershell_credential_async.py | 2 +- .../azure-identity/tests/test_shared_cache_credential.py | 2 +- .../azure-identity/tests/test_shared_cache_credential_async.py | 2 +- sdk/identity/azure-identity/tests/test_vscode_credential.py | 2 +- .../azure-identity/tests/test_vscode_credential_async.py | 2 +- 21 files changed, 20 insertions(+), 21 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index a7cf596b9687..2d65f1e6205a 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -38,7 +38,7 @@ class AzureCliCredential(object): This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. """ - def __init__(self, **kwargs): + def __init__(self, **kwargs): # pylint: disable=unused-import disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py index 1bcde2850a9c..6fd20184c3ce 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py @@ -55,7 +55,7 @@ class AzurePowerShellCredential(object): This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. """ - def __init__(self, **kwargs): + def __init__(self, **kwargs): # pylint: disable=unused-import # type: (**Any) -> None disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py b/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py index f8cd629a9ff0..689d2fa9e31e 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py @@ -45,7 +45,7 @@ class CertificateCredential(ClientCredentialBase): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword str regional_authority: to which the credential will authenticate. + :keyword str regional_authority: to which the credential will authenticate. This argument should be used only by applications deployed to Azure VMs. """ diff --git a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py index 5089fb1ba246..01848fcecb96 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py @@ -10,7 +10,6 @@ from .._constants import EnvironmentVariables from .._internal import get_default_authority, normalize_authority, resolve_tenant, validate_tenant_id from .._persistent_cache import _load_persistent_cache -from .._constants import EnvironmentVariables try: from typing import TYPE_CHECKING diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index b398ba2e88de..8a5d8eb60496 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -34,7 +34,7 @@ class AzureCliCredential(AsyncContextManager): This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. """ - def __init__(self, **kwargs: "Any") -> None: + def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-import disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py index 07a384c6295f..c3230563b806 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py @@ -32,7 +32,7 @@ class AzurePowerShellCredential(AsyncContextManager): This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. """ - def __init__(self, **kwargs: "Any") -> None: + def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-import disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/tests/test_auth_code.py b/sdk/identity/azure-identity/tests/test_auth_code.py index 9af2668d463c..e286f7a40815 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code.py +++ b/sdk/identity/azure-identity/tests/test_auth_code.py @@ -118,7 +118,7 @@ def test_auth_code_credential(): assert transport.send.call_count == 2 -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_auth_code_async.py b/sdk/identity/azure-identity/tests/test_auth_code_async.py index f948efc6de19..2b71650cfce1 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code_async.py +++ b/sdk/identity/azure-identity/tests/test_auth_code_async.py @@ -142,7 +142,7 @@ async def test_auth_code_credential(): assert transport.send.call_count == 2 -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_cli_credential.py b/sdk/identity/azure-identity/tests/test_cli_credential.py index 3bc757ac32f3..e707736afb8c 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential.py @@ -152,7 +152,7 @@ def test_timeout(): AzureCliCredential().get_token("scope") -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): default_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_cli_credential_async.py b/sdk/identity/azure-identity/tests/test_cli_credential_async.py index 29b3695f955b..a1ab541f21f5 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential_async.py @@ -185,7 +185,7 @@ async def test_timeout(): assert proc.kill.call_count == 1 -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): default_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential.py b/sdk/identity/azure-identity/tests/test_client_secret_credential.py index f3bada708850..134848b46851 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential.py @@ -212,7 +212,7 @@ def test_cache_multiple_clients(): assert len(cache.find(TokenCache.CredentialType.ACCESS_TOKEN)) == 2 -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py index cfade2b28c59..bcd4230c7505 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py @@ -251,7 +251,7 @@ async def test_cache_multiple_clients(): @pytest.mark.asyncio -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_interactive_credential.py b/sdk/identity/azure-identity/tests/test_interactive_credential.py index 8676e257f72d..7bab3d3f24d6 100644 --- a/sdk/identity/azure-identity/tests/test_interactive_credential.py +++ b/sdk/identity/azure-identity/tests/test_interactive_credential.py @@ -282,7 +282,7 @@ def _request_token(self, *_, **__): assert record.username == username -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_obo.py b/sdk/identity/azure-identity/tests/test_obo.py index 393b379c942b..413b149be398 100644 --- a/sdk/identity/azure-identity/tests/test_obo.py +++ b/sdk/identity/azure-identity/tests/test_obo.py @@ -92,7 +92,7 @@ def test_obo_cert(self): credential.get_token(self.obo_settings["scope"]) -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_obo_async.py b/sdk/identity/azure-identity/tests/test_obo_async.py index 3b745799b0a0..c39957be0afd 100644 --- a/sdk/identity/azure-identity/tests/test_obo_async.py +++ b/sdk/identity/azure-identity/tests/test_obo_async.py @@ -69,7 +69,7 @@ async def test_context_manager(): @pytest.mark.asyncio -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_powershell_credential.py b/sdk/identity/azure-identity/tests/test_powershell_credential.py index da56ccefce60..bbe6f652bc0c 100644 --- a/sdk/identity/azure-identity/tests/test_powershell_credential.py +++ b/sdk/identity/azure-identity/tests/test_powershell_credential.py @@ -243,7 +243,7 @@ def Popen(args, **kwargs): assert Fake.calls == 2 -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 diff --git a/sdk/identity/azure-identity/tests/test_powershell_credential_async.py b/sdk/identity/azure-identity/tests/test_powershell_credential_async.py index b87ccfbe978d..baa73992c1fc 100644 --- a/sdk/identity/azure-identity/tests/test_powershell_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_powershell_credential_async.py @@ -244,7 +244,7 @@ async def mock_exec(*args, **kwargs): assert calls == 2 -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py index 036b99cccd2c..0a7cb9baf762 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py @@ -825,7 +825,7 @@ def test_claims_challenge(): assert kwargs["claims_challenge"] == expected_claims -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): default_tenant = "organizations" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py index 897e22fdb00a..28583edaed46 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py @@ -606,7 +606,7 @@ async def test_initialization(): @pytest.mark.asyncio -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential.py b/sdk/identity/azure-identity/tests/test_vscode_credential.py index c2d5a97036fd..18b07de008a9 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential.py @@ -277,7 +277,7 @@ def test_no_user_settings(): assert transport.send.call_count == 1 -def test_allow_multitenant_authentication(): +def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py index 6c98036e0c2e..999e31382602 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py @@ -268,7 +268,7 @@ async def test_no_user_settings(): @pytest.mark.asyncio -async def test_allow_multitenant_authentication(): +async def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" From 7a71837eeaa4347f1a535e9c76d73dfc4c65f6fb Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 11:32:26 -0700 Subject: [PATCH 03/11] update --- .../azure-identity/azure/identity/_credentials/azure_cli.py | 2 +- .../azure/identity/_credentials/azure_powershell.py | 2 +- .../azure-identity/azure/identity/aio/_credentials/azure_cli.py | 2 +- .../azure/identity/aio/_credentials/azure_powershell.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index 2d65f1e6205a..df20ba568af5 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -38,7 +38,7 @@ class AzureCliCredential(object): This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. """ - def __init__(self, **kwargs): # pylint: disable=unused-import + def __init__(self, **kwargs): # pylint: disable=unused-argument disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py index 6fd20184c3ce..0b8cdac35f95 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py @@ -55,7 +55,7 @@ class AzurePowerShellCredential(object): This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. """ - def __init__(self, **kwargs): # pylint: disable=unused-import + def __init__(self, **kwargs): # pylint: disable=unused-argument # type: (**Any) -> None disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index 8a5d8eb60496..bc67463a0f06 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -34,7 +34,7 @@ class AzureCliCredential(AsyncContextManager): This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. """ - def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-import + def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-argument disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py index c3230563b806..46a8a20c4e03 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py @@ -32,7 +32,7 @@ class AzurePowerShellCredential(AsyncContextManager): This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. """ - def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-import + def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-argument disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) self._allow_multitenant = not disable_multitenant From 0ac532bed7415bf833db4e9e00761232c1aee24d Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 14:15:38 -0700 Subject: [PATCH 04/11] updates --- sdk/identity/azure-identity/CHANGELOG.md | 8 +------- .../azure/identity/_credentials/azure_cli.py | 6 ++---- .../azure/identity/_credentials/azure_powershell.py | 6 ++---- .../azure-identity/azure/identity/_credentials/silent.py | 7 +------ .../azure-identity/azure/identity/_internal/__init__.py | 8 +++++--- .../azure/identity/_internal/aad_client_base.py | 4 ++-- .../azure/identity/_internal/msal_credentials.py | 8 +------- .../azure/identity/aio/_credentials/azure_cli.py | 5 ++--- .../azure/identity/aio/_credentials/azure_powershell.py | 5 ++--- 9 files changed, 18 insertions(+), 39 deletions(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 4905d2b848fa..6439654fb55a 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -2,19 +2,13 @@ ## 1.7.0b5 (Unreleased) -### Features Added +### Breaking Changes - `allow_multitenant_authentication` argument is deprecated and the setting is always on. To disable it, set environment variable AZURE_IDENTITY_DISABLE_MULTITENANTAUTH to "True". - `azure.identity.RegionalAuthority` is removed. -### Breaking Changes - -### Bugs Fixed - -### Other Changes - ## 1.7.0b4 (2021-09-09) ### Features Added diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index df20ba568af5..ae3f9cd93a8a 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -39,9 +39,7 @@ class AzureCliCredential(object): """ def __init__(self, **kwargs): # pylint: disable=unused-argument - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - self._allow_multitenant = not disable_multitenant - + pass def __enter__(self): return self @@ -72,7 +70,7 @@ def get_token(self, *scopes, **kwargs): resource = _scopes_to_resource(*scopes) command = COMMAND_LINE.format(resource) - tenant = resolve_tenant("", self._allow_multitenant, **kwargs) + tenant = resolve_tenant("", **kwargs) if tenant: command += " --tenant " + tenant output = _run_command(command) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py index 0b8cdac35f95..2cf6bea936d8 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py @@ -56,9 +56,7 @@ class AzurePowerShellCredential(object): """ def __init__(self, **kwargs): # pylint: disable=unused-argument - # type: (**Any) -> None - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - self._allow_multitenant = not disable_multitenant + pass def __enter__(self): return self @@ -88,7 +86,7 @@ def get_token(self, *scopes, **kwargs): :raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked Azure PowerShell but didn't receive an access token """ - tenant_id = resolve_tenant("", self._allow_multitenant, **kwargs) + tenant_id = resolve_tenant("", **kwargs) command_line = get_command_line(scopes, tenant_id) output = run_command_line(command_line) token = parse_token(output) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/silent.py b/sdk/identity/azure-identity/azure/identity/_credentials/silent.py index 27440319944b..96335ed4870f 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/silent.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/silent.py @@ -36,11 +36,6 @@ def __init__(self, authentication_record, **kwargs): # authenticate in the tenant that produced the record unless "tenant_id" specifies another self._tenant_id = kwargs.pop("tenant_id", None) or self._auth_record.tenant_id validate_tenant_id(self._tenant_id) - if self._tenant_id == "adfs": - self._allow_multitenant = False - else: - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - self._allow_multitenant = not disable_multitenant self._cache = kwargs.pop("_cache", None) self._client_applications = {} # type: Dict[str, PublicClientApplication] self._client = MsalClient(**kwargs) @@ -79,7 +74,7 @@ def _initialize(self): self._initialized = True def _get_client_application(self, **kwargs): - tenant_id = resolve_tenant(self._tenant_id, self._allow_multitenant, **kwargs) + tenant_id = resolve_tenant(self._tenant_id, **kwargs) if tenant_id not in self._client_applications: # CP1 = can handle claims challenges (CAE) capabilities = None if "AZURE_IDENTITY_DISABLE_CP1" in os.environ else ["CP1"] diff --git a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py index e31a88aa1bc7..a0f0c0a009a9 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py @@ -66,8 +66,8 @@ def validate_tenant_id(tenant_id): ) -def resolve_tenant(default_tenant, allow_multitenant=True, tenant_id=None, **_): - # type: (str, Optional[bool], Optional[str], **Any) -> str +def resolve_tenant(default_tenant, tenant_id=None, **_): + # type: (str, Optional[str], **Any) -> str """Returns the correct tenant for a token request given a credential's configuration""" if ( tenant_id is None @@ -76,7 +76,9 @@ def resolve_tenant(default_tenant, allow_multitenant=True, tenant_id=None, **_): return default_tenant disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - if disable_multitenant or not allow_multitenant: + if default_tenant == "adfs": + disable_multitenant = True + if disable_multitenant: raise ClientAuthenticationError( 'The specified tenant for this token request, "{}", does not match'.format(tenant_id) + ' the configured tenant, and "multitenant_authentication" is disabled.' diff --git a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py index 519ed0a20164..c51661d9d530 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py @@ -61,7 +61,7 @@ def __init__( def get_cached_access_token(self, scopes, **kwargs): # type: (Iterable[str], **Any) -> Optional[AccessToken] - tenant = resolve_tenant(self._tenant_id, self._allow_multitenant, **kwargs) + tenant = resolve_tenant(self._tenant_id, **kwargs) tokens = self._cache.find( TokenCache.CredentialType.ACCESS_TOKEN, target=list(scopes), @@ -260,7 +260,7 @@ def _get_refresh_token_request(self, scopes, refresh_token, **kwargs): def _get_token_url(self, **kwargs): # type: (**Any) -> str - tenant = resolve_tenant(self._tenant_id, self._allow_multitenant, **kwargs) + tenant = resolve_tenant(self._tenant_id, **kwargs) return "/".join((self._authority, tenant, "oauth2/v2.0/token")) def _post(self, data, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py index 01848fcecb96..d59f1738b2ff 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py @@ -33,12 +33,6 @@ def __init__(self, client_id, client_credential=None, **kwargs): ) self._tenant_id = kwargs.pop("tenant_id", None) or "organizations" validate_tenant_id(self._tenant_id) - if self._tenant_id == "adfs": - self._allow_multitenant = False - else: - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - self._allow_multitenant = not disable_multitenant - self._client = MsalClient(**kwargs) self._client_applications = {} # type: Dict[str, msal.ClientApplication] self._client_credential = client_credential @@ -67,7 +61,7 @@ def close(self): def _get_app(self, **kwargs): # type: (**Any) -> msal.ClientApplication - tenant_id = resolve_tenant(self._tenant_id, self._allow_multitenant, **kwargs) + tenant_id = resolve_tenant(self._tenant_id, **kwargs) if tenant_id not in self._client_applications: # CP1 = can handle claims challenges (CAE) capabilities = None if "AZURE_IDENTITY_DISABLE_CP1" in os.environ else ["CP1"] diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index bc67463a0f06..fec7214b7a62 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -35,8 +35,7 @@ class AzureCliCredential(AsyncContextManager): """ def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-argument - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - self._allow_multitenant = not disable_multitenant + pass @log_get_token_async async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": @@ -60,7 +59,7 @@ async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": resource = _scopes_to_resource(*scopes) command = COMMAND_LINE.format(resource) - tenant = resolve_tenant("", self._allow_multitenant, **kwargs) + tenant = resolve_tenant("", **kwargs) if tenant: command += " --tenant " + tenant output = await _run_command(command) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py index 46a8a20c4e03..a32d0b96f076 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py @@ -33,8 +33,7 @@ class AzurePowerShellCredential(AsyncContextManager): """ def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-argument - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - self._allow_multitenant = not disable_multitenant + pass @log_get_token_async async def get_token( @@ -59,7 +58,7 @@ async def get_token( if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop): return _SyncCredential().get_token(*scopes, **kwargs) - tenant_id = resolve_tenant("", self._allow_multitenant, **kwargs) + tenant_id = resolve_tenant("", **kwargs) command_line = get_command_line(scopes, tenant_id) output = await run_command_line(command_line) token = parse_token(output) From 59fab5a7bb0da0148ef8284fafcf5afd8c2045f5 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 16:14:39 -0700 Subject: [PATCH 05/11] update --- .../azure-identity/azure/identity/_credentials/azure_cli.py | 3 +-- .../azure/identity/_credentials/azure_powershell.py | 4 +--- .../azure-identity/azure/identity/_credentials/silent.py | 1 - .../azure/identity/aio/_credentials/azure_cli.py | 1 - .../azure/identity/aio/_credentials/azure_powershell.py | 2 -- 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index ae3f9cd93a8a..8a86abdb2e53 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -20,7 +20,6 @@ from .. import CredentialUnavailableError from .._internal import _scopes_to_resource, resolve_tenant from .._internal.decorators import log_get_token -from .._constants import EnvironmentVariables if TYPE_CHECKING: # pylint:disable=ungrouped-imports @@ -51,7 +50,7 @@ def close(self): """Calling this method is unnecessary.""" @log_get_token("AzureCliCredential") - def get_token(self, *scopes, **kwargs): + def get_token(self, *scopes, **kwargs): # pylint: disable=no-self-use # type: (*str, **Any) -> AccessToken """Request an access token for `scopes`. diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py index 2cf6bea936d8..d49963cce853 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py @@ -4,7 +4,6 @@ # ------------------------------------ import base64 import logging -import os import platform import subprocess import sys @@ -19,7 +18,6 @@ from .. import CredentialUnavailableError from .._internal import _scopes_to_resource, resolve_tenant from .._internal.decorators import log_get_token -from .._constants import EnvironmentVariables if TYPE_CHECKING: # pylint:disable=ungrouped-imports @@ -69,7 +67,7 @@ def close(self): """Calling this method is unnecessary.""" @log_get_token("AzurePowerShellCredential") - def get_token(self, *scopes, **kwargs): + def get_token(self, *scopes, **kwargs): # pylint: disable=no-self-use # type: (*str, **Any) -> AccessToken """Request an access token for `scopes`. diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/silent.py b/sdk/identity/azure-identity/azure/identity/_credentials/silent.py index 96335ed4870f..08e0611fa5e4 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/silent.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/silent.py @@ -18,7 +18,6 @@ from .._internal.msal_client import MsalClient from .._internal.shared_token_cache import NO_TOKEN from .._persistent_cache import _load_persistent_cache, TokenCachePersistenceOptions -from .._constants import EnvironmentVariables if TYPE_CHECKING: # pylint:disable=unused-import,ungrouped-imports diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index fec7214b7a62..79943cf7355d 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -10,7 +10,6 @@ from azure.core.exceptions import ClientAuthenticationError from .._internal import AsyncContextManager from .._internal.decorators import log_get_token_async -from ..._constants import EnvironmentVariables from ... import CredentialUnavailableError from ..._credentials.azure_cli import ( AzureCliCredential as _SyncAzureCliCredential, diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py index a32d0b96f076..c6818489653a 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py @@ -3,13 +3,11 @@ # Licensed under the MIT License. # ------------------------------------ import asyncio -import os import sys from typing import cast, TYPE_CHECKING from .._internal import AsyncContextManager from .._internal.decorators import log_get_token_async -from ..._constants import EnvironmentVariables from ... import CredentialUnavailableError from ..._credentials.azure_powershell import ( AzurePowerShellCredential as _SyncCredential, From 678842a481f83ab9b2f220e8bb07e4ede62061d4 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 16:38:20 -0700 Subject: [PATCH 06/11] update --- .../azure/identity/_credentials/azure_cli.py | 1 + .../identity/_credentials/certificate.py | 2 - .../identity/_credentials/client_secret.py | 2 - .../azure/identity/_internal/__init__.py | 5 +-- .../identity/_internal/aad_client_base.py | 3 +- .../identity/_internal/msal_credentials.py | 4 +- .../azure-identity/tests/test_aad_client.py | 5 --- .../tests/test_aad_client_async.py | 5 --- .../azure-identity/tests/test_auth_code.py | 29 ------------- .../tests/test_auth_code_async.py | 30 -------------- .../tests/test_certificate_credential.py | 20 +-------- .../test_certificate_credential_async.py | 8 +--- .../tests/test_client_secret_credential.py | 41 ------------------- .../test_client_secret_credential_async.py | 28 ------------- .../tests/test_interactive_credential.py | 40 ------------------ .../tests/test_shared_cache_credential.py | 2 +- .../tests/test_vscode_credential.py | 28 ------------- .../tests/test_vscode_credential_async.py | 29 ------------- 18 files changed, 7 insertions(+), 275 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index 8a86abdb2e53..b0b131f6dd87 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -39,6 +39,7 @@ class AzureCliCredential(object): def __init__(self, **kwargs): # pylint: disable=unused-argument pass + def __enter__(self): return self diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py b/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py index 689d2fa9e31e..1169fb178942 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/certificate.py @@ -45,8 +45,6 @@ class CertificateCredential(ClientCredentialBase): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword str regional_authority: to which the credential will authenticate. - This argument should be used only by applications deployed to Azure VMs. """ def __init__(self, tenant_id, client_id, certificate_path=None, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py b/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py index 6d4d3acb39eb..4b68e401a023 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py @@ -24,8 +24,6 @@ class ClientSecretCredential(ClientCredentialBase): :keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential will cache tokens in memory. :paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions - :keyword str regional_authority: to which the credential will authenticate. - This argument should be used only by applications deployed to Azure VMs. """ def __init__(self, tenant_id, client_id, client_secret, **kwargs): diff --git a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py index a0f0c0a009a9..5d39520247eb 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py @@ -79,10 +79,7 @@ def resolve_tenant(default_tenant, tenant_id=None, **_): if default_tenant == "adfs": disable_multitenant = True if disable_multitenant: - raise ClientAuthenticationError( - 'The specified tenant for this token request, "{}", does not match'.format(tenant_id) - + ' the configured tenant, and "multitenant_authentication" is disabled.' - ) + return default_tenant return tenant_id diff --git a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py index c51661d9d530..d8821235d17c 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py @@ -47,13 +47,12 @@ class AadClientBase(ABC): _POST = ["POST"] def __init__( - self, tenant_id, client_id, authority=None, cache=None, allow_multitenant=True, **kwargs + self, tenant_id, client_id, authority=None, cache=None, **kwargs ): # type: (str, str, Optional[str], Optional[TokenCache], bool, **Any) -> None self._authority = normalize_authority(authority) if authority else get_default_authority() self._tenant_id = tenant_id - self._allow_multitenant = allow_multitenant self._cache = cache or TokenCache() self._client_id = client_id diff --git a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py index d59f1738b2ff..4ab266ae26c3 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py @@ -28,9 +28,7 @@ def __init__(self, client_id, client_credential=None, **kwargs): # type: (str, Optional[Union[str, Dict]], **Any) -> None authority = kwargs.pop("authority", None) self._authority = normalize_authority(authority) if authority else get_default_authority() - self._regional_authority = kwargs.pop( - "regional_authority", os.environ.get(EnvironmentVariables.AZURE_REGIONAL_AUTHORITY_NAME) - ) + self._regional_authority = os.environ.get(EnvironmentVariables.AZURE_REGIONAL_AUTHORITY_NAME) self._tenant_id = kwargs.pop("tenant_id", None) or "organizations" validate_tenant_id(self._tenant_id) self._client = MsalClient(**kwargs) diff --git a/sdk/identity/azure-identity/tests/test_aad_client.py b/sdk/identity/azure-identity/tests/test_aad_client.py index bd8f1dc08f8c..3bee135d4268 100644 --- a/sdk/identity/azure-identity/tests/test_aad_client.py +++ b/sdk/identity/azure-identity/tests/test_aad_client.py @@ -308,8 +308,3 @@ def test_multitenant_cache(): assert client_c.get_cached_access_token([scope]) is None token = client_c.get_cached_access_token([scope], tenant_id=tenant_a) assert token.token == expected_token - with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True - ): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - client_c.get_cached_access_token([scope], tenant_id=tenant_a) diff --git a/sdk/identity/azure-identity/tests/test_aad_client_async.py b/sdk/identity/azure-identity/tests/test_aad_client_async.py index 59e2b7783c1b..ab30393f93d3 100644 --- a/sdk/identity/azure-identity/tests/test_aad_client_async.py +++ b/sdk/identity/azure-identity/tests/test_aad_client_async.py @@ -312,8 +312,3 @@ async def test_multitenant_cache(): assert client_c.get_cached_access_token([scope]) is None token = client_c.get_cached_access_token([scope], tenant_id=tenant_a) assert token.token == expected_token - with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True - ): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = client_c.get_cached_access_token([scope], tenant_id=tenant_a) diff --git a/sdk/identity/azure-identity/tests/test_auth_code.py b/sdk/identity/azure-identity/tests/test_auth_code.py index e286f7a40815..4e761a4e7ba5 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code.py +++ b/sdk/identity/azure-identity/tests/test_auth_code.py @@ -150,32 +150,3 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token - - -def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - def send(request, **_): - parsed = urlparse(request.url) - tenant = parsed.path.split("/")[1] - token = expected_token if tenant == expected_tenant else expected_token * 2 - return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**")) - - credential = AuthorizationCodeCredential( - expected_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send) - ) - - token = credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token * 2 - - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_auth_code_async.py b/sdk/identity/azure-identity/tests/test_auth_code_async.py index 2b71650cfce1..ff7aea158172 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code_async.py +++ b/sdk/identity/azure-identity/tests/test_auth_code_async.py @@ -174,33 +174,3 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token - - -async def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - async def send(request, **_): - parsed = urlparse(request.url) - tenant = parsed.path.split("/")[1] - token = expected_token if tenant == expected_tenant else expected_token * 2 - return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**")) - - credential = AuthorizationCodeCredential( - expected_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send) - ) - - token = await credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = await credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token * 2 - - # but fail if multi tenant is disabled - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - await credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_certificate_credential.py b/sdk/identity/azure-identity/tests/test_certificate_credential.py index b8f6d8d5886b..9215882abb3a 100644 --- a/sdk/identity/azure-identity/tests/test_certificate_credential.py +++ b/sdk/identity/azure-identity/tests/test_certificate_credential.py @@ -152,17 +152,6 @@ def test_regional_authority(): for region in RegionalAuthority: mock_confidential_client.reset_mock() - with patch.dict("os.environ", {}, clear=True): - credential = CertificateCredential("tenant", "client-id", PEM_CERT_PATH, regional_authority=region) - with patch("msal.ConfidentialClientApplication", mock_confidential_client): - # must call get_token because the credential constructs the MSAL application lazily - credential.get_token("scope") - - assert mock_confidential_client.call_count == 1 - _, kwargs = mock_confidential_client.call_args - assert kwargs["azure_region"] == region - mock_confidential_client.reset_mock() - # region can be configured via environment variable with patch.dict("os.environ", {EnvironmentVariables.AZURE_REGIONAL_AUTHORITY_NAME: region}, clear=True): credential = CertificateCredential("tenant", "client-id", PEM_CERT_PATH) @@ -360,7 +349,7 @@ def test_certificate_arguments(): @pytest.mark.parametrize("cert_path,cert_password", ALL_CERTS) -def test_allow_multitenant_authentication(cert_path, cert_password): +def test_multitenant_authentication(cert_path, cert_password): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -424,10 +413,3 @@ def send(request, **_): token = credential.get_token("scope", tenant_id="un" + expected_tenant) assert token.token == expected_token - - # ...unless the compat switch is enabled - with patch.dict( - os.environ, {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True - ): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_certificate_credential_async.py b/sdk/identity/azure-identity/tests/test_certificate_credential_async.py index 5ee6045bdd0c..8c5cc5b67efa 100644 --- a/sdk/identity/azure-identity/tests/test_certificate_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_certificate_credential_async.py @@ -270,7 +270,7 @@ def test_certificate_arguments(): @pytest.mark.asyncio @pytest.mark.parametrize("cert_path,cert_password", ALL_CERTS) -async def test_allow_multitenant_authentication(cert_path, cert_password): +async def test_multitenant_authentication(cert_path, cert_password): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" @@ -329,9 +329,3 @@ async def send(request, **_): token = await credential.get_token("scope", tenant_id="un" + expected_tenant) assert token.token == expected_token * 2 - - with patch.dict( - "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}, clear=True - ): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential.py b/sdk/identity/azure-identity/tests/test_client_secret_credential.py index 134848b46851..25576abf2506 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential.py @@ -129,17 +129,6 @@ def test_regional_authority(): for region in RegionalAuthority: mock_confidential_client.reset_mock() - with patch.dict("os.environ", {}, clear=True): - credential = ClientSecretCredential("tenant", "client-id", "secret", regional_authority=region) - with patch("msal.ConfidentialClientApplication", mock_confidential_client): - # must call get_token because the credential constructs the MSAL application lazily - credential.get_token("scope") - - assert mock_confidential_client.call_count == 1 - _, kwargs = mock_confidential_client.call_args - assert kwargs["azure_region"] == region - mock_confidential_client.reset_mock() - # region can be configured via environment variable with patch.dict("os.environ", {EnvironmentVariables.AZURE_REGIONAL_AUTHORITY_NAME: region}, clear=True): credential = ClientSecretCredential("tenant", "client-id", "secret") @@ -243,33 +232,3 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token - - -def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - def send(request, **_): - parsed = urlparse(request.url) - if "/oauth2/v2.0/token" not in parsed.path: - return get_discovery_response("https://{}/{}".format(parsed.netloc, expected_tenant)) - - tenant = parsed.path.split("/")[1] - token = expected_token if tenant == expected_tenant else expected_token * 2 - return mock_response(json_payload=build_aad_response(access_token=token)) - - credential = ClientSecretCredential(expected_tenant, "client-id", "secret", transport=Mock(send=send)) - - token = credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token - - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py index bcd4230c7505..01d9e8635869 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py @@ -279,31 +279,3 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token - - -@pytest.mark.asyncio -async def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - async def send(request, **_): - parsed = urlparse(request.url) - tenant = parsed.path.split("/")[1] - token = expected_token if tenant == expected_tenant else expected_token * 2 - return mock_response(json_payload=build_aad_response(access_token=token)) - - credential = ClientSecretCredential(expected_tenant, "client-id", "secret", transport=Mock(send=send)) - - token = await credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = await credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token * 2 - - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_interactive_credential.py b/sdk/identity/azure-identity/tests/test_interactive_credential.py index 7bab3d3f24d6..09b169591de5 100644 --- a/sdk/identity/azure-identity/tests/test_interactive_credential.py +++ b/sdk/identity/azure-identity/tests/test_interactive_credential.py @@ -325,43 +325,3 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token - - -def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - def request_token(*_, **__): - return build_aad_response( - access_token=expected_token, - id_token_claims=id_token_claims( - aud="...", - iss="http://localhost/tenant", - sub="subject", - preferred_username="...", - tenant_id="...", - object_id="...", - ), - ) - - def send(request, **_): - assert "/oauth2/v2.0/token" not in request.url, 'mock "request_token" should prevent sending a token request' - parsed = urlparse(request.url) - tenant = parsed.path.split("/")[1] - return get_discovery_response("https://{}/{}".format(parsed.netloc, tenant)) - - credential = MockCredential(tenant_id=expected_tenant, transport=Mock(send=send), request_token=request_token) - - token = credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token - - with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py index 0a7cb9baf762..4d7c477cf2a5 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py @@ -865,7 +865,7 @@ def send(request, **_): assert token.token == first_token -def test_allow_multitenant_authentication_auth_record(): +def test_multitenant_authentication_auth_record(): default_tenant = "organizations" first_token = "***" second_tenant = "second-tenant" diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential.py b/sdk/identity/azure-identity/tests/test_vscode_credential.py index 18b07de008a9..4258377de124 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential.py @@ -307,31 +307,3 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token - - -def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - def send(request, **_): - parsed = urlparse(request.url) - tenant = parsed.path.split("/")[1] - token = expected_token if tenant == expected_tenant else expected_token * 2 - return mock_response(json_payload=build_aad_response(access_token=token)) - - credential = get_credential(tenant_id=expected_tenant, transport=mock.Mock(send=send)) - - with mock.patch(GET_REFRESH_TOKEN, lambda _: "**"): - token = credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token * 2 - - with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = credential.get_token("scope", tenant_id="un" + expected_tenant) diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py index 999e31382602..6745a48d0abd 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py @@ -298,32 +298,3 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token - - -@pytest.mark.asyncio -async def test_multitenant_authentication_not_allowed(): - expected_tenant = "expected-tenant" - expected_token = "***" - - async def send(request, **_): - parsed = urlparse(request.url) - tenant = parsed.path.split("/")[1] - token = expected_token if tenant == expected_tenant else expected_token * 2 - return mock_response(json_payload=build_aad_response(access_token=token)) - - credential = get_credential(tenant_id=expected_tenant, transport=mock.Mock(send=send)) - - with mock.patch(GET_REFRESH_TOKEN, lambda _: "**"): - token = await credential.get_token("scope") - assert token.token == expected_token - - # explicitly specifying the configured tenant is okay - token = await credential.get_token("scope", tenant_id=expected_tenant) - assert token.token == expected_token - - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) - assert token.token == expected_token * 2 - - with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): - with pytest.raises(ClientAuthenticationError, match="multitenant_authentication"): - token = await credential.get_token("scope", tenant_id="un" + expected_tenant) From dae957f8cc66b77cc2ca3d3581ab4d33fa159a14 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 29 Sep 2021 17:04:06 -0700 Subject: [PATCH 07/11] updates --- .../azure-identity/azure/identity/_internal/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py index 5d39520247eb..a3612eb830b5 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py @@ -7,8 +7,6 @@ from six.moves.urllib_parse import urlparse -from azure.core.exceptions import ClientAuthenticationError - from .._constants import EnvironmentVariables, KnownAuthorities if TYPE_CHECKING: From da3403dd459a4c18566ad303e1a0525954608244 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Thu, 30 Sep 2021 13:22:16 -0700 Subject: [PATCH 08/11] update --- .../azure/identity/_credentials/azure_cli.py | 3 --- .../azure/identity/_credentials/azure_powershell.py | 3 --- .../azure-identity/azure/identity/_credentials/default.py | 4 ++-- .../azure-identity/azure/identity/_internal/__init__.py | 8 ++------ .../azure/identity/aio/_credentials/azure_cli.py | 3 --- .../azure/identity/aio/_credentials/azure_powershell.py | 3 --- .../azure/identity/aio/_credentials/default.py | 4 ++-- 7 files changed, 6 insertions(+), 22 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index b0b131f6dd87..a94e001852ec 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -37,9 +37,6 @@ class AzureCliCredential(object): This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. """ - def __init__(self, **kwargs): # pylint: disable=unused-argument - pass - def __enter__(self): return self diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py index d49963cce853..78c34b81d76e 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py @@ -53,9 +53,6 @@ class AzurePowerShellCredential(object): This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. """ - def __init__(self, **kwargs): # pylint: disable=unused-argument - pass - def __enter__(self): return self diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index f62e1eb899fe..75c2111362bb 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -133,9 +133,9 @@ def __init__(self, **kwargs): if not exclude_visual_studio_code_credential: credentials.append(VisualStudioCodeCredential(**vscode_args)) if not exclude_cli_credential: - credentials.append(AzureCliCredential(**kwargs)) + credentials.append(AzureCliCredential()) if not exclude_powershell_credential: - credentials.append(AzurePowerShellCredential(**kwargs)) + credentials.append(AzurePowerShellCredential()) if not exclude_interactive_browser_credential: if interactive_browser_client_id: credentials.append( diff --git a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py index a3612eb830b5..d9c7203734a5 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/__init__.py @@ -69,15 +69,11 @@ def resolve_tenant(default_tenant, tenant_id=None, **_): """Returns the correct tenant for a token request given a credential's configuration""" if ( tenant_id is None - or tenant_id == default_tenant + or default_tenant == "adfs" + or os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH) ): return default_tenant - disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False) - if default_tenant == "adfs": - disable_multitenant = True - if disable_multitenant: - return default_tenant return tenant_id diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index 79943cf7355d..869cf5de69ae 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -33,9 +33,6 @@ class AzureCliCredential(AsyncContextManager): This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity. """ - def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-argument - pass - @log_get_token_async async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": """Request an access token for `scopes`. diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py index c6818489653a..0881dd4e2b99 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_powershell.py @@ -30,9 +30,6 @@ class AzurePowerShellCredential(AsyncContextManager): This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity. """ - def __init__(self, **kwargs: "Any") -> None: # pylint: disable=unused-argument - pass - @log_get_token_async async def get_token( self, *scopes: str, **kwargs: "Any" diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index b8c690b21aba..5b32b0429c54 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -118,9 +118,9 @@ def __init__(self, **kwargs: "Any") -> None: if not exclude_visual_studio_code_credential: credentials.append(VisualStudioCodeCredential(**vscode_args)) if not exclude_cli_credential: - credentials.append(AzureCliCredential(**kwargs)) + credentials.append(AzureCliCredential()) if not exclude_powershell_credential: - credentials.append(AzurePowerShellCredential(**kwargs)) + credentials.append(AzurePowerShellCredential()) super().__init__(*credentials) From c411b7915f2edf504027ae14c04ff2e174dbea51 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Thu, 30 Sep 2021 13:26:02 -0700 Subject: [PATCH 09/11] update --- sdk/identity/azure-identity/CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 6439654fb55a..0985f11ec623 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -4,10 +4,11 @@ ### Breaking Changes -- `allow_multitenant_authentication` argument is deprecated and the setting is always on. To - disable it, set environment variable - AZURE_IDENTITY_DISABLE_MULTITENANTAUTH to "True". +- The `allow_multitenant_authentication` argument has been removed and the default behavior is now as if it were true. + The multitenant authentication feature can be totally disabled by setting the environment variable + `AZURE_IDENTITY_DISABLE_MULTITENANTAUTH` to `True`. - `azure.identity.RegionalAuthority` is removed. +- `regional_authority` argument is removed for `CertificateCredential` and `ClientSecretCredential` ## 1.7.0b4 (2021-09-09) From 67e7d506ce3d6e86cff9c6f91e91e870252d13a8 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Thu, 30 Sep 2021 14:16:03 -0700 Subject: [PATCH 10/11] update --- .../identity/_internal/aad_client_base.py | 2 +- .../azure-identity/tests/test_auth_code.py | 27 +++++++++++++ .../tests/test_auth_code_async.py | 27 +++++++++++++ .../tests/test_cli_credential.py | 28 ++++++++++++++ .../tests/test_cli_credential_async.py | 27 +++++++++++++ .../tests/test_client_secret_credential.py | 25 ++++++++++++ .../test_client_secret_credential_async.py | 26 +++++++++++++ .../tests/test_interactive_credential.py | 35 +++++++++++++++++ .../tests/test_powershell_credential.py | 25 ++++++++++++ .../tests/test_powershell_credential_async.py | 25 ++++++++++++ .../tests/test_shared_cache_credential.py | 38 +++++++++++++++++++ .../test_shared_cache_credential_async.py | 34 +++++++++++++++++ .../tests/test_vscode_credential.py | 26 +++++++++++++ .../tests/test_vscode_credential_async.py | 27 +++++++++++++ 14 files changed, 371 insertions(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py index d8821235d17c..f33cf40ce227 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/aad_client_base.py @@ -49,7 +49,7 @@ class AadClientBase(ABC): def __init__( self, tenant_id, client_id, authority=None, cache=None, **kwargs ): - # type: (str, str, Optional[str], Optional[TokenCache], bool, **Any) -> None + # type: (str, str, Optional[str], Optional[TokenCache], **Any) -> None self._authority = normalize_authority(authority) if authority else get_default_authority() self._tenant_id = tenant_id diff --git a/sdk/identity/azure-identity/tests/test_auth_code.py b/sdk/identity/azure-identity/tests/test_auth_code.py index 4e761a4e7ba5..f2fe752ff528 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code.py +++ b/sdk/identity/azure-identity/tests/test_auth_code.py @@ -150,3 +150,30 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token + +def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + def send(request, **_): + parsed = urlparse(request.url) + tenant = parsed.path.split("/")[1] + token = expected_token if tenant == expected_tenant else expected_token * 2 + return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**")) + + credential = AuthorizationCodeCredential( + expected_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send) + ) + + token = credential.get_token("scope") + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_auth_code_async.py b/sdk/identity/azure-identity/tests/test_auth_code_async.py index ff7aea158172..e5a91ca62750 100644 --- a/sdk/identity/azure-identity/tests/test_auth_code_async.py +++ b/sdk/identity/azure-identity/tests/test_auth_code_async.py @@ -174,3 +174,30 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token + +async def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + async def send(request, **_): + parsed = urlparse(request.url) + tenant = parsed.path.split("/")[1] + token = expected_token if tenant == expected_tenant else expected_token * 2 + return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**")) + + credential = AuthorizationCodeCredential( + expected_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send) + ) + + token = await credential.get_token("scope") + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_cli_credential.py b/sdk/identity/azure-identity/tests/test_cli_credential.py index e707736afb8c..6eb71b97e722 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential.py @@ -186,3 +186,31 @@ def fake_check_output(command_line, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token + +def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + def fake_check_output(command_line, **_): + match = re.search("--tenant (.*)", command_line[-1]) + assert match is None or match[1] == expected_tenant + return json.dumps( + { + "expiresOn": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"), + "accessToken": expected_token, + "subscription": "some-guid", + "tenant": expected_token, + "tokenType": "Bearer", + } + ) + + credential = AzureCliCredential() + with mock.patch(CHECK_OUTPUT, fake_check_output): + token = credential.get_token("scope") + assert token.token == expected_token + + with mock.patch.dict( + "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"} + ): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_cli_credential_async.py b/sdk/identity/azure-identity/tests/test_cli_credential_async.py index a1ab541f21f5..2276a1bff3e5 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential_async.py @@ -220,3 +220,30 @@ async def fake_exec(*args, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token + +async def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + async def fake_exec(*args, **_): + match = re.search("--tenant (.*)", args[-1]) + assert match is None or match[1] == expected_tenant + output = json.dumps( + { + "expiresOn": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"), + "accessToken": expected_token, + "subscription": "some-guid", + "tenant": expected_token, + "tokenType": "Bearer", + } + ).encode() + return mock.Mock(communicate=mock.Mock(return_value=get_completed_future((output, b""))), returncode=0) + + credential = AzureCliCredential() + with mock.patch(SUBPROCESS_EXEC, fake_exec): + token = await credential.get_token("scope") + assert token.token == expected_token + + with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential.py b/sdk/identity/azure-identity/tests/test_client_secret_credential.py index 25576abf2506..2528bf0ad191 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential.py @@ -232,3 +232,28 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token + +def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + def send(request, **_): + parsed = urlparse(request.url) + if "/oauth2/v2.0/token" not in parsed.path: + return get_discovery_response("https://{}/{}".format(parsed.netloc, expected_tenant)) + + tenant = parsed.path.split("/")[1] + token = expected_token if tenant == expected_tenant else expected_token * 2 + return mock_response(json_payload=build_aad_response(access_token=token)) + + credential = ClientSecretCredential(expected_tenant, "client-id", "secret", transport=Mock(send=send)) + + token = credential.get_token("scope") + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py index 01d9e8635869..03e8d323c81d 100644 --- a/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_client_secret_credential_async.py @@ -279,3 +279,29 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token + +@pytest.mark.asyncio +async def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + async def send(request, **_): + parsed = urlparse(request.url) + tenant = parsed.path.split("/")[1] + token = expected_token if tenant == expected_tenant else expected_token * 2 + return mock_response(json_payload=build_aad_response(access_token=token)) + + credential = ClientSecretCredential(expected_tenant, "client-id", "secret", transport=Mock(send=send)) + + token = await credential.get_token("scope") + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_interactive_credential.py b/sdk/identity/azure-identity/tests/test_interactive_credential.py index 09b169591de5..ba6e407e3aec 100644 --- a/sdk/identity/azure-identity/tests/test_interactive_credential.py +++ b/sdk/identity/azure-identity/tests/test_interactive_credential.py @@ -325,3 +325,38 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token + +def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + def request_token(*_, **__): + return build_aad_response( + access_token=expected_token, + id_token_claims=id_token_claims( + aud="...", + iss="http://localhost/tenant", + sub="subject", + preferred_username="...", + tenant_id="...", + object_id="...", + ), + ) + + def send(request, **_): + assert "/oauth2/v2.0/token" not in request.url, 'mock "request_token" should prevent sending a token request' + parsed = urlparse(request.url) + tenant = parsed.path.split("/")[1] + return get_discovery_response("https://{}/{}".format(parsed.netloc, tenant)) + + credential = MockCredential(tenant_id=expected_tenant, transport=Mock(send=send), request_token=request_token) + + token = credential.get_token("scope") + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_powershell_credential.py b/sdk/identity/azure-identity/tests/test_powershell_credential.py index bbe6f652bc0c..3766b84cb6e8 100644 --- a/sdk/identity/azure-identity/tests/test_powershell_credential.py +++ b/sdk/identity/azure-identity/tests/test_powershell_credential.py @@ -273,3 +273,28 @@ def fake_Popen(command, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token + +def test_multitenant_authentication_not_allowed(): + expected_token = "***" + + def fake_Popen(command, **_): + assert command[-1].startswith("pwsh -NonInteractive -EncodedCommand ") + encoded_script = command[-1].split()[-1] + decoded_script = base64.b64decode(encoded_script).decode("utf-16-le") + match = re.search(r"Get-AzAccessToken -ResourceUrl '(\S+)'(?: -TenantId (\S+))?", decoded_script) + tenant = match.groups()[1] + + assert tenant is None, "credential shouldn't accept an explicit tenant ID" + stdout = "azsdk%{}%{}".format(expected_token, int(time.time()) + 3600) + + communicate = Mock(return_value=(stdout, "")) + return Mock(communicate=communicate, returncode=0) + + credential = AzurePowerShellCredential() + with patch(POPEN, fake_Popen): + token = credential.get_token("scope") + assert token.token == expected_token + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = credential.get_token("scope", tenant_id="some tenant") + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_powershell_credential_async.py b/sdk/identity/azure-identity/tests/test_powershell_credential_async.py index baa73992c1fc..0dcc56267f78 100644 --- a/sdk/identity/azure-identity/tests/test_powershell_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_powershell_credential_async.py @@ -275,3 +275,28 @@ async def fake_exec(*args, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token + +async def test_multitenant_authentication_not_allowed(): + expected_token = "***" + + async def fake_exec(*args, **_): + command = args[2] + assert command.startswith("pwsh -NonInteractive -EncodedCommand ") + encoded_script = command.split()[-1] + decoded_script = base64.b64decode(encoded_script).decode("utf-16-le") + match = re.search(r"Get-AzAccessToken -ResourceUrl '(\S+)'(?: -TenantId (\S+))?", decoded_script) + tenant = match[2] + + assert tenant is None, "credential shouldn't accept an explicit tenant ID" + stdout = "azsdk%{}%{}".format(expected_token, int(time.time()) + 3600) + communicate = Mock(return_value=get_completed_future((stdout.encode(), b""))) + return Mock(communicate=communicate, returncode=0) + + credential = AzurePowerShellCredential() + with patch(CREATE_SUBPROCESS_EXEC, fake_exec): + token = await credential.get_token("scope") + assert token.token == expected_token + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = await credential.get_token("scope", tenant_id="some tenant") + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py index 4d7c477cf2a5..287815c00cce 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential.py @@ -944,3 +944,41 @@ def populated_cache(*accounts): cache.add(account) cache.add = lambda *_, **__: None # prevent anything being added to the cache return cache + +def test_multitenant_authentication_not_allowed(): + default_tenant = "organizations" + expected_token = "***" + + def send(request, **_): + parsed = urlparse(request.url) + tenant_id = parsed.path.split("/")[1] + assert tenant_id == default_tenant + return mock_response( + json_payload=build_aad_response( + access_token=expected_token, + id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), + ) + ) + + tenant_id = "tenant-id" + client_id = "client-id" + authority = "localhost" + object_id = "object-id" + username = "me" + + expected_account = get_account_event( + username, object_id, tenant_id, authority=authority, client_id=client_id, refresh_token="**" + ) + cache = populated_cache(expected_account) + + credential = SharedTokenCacheCredential(authority=authority, transport=Mock(send=send), _cache=cache) + + token = credential.get_token("scope") + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id=default_tenant) + assert token.token == expected_token + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = credential.get_token("scope", tenant_id="some tenant") + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py b/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py index 28583edaed46..9346755360f1 100644 --- a/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_shared_cache_credential_async.py @@ -642,3 +642,37 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token + +@pytest.mark.asyncio +async def test_multitenant_authentication_not_allowed(): + default_tenant = "organizations" + expected_token = "***" + + async def send(request, **_): + parsed = urlparse(request.url) + tenant_id = parsed.path.split("/")[1] + assert tenant_id == default_tenant + return mock_response( + json_payload=build_aad_response( + access_token=expected_token, + id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), + ) + ) + + authority = "localhost" + expected_account = get_account_event( + "user", "object-id", "tenant-id", authority=authority, client_id="client-id", refresh_token="**" + ) + cache = populated_cache(expected_account) + + credential = SharedTokenCacheCredential(authority=authority, transport=Mock(send=send), _cache=cache) + + token = await credential.get_token("scope") + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id=default_tenant) + assert token.token == expected_token + + with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = await credential.get_token("scope", tenant_id="some tenant") + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential.py b/sdk/identity/azure-identity/tests/test_vscode_credential.py index 4258377de124..befe86ad7ce3 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential.py @@ -307,3 +307,29 @@ def send(request, **_): # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token + +def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + def send(request, **_): + parsed = urlparse(request.url) + tenant = parsed.path.split("/")[1] + token = expected_token if tenant == expected_tenant else expected_token * 2 + return mock_response(json_payload=build_aad_response(access_token=token)) + + credential = get_credential(tenant_id=expected_tenant, transport=mock.Mock(send=send)) + + with mock.patch(GET_REFRESH_TOKEN, lambda _: "**"): + token = credential.get_token("scope") + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 + + with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token diff --git a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py index 6745a48d0abd..ff5716c7d9ad 100644 --- a/sdk/identity/azure-identity/tests/test_vscode_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_vscode_credential_async.py @@ -298,3 +298,30 @@ async def send(request, **_): # should still default to the first tenant token = await credential.get_token("scope") assert token.token == first_token + +@pytest.mark.asyncio +async def test_multitenant_authentication_not_allowed(): + expected_tenant = "expected-tenant" + expected_token = "***" + + async def send(request, **_): + parsed = urlparse(request.url) + tenant = parsed.path.split("/")[1] + token = expected_token if tenant == expected_tenant else expected_token * 2 + return mock_response(json_payload=build_aad_response(access_token=token)) + + credential = get_credential(tenant_id=expected_tenant, transport=mock.Mock(send=send)) + + with mock.patch(GET_REFRESH_TOKEN, lambda _: "**"): + token = await credential.get_token("scope") + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id=expected_tenant) + assert token.token == expected_token + + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token * 2 + + with mock.patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): + token = await credential.get_token("scope", tenant_id="un" + expected_tenant) + assert token.token == expected_token From 66f7dcd628db0736cd530184f0b7326a543cc2cc Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Fri, 1 Oct 2021 11:07:46 -0700 Subject: [PATCH 11/11] Update sdk/identity/azure-identity/CHANGELOG.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: McCoy PatiƱo <39780829+mccoyp@users.noreply.github.com> --- sdk/identity/azure-identity/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 0985f11ec623..64847d7e5a20 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -3,6 +3,8 @@ ## 1.7.0b5 (Unreleased) ### Breaking Changes +> These changes do not impact the API of stable versions such as 1.6.0. +> Only code written against a beta version such as 1.7.0b1 may be affected. - The `allow_multitenant_authentication` argument has been removed and the default behavior is now as if it were true. The multitenant authentication feature can be totally disabled by setting the environment variable