123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- class YadisServiceManager(object):
- """Holds the state of a list of selected Yadis services, managing
- storing it in a session and iterating over the services in order."""
- def __init__(self, starting_url, yadis_url, services, session_key):
- # The URL that was used to initiate the Yadis protocol
- self.starting_url = starting_url
- # The URL after following redirects (the identifier)
- self.yadis_url = yadis_url
- # List of service elements
- self.services = list(services)
- self.session_key = session_key
- # Reference to the current service object
- self._current = None
- def __len__(self):
- """How many untried services remain?"""
- return len(self.services)
- def __iter__(self):
- return self
- def next(self):
- """Return the next service
- self.current() will continue to return that service until the
- next call to this method."""
- try:
- self._current = self.services.pop(0)
- except IndexError:
- raise StopIteration
- else:
- return self._current
- def current(self):
- """Return the current service.
- Returns None if there are no services left.
- """
- return self._current
- def forURL(self, url):
- return url in [self.starting_url, self.yadis_url]
- def started(self):
- """Has the first service been returned?"""
- return self._current is not None
- def store(self, session):
- """Store this object in the session, by its session key."""
- session[self.session_key] = self
- class Discovery(object):
- """State management for discovery.
- High-level usage pattern is to call .getNextService(discover) in
- order to find the next available service for this user for this
- session. Once a request completes, call .finish() to clean up the
- session state.
- @ivar session: a dict-like object that stores state unique to the
- requesting user-agent. This object must be able to store
- serializable objects.
- @ivar url: the URL that is used to make the discovery request
- @ivar session_key_suffix: The suffix that will be used to identify
- this object in the session object.
- """
- DEFAULT_SUFFIX = 'auth'
- PREFIX = '_yadis_services_'
- def __init__(self, session, url, session_key_suffix=None):
- """Initialize a discovery object"""
- self.session = session
- self.url = url
- if session_key_suffix is None:
- session_key_suffix = self.DEFAULT_SUFFIX
- self.session_key_suffix = session_key_suffix
- def getNextService(self, discover):
- """Return the next authentication service for the pair of
- user_input and session. This function handles fallback.
- @param discover: a callable that takes a URL and returns a
- list of services
- @type discover: str -> [service]
- @return: the next available service
- """
- manager = self.getManager()
- if manager is not None and not manager:
- self.destroyManager()
- if not manager:
- yadis_url, services = discover(self.url)
- manager = self.createManager(services, yadis_url)
- if manager:
- service = manager.next()
- manager.store(self.session)
- else:
- service = None
- return service
- def cleanup(self, force=False):
- """Clean up Yadis-related services in the session and return
- the most-recently-attempted service from the manager, if one
- exists.
- @param force: True if the manager should be deleted regardless
- of whether it's a manager for self.url.
- @return: current service endpoint object or None if there is
- no current service
- """
- manager = self.getManager(force=force)
- if manager is not None:
- service = manager.current()
- self.destroyManager(force=force)
- else:
- service = None
- return service
- ### Lower-level methods
- def getSessionKey(self):
- """Get the session key for this starting URL and suffix
- @return: The session key
- @rtype: str
- """
- return self.PREFIX + self.session_key_suffix
- def getManager(self, force=False):
- """Extract the YadisServiceManager for this object's URL and
- suffix from the session.
- @param force: True if the manager should be returned
- regardless of whether it's a manager for self.url.
- @return: The current YadisServiceManager, if it's for this
- URL, or else None
- """
- manager = self.session.get(self.getSessionKey())
- if (manager is not None and (manager.forURL(self.url) or force)):
- return manager
- else:
- return None
- def createManager(self, services, yadis_url=None):
- """Create a new YadisService Manager for this starting URL and
- suffix, and store it in the session.
- @raises KeyError: When I already have a manager.
- @return: A new YadisServiceManager or None
- """
- key = self.getSessionKey()
- if self.getManager():
- raise KeyError('There is already a %r manager for %r' %
- (key, self.url))
- if not services:
- return None
- manager = YadisServiceManager(self.url, yadis_url, services, key)
- manager.store(self.session)
- return manager
- def destroyManager(self, force=False):
- """Delete any YadisServiceManager with this starting URL and
- suffix from the session.
- If there is no service manager or the service manager is for a
- different URL, it silently does nothing.
- @param force: True if the manager should be deleted regardless
- of whether it's a manager for self.url.
- """
- if self.getManager(force=force) is not None:
- key = self.getSessionKey()
- del self.session[key]
|