예제 #1
0
def test_cache_scopes():
    scope_a = "scope_a"
    scope_b = "scope_b"
    scope_ab = scope_a + " " + scope_b
    expected_tokens = {
        scope_a: {"access_token": scope_a, "expires_in": 1 << 31, "ext_expires_in": 1 << 31, "token_type": "Bearer"},
        scope_b: {"access_token": scope_b, "expires_in": 1 << 31, "ext_expires_in": 1 << 31, "token_type": "Bearer"},
        scope_ab: {"access_token": scope_ab, "expires_in": 1 << 31, "ext_expires_in": 1 << 31, "token_type": "Bearer"},
    }

    def mock_send(request, **kwargs):
        token = expected_tokens[request.data["resource"]]
        return mock_response(json_payload=token)

    client = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send))

    # if the cache has a token for a & b, it should hit for a, b, a & b
    token = client.request_token([scope_a, scope_b], form_data={"resource": scope_ab})
    assert token.token == scope_ab
    for scope in (scope_a, scope_b):
        assert client.get_cached_token([scope]).token == scope_ab
    assert client.get_cached_token([scope_a, scope_b]).token == scope_ab

    # if the cache has only tokens for a and b alone, a & b should miss
    client = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send))
    for scope in (scope_a, scope_b):
        token = client.request_token([scope], form_data={"resource": scope})
        assert token.token == scope
        assert client.get_cached_token([scope]).token == scope
    assert not client.get_cached_token([scope_a, scope_b])
def test_authn_client_deserialization():
    now = 6
    expires_in = 59 - now
    expires_on = now + expires_in
    access_token = "***"
    expected_access_token = AccessToken(access_token, expires_on)
    scope = "scope"

    # response with expires_on only
    token_payload = {
        "access_token": access_token,
        "expires_on": expires_on,
        "token_type": "Bearer",
        "resource": scope
    }
    mock_send = Mock(return_value=mock_response(json_payload=token_payload))
    token = AuthnClient(endpoint="http://foo",
                        transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token

    # response with expires_on only and it's a datetime string (App Service MSI)
    token_payload = {
        "access_token": access_token,
        "expires_on": "01/01/1970 00:00:{} +00:00".format(now + expires_in),
        "token_type": "Bearer",
        "resource": scope,
    }
    token = AuthnClient(endpoint="http://foo",
                        transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token

    # response with string expires_in and expires_on (IMDS, Cloud Shell)
    token_payload = {
        "access_token": access_token,
        "expires_in": str(expires_in),
        "expires_on": str(expires_on),
        "token_type": "Bearer",
        "resource": scope,
    }
    token = AuthnClient(endpoint="http://foo",
                        transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token

    # response with int expires_in (AAD)
    token_payload = {
        "access_token": access_token,
        "expires_in": expires_in,
        "token_type": "Bearer",
        "ext_expires_in": expires_in,
    }
    with patch("azure.identity._authn_client.time.time") as mock_time:
        mock_time.return_value = now
        token = AuthnClient(
            endpoint="http://foo",
            transport=Mock(send=mock_send)).request_token(scope)
        assert token == expected_access_token
def test_authn_client_deserialization():
    now = 6
    expires_in = 59 - now
    expires_on = now + expires_in
    access_token = "***"
    expected_access_token = AccessToken(access_token, expires_on)
    scope = "scope"

    mock_response = Mock(headers={"content-type": "application/json"}, status_code=200, content_type="application/json")
    mock_send = Mock(return_value=mock_response)

    # response with expires_on only
    mock_response.text = lambda: json.dumps(
        {"access_token": access_token, "expires_on": expires_on, "token_type": "Bearer", "resource": scope}
    )
    token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token

    # response with expires_on only and it's a datetime string (App Service MSI)
    mock_response.text = lambda: json.dumps(
        {
            "access_token": access_token,
            "expires_on": "01/01/1970 00:00:{} +00:00".format(now + expires_in),
            "token_type": "Bearer",
            "resource": scope,
        }
    )
    token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token

    # response with string expires_in and expires_on (IMDS, Cloud Shell)
    mock_response.text = lambda: json.dumps(
        {
            "access_token": access_token,
            "expires_in": str(expires_in),
            "expires_on": str(expires_on),
            "token_type": "Bearer",
            "resource": scope,
        }
    )
    token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token

    # response with int expires_in (AAD)
    mock_response.text = lambda: json.dumps(
        {"access_token": access_token, "expires_in": expires_in, "token_type": "Bearer", "ext_expires_in": expires_in}
    )
    with patch("azure.identity._authn_client.time.time") as mock_time:
        mock_time.return_value = now
        token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token(scope)
        assert token == expected_access_token
예제 #4
0
def test_authn_client_deserialization():
    # using the synchronous AuthnClient to drive this test but the functionality tested is
    # in the sans I/O AuthnClientBase, shared with AsyncAuthnClient
    now = 6
    expires_in = 1800
    expires_on = now + expires_in
    expected_token = "token"

    mock_response = Mock(headers={"content-type": "application/json"},
                         status_code=200,
                         content_type=["application/json"])
    mock_send = Mock(return_value=mock_response)

    # response with expires_on only
    mock_response.text = lambda: json.dumps({
        "access_token": expected_token,
        "expires_on": expires_on,
        "token_type": "Bearer"
    })
    token = AuthnClient("http://foo",
                        transport=Mock(send=mock_send)).request_token("scope")
    assert token.token == expected_token
    assert token.expires_on == expires_on

    # response with expires_in and expires_on
    mock_response.text = lambda: json.dumps({
        "access_token": expected_token,
        "expires_in": expires_in,
        "expires_on": expires_on,
        "token_type": "Bearer"
    })
    token = AuthnClient("http://foo",
                        transport=Mock(send=mock_send)).request_token("scope")
    assert token.token == expected_token
    assert token.expires_on == expires_on

    # response with expires_in only
    mock_response.text = lambda: json.dumps({
        "access_token": expected_token,
        "expires_in": expires_in,
        "token_type": "Bearer"
    })
    with patch("azure.identity._authn_client.time.time") as mock_time:
        mock_time.return_value = now
        token = AuthnClient(
            "http://foo",
            transport=Mock(send=mock_send)).request_token("scope")
        assert token.token == expected_token
        assert token.expires_on == expires_on
예제 #5
0
def test_cache_expiry():
    access_token = "token"
    now = 42
    expires_in = 1800
    expires_on = now + expires_in
    expected_token = AccessToken(access_token, expires_on)
    token_payload = {"access_token": access_token, "expires_in": expires_in, "token_type": "Bearer"}
    mock_send = Mock(return_value=mock_response(json_payload=token_payload))

    client = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send))
    with patch("azure.identity._authn_client.time.time") as mock_time:
        # populate the cache with a valid token
        mock_time.return_value = now
        token = client.request_token("scope")
        assert token.token == expected_token.token
        assert token.expires_on == expected_token.expires_on

        cached_token = client.get_cached_token("scope")
        assert cached_token == expected_token

        # advance time past the cached token's expires_on
        mock_time.return_value = expires_on + 3600
        cached_token = client.get_cached_token("scope")
        assert not cached_token

        # request a new token
        new_token = "new token"
        token_payload["access_token"] = new_token
        token = client.request_token("scope")
        assert token.token == new_token

        # it should be cached
        cached_token = client.get_cached_token("scope")
        assert cached_token.token == new_token
def test_request_url():
    authority = "authority.com"
    tenant = "expected_tenant"

    def validate_url(url):
        scheme, netloc, path, _, _, _ = urlparse(url)
        assert scheme == "https"
        assert netloc == authority
        assert path.startswith("/" + tenant)

    def mock_send(request, **kwargs):
        validate_url(request.url)
        return mock_response(json_payload={"token_type": "Bearer", "expires_in": 42, "access_token": "***"})

    client = AuthnClient(tenant=tenant, transport=Mock(send=mock_send), authority=authority)
    client.request_token(("scope",))
    request = client.get_refresh_token_grant_request({"secret": "***"}, "scope")
    validate_url(request.url)
예제 #7
0
def test_request_url(authority):
    tenant_id = "expected_tenant"
    parsed_authority = urlparse(authority)
    expected_netloc = parsed_authority.netloc or authority  # "localhost" parses to netloc "", path "localhost"

    def validate_url(url):
        actual = urlparse(url)
        assert actual.scheme == "https"
        assert actual.netloc == expected_netloc
        assert actual.path.startswith("/" + tenant_id)

    def mock_send(request, **kwargs):
        validate_url(request.url)
        return mock_response(json_payload={
            "token_type": "Bearer",
            "expires_in": 42,
            "access_token": "***"
        })

    client = AuthnClient(tenant=tenant_id,
                         transport=Mock(send=mock_send),
                         authority=authority)
    client.request_token(("scope", ))
    request = client.get_refresh_token_grant_request({"secret": "***"},
                                                     "scope")
    validate_url(request.url)

    # authority can be configured via environment variable
    with patch.dict("os.environ",
                    {EnvironmentVariables.AZURE_AUTHORITY_HOST: authority},
                    clear=True):
        client = AuthnClient(tenant=tenant_id, transport=Mock(send=mock_send))
        client.request_token(("scope", ))
        request = client.get_refresh_token_grant_request({"secret": "***"},
                                                         "scope")
        validate_url(request.url)
예제 #8
0
def test_caching_when_only_expires_in_set():
    """the cache should function when auth responses don't include an explicit expires_on"""

    access_token = "token"
    now = 42
    expires_in = 1800
    expires_on = now + expires_in
    expected_token = AccessToken(access_token, expires_on)

    mock_send = Mock(
        return_value=mock_response(
            json_payload={"access_token": access_token, "expires_in": expires_in, "token_type": "Bearer"}
        )
    )

    client = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send))
    with patch("azure.identity._authn_client.time.time") as mock_time:
        mock_time.return_value = 42
        token = client.request_token(["scope"])
        assert token.token == expected_token.token
        assert token.expires_on == expected_token.expires_on

        cached_token = client.get_cached_token(["scope"])
        assert cached_token == expected_token
def test_expires_in_strings():
    expected_token = "token"

    mock_response = Mock(headers={"content-type": "application/json"}, status_code=200, content_type="application/json")
    mock_send = Mock(return_value=mock_response)

    mock_response.text = lambda: json.dumps(
        {"access_token": expected_token, "expires_in": "42", "ext_expires_in": "42", "token_type": "Bearer"}
    )

    now = int(time.time())
    with patch("azure.identity._authn_client.time.time") as mock_time:
        mock_time.return_value = now
        token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token("scope")
    assert token.token == expected_token
    assert token.expires_on == now + 42
예제 #10
0
def test_deserialization_app_service_msi():
    now = 6
    expires_in = 59 - now
    expires_on = now + expires_in
    access_token = "***"
    expected_access_token = AccessToken(access_token, expires_on)
    scope = "scope"

    # response with expires_on only and it's a datetime string (App Service MSI, Linux)
    token_payload = {
        "access_token": access_token,
        "expires_on": "01/01/1970 00:00:{} +00:00".format(now + expires_in),
        "token_type": "Bearer",
        "resource": scope,
    }
    mock_send = Mock(return_value=mock_response(json_payload=token_payload))
    token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token
예제 #11
0
def test_expires_in_strings():
    expected_token = "token"

    mock_send = Mock(return_value=mock_response(
        json_payload={
            "access_token": expected_token,
            "expires_in": "42",
            "ext_expires_in": "42",
            "token_type": "Bearer",
        }))

    now = int(time.time())
    with patch("azure.identity._authn_client.time.time") as mock_time:
        mock_time.return_value = now
        token = AuthnClient(
            endpoint="http://foo",
            transport=Mock(send=mock_send)).request_token("scope")
    assert token.token == expected_token
    assert token.expires_on == now + 42
def test_cache_expiry():
    access_token = "token"
    now = 42
    expires_in = 1800
    expires_on = now + expires_in
    expected_token = AccessToken(access_token, expires_on)
    token_payload = {
        "access_token": access_token,
        "expires_in": expires_in,
        "token_type": "Bearer"
    }
    mock_response = Mock(
        text=lambda: json.dumps(token_payload),
        headers={"content-type": "application/json"},
        status_code=200,
        content_type=["application/json"],
    )
    mock_send = Mock(return_value=mock_response)

    client = AuthnClient("http://foo", transport=Mock(send=mock_send))
    with patch("azure.identity._authn_client.time.time") as mock_time:
        # populate the cache with a valid token
        mock_time.return_value = now
        token = client.request_token("scope")
        assert token.token == expected_token.token
        assert token.expires_on == expected_token.expires_on

        cached_token = client.get_cached_token("scope")
        assert cached_token == expected_token

        # advance time past the cached token's expires_on
        mock_time.return_value = expires_on + 3600
        cached_token = client.get_cached_token("scope")
        assert not cached_token

        # request a new token
        new_token = "new token"
        token_payload["access_token"] = new_token
        token = client.request_token("scope")
        assert token.token == new_token

        # it should be cached
        cached_token = client.get_cached_token("scope")
        assert cached_token.token == new_token
예제 #13
0
def test_deserialization_expires_strings():
    now = 6
    expires_in = 59 - now
    expires_on = now + expires_in
    access_token = "***"
    expected_access_token = AccessToken(access_token, expires_on)
    scope = "scope"

    # response with string expires_in and expires_on (IMDS, Cloud Shell)
    token_payload = {
        "access_token": access_token,
        "expires_in": str(expires_in),
        "ext_expires_in": str(expires_in),
        "expires_on": str(expires_on),
        "token_type": "Bearer",
        "resource": scope,
    }
    mock_send = Mock(return_value=mock_response(json_payload=token_payload))
    token = AuthnClient(endpoint="http://foo", transport=Mock(send=mock_send)).request_token(scope)
    assert token == expected_access_token
예제 #14
0
def test_should_refresh():
    client = AuthnClient(endpoint="http://foo")
    now = int(time.time())

    # do not need refresh
    token = AccessToken("token", now + DEFAULT_REFRESH_OFFSET + 1)
    should_refresh = client.should_refresh(token)
    assert not should_refresh

    # need refresh
    token = AccessToken("token", now + DEFAULT_REFRESH_OFFSET - 1)
    should_refresh = client.should_refresh(token)
    assert should_refresh

    # not exceed cool down time, do not refresh
    token = AccessToken("token", now + DEFAULT_REFRESH_OFFSET - 1)
    client._last_refresh_time = now - DEFAULT_TOKEN_REFRESH_RETRY_DELAY + 1
    should_refresh = client.should_refresh(token)
    assert not should_refresh