async def _retrieve_subscription_from_cache(cls, owner_id): r = await utils.get_aredis_for_cache() encrypted_sub = await r.get("subscription-cache-owner-%s" % owner_id) if encrypted_sub: return cls.from_dict( owner_id, json.loads(crypto.decrypt(encrypted_sub).decode()) )
def test_key_rotation( cleanup_secrets: None, monkeypatch: pytest.MonkeyPatch, ) -> None: x = "this is an amazing string, right? 🙄".encode() monkeypatch.setattr(config, "CACHE_TOKEN_SECRET", "old password") importlib.reload(crypto) # regen digest with new secret encryped_old = crypto.encrypt(x) monkeypatch.setattr(config, "CACHE_TOKEN_SECRET", "new password") monkeypatch.setattr(config, "CACHE_TOKEN_SECRET_OLD", "old password") importlib.reload(crypto) # regen digest with new secrets encryped_new = crypto.encrypt(x) assert encryped_new != encryped_old assert x == crypto.decrypt(encryped_new) assert x == crypto.decrypt(encryped_old)
async def _retrieve_subscription_from_cache( cls: typing.Type[SubscriptionT], redis: utils.RedisCache, owner_id: int) -> typing.Optional[SubscriptionT]: async with await redis.pipeline() as pipe: await pipe.get(cls._cache_key(owner_id)) await pipe.ttl(cls._cache_key(owner_id)) encrypted_sub, ttl = typing.cast(typing.Tuple[str, int], await pipe.execute()) if encrypted_sub: return cls.from_dict( redis, owner_id, json.loads(crypto.decrypt(encrypted_sub.encode()).decode()), ttl, ) return None
async def _retrieve_from_cache( cls, redis: utils.RedisCache, owner_id: int) -> typing.Optional["UserTokens"]: async with await redis.pipeline() as pipe: await pipe.get(cls._cache_key(owner_id)) await pipe.ttl(cls._cache_key(owner_id)) encrypted_tokens, ttl = typing.cast(typing.Tuple[str, int], await pipe.execute()) if encrypted_tokens: return cls( redis, owner_id, json.loads(crypto.decrypt( encrypted_tokens.encode()).decode())["tokens"], ttl, ) return None
async def update( cls, redis: redis_utils.RedisCache, api_access_key: str, data: ApplicationDashboardJSON, ) -> None: if data["github_account"] is None: account_scope = None else: account_scope = data["github_account"]["login"] encrypted_application = await redis.get( cls._cache_key(api_access_key, account_scope)) if encrypted_application is not None: decrypted_application = typing.cast( CachedApplication, json.loads( crypto.decrypt(encrypted_application.encode()).decode()), ) if "account_scope" not in decrypted_application: # TODO(sileht): Backward compat, delete me return None if data["github_account"] is None: full_account_scope = None else: full_account_scope = ApplicationAccountScope({ "id": data["github_account"]["id"], "login": data["github_account"]["login"], }) app = cls( redis, data["id"], data["name"], decrypted_application["api_access_key"], decrypted_application["api_secret_key"], full_account_scope, ) await app.save_to_cache() return None
async def _retrieve_from_cache( cls, redis: redis_utils.RedisCache, api_access_key: str, api_secret_key: str, account_scope: typing.Optional[github_types.GitHubLogin], ) -> typing.Optional["ApplicationSaas"]: async with await redis.pipeline() as pipe: await pipe.get(cls._cache_key(api_access_key, account_scope)) await pipe.ttl(cls._cache_key(api_access_key, account_scope)) encrypted_application, ttl = typing.cast(typing.Tuple[str, int], await pipe.execute()) if encrypted_application: decrypted_application = typing.cast( CachedApplication, json.loads( crypto.decrypt(encrypted_application.encode()).decode()), ) if decrypted_application["api_secret_key"] != api_secret_key: # Don't raise ApplicationUserNotFound yet, check the database first return None if "account_scope" not in decrypted_application: # TODO(sileht): Backward compat, delete me return None if "id" not in decrypted_application: # TODO(sileht): Backward compat, delete me return None return cls( redis, decrypted_application["id"], decrypted_application["name"], decrypted_application["api_access_key"], decrypted_application["api_secret_key"], decrypted_application["account_scope"], ttl, ) return None
async def _retrieve_from_cache( cls, redis: utils.RedisCache, owner_id: int) -> typing.Optional["UserTokensSaas"]: async with await redis.pipeline() as pipe: await pipe.get(cls._cache_key(owner_id)) await pipe.ttl(cls._cache_key(owner_id)) encrypted_tokens, ttl = typing.cast(typing.Tuple[str, int], await pipe.execute()) if encrypted_tokens: decrypted_tokens = json.loads( crypto.decrypt(encrypted_tokens.encode()).decode()) if "tokens" in decrypted_tokens: # Old cache format, just drop it return None if (decrypted_tokens["user_tokens"] and "id" not in decrypted_tokens["user_tokens"][0]): # Old cache format, just drop it return None return cls(redis, owner_id, decrypted_tokens["user_tokens"], ttl) return None
def test_encrypt() -> None: x = "this is an amazing string, right? 🙄".encode() assert x == crypto.decrypt(crypto.encrypt(x)) assert x == crypto.decrypt(crypto.encrypt(x)) assert x == crypto.decrypt( crypto.encrypt(crypto.decrypt(crypto.encrypt(x))))