1 class YadisServiceManager(object):
2 """Holds the state of a list of selected Yadis services, managing
3 storing it in a session and iterating over the services in order."""
5 def __init__(self
, starting_url
, yadis_url
, services
, session_key
):
6 # The URL that was used to initiate the Yadis protocol
7 self
.starting_url
= starting_url
9 # The URL after following redirects (the identifier)
10 self
.yadis_url
= yadis_url
12 # List of service elements
13 self
.services
= list(services
)
15 self
.session_key
= session_key
17 # Reference to the current service object
21 """How many untried services remain?"""
22 return len(self
.services
)
28 """Return the next service
30 self.current() will continue to return that service until the
31 next call to this method."""
33 self
._current
= self
.services
.pop(0)
40 """Return the current service.
42 Returns None if there are no services left.
46 def forURL(self
, url
):
47 return url
in [self
.starting_url
, self
.yadis_url
]
50 """Has the first service been returned?"""
51 return self
._current
is not None
53 def store(self
, session
):
54 """Store this object in the session, by its session key."""
55 session
[self
.session_key
] = self
57 class Discovery(object):
58 """State management for discovery.
60 High-level usage pattern is to call .getNextService(discover) in
61 order to find the next available service for this user for this
62 session. Once a request completes, call .finish() to clean up the
65 @ivar session: a dict-like object that stores state unique to the
66 requesting user-agent. This object must be able to store
69 @ivar url: the URL that is used to make the discovery request
71 @ivar session_key_suffix: The suffix that will be used to identify
72 this object in the session object.
75 DEFAULT_SUFFIX
= 'auth'
76 PREFIX
= '_yadis_services_'
78 def __init__(self
, session
, url
, session_key_suffix
=None):
79 """Initialize a discovery object"""
80 self
.session
= session
82 if session_key_suffix
is None:
83 session_key_suffix
= self
.DEFAULT_SUFFIX
85 self
.session_key_suffix
= session_key_suffix
87 def getNextService(self
, discover
):
88 """Return the next authentication service for the pair of
89 user_input and session. This function handles fallback.
92 @param discover: a callable that takes a URL and returns a
95 @type discover: str -> [service]
98 @return: the next available service
100 manager
= self
.getManager()
101 if manager
is not None and not manager
:
102 self
.destroyManager()
105 yadis_url
, services
= discover(self
.url
)
106 manager
= self
.createManager(services
, yadis_url
)
109 service
= manager
.next()
110 manager
.store(self
.session
)
116 def cleanup(self
, force
=False):
117 """Clean up Yadis-related services in the session and return
118 the most-recently-attempted service from the manager, if one
121 @param force: True if the manager should be deleted regardless
122 of whether it's a manager for self.url.
124 @return: current service endpoint object or None if there is
127 manager
= self
.getManager(force
=force
)
128 if manager
is not None:
129 service
= manager
.current()
130 self
.destroyManager(force
=force
)
136 ### Lower-level methods
138 def getSessionKey(self
):
139 """Get the session key for this starting URL and suffix
141 @return: The session key
144 return self
.PREFIX
+ self
.session_key_suffix
146 def getManager(self
, force
=False):
147 """Extract the YadisServiceManager for this object's URL and
148 suffix from the session.
150 @param force: True if the manager should be returned
151 regardless of whether it's a manager for self.url.
153 @return: The current YadisServiceManager, if it's for this
156 manager
= self
.session
.get(self
.getSessionKey())
157 if (manager
is not None and (manager
.forURL(self
.url
) or force
)):
162 def createManager(self
, services
, yadis_url
=None):
163 """Create a new YadisService Manager for this starting URL and
164 suffix, and store it in the session.
166 @raises KeyError: When I already have a manager.
168 @return: A new YadisServiceManager or None
170 key
= self
.getSessionKey()
171 if self
.getManager():
172 raise KeyError('There is already a %r manager for %r' %
178 manager
= YadisServiceManager(self
.url
, yadis_url
, services
, key
)
179 manager
.store(self
.session
)
182 def destroyManager(self
, force
=False):
183 """Delete any YadisServiceManager with this starting URL and
184 suffix from the session.
186 If there is no service manager or the service manager is for a
187 different URL, it silently does nothing.
189 @param force: True if the manager should be deleted regardless
190 of whether it's a manager for self.url.
192 if self
.getManager(force
=force
) is not None:
193 key
= self
.getSessionKey()
194 del self
.session
[key
]