Fix #1043: Add a prefix to the generated key in RedisStrategy

This commit is contained in:
François Voron
2022-07-23 07:53:57 +00:00
committed by GitHub
parent 184d3ed101
commit d7a1e3420c
3 changed files with 20 additions and 10 deletions

View File

@@ -26,6 +26,7 @@ As you can see, instantiation is quite simple. It accepts the following argument
* `redis` (`redis.asyncio.Redis`): An instance of `redis.asyncio.Redis`. Note that the `decode_responses` flag set to `True` is necessary.
* `lifetime_seconds` (`Optional[int]`): The lifetime of the token in seconds. Defaults to `None`, which means the token doesn't expire.
* `key_prefix` (`str`): The prefix used to set the key in the Redis stored. Defaults to `fastapi_users_token:`.
!!! tip "Why it's inside a function?"
To allow strategies to be instantiated dynamically with other dependencies, they have to be provided as a callable to the authentication backend.

View File

@@ -10,10 +10,15 @@ from fastapi_users.manager import BaseUserManager
class RedisStrategy(Strategy[models.UP, models.ID], Generic[models.UP, models.ID]):
def __init__(
self, redis: redis.asyncio.Redis, lifetime_seconds: Optional[int] = None
self,
redis: redis.asyncio.Redis,
lifetime_seconds: Optional[int] = None,
*,
key_prefix: str = "fastapi_users_token:",
):
self.redis = redis
self.lifetime_seconds = lifetime_seconds
self.key_prefix = key_prefix
async def read_token(
self, token: Optional[str], user_manager: BaseUserManager[models.UP, models.ID]
@@ -21,7 +26,7 @@ class RedisStrategy(Strategy[models.UP, models.ID], Generic[models.UP, models.ID
if token is None:
return None
user_id = await self.redis.get(token)
user_id = await self.redis.get(f"{self.key_prefix}{token}")
if user_id is None:
return None
@@ -33,8 +38,10 @@ class RedisStrategy(Strategy[models.UP, models.ID], Generic[models.UP, models.ID
async def write_token(self, user: models.UP) -> str:
token = secrets.token_urlsafe()
await self.redis.set(token, str(user.id), ex=self.lifetime_seconds)
await self.redis.set(
f"{self.key_prefix}{token}", str(user.id), ex=self.lifetime_seconds
)
return token
async def destroy_token(self, token: str, user: models.UP) -> None:
await self.redis.delete(token)
await self.redis.delete(f"{self.key_prefix}{token}")

View File

@@ -68,7 +68,7 @@ class TestReadToken:
redis: RedisMock,
user_manager,
):
await redis.set("TOKEN", "bar")
await redis.set(f"{redis_strategy.key_prefix}TOKEN", "bar")
authenticated_user = await redis_strategy.read_token("TOKEN", user_manager)
assert authenticated_user is None
@@ -79,7 +79,9 @@ class TestReadToken:
redis: RedisMock,
user_manager,
):
await redis.set("TOKEN", "d35d213e-f3d8-4f08-954a-7e0d1bea286f")
await redis.set(
f"{redis_strategy.key_prefix}TOKEN", "d35d213e-f3d8-4f08-954a-7e0d1bea286f"
)
authenticated_user = await redis_strategy.read_token("TOKEN", user_manager)
assert authenticated_user is None
@@ -91,7 +93,7 @@ class TestReadToken:
user_manager,
user,
):
await redis.set("TOKEN", str(user.id))
await redis.set(f"{redis_strategy.key_prefix}TOKEN", str(user.id))
authenticated_user = await redis_strategy.read_token("TOKEN", user_manager)
assert authenticated_user is not None
assert authenticated_user.id == user.id
@@ -104,7 +106,7 @@ async def test_write_token(
):
token = await redis_strategy.write_token(user)
value = await redis.get(token)
value = await redis.get(f"{redis_strategy.key_prefix}{token}")
assert value == str(user.id)
@@ -113,8 +115,8 @@ async def test_write_token(
async def test_destroy_token(
redis_strategy: RedisStrategy[UserModel, IDType], redis: RedisMock, user
):
await redis.set("TOKEN", str(user.id))
await redis.set(f"{redis_strategy.key_prefix}TOKEN", str(user.id))
await redis_strategy.destroy_token("TOKEN", user)
assert await redis.get("TOKEN") is None
assert await redis.get(f"{redis_strategy.key_prefix}TOKEN") is None