Esempio n. 1
0
async def test_get_token():
    """The credential should parse the CLI's output to an AccessToken"""

    access_token = "access token"
    valid_seconds = 42
    successful_output = json.dumps({
        # expiresOn is a naive datetime representing valid_seconds from the epoch
        "expiresOn":
        datetime.fromtimestamp(valid_seconds).strftime("%Y-%m-%d %H:%M:%S.%f"),
        "accessToken":
        access_token,
        "subscription":
        "some-guid",
        "tenant":
        "some-guid",
        "tokenType":
        "Bearer",
    })

    with mock.patch(SUBPROCESS_EXEC, mock_exec(successful_output)):
        credential = AzureCliCredential()
        token = await credential.get_token("scope")

    assert token.token == access_token
    assert type(token.expires_on) == int
    assert token.expires_on == valid_seconds
async def test_cannot_execute_shell():
    """The credential should raise CredentialUnavailableError when the subprocess doesn't start"""

    with mock.patch(SUBPROCESS_EXEC, mock.Mock(side_effect=OSError())):
        with pytest.raises(CredentialUnavailableError):
            credential = AzureCliCredential()
            await credential.get_token("scope")
async def test_get_token():
    """The credential should parse the CLI's output to an AccessToken"""

    access_token = "access token"
    expected_expires_on = 1602015811
    successful_output = json.dumps({
        "expiresOn":
        datetime.fromtimestamp(expected_expires_on).strftime(
            "%Y-%m-%d %H:%M:%S.%f"),
        "accessToken":
        access_token,
        "subscription":
        "some-guid",
        "tenant":
        "some-guid",
        "tokenType":
        "Bearer",
    })

    with mock.patch(SUBPROCESS_EXEC, mock_exec(successful_output)):
        credential = AzureCliCredential()
        token = await credential.get_token("scope")

    assert token.token == access_token
    assert type(token.expires_on) == int
    assert token.expires_on == expected_expires_on
async def test_cli_not_installed_windows():
    """The credential should raise CredentialUnavailableError when the CLI isn't installed"""

    output = "'az' is not recognized as an internal or external command, operable program or batch file."
    with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)):
        with pytest.raises(CredentialUnavailableError, match=CLI_NOT_FOUND):
            credential = AzureCliCredential()
            await credential.get_token("scope")
async def test_not_logged_in():
    """When the CLI isn't logged in, the credential should raise CredentialUnavailableError"""

    output = "ERROR: Please run 'az login' to setup account."
    with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)):
        with pytest.raises(CredentialUnavailableError, match=NOT_LOGGED_IN):
            credential = AzureCliCredential()
            await credential.get_token("scope")
async def test_unexpected_error():
    """When the CLI returns an unexpected error, the credential should raise an error containing the CLI's output"""

    output = "something went wrong"
    with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=42)):
        with pytest.raises(ClientAuthenticationError, match=output):
            credential = AzureCliCredential()
            await credential.get_token("scope")
async def test_cli_not_installed_linux():
    """The credential should raise CredentialUnavailableError when the CLI isn't installed"""

    output = "/bin/sh: 1: az: not found"
    with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=127)):
        with pytest.raises(CredentialUnavailableError, match=CLI_NOT_FOUND):
            credential = AzureCliCredential()
            await credential.get_token("scope")
async def test_not_logged_in():
    """When the CLI isn't logged in, the credential should raise an error containing the CLI's output"""

    output = "ERROR: Please run 'az login' to setup account."
    with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)):
        with pytest.raises(ClientAuthenticationError, match=output):
            credential = AzureCliCredential()
            await credential.get_token("scope")
Esempio n. 9
0
 async def get_subscriptions(cls) -> list[dict[str, str]]:
     res = []
     auth = AzureCliCredential()
     async with SubscriptionClient(auth) as client:
         subs = client.subscriptions.list()
         async for sub in subs:
             res.append(sub.as_dict())
     return res
async def test_subprocess_error_does_not_expose_token(output):
    """Errors from the subprocess shouldn't expose access tokens in CLI output"""

    with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)):
        with pytest.raises(ClientAuthenticationError) as ex:
            credential = AzureCliCredential()
            await credential.get_token("scope")

    assert "secret value" not in str(ex.value)
    assert "secret value" not in repr(ex.value)
async def test_parsing_error_does_not_expose_token(output):
    """Errors during CLI output parsing shouldn't expose access tokens in that output"""

    with mock.patch(SUBPROCESS_EXEC, mock_exec(output)):
        with pytest.raises(ClientAuthenticationError) as ex:
            credential = AzureCliCredential()
            await credential.get_token("scope")

    assert "secret value" not in str(ex.value)
    assert "secret value" not in repr(ex.value)
Esempio n. 12
0
    def _initialize_credentials(self):
        if self.subscription_id is not None \
           and self.arm_base_url is not None:
            if self.vscode_tenant_id is None:
                self.vscode_tenant_id = self._get_tenant_id(
                    arm_base_url=self.arm_base_url,
                    subscription_id=self.subscription_id)
            if self.shared_cache_tenant_id is None:
                self.shared_cache_tenant_id = self._get_tenant_id(
                    arm_base_url=self.arm_base_url,
                    subscription_id=self.subscription_id)
            if self.interactive_browser_tenant_id is None:
                self.interactive_browser_tenant_id = self._get_tenant_id(
                    arm_base_url=self.arm_base_url,
                    subscription_id=self.subscription_id)

        credentials = []  # type: List[AsyncTokenCredential]
        if not self.exclude_token_file_credential:
            credentials.append(_TokenFileCredential())
        if not self.exclude_environment_credential:
            credentials.append(EnvironmentCredential(authority=self.authority))
        if not self.exclude_managed_identity_credential:
            credentials.append(
                ManagedIdentityCredential(
                    client_id=self.managed_identity_client_id))
        if not self.exclude_shared_token_cache_credential and SharedTokenCacheCredential.supported(
        ):
            try:
                # username and/or tenant_id are only required when the cache contains tokens for multiple identities
                shared_cache = SharedTokenCacheCredential(
                    username=self.shared_cache_username,
                    tenant_id=self.shared_cache_tenant_id,
                    authority=self.authority)
                credentials.append(shared_cache)
            except Exception as ex:  # pylint:disable=broad-except
                _LOGGER.info("Shared token cache is unavailable: '%s'", ex)
        if not self.exclude_visual_studio_code_credential:
            credentials.append(
                VisualStudioCodeCredential(tenant_id=self.vscode_tenant_id))
        if not self.exclude_cli_credential:
            credentials.append(AzureCliCredential())
        if not self.exclude_powershell_credential:
            credentials.append(AzurePowerShellCredential())
        if not self.exclude_interactive_browser_credential:
            credentials.append(
                InteractiveBrowserCredential(
                    tenant_id=self.interactive_browser_tenant_id))
        if not self.exclude_device_code_credential:
            credentials.append(
                DeviceCodeCredential(
                    tenant_id=self.interactive_browser_tenant_id))

        self.credentials = credentials
async def test_windows_fallback():
    """The credential should fall back to the sync implementation when not using ProactorEventLoop on Windows"""

    sync_get_token = mock.Mock()
    with mock.patch("azure.identity.aio._credentials.azure_cli._SyncAzureCliCredential") as fallback:
        fallback.return_value = mock.Mock(get_token=sync_get_token)
        with mock.patch(AzureCliCredential.__module__ + ".asyncio.get_event_loop"):
            # asyncio.get_event_loop now returns Mock, i.e. never ProactorEventLoop
            credential = AzureCliCredential()
            await credential.get_token("scope")

    assert sync_get_token.call_count == 1
Esempio n. 14
0
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"
Esempio n. 15
0
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"
    second_token = first_token * 2

    async def fake_exec(*args, **_):
        match = re.search("--tenant (.*)", args[-1])
        tenant = match[1] if match else default_tenant
        assert tenant in (
            default_tenant,
            second_tenant), 'unexpected tenant "{}"'.format(tenant)
        output = json.dumps({
            "expiresOn":
            datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            "accessToken":
            first_token if tenant == default_tenant else second_token,
            "subscription":
            "some-guid",
            "tenant":
            tenant,
            "tokenType":
            "Bearer",
        }).encode()
        return mock.Mock(
            communicate=mock.Mock(return_value=get_completed_future((output,
                                                                     b""))),
            returncode=0)

    credential = AzureCliCredential(allow_multitenant_authentication=True)
    with mock.patch(SUBPROCESS_EXEC, fake_exec):
        token = await credential.get_token("scope")
        assert token.token == first_token

        token = await credential.get_token("scope", tenant_id=default_tenant)
        assert token.token == first_token

        token = await credential.get_token("scope", tenant_id=second_tenant)
        assert token.token == second_token

        # 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
async def test_context_manager():
    """The credential must be a context manager, although it does nothing as one because it has no transport"""

    async with AzureCliCredential():
        pass
Esempio n. 18
0
 def __init__(self, subscription):
     self.subscription = subscription
     self.auth = AzureCliCredential()