""" Security service for token management operations. Provides centralized access to token refresh and management functionality. """ import logging from typing import Optional, Callable from modules.datamodels.datamodelSecurity import Token from modules.security.tokenManager import TokenManager logger = logging.getLogger(__name__) class SecurityService: """Security service providing token management operations.""" def __init__(self, services): """Initialize security service with service center access. Args: services: Service center instance providing access to interfaces """ self.services = services self._tokenManager = TokenManager() def getFreshToken(self, connectionId: str, secondsBeforeExpiry: int = 30 * 60) -> Optional[Token]: """Get a fresh token for a connection, refreshing when expiring soon. Reads the latest stored token via interface layer, then uses ensureFreshToken to refresh if needed and persists the refreshed token via interface layer. Args: connectionId: ID of the connection to get token for secondsBeforeExpiry: Threshold window to proactively refresh (default: 30 minutes) Returns: Token object or None if not found/expired """ try: # Use interface from services instead of getRootInterface() interfaceDbApp = self.services.interfaceDbApp token = interfaceDbApp.getConnectionToken(connectionId) if not token: return None return self._tokenManager.ensureFreshToken( token, secondsBeforeExpiry=secondsBeforeExpiry, saveCallback=lambda t: interfaceDbApp.saveConnectionToken(t) ) except Exception as e: logger.error(f"getFreshToken: Error fetching or refreshing token for connection {connectionId}: {e}") return None def refreshToken(self, oldToken: Token) -> Optional[Token]: """Refresh an expired token using the appropriate OAuth service. Args: oldToken: Token object to refresh Returns: Refreshed Token object or None if refresh failed """ try: return self._tokenManager.refreshToken(oldToken) except Exception as e: logger.error(f"refreshToken: Error refreshing token: {e}") return None def ensureFreshToken(self, token: Token, *, secondsBeforeExpiry: int = 30 * 60, saveCallback: Optional[Callable[[Token], None]] = None) -> Optional[Token]: """Ensure a token is fresh; refresh if expiring within threshold. Args: token: Existing token to validate/refresh secondsBeforeExpiry: Threshold window to proactively refresh (default: 30 minutes) saveCallback: Optional function to persist a refreshed token Returns: A fresh token (refreshed or original) or None if refresh failed """ try: return self._tokenManager.ensureFreshToken( token, secondsBeforeExpiry=secondsBeforeExpiry, saveCallback=saveCallback ) except Exception as e: logger.error(f"ensureFreshToken: Error ensuring fresh token: {e}") return None def refreshMicrosoftToken(self, refreshToken: str, userId: str, oldToken: Token) -> Optional[Token]: """Refresh Microsoft OAuth token using refresh token. Args: refreshToken: Microsoft refresh token userId: User ID owning the token oldToken: Previous token object to preserve connection ID Returns: New Token object or None if refresh failed """ try: return self._tokenManager.refreshMicrosoftToken(refreshToken, userId, oldToken) except Exception as e: logger.error(f"refreshMicrosoftToken: Error refreshing Microsoft token: {e}") return None def refreshGoogleToken(self, refreshToken: str, userId: str, oldToken: Token) -> Optional[Token]: """Refresh Google OAuth token using refresh token. Args: refreshToken: Google refresh token userId: User ID owning the token oldToken: Previous token object to preserve connection ID Returns: New Token object or None if refresh failed """ try: return self._tokenManager.refreshGoogleToken(refreshToken, userId, oldToken) except Exception as e: logger.error(f"refreshGoogleToken: Error refreshing Google token: {e}") return None