Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
token_cache.py89 linesDownload Raw Back to msal_extensions
1"""Generic functions and types for working with a TokenCache that is not platform specific."""2import os3import time4import logging5 6import msal7 8from .cache_lock import CrossPlatLock9from .persistence import _mkdir_p, PersistenceNotFound10 11 12logger = logging.getLogger(__name__)13 14class PersistedTokenCache(msal.SerializableTokenCache):15    """A token cache backed by a persistence layer, coordinated by a file lock,16    to sustain a certain level of multi-process concurrency for a desktop app.17 18    The scenario is that multiple instances of same desktop app19    (or even multiple different apps)20    create their own ``PersistedTokenCache`` instances,21    which are all backed by the same token cache file on disk22    (known as a persistence). The goal is to have Single Sign On (SSO).23 24    Each instance of ``PersistedTokenCache`` holds a snapshot of the token cache25    in memory.26    Each :func:`~find` call will27    automatically reload token cache from the persistence when necessary,28    so that it will have fresh data.29    Each :func:`~modify` call will30    automatically reload token cache from the persistence when necessary,31    so that new writes will be appended on top of latest token cache data,32    and then the new data will be immediately flushed back to the persistence.33 34    Note: :func:`~deserialize` and :func:`~serialize` remain the same35    as their counterparts in the parent class ``msal.SerializableTokenCache``.36    In other words, they do not have the "reload from persistence if necessary"37    nor the "flush back to persistence" behavior.38    """39 40    def __init__(self, persistence, lock_location=None):41        super(PersistedTokenCache, self).__init__()42        self._lock_location = (43            os.path.expanduser(lock_location) if lock_location44            else persistence.get_location() + ".lockfile")45        _mkdir_p(os.path.dirname(self._lock_location))46        self._persistence = persistence47        self._last_sync = 0  # _last_sync is a Unixtime48        self.is_encrypted = persistence.is_encrypted49 50    def _reload_if_necessary(self):51        # type: () -> None52        """Reload cache from persistence layer, if necessary"""53        try:54            if self._last_sync < self._persistence.time_last_modified():55                self.deserialize(self._persistence.load())56                self._last_sync = time.time()57        except PersistenceNotFound:58            # From cache's perspective, a nonexistent persistence is a NO-OP.59            pass60        # However, existing data unable to be decrypted will still be bubbled up.61 62    def modify(self, credential_type, old_entry, new_key_value_pairs=None):63        with CrossPlatLock(self._lock_location):64            self._reload_if_necessary()65            super(PersistedTokenCache, self).modify(66                credential_type,67                old_entry,68                new_key_value_pairs=new_key_value_pairs)69            self._persistence.save(self.serialize())70            self._last_sync = time.time()71 72    def find(self, credential_type, **kwargs):  # pylint: disable=arguments-differ73        # Use optimistic locking rather than CrossPlatLock(self._lock_location)74        retry = 375        for attempt in range(1, retry + 1):76            try:77                self._reload_if_necessary()78            except Exception:  # pylint: disable=broad-except79                # Presumably other processes are writing the file, causing dirty read80                if attempt < retry:81                    logger.debug("Unable to load token cache file in No. %d attempt", attempt)82                    time.sleep(0.5)83                else:84                    raise  # End of retry. Re-raise the exception as-is.85            else:  # If reload encountered no error, the data is considered intact86                return super(PersistedTokenCache, self).find(credential_type, **kwargs)87        return []  # Not really reachable here. Just to keep pylint happy.88 89 
codekingpro/portable-devtools · Team Ai