diff --git a/coredis/recipes/credentials/iam_provider.py b/coredis/recipes/credentials/iam_provider.py index ca640ecf..52489b62 100644 --- a/coredis/recipes/credentials/iam_provider.py +++ b/coredis/recipes/credentials/iam_provider.py @@ -2,11 +2,12 @@ from urllib.parse import ParseResult, urlencode, urlunparse -# botocore and cachetools will need to be installed in addition -# to coredis' dependencies -import botocore.session +# aiobotocore, botocore and cachetools will need to be installed in addition +# to coredis dependencies. These can also be requested by installing coredis +# as coredis[recipes] +import aiobotocore.session +from aiobotocore.signers import AioRequestSigner from botocore.model import ServiceId -from botocore.signers import RequestSigner from cachetools import TTLCache, cached from coredis.credentials import AbstractCredentialProvider, UserPass @@ -23,15 +24,7 @@ def __init__(self, user: str, cluster_name: str, region: str = "us-east-1") -> N self.cluster_name: str = cluster_name self.region: str = region - session = botocore.session.get_session() - self.request_signer = RequestSigner( - ServiceId("elasticache"), - self.region, - "elasticache", - "v4", - session.get_credentials(), - session.get_component("event_emitter"), - ) + self.session = aiobotocore.session.get_session() @cached(cache=TTLCache(maxsize=128, ttl=900)) # type: ignore[misc] async def get_credentials(self) -> UserPass: @@ -40,6 +33,14 @@ async def get_credentials(self) -> UserPass: IAM enabled Elasticache instance. The token will be cached for its lifetime (15 minutes) to avoid unnecessary requests. """ + request_signer = AioRequestSigner( + ServiceId("elasticache"), + self.region, + "elasticache", + "v4", + await self.session.get_credentials(), + self.session.get_component("event_emitter"), + ) query_params = {"Action": "connect", "User": self.user} url = urlunparse( ParseResult( @@ -51,12 +52,11 @@ async def get_credentials(self) -> UserPass: fragment="", ) ) - signed_url = self.request_signer.generate_presigned_url( + signed_url = await request_signer.generate_presigned_url( {"method": "GET", "url": url, "body": {}, "headers": {}, "context": {}}, operation_name="connect", expires_in=900, region_name=self.region, ) - # Need to strip the protocol so that Elasticache accepts it return UserPass(self.user, signed_url.removeprefix("https://")) diff --git a/docs/source/recipes/credentials.rst b/docs/source/recipes/credentials.rst index a9fb2e30..e9bbb216 100644 --- a/docs/source/recipes/credentials.rst +++ b/docs/source/recipes/credentials.rst @@ -9,7 +9,7 @@ The implementation is based on `the Elasticache IAM provider described in redis The :class:`~coredis.recipes.credentials.ElastiCacheIAMProvider` implements the :class:`~coredis.credentials.AbstractCredentialProvider` interface. -It uses :pypi:`botocore` to generate a short-lived authentication token +It uses :pypi:`aiobotocore` to generate a short-lived authentication token which can be used to authenticate with an IAM enabled Elasticache cluster. The token is cached for its lifetime of 15 minutes to reduce the number of unnecessary requests. diff --git a/requirements/recipes.txt b/requirements/recipes.txt index b0e7e24d..be2c4e89 100644 --- a/requirements/recipes.txt +++ b/requirements/recipes.txt @@ -1,3 +1,3 @@ -botocore +aiobotocore cachetools