Skip to content

Commit

Permalink
dont clear cache on renewal
Browse files Browse the repository at this point in the history
  • Loading branch information
aaschaer committed May 7, 2024
1 parent ebc3cb2 commit b11e7e6
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 27 deletions.
12 changes: 2 additions & 10 deletions src/globus_sdk/experimental/globus_app/authorizer_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ def _get_tokens_or_error(self, resource_server: str) -> dict[str, t.Any]:
def store(self, token_res: OAuthTokenResponse) -> None:
"""
Store tokens in the underlying ``ValidatingStorageAdapter`` and clear cache.
This is called automatically by ``AuthorizerFactory`` subclasses that return
a ``RenewingAuthorizer``.
:param token_res: An ``OAuthTokenResponse`` containing tokens to be stored
in the underlying ``ValidatingStorageAdapter``.
Expand Down Expand Up @@ -84,12 +82,6 @@ def _make_authorizer(self, resource_server: str) -> GlobusAuthorizer:
class AccessTokenAuthorizerFactory(AuthorizerFactory):
"""
An ``AuthorizerFactory`` that constructs ``AccessTokenAuthorizer``.
Note that since ``AccessTokenAuthorizer`` is static and does not provide an
``on_refresh`` interface an ``AccessTokenAuthorizerFactory`` will not
have its cache cleared automatically. If external logic retrieves new tokens,
it should call ``AccessTokenAuthorizerFactory.on_refresh`` with the token
response.
"""

def _make_authorizer(self, resource_server: str) -> AccessTokenAuthorizer:
Expand Down Expand Up @@ -148,7 +140,7 @@ def _make_authorizer(self, resource_server: str) -> RefreshTokenAuthorizer:
auth_client=self.auth_login_client,
access_token=tokens.get("access_token"),
expires_at=tokens.get("expires_at_seconds"),
on_refresh=self.store,
on_refresh=self.storage_adapter.store,
)


Expand Down Expand Up @@ -203,5 +195,5 @@ def _make_authorizer(
scopes=scopes,
access_token=access_token,
expires_at=expires_at,
on_refresh=self.store,
on_refresh=self.storage_adapter.store,
)
75 changes: 58 additions & 17 deletions tests/unit/experimental/globus_app/test_authorizer_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,44 @@ def test_refresh_token_authorizer_factory():
)
mock_storage_adapter = MockValidatingStorageAdapter()
mock_storage_adapter.store(initial_data)

refresh_data = make_mock_token_response(
{
"foo": {
"refresh_token": "foo_refresh_token",
"access_token": "foo_access_token_2",
"expires_at_seconds": int(time.time()) + 30,
}
}
)
mock_auth_login_client = mock.Mock()
mock_refresh = mock.Mock()
mock_refresh.return_value = refresh_data
mock_auth_login_client.oauth2_refresh_token = mock_refresh

factory = RefreshTokenAuthorizerFactory(
validating_storage_adaptor=mock_storage_adapter,
auth_login_client=mock_auth_login_client,
)

# calling get authorizer creates a new authorizer with existing token data
authorizer = factory.get_authorizer("foo")
assert authorizer.access_token == "foo_access_token"
authorizer1 = factory.get_authorizer("foo")
assert authorizer1.access_token == "foo_access_token"
assert mock_auth_login_client.oauth2_refresh_token.call_count == 0

# standard refresh doesn't change the authorizer
authorizer1._get_new_access_token()
authorizer2 = factory.get_authorizer("foo")
assert authorizer2 is authorizer1
assert authorizer2.access_token == "foo_access_token_2"
assert mock_auth_login_client.oauth2_refresh_token.call_count == 1

# manually calling store clears cache
factory.store(initial_data)
authorizer3 = factory.get_authorizer("foo")
assert authorizer3 is not authorizer1
assert authorizer3.access_token == "foo_access_token"


def test_refresh_token_authorizer_factory_no_access_token():
initial_data = make_mock_token_response(
Expand Down Expand Up @@ -126,18 +152,12 @@ def test_refresh_token_authorizer_factory_no_access_token():
auth_login_client=mock_auth_login_client,
)

# calling get_authorizer once creates a new authorizer automatically making a
# refresh call to get an access token that is then stored
# calling get_authorizer automatically causes a refresh call to get an access token
authorizer = factory.get_authorizer("foo")
assert authorizer.access_token == "foo_access_token"
assert mock_refresh.call_count == 1
assert mock_storage_adapter.token_data["foo"]["access_token"] == "foo_access_token"

# calling get_authorizer again gets the same cached authorizer
authorizer2 = factory.get_authorizer("foo")
assert authorizer is authorizer2
assert mock_refresh.call_count == 1


def test_refresh_token_authorizer_factory_no_refresh_token():
initial_data = make_mock_token_response(
Expand Down Expand Up @@ -167,18 +187,45 @@ def test_client_credentials_authorizer_factory():
)
mock_storage_adapter = MockValidatingStorageAdapter()
mock_storage_adapter.store(initial_data)

client_token_data = make_mock_token_response(
{
"foo": {
"access_token": "foo_access_token_2",
"expires_at_seconds": int(time.time()) + 30,
}
}
)
mock_confidential_client = mock.Mock()
mock_client_credentials_tokens = mock.Mock()
mock_client_credentials_tokens.return_value = client_token_data
mock_confidential_client.oauth2_client_credentials_tokens = (
mock_client_credentials_tokens
)

factory = ClientCredentialsAuthorizerFactory(
validating_storage_adaptor=mock_storage_adapter,
confidential_client=mock_confidential_client,
)

# calling get_authorizer once creates a new authorizer using existing tokens
authorizer = factory.get_authorizer("foo")
assert authorizer.access_token == "foo_access_token"
authorizer1 = factory.get_authorizer("foo")
assert authorizer1.access_token == "foo_access_token"
assert mock_confidential_client.oauth2_client_credentials_tokens.call_count == 0

# renewing with existing tokens doesn't change the authorizer
authorizer1._get_new_access_token()
authorizer2 = factory.get_authorizer("foo")
assert authorizer2 is authorizer1
assert authorizer2.access_token == "foo_access_token_2"
assert mock_confidential_client.oauth2_client_credentials_tokens.call_count == 1

# manually calling store clears cache
factory.store(initial_data)
authorizer3 = factory.get_authorizer("foo")
assert authorizer3 is not authorizer1
assert authorizer3.access_token == "foo_access_token"


def test_client_credentials_authorizer_factory_no_tokens():
mock_storage_adapter = MockValidatingStorageAdapter()
Expand Down Expand Up @@ -208,12 +255,6 @@ def test_client_credentials_authorizer_factory_no_tokens():
authorizer = factory.get_authorizer("foo")
assert authorizer.access_token == "foo_access_token"
assert mock_client_credentials_tokens.call_count == 1
assert mock_storage_adapter.token_data["foo"]["access_token"] == "foo_access_token"

# calling get_authorizer again gets the same cached authorizer
authorizer2 = factory.get_authorizer("foo")
assert authorizer is authorizer2
assert mock_client_credentials_tokens.call_count == 1


def test_client_credentials_authorizer_factory_no_scopes():
Expand Down

0 comments on commit b11e7e6

Please sign in to comment.