示例#1
0
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(CHECK_OUTPUT, raise_called_process_error(1, output)):
        with pytest.raises(ClientAuthenticationError, match=output):
            AzureCliCredential().get_token("scope")
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(CHECK_OUTPUT, mock.Mock(return_value=successful_output)):
        token = AzureCliCredential().get_token("scope")

    assert token.token == access_token
    assert type(token.expires_on) == int
    assert token.expires_on == valid_seconds
示例#3
0
def test_multitenant_authentication():
    default_tenant = "first-tenant"
    first_token = "***"
    second_tenant = "second-tenant"
    second_token = first_token * 2

    def fake_check_output(command_line, **_):
        match = re.search("--tenant (.*)", command_line[-1])
        tenant = match.groups()[0] if match else default_tenant
        assert tenant in (default_tenant, second_tenant), 'unexpected tenant "{}"'.format(tenant)
        return 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",
            }
        )

    credential = AzureCliCredential()
    with mock.patch(CHECK_OUTPUT, fake_check_output):
        token = credential.get_token("scope")
        assert token.token == first_token

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

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

        # should still default to the first tenant
        token = credential.get_token("scope")
        assert token.token == first_token
示例#4
0
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(CHECK_OUTPUT, raise_called_process_error(127, output)):
        with pytest.raises(CredentialUnavailableError, match=CLI_NOT_FOUND):
            AzureCliCredential().get_token("scope")
示例#5
0
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(CHECK_OUTPUT, raise_called_process_error(42, output)):
        with pytest.raises(ClientAuthenticationError, match=output):
            AzureCliCredential().get_token("scope")
def test_caching():
    scope = "scope"
    now = int(time.time())
    expected_expires_on = now + 3600
    expected_token = "*"
    transport = validating_transport(
        requests=[Request(url="http://localhost")],
        responses=[
            mock_response(
                json_payload={
                    "access_token": expected_token,
                    "expires_in": 3600,
                    "expires_on": expected_expires_on,
                    "resource": scope,
                    "token_type": "Bearer",
                }
            )
        ],
    )
    client = ManagedIdentityClient(
        request_factory=lambda _, __: HttpRequest("GET", "http://localhost"), transport=transport
    )

    token = client.get_cached_token(scope)
    assert not token

    with mock.patch(ManagedIdentityClient.__module__ + ".time.time", lambda: now):
        token = client.request_token(scope)
    assert token.expires_on == expected_expires_on
    assert token.token == expected_token

    token = client.get_cached_token(scope)
    assert token.expires_on == expected_expires_on
    assert token.token == expected_token
示例#7
0
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(CHECK_OUTPUT, raise_called_process_error(1, output)):
        with pytest.raises(CredentialUnavailableError, match=CLI_NOT_FOUND):
            AzureCliCredential().get_token("scope")
示例#8
0
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
示例#9
0
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(CHECK_OUTPUT, mock.Mock(return_value=successful_output)):
        token = AzureCliCredential().get_token("scope")

    assert token.token == access_token
    assert type(token.expires_on) == int
    assert token.expires_on == expected_expires_on
示例#10
0
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(CHECK_OUTPUT, raise_called_process_error(1, output)):
        with pytest.raises(CredentialUnavailableError, match=NOT_LOGGED_IN):
            AzureCliCredential().get_token("scope")
def test_timeout():
    """The credential should raise CredentialUnavailableError when the subprocess times out"""

    from subprocess import TimeoutExpired

    with mock.patch(CHECK_OUTPUT, mock.Mock(side_effect=TimeoutExpired("", 42))):
        with pytest.raises(CredentialUnavailableError):
            AzureCliCredential().get_token("scope")
示例#12
0
def test_multiple_scopes():
    """The credential should raise ValueError when get_token is called with more than one scope"""

    with mock.patch("os.environ", {EnvironmentVariables.MSI_ENDPOINT: "https://url"}):
        credential = MsiCredential()

    with pytest.raises(ValueError):
        credential.get_token("one scope", "and another")
示例#13
0
def test_no_scopes():
    """The credential should raise ValueError when get_token is called with no scopes"""

    with mock.patch("os.environ", {EnvironmentVariables.MSI_ENDPOINT: "https://url"}):
        credential = MsiCredential()

    with pytest.raises(ValueError):
        credential.get_token()
示例#14
0
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(CHECK_OUTPUT, mock.Mock(return_value=output)):
        with pytest.raises(ClientAuthenticationError) as ex:
            AzureCliCredential().get_token("scope")

    assert "secret value" not in str(ex.value)
    assert "secret value" not in repr(ex.value)
示例#15
0
def test_subprocess_error_does_not_expose_token(output):
    """Errors from the subprocess shouldn't expose access tokens in CLI output"""

    with mock.patch(CHECK_OUTPUT, raise_called_process_error(1, output=output)):
        with pytest.raises(ClientAuthenticationError) as ex:
            AzureCliCredential().get_token("scope")

    assert "secret value" not in str(ex.value)
    assert "secret value" not in repr(ex.value)
示例#16
0
def test_username_password_configuration():
    """the credential should pass expected values and any keyword arguments to its inner credential"""

    client_id = "client-id"
    username = "******"
    password = "******"
    bar = "bar"

    environment = {
        EnvironmentVariables.AZURE_CLIENT_ID: client_id,
        EnvironmentVariables.AZURE_USERNAME: username,
        EnvironmentVariables.AZURE_PASSWORD: password,
    }
    with mock.patch(EnvironmentCredential.__module__ +
                    ".UsernamePasswordCredential") as mock_credential:
        with mock.patch.dict("os.environ", environment, clear=True):
            EnvironmentCredential(foo=bar)

    assert mock_credential.call_count == 1
    _, kwargs = mock_credential.call_args
    assert kwargs["client_id"] == client_id
    assert kwargs["username"] == username
    assert kwargs["password"] == password
    assert kwargs["foo"] == bar

    # optional tenant id should be used when set
    tenant_id = "tenant-id"
    environment = dict(environment,
                       **{EnvironmentVariables.AZURE_TENANT_ID: tenant_id})
    with mock.patch(EnvironmentCredential.__module__ +
                    ".UsernamePasswordCredential") as mock_credential:
        with mock.patch.dict("os.environ", environment, clear=True):
            EnvironmentCredential(foo=bar)

    assert mock_credential.call_count == 1
    _, kwargs = mock_credential.call_args
    assert kwargs["client_id"] == client_id
    assert kwargs["username"] == username
    assert kwargs["password"] == password
    assert kwargs["tenant_id"] == tenant_id
    assert kwargs["foo"] == bar
def test_passes_authority_argument(credential_name, environment_variables):
    """the credential pass the 'authority' keyword argument to its inner credential"""

    authority = "authority"

    with mock.patch.dict("os.environ", {variable: "foo" for variable in environment_variables}, clear=True):
        with mock.patch(EnvironmentCredential.__module__ + "." + credential_name) as mock_credential:
            EnvironmentCredential(authority=authority)

    assert mock_credential.call_count == 1
    _, kwargs = mock_credential.call_args
    assert kwargs["authority"] == authority
示例#18
0
def test_token_cache_persistence_options():
    with mock.patch("azure.identity._persistent_cache.msal_extensions"):
        # [START snippet]
        cache_options = TokenCachePersistenceOptions()
        credential = InteractiveBrowserCredential(
            cache_persistence_options=cache_options)

        # specify a cache name to isolate the cache from other applications
        TokenCachePersistenceOptions(name="my_application")

        # configure the cache to fall back to unencrypted storage when encryption isn't available
        TokenCachePersistenceOptions(allow_unencrypted_storage=True)
示例#19
0
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"
def test_certificate_configuration():
    """the credential should pass expected values and any keyword arguments to its inner credential"""

    client_id = "client-id"
    certificate_path = "..."
    tenant_id = "tenant_id"
    bar = "bar"

    environment = {
        EnvironmentVariables.AZURE_CLIENT_ID: client_id,
        EnvironmentVariables.AZURE_CLIENT_CERTIFICATE_PATH: certificate_path,
        EnvironmentVariables.AZURE_TENANT_ID: tenant_id,
    }
    with mock.patch(EnvironmentCredential.__module__ + ".CertificateCredential") as mock_credential:
        with mock.patch.dict("os.environ", environment, clear=True):
            EnvironmentCredential(foo=bar)

    assert mock_credential.call_count == 1
    _, kwargs = mock_credential.call_args
    assert kwargs["client_id"] == client_id
    assert kwargs["certificate_path"] == certificate_path
    assert kwargs["tenant_id"] == tenant_id
    assert kwargs["foo"] == bar
示例#21
0
def test_cannot_execute_shell():
    """The credential should raise CredentialUnavailableError when the subprocess doesn't start"""

    with mock.patch(CHECK_OUTPUT, mock.Mock(side_effect=OSError())):
        with pytest.raises(CredentialUnavailableError):
            AzureCliCredential().get_token("scope")