import config import pickle import redis from dataclasses import dataclass from datetime import datetime from typing import Dict, Optional from marshmallow import EXCLUDE from utils.decorators import redis_exception_handler from external_api.base.clients.auth_strategies.Base import AuthStrategy from external_api.base.clients.client import ApiClient @dataclass class DelphiTokenHolder: access_token: str expires_at: int class Meta: unknown = EXCLUDE def is_expired(self) -> bool: expires_data = datetime.fromtimestamp(self.expires_at) return expires_data < datetime.now() @dataclass class DelphiCredentials: clientId: str clientSecret: str grantType: str = "client_credentials" class Meta: unknown = EXCLUDE class DelphiAuthStrategy(AuthStrategy): token_holder: Optional[DelphiTokenHolder] = None http_client: ApiClient credentials: DelphiCredentials redis = redis.Redis(host=config.REDIS_HOST) redis_token_key = "delphi_token" def __init__(self, client: ApiClient, credentials: DelphiCredentials) -> None: self.http_client = client self.credentials = credentials async def authorize(self, headers: Dict[str, str]) -> Dict[str, str]: token = await self.get_token() headers[self.AUTH_HEADER] = self.build_authorization_header(token) return headers async def get_token(self) -> str: self.token_holder = self.get_cached_token() if not self.token_holder or self.token_holder.is_expired(): self.token_holder = await self.refresh_token() return self.token_holder.access_token @redis_exception_handler async def refresh_token(self) -> DelphiTokenHolder: token = await self.load_delphi_token() self.redis.set(self.redis_token_key, pickle.dumps(token), config.DELPHI_TOKEN_CACHE_LIFESPAN) return token @redis_exception_handler def get_cached_token(self) -> Optional[DelphiTokenHolder]: token = None cached_token = self.redis.get(self.redis_token_key) if cached_token is not None: token = pickle.loads(cached_token) return token async def load_delphi_token(self) -> DelphiTokenHolder: params = { "client_id": self.credentials.clientId, "client_secret": self.credentials.clientSecret, "grant_type": self.credentials.grantType, } return await self.http_client.post("/oauth/token", payload=params, response_type=DelphiTokenHolder)