Claimed (cygnus)
Wed Sep 5 15:20:45 PDT 2007 http://j3h.us/
* Fix the extraction of return_to URLs for relying party verification
hunk ./openid/server/trustroot.py 343
-def extractReturnToURLs(rp_uri, xrds_text):
- """Given a relying party discovery URL and its corresponding XRDS
- document, return a list of return_to URLs.
-
- @param rp_uri: The discovery URL
- @param xrds_text: The xrds document, as a string
- @returns: A list of all relying party URLs that were found in the document.
- @rtype: [str]
- """
- assert isinstance(xrds_text, basestring), xrds_text
- return services.applyFilter(rp_uri, xrds_text, _extractReturnURL)
-
hunk ./openid/server/trustroot.py 368
-def verifyWithRelyingPartyURL(relying_party_url):
- """Verify that the return_to URL is listed by the relying party URL.
-
- Similar to verifyReturnTo, except it takes a relying party URL instead
- of a realm.
+def getAllowedReturnURLs(relying_party_url):
+ """Given a relying party discovery URL return a list of return_to URLs.
hunk ./openid/server/trustroot.py 372
- relying_party_url, extractReturnToURLs)
+ relying_party_url, _extractReturnURL)
hunk ./openid/server/trustroot.py 382
-def verifyReturnTo(realm_str, return_to, _vrfy=verifyWithRelyingPartyURL):
+def verifyReturnTo(realm_str, return_to, _vrfy=getAllowedReturnURLs):
hunk ./openid/test/test_rpverify.py 6
-from openid.yadis.etxrd import XRDSError
+from openid.yadis.discover import DiscoveryResult, DiscoveryFailure
+from openid.yadis import services
hunk ./openid/test/test_rpverify.py 42
+ def setUp(self):
+ self.original_discover = services.discover
+ services.discover = self.mockDiscover
+ self.data = None
+
+ def tearDown(self):
+ services.discover = self.original_discover
+
+ def mockDiscover(self, uri):
+ result = DiscoveryResult(uri)
+ result.response_text = self.data
+ result.normalized_uri = uri
+ return result
+
hunk ./openid/test/test_rpverify.py 61
- actual_return_urls = list(trustroot.extractReturnToURLs(
- self.disco_url, data))
+ self.data = data
+ actual_return_urls = list(trustroot.getAllowedReturnURLs(
+ self.disco_url))
+
hunk ./openid/test/test_rpverify.py 67
- def failUnlessXRDSError(self, text):
- self.failUnlessRaises(XRDSError, trustroot.extractReturnToURLs, self.disco_url, text)
+ def failUnlessDiscoveryFailure(self, text):
+ self.data = text
+ self.failUnlessRaises(
+ DiscoveryFailure, trustroot.getAllowedReturnURLs, self.disco_url)
hunk ./openid/test/test_rpverify.py 73
- self.failUnlessXRDSError('')
+ self.failUnlessDiscoveryFailure('')
hunk ./openid/test/test_rpverify.py 76
- self.failUnlessXRDSError('>')
+ self.failUnlessDiscoveryFailure('>')
Wed Sep 5 14:02:41 PDT 2007 Kevin Turner <kevin@janrain.com>
* server.trustroot.verifyReturnTo: verifyReturnTo needed a return_to
hunk ./openid/server/trustroot.py 19
+from openid import oidutil
hunk ./openid/server/trustroot.py 41
+class RealmVerificationRedirected(Exception):
+ """Attempting to verify this realm resulted in a redirect.
+ """
+ def __init__(self, relying_party_url, rp_url_after_redirects):
+ self.relying_party_url = relying_party_url
+ self.rp_url_after_redirects = rp_url_after_redirects
+
+ def __str__(self):
+ return ("Attempting to verify %r resulted in "
+ "redirect to %r" %
+ (self.relying_party_url,
+ self.rp_url_after_redirects))
+
+
hunk ./openid/server/trustroot.py 286
- """Given the a return_to string, return a discovery URL for
- the relying party
+ """Return a discovery URL for this realm.
hunk ./openid/server/trustroot.py 288
- This function does not check to make sure that the realm or
- return_to are valid or match each other. Its behaviour on invalid
- inputs is undefined.
+ This function does not check to make sure that the realm is
+ valid. Its behaviour on invalid inputs is undefined.
hunk ./openid/server/trustroot.py 380
-def verifyWithRelyingPartyURL(relying_party_url, return_to):
+def verifyWithRelyingPartyURL(relying_party_url):
hunk ./openid/server/trustroot.py 391
- return False
+ raise RealmVerificationRedirected(
+ relying_party_url, rp_url_after_redirects)
hunk ./openid/server/trustroot.py 394
- # Return whether the return_to URL matches any one of the
- # discovered return_to URLs
- return returnToMatches(return_to_urls, return_to)
+ return return_to_urls
hunk ./openid/server/trustroot.py 397
-def verifyReturnTo(realm_str, _vrfy=verifyWithRelyingPartyURL):
+def verifyReturnTo(realm_str, return_to, _vrfy=verifyWithRelyingPartyURL):
hunk ./openid/server/trustroot.py 413
- return _vrfy(realm.buildDiscoveryURL())
+ try:
+ allowable_urls = _vrfy(realm.buildDiscoveryURL())
+ except RealmVerificationRedirected, err:
+ oidutil.log(str(err))
+ return False
+ [_$_]
+ if returnToMatches(allowable_urls, return_to):
+ return True
+ else:
+ oidutil.log("Failed to validate return_to %r for realm %r, was not "
+ "in %s" % (return_to, realm_str, allowable_urls))
+ return False
hunk ./openid/test/test_rpverify.py 8
+from openid.test.support import CatchLogs
hunk ./openid/test/test_rpverify.py 179
-class TestVerifyReturnTo(unittest.TestCase):
+class TestVerifyReturnTo(unittest.TestCase, CatchLogs):
+
+ def setUp(self):
+ CatchLogs.setUp(self)
+
+ def tearDown(self):
+ CatchLogs.tearDown(self)
+ [_$_]
hunk ./openid/test/test_rpverify.py 188
- self.failIf(trustroot.verifyReturnTo(''))
+ self.failIf(trustroot.verifyReturnTo('', 'http://example.com/'))
hunk ./openid/test/test_rpverify.py 191
- sentinel = object()
hunk ./openid/test/test_rpverify.py 196
- return sentinel
+ return [return_to]
hunk ./openid/test/test_rpverify.py 199
- trustroot.verifyReturnTo(realm, _vrfy=vrfy) is sentinel)
+ trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy))
+ self.failUnlessLogEmpty()
+
+ def test_verifyFailWithDiscoveryCalled(self):
+ realm = 'http://*.example.com/'
+ return_to = 'http://www.example.com/foo'
+
+ def vrfy(disco_url):
+ self.failUnlessEqual('http://www.example.com/', disco_url)
+ return ['http://something-else.invalid/']
+
+ self.failIf(
+ trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy))
+ self.failUnlessLogMatches("Failed to validate return_to")
+
+ def test_verifyFailIfDiscoveryRedirects(self):
+ realm = 'http://*.example.com/'
+ return_to = 'http://www.example.com/foo'
+
+ def vrfy(disco_url):
+ raise trustroot.RealmVerificationRedirected(
+ disco_url, "http://redirected.invalid")
+
+ self.failIf(
+ trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy))
+ self.failUnlessLogMatches("Attempting to verify")
Fri Aug 31 16:34:00 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.consumer: OpenID 2 draft 12 fragment handling
hunk ./openid/consumer/consumer.py 193
-from urlparse import urlparse
+from urlparse import urlparse, urldefrag
hunk ./openid/consumer/consumer.py 903
+ # Fragments do not influence discovery, so we can't compare a
+ # claimed identifier with a fragment to discovered information.
+ defragged_claimed_id, _ = urldefrag(to_match.claimed_id)
+
hunk ./openid/consumer/consumer.py 911
- elif not endpoint:
+ if not endpoint:
hunk ./openid/consumer/consumer.py 913
- return self._discoverAndVerify(to_match)
+ endpoint = self._discoverAndVerify(to_match)
hunk ./openid/consumer/consumer.py 915
- elif to_match.claimed_id != endpoint.claimed_id:
+ elif defragged_claimed_id != endpoint.claimed_id:
hunk ./openid/consumer/consumer.py 918
- (endpoint.claimed_id, to_match.claimed_id))
- return self._discoverAndVerify(to_match)
+ (endpoint.claimed_id, defragged_claimed_id))
+ endpoint = self._discoverAndVerify(to_match)
hunk ./openid/consumer/consumer.py 926
- return endpoint
+
+ # The endpoint we return should have the claimed ID from the
+ # message we just verified, fragment and all.
+ if endpoint.claimed_id != to_match.claimed_id:
+ endpoint = copy.copy(endpoint)
+ endpoint.claimed_id = to_match.claimed_id
+ return endpoint
hunk ./openid/consumer/consumer.py 980
- if to_match.claimed_id != endpoint.claimed_id:
+ # Fragments do not influence discovery, so we can't compare a
+ # claimed identifier with a fragment to discovered information.
+ defragged_claimed_id, _ = urldefrag(to_match.claimed_id)
+ if defragged_claimed_id != endpoint.claimed_id:
hunk ./openid/consumer/consumer.py 987
- (to_match.claimed_id, endpoint.claimed_id))
+ (defragged_claimed_id, endpoint.claimed_id))
hunk ./openid/test/test_verifydisco.py 21
+ def tearDown(self):
+ CatchLogs.tearDown(self)
+ TestIdRes.tearDown(self)
+
hunk ./openid/test/test_verifydisco.py 88
- sentinel = object()
+ sentinel = discover.OpenIDServiceEndpoint()
+ sentinel.claimed_id = 'monkeysoft'
hunk ./openid/test/test_verifydisco.py 106
- sentinel = object()
+ sentinel = discover.OpenIDServiceEndpoint()
+ sentinel.claimed_id = 'monkeysoft'
hunk ./openid/test/test_verifydisco.py 180
+ def test_openid2Fragment(self):
+ claimed_id = "http://unittest.invalid/"
+ claimed_id_frag = claimed_id + "#fragment"
+ endpoint = discover.OpenIDServiceEndpoint()
+ endpoint.local_id = 'my identity'
+ endpoint.claimed_id = claimed_id
+ endpoint.server_url = 'Phone Home'
+ endpoint.type_uris = [discover.OPENID_2_0_TYPE]
+
+ msg = message.Message.fromOpenIDArgs(
+ {'ns':message.OPENID2_NS,
+ 'identity':endpoint.local_id,
+ 'claimed_id': claimed_id_frag,
+ 'op_endpoint': endpoint.server_url})
+ result = self.consumer._verifyDiscoveryResults(msg, endpoint)
+ [_$_]
+ self.failUnlessEqual(result.local_id, endpoint.local_id)
+ self.failUnlessEqual(result.server_url, endpoint.server_url)
+ self.failUnlessEqual(result.type_uris, endpoint.type_uris)
+
+ self.failUnlessEqual(result.claimed_id, claimed_id_frag)
+ [_$_]
+ self.failUnlessLogEmpty()
+
+
Fri Aug 31 15:11:04 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.consumer: put more debugging information in TypeURIMismatch
hunk ./openid/consumer/consumer.py 518
+ def __init__(self, expected, endpoint):
+ ProtocolError.__init__(self, expected, endpoint)
+ self.expected = expected
+ self.endpoint = endpoint
+
+ def __str__(self):
+ s = '<%s.%s: Required type %s not found in %s for endpoint %s>' % (
+ self.__class__.__module__, self.__class__.__name__,
+ self.expected, self.endpoint.type_uris, self.endpoint)
+ return s
+
+
+
hunk ./openid/consumer/consumer.py 968
- raise TypeURIMismatch(
- 'Required type %r not present' % (type_uri,))
+ raise TypeURIMismatch(type_uri, endpoint)
Fri Aug 31 14:55:31 PDT 2007 http://j3h.us/
* Remove fragments as part of normalization
hunk ./openid/consumer/discover.py 291
- return urinorm.urinorm(url)
+ normalized = urinorm.urinorm(url)
hunk ./openid/consumer/discover.py 294
+ else:
+ return urlparse.urldefrag(normalized)[0]
hunk ./openid/test/test_discover.py 274
+
+ def test_html1Fragment(self):
+ content_type = 'text/html'
+ data = readDataFile('openid.html')
+ expected_services = 1
+
+ self.documents[self.id_url] = (content_type, data)
+ expected_id = self.id_url
+ self.id_url = self.id_url + '#fragment'
+ id_url, services = discover.discover(self.id_url)
+ self.failUnlessEqual(expected_services, len(services))
+ self.failUnlessEqual(expected_id, id_url)
+
+ self._checkService(
+ services[0],
+ used_yadis=False,
+ types=['1.1'],
+ server_url="http://www.myopenid.com/server",
+ claimed_id=expected_id,
+ local_id='http://smoker.myopenid.com/',
+ )
Finished
Wed Aug 29 15:25:57 PDT 2007 http://j3h.us/
* Spec draft 12 compliance: use www in place of * for realm verification
Between drafts 11 and 12, the specified behaviour was to use the
return-to URL's domain.
hunk ./openid/server/trustroot.py 270
- def buildDiscoveryURL(self, return_to):
+ def buildDiscoveryURL(self):
hunk ./openid/server/trustroot.py 286
- if not self.wildcard:
+ if self.wildcard:
+ # Use "www." in place of the star
+ assert self.host.startswith('.'), self.host
+ www_domain = 'www' + self.host
+ return '%s://%s%s' % (self.proto, www_domain, self.path)
+ else:
hunk ./openid/server/trustroot.py 294
- # Use the domain name from the return_to URL and everything else
- # from the realm (trust_root)
- return_to_url_parts = urlparse(return_to)
- return_to_domain = return_to_url_parts[1]
-
- return '%s://%s%s' % (self.proto, return_to_domain, self.path)
-
hunk ./openid/server/trustroot.py 384
-def verifyReturnTo(realm_str, return_to,
- _vrfy=verifyWithRelyingPartyURL):
+# _vrfy parameter is there to make testing easier
+def verifyReturnTo(realm_str, _vrfy=verifyWithRelyingPartyURL):
hunk ./openid/server/trustroot.py 401
- discovery_url = realm.buildDiscoveryURL(return_to)
- return _vrfy(discovery_url, return_to)
+ return _vrfy(realm.buildDiscoveryURL())
hunk ./openid/test/test_rpverify.py 17
- def failUnlessDiscoURL(self, realm, return_to,
- expected_discovery_url):
+ def failUnlessDiscoURL(self, realm, expected_discovery_url):
hunk ./openid/test/test_rpverify.py 22
- actual_discovery_url = realm_obj.buildDiscoveryURL(return_to)
+ actual_discovery_url = realm_obj.buildDiscoveryURL()
hunk ./openid/test/test_rpverify.py 29
- 'http://example.com/foo',
hunk ./openid/test/test_rpverify.py 32
- """There is a wildcard, but there is no difference in the path"""
- self.failUnlessDiscoURL('http://*.example.com/foo',
- 'http://example.com/foo',
- 'http://example.com/foo')
-
- def test_wildcardSibling(self):
- """There is a wildcard, there is no difference in the path,
- and the domain name on the return_to URL has more subdomains
- in it than segments in the realm"""
- self.failUnlessDiscoURL('http://*.example.com/foo',
- 'http://strong.types.example.com/foo',
- 'http://strong.types.example.com/foo')
-
- def test_pathDifference(self):
- """There is no wildcard and the return_to URL's path is not
- the same as the realm
- """
- self.failUnlessDiscoURL('http://example.com/foo',
- 'http://example.com/foo/bar',
- 'http://example.com/foo')
-
- def test_queryAdded(self):
- """There is no a wildcard and the return_to URL has a query is not
- the same as the realm
- """
- self.failUnlessDiscoURL('http://example.com/foo',
- 'http://example.com/foo?x=y',
- 'http://example.com/foo')
-
- def test_pathDifference_wild(self):
- """There is a wildcard and the return_to URL's path is not
- the same as the realm
- """
- self.failUnlessDiscoURL('http://*.example.com/foo',
- 'http://example.com/foo/bar',
- 'http://example.com/foo')
-
- def test_queryAdded_wild(self):
- """There is a wildcard and the return_to URL has a query is not
- the same as the realm
+ """There is a wildcard
hunk ./openid/test/test_rpverify.py 35
- 'http://example.com/foo?x=y',
- 'http://example.com/foo')
-
-
+ 'http://www.example.com/foo')
hunk ./openid/test/test_rpverify.py 180
- self.failIf(trustroot.verifyReturnTo('', None))
+ self.failIf(trustroot.verifyReturnTo(''))
hunk ./openid/test/test_rpverify.py 186
- def vrfy(disco_url, passed_return_to):
+
+ def vrfy(disco_url):
hunk ./openid/test/test_rpverify.py 189
- self.failUnlessEqual(return_to, passed_return_to)
hunk ./openid/test/test_rpverify.py 192
- trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy) is sentinel)
+ trustroot.verifyReturnTo(realm, _vrfy=vrfy) is sentinel)
Sun Jul 1 22:21:23 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.consumer.GenericConsumer._verifyDiscoveredServices: split from _discoverAndVerify [async]
and yes, there are far, far too many things with "verify" and
"discovery" in the name.
hunk ./openid/consumer/consumer.py 997
- @raises ProtocolError: when discovery fails.
+ @raises DiscoveryFailure: when discovery fails.
hunk ./openid/consumer/consumer.py 1004
+ return _verifyDiscoveredServices(services, to_match)
+
+
+ def _verifyDiscoveredServices(self, services, to_match):
+ """See @L{_discoverAndVerify)"""
Sun Jul 1 20:33:42 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.consumer.GenericConsumer._extractSupporedAssociationType: factored out from _negotiateAssociation [async]
hunk ./openid/consumer/consumer.py 1127
- @rtype: openid.association.Association
+ @rtype: L{openid.association.Association}
hunk ./openid/consumer/consumer.py 1136
- # Any error message whose code is not 'unsupported-type'
- # should be considered a total failure.
- if why.error_code != 'unsupported-type' or \
- why.message.isOpenID1():
- oidutil.log(
- 'Server error when requesting an association from %r: %s'
- % (endpoint.server_url, why.error_text))
- return None
-
- # The server didn't like the association/session type
- # that we sent, and it sent us back a message that
- # might tell us how to handle it.
- oidutil.log(
- 'Unsupported association type %s: %s' % (assoc_type,
- why.error_text,))
-
- # Extract the session_type and assoc_type from the
- # error message
- assoc_type = why.message.getArg(OPENID_NS, 'assoc_type')
- session_type = why.message.getArg(OPENID_NS, 'session_type')
-
- if assoc_type is None or session_type is None:
- oidutil.log('Server responded with unsupported association '
- 'session but did not supply a fallback.')
- return None
- elif not self.negotiator.isAllowed(assoc_type, session_type):
- fmt = ('Server sent unsupported session/association type: '
- 'session_type=%s, assoc_type=%s')
- oidutil.log(fmt % (session_type, assoc_type))
- return None
- else:
+ supportedTypes = self._extractSupportedAssociationType(why,
+ endpoint,
+ assoc_type)
+ if supportedTypes is not None:
+ assoc_type, session_type = supportedTypes
hunk ./openid/consumer/consumer.py 1160
+ def _extractSupportedAssociationType(self, server_error, endpoint,
+ assoc_type):
+ """Handle ServerErrors resulting from association requests.
+
+ @returns: If server replied with an C{unsupported-type} error,
+ return a tuple of supported C{association_type}, C{session_type}.
+ Otherwise logs the error and returns None.
+ @rtype: tuple or None
+ """
+ # Any error message whose code is not 'unsupported-type'
+ # should be considered a total failure.
+ if server_error.error_code != 'unsupported-type' or \
+ server_error.message.isOpenID1():
+ oidutil.log(
+ 'Server error when requesting an association from %r: %s'
+ % (endpoint.server_url, server_error.error_text))
+ return None
+
+ # The server didn't like the association/session type
+ # that we sent, and it sent us back a message that
+ # might tell us how to handle it.
+ oidutil.log(
+ 'Unsupported association type %s: %s' % (assoc_type,
+ server_error.error_text,))
+
+ # Extract the session_type and assoc_type from the
+ # error message
+ assoc_type = server_error.message.getArg(OPENID_NS, 'assoc_type')
+ session_type = server_error.message.getArg(OPENID_NS, 'session_type')
+
+ if assoc_type is None or session_type is None:
+ oidutil.log('Server responded with unsupported association '
+ 'session but did not supply a fallback.')
+ return None
+ elif not self.negotiator.isAllowed(assoc_type, session_type):
+ fmt = ('Server sent unsupported session/association type: '
+ 'session_type=%s, assoc_type=%s')
+ oidutil.log(fmt % (session_type, assoc_type))
+ return None
+ else:
+ return assoc_type, session_type
+
+
Sat Jun 30 21:32:58 PDT 2007 Kevin Turner <kevin@janrain.com>
* openid.consumer.consumer._httpResponseToMessage: split from makeKVPost [async]
hunk ./openid/consumer/consumer.py 215
+
hunk ./openid/consumer/consumer.py 228
- response_message = Message.fromKVForm(resp.body)
- if resp.status == 400:
+ # Process response in separate function that can be shared by async code.
+ return _httpResponseToMessage(resp, server_url)
+
+
+def _httpResponseToMessage(response, server_url):
+ """Adapt a POST response to a Message.
+
+ @type response: L{openid.fetchers.HTTPResponse}
+ @param response: Result of a POST to an OpenID endpoint.
+
+ @rtype: L{openid.message.Message}
+
+ @raises openid.fetchers.HTTPFetchingError: if the server returned a
+ status of other than 200 or 400.
+
+ @raises ServerError: if the server returned an OpenID error.
+ """
+ # Should this function be named Message.fromHTTPResponse instead?
+ response_message = Message.fromKVForm(response.body)
+ if response.status == 400:
hunk ./openid/consumer/consumer.py 250
- elif resp.status != 200:
+ elif response.status != 200:
hunk ./openid/consumer/consumer.py 252
- error_message = fmt % (server_url, resp.status)
+ error_message = fmt % (server_url, response.status)
hunk ./openid/consumer/consumer.py 258
+
hunk ./openid/test/test_consumer.py 17
- ProtocolError
+ ProtocolError, _httpResponseToMessage
hunk ./openid/test/test_consumer.py 1902
+
+
+
+class TestKVPost(unittest.TestCase):
+ def setUp(self):
+ self.server_url = 'http://unittest/%s' % (self.id(),)
+
+ def test_200(self):
+ from openid.fetchers import HTTPResponse
+ response = HTTPResponse()
+ response.status = 200
+ response.body = "foo:bar\nbaz:quux\n"
+ r = _httpResponseToMessage(response, self.server_url)
+ expected_msg = Message.fromOpenIDArgs({'foo':'bar','baz':'quux'})
+ self.failUnlessEqual(expected_msg, r)
+
+
+ def test_400(self):
+ response = HTTPResponse()
+ response.status = 400
+ response.body = "error:bonk\nerror_code:7\n"
+ try:
+ r = _httpResponseToMessage(response, self.server_url)
+ except ServerError, e:
+ self.failUnlessEqual(e.error_text, 'bonk')
+ self.failUnlessEqual(e.error_code, '7')
+ else:
+ self.fail("Expected ServerError, got return %r" % (r,))
+
+
+ def test_500(self):
+ # 500 as an example of any non-200, non-400 code.
+ response = HTTPResponse()
+ response.status = 500
+ response.body = "foo:bar\nbaz:quux\n"
+ self.failUnlessRaises(fetchers.HTTPFetchingError,
+ _httpResponseToMessage, response,
+ self.server_url)
+
+
+
+
Mon Jun 18 16:46:29 PDT 2007 Josh Hoyt <josh@janrain.com>
* Added returnToVerified method of CheckIDRequest
Passes the appropriate data to trustroot.verifyReturnTo
hunk ./openid/server/server.py 107
-from openid.server.trustroot import TrustRoot
+from openid.server.trustroot import TrustRoot, verifyReturnTo
hunk ./openid/server/server.py 662
+ def returnToVerified(self):
+ """Does the relying party publish the return_to URL for this
+ response under the realm? It is up to the provider to set a
+ policy for what kinds of realms should be allowed. This
+ return_to URL verification reduces vulnerability to data-theft
+ attacks based on open proxies, corss-site-scripting, or open
+ redirectors.
+
+ This check should only be performed after making sure that the
+ return_to URL matches the realm.
+
+ @see: trustRootValid
+
+ @raises openid.yadis.discover.DiscoveryFailure: if the realm
+ URL does not support Yadis discovery (and so does not
+ support the verification process).
+
+ @returntype: bool
+
+ @returns: True if the realm publishes a document with the
+ return_to URL listed
+ """
+ return verifyReturnTo(self.trust_root, self.return_to)
+
hunk ./openid/test/test_server.py 811
+ def test_returnToVerified_callsVerify(self):
+ """Make sure that verifyReturnTo is calling the trustroot
+ function verifyReturnTo
+ """
+ def withVerifyReturnTo(new_verify, callable):
+ old_verify = server.verifyReturnTo
+ try:
+ server.verifyReturnTo = new_verify
+ return callable()
+ finally:
+ server.verifyReturnTo = old_verify
+
+ # Ensure that exceptions are passed through
+ sentinel = Exception()
+ def vrfyExc(trust_root, return_to):
+ self.failUnlessEqual(self.request.trust_root, trust_root)
+ self.failUnlessEqual(self.request.return_to, return_to)
+ raise sentinel
+
+ try:
+ withVerifyReturnTo(vrfyExc, self.request.returnToVerified)
+ except Exception, e:
+ self.failUnless(e is sentinel, e)
+
+ # Ensure that True and False are passed through unchanged
+ def constVerify(val):
+ def verify(trust_root, return_to):
+ self.failUnlessEqual(self.request.trust_root, trust_root)
+ self.failUnlessEqual(self.request.return_to, return_to)
+ return val
+ return verify
+
+ for val in [True, False]:
+ self.failUnlessEqual(
+ val,
+ withVerifyReturnTo(constVerify(val),
+ self.request.returnToVerified))
+
Thu Jun 7 16:48:48 PDT 2007 Josh Hoyt <josh@janrain.com>
* Added function to openid.yadis.etxrd that parses the Expires date out of an XRD
hunk ./openid/yadis/etxrd.py 24
+from datetime import datetime
+from time import strptime
+
hunk ./openid/yadis/etxrd.py 122
+expires_tag = mkXRDTag('Expires')
hunk ./openid/yadis/etxrd.py 148
+def getXRDExpiration(xrd_element, default=None):
+ """Return the expiration date of this XRD element, or None if no
+ expiration was specified.
+
+ @type xrd_element: ElementTree node
+
+ @param default: The value to use as the expiration if no
+ expiration was specified in the XRD.
+
+ @rtype: datetime.datetime
+
+ @raises ValueError: If the xrd:Expires element is present, but its
+ contents are not formatted according to the specification.
+ """
+ expires_element = xrd_element.find(expires_tag)
+ if expires_element is None:
+ return default
+ else:
+ expires_string = expires_element.text
+
+ # Will raise ValueError if the string is not the expected format
+ expires_time = strptime(expires_string, "%Y-%m-%dT%H:%M:%SZ")
+ return datetime(*expires_time[0:6])
Wed Jun 6 15:10:45 PDT 2007 Josh Hoyt <josh@janrain.com>
* Completed realm verification
hunk ./openid/server/trustroot.py 6
+
+It also implements relying party return_to URL verification, based on
+the realm.
hunk ./openid/server/trustroot.py 11
+__all__ = [
+ 'TrustRoot',
+ 'RP_RETURN_TO_URL_TYPE',
+ 'extractReturnToURLs',
+ 'returnToMatches',
+ 'verifyReturnTo',
+ ]
+
+from openid.yadis import services
hunk ./openid/server/trustroot.py 304
+# The URI for relying party discovery, used in realm verification.
+#
+# XXX: This should probably live somewhere else (like in
+# openid.consumer or openid.yadis somewhere)
+RP_RETURN_TO_URL_TYPE = 'http://specs.openid.net/auth/2.0/return_to'
+
+def _extractReturnURL(endpoint):
+ """If the endpoint is a relying party OpenID return_to endpoint,
+ return the endpoint URL. Otherwise, return None.
+
+ This function is intended to be used as a filter for the Yadis
+ filtering interface.
+
+ @see: C{L{openid.yadis.services}}
+ @see: C{L{openid.yadis.filters}}
+
+ @param endpoint: An XRDS BasicServiceEndpoint, as returned by
+ performing Yadis dicovery.
+
+ @returns: The endpoint URL or None if the endpoint is not a
+ relying party endpoint.
+ @rtype: str or NoneType
+ """
+ if endpoint.matchTypes([RP_RETURN_TO_URL_TYPE]):
+ return endpoint.uri
+ else:
+ return None
+
+def extractReturnToURLs(rp_uri, xrds_text):
+ """Given a relying party discovery URL and its corresponding XRDS
+ document, return a list of return_to URLs.
+
+ @param rp_uri: The discovery URL
+ @param xrds_text: The xrds document, as a string
+ @returns: A list of all relying party URLs that were found in the document.
+ @rtype: [str]
+ """
+ assert isinstance(xrds_text, basestring), xrds_text
+ return services.applyFilter(rp_uri, xrds_text, _extractReturnURL)
+
+def returnToMatches(allowed_return_to_urls, return_to):
+ """Is the return_to URL under one of the supplied allowed
+ return_to URLs?"""
+
+ for allowed_return_to in allowed_return_to_urls:
+ # A return_to pattern works the same as a realm, except that
+ # it's not allowed to use a wildcard. We'll model this by
+ # parsing it as a realm, and not trying to match it if it has
+ # a wildcard.
+
+ return_realm = TrustRoot.parse(allowed_return_to)
+ if (# Parses as a trust root
+ return_realm is not None and
+
+ # Does not have a wildcard
+ not return_realm.wildcard and
+
+ # Matches the return_to that we passed in with it
+ return_realm.validateURL(return_to)
+ ):
+ return True
+
+ # No URL in the list matched
+ return False
+
+def verifyWithRelyingPartyURL(relying_party_url, return_to):
+ """Verify that the return_to URL is listed by the relying party URL.
+
+ Similar to verifyReturnTo, except it takes a relying party URL instead
+ of a realm.
+ """
+ (rp_url_after_redirects, return_to_urls) = services.getServiceEndpoints(
+ relying_party_url, extractReturnToURLs)
+
+ if rp_url_after_redirects != relying_party_url:
+ # Verification caused a redirect
+ return False
+
+ # Return whether the return_to URL matches any one of the
+ # discovered return_to URLs
+ return returnToMatches(return_to_urls, return_to)
+
+def verifyReturnTo(realm_str, return_to,
+ _vrfy=verifyWithRelyingPartyURL):
+ """Verify that a return_to URL is valid for the given realm.
+
+ This function builds a discovery URL, performs Yadis discovery on
+ it, makes sure that the URL does not redirect, parses out the
+ return_to URLs, and finally checks to see if the current return_to
+ URL matches the return_to.
+
+ @raises DiscoveryFailure: When Yadis discovery fails
+ @returns: True if the return_to URL is valid for the realm
+ """
+ realm = TrustRoot.parse(realm_str)
+ if realm is None:
+ # The realm does not parse as a URL pattern
+ return False
+
+ discovery_url = realm.buildDiscoveryURL(return_to)
+ return _vrfy(discovery_url, return_to)
+
hunk ./openid/test/test_rpverify.py 6
+from openid.yadis.etxrd import XRDSError
hunk ./openid/test/test_rpverify.py 79
+
+
+class TestExtractReturnToURLs(unittest.TestCase):
+ disco_url = 'http://example.com/'
+
+ def failUnlessFileHasReturnURLs(self, filename, expected_return_urls):
+ self.failUnlessXRDSHasReturnURLs(file(filename).read(),
+ expected_return_urls)
+
+ def failUnlessXRDSHasReturnURLs(self, data, expected_return_urls):
+ actual_return_urls = list(trustroot.extractReturnToURLs(
+ self.disco_url, data))
+ self.failUnlessEqual(expected_return_urls, actual_return_urls)
+
+ def failUnlessXRDSError(self, text):
+ self.failUnlessRaises(XRDSError, trustroot.extractReturnToURLs, self.disco_url, text)
+
+ def test_empty(self):
+ self.failUnlessXRDSError('')
+
+ def test_badXML(self):
+ self.failUnlessXRDSError('>')
+
+ def test_noEntries(self):
+ self.failUnlessXRDSHasReturnURLs('''\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS xmlns:xrds="xri://$xrds"
+ xmlns="xri://$xrd*($v*2.0)"
+ >
+ <XRD>
+ </XRD>
+</xrds:XRDS>
+''', [])
+
+ def test_noReturnToEntries(self):
+ self.failUnlessXRDSHasReturnURLs('''\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS xmlns:xrds="xri://$xrds"
+ xmlns="xri://$xrd*($v*2.0)"
+ >
+ <XRD>
+ <Service priority="10">
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
+ <URI>http://www.myopenid.com/server</URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
+''', [])
+
+ def test_oneEntry(self):
+ self.failUnlessXRDSHasReturnURLs('''\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS xmlns:xrds="xri://$xrds"
+ xmlns="xri://$xrd*($v*2.0)"
+ >
+ <XRD>
+ <Service>
+ <Type>http://specs.openid.net/auth/2.0/return_to</Type>
+ <URI>http://rp.example.com/return</URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
+''', ['http://rp.example.com/return'])
+
+ def test_twoEntries(self):
+ self.failUnlessXRDSHasReturnURLs('''\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS xmlns:xrds="xri://$xrds"
+ xmlns="xri://$xrd*($v*2.0)"
+ >
+ <XRD>
+ <Service priority="0">
+ <Type>http://specs.openid.net/auth/2.0/return_to</Type>
+ <URI>http://rp.example.com/return</URI>
+ </Service>
+ <Service priority="1">
+ <Type>http://specs.openid.net/auth/2.0/return_to</Type>
+ <URI>http://other.rp.example.com/return</URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
+''', ['http://rp.example.com/return',
+ 'http://other.rp.example.com/return'])
+
+ def test_twoEntries_withOther(self):
+ self.failUnlessXRDSHasReturnURLs('''\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS xmlns:xrds="xri://$xrds"
+ xmlns="xri://$xrd*($v*2.0)"
+ >
+ <XRD>
+ <Service priority="0">
+ <Type>http://specs.openid.net/auth/2.0/return_to</Type>
+ <URI>http://rp.example.com/return</URI>
+ </Service>
+ <Service priority="1">
+ <Type>http://specs.openid.net/auth/2.0/return_to</Type>
+ <URI>http://other.rp.example.com/return</URI>
+ </Service>
+ <Service priority="0">
+ <Type>http://example.com/LOLCATS</Type>
+ <URI>http://example.com/invisible+uri</URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
+''', ['http://rp.example.com/return',
+ 'http://other.rp.example.com/return'])
+
+
+
+class TestReturnToMatches(unittest.TestCase):
+ def test_noEntries(self):
+ self.failIf(trustroot.returnToMatches([], 'anything'))
+
+ def test_exactMatch(self):
+ r = 'http://example.com/return.to'
+ self.failUnless(trustroot.returnToMatches([r], r))
+
+ def test_garbageMatch(self):
+ r = 'http://example.com/return.to'
+ self.failUnless(trustroot.returnToMatches(
+ ['This is not a URL at all. In fact, it has characters, like "<" that are not allowed in URLs',
+ r],
+ r))
+
+ def test_descendant(self):
+ r = 'http://example.com/return.to'
+ self.failUnless(trustroot.returnToMatches(
+ [r],
+ 'http://example.com/return.to/user:joe'))
+
+ def test_wildcard(self):
+ self.failIf(trustroot.returnToMatches(
+ ['http://*.example.com/return.to'],
+ 'http://example.com/return.to'))
+
+ def test_noMatch(self):
+ r = 'http://example.com/return.to'
+ self.failIf(trustroot.returnToMatches(
+ [r],
+ 'http://example.com/xss_exploit'))
+
+class TestVerifyReturnTo(unittest.TestCase):
+ def test_bogusRealm(self):
+ self.failIf(trustroot.verifyReturnTo('', None))
+
+ def test_verifyWithDiscoveryCalled(self):
+ sentinel = object()
+ realm = 'http://*.example.com/'
+ return_to = 'http://www.example.com/foo'
+ def vrfy(disco_url, passed_return_to):
+ self.failUnlessEqual('http://www.example.com/', disco_url)
+ self.failUnlessEqual(return_to, passed_return_to)
+ return sentinel
+
+ self.failUnless(
+ trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy) is sentinel)
+
Wed May 30 17:07:37 PDT 2007 Josh Hoyt <josh@janrain.com>
* Added relying party discovery URL building to trustroot
hunk ./admin/runtests 70
+ from openid.test import test_rpverify
hunk ./admin/runtests 94
+ test_rpverify,
hunk ./openid/server/trustroot.py 257
+
+ def buildDiscoveryURL(self, return_to):
+ """Given the a return_to string, return a discovery URL for
+ the relying party
+
+ This function does not check to make sure that the realm or
+ return_to are valid or match each other. Its behaviour on invalid
+ inputs is undefined.
+
+ @param return_to: The relying party return URL of the OpenID
+ authentication request
+
+ @rtype: str
+
+ @returns: The URL upon which relying party discovery should be run
+ in order to verify the return_to URL
+ """
+ if not self.wildcard:
+ return self.unparsed
+
+ # Use the domain name from the return_to URL and everything else
+ # from the realm (trust_root)
+ return_to_url_parts = urlparse(return_to)
+ return_to_domain = return_to_url_parts[1]
+
+ return '%s://%s%s' % (self.proto, return_to_domain, self.path)
addfile ./openid/test/test_rpverify.py
hunk ./openid/test/test_rpverify.py 1
+"""Unit tests for verification of return_to URLs for a realm
+"""
+
+__all__ = ['TestBuildDiscoveryURL']
+
+from openid.server import trustroot
+import unittest
+
+# Too many methods does not apply to unit test objects
+#pylint:disable-msg=R0904
+class TestBuildDiscoveryURL(unittest.TestCase):
+ """Tests for building the discovery URL from a realm and a
+ return_to URL
+ """
+
+ def failUnlessDiscoURL(self, realm, return_to,
+ expected_discovery_url):
+ """Build a discovery URL out of the realm and a return_to and
+ make sure that it matches the expected discovery URL
+ """
+ realm_obj = trustroot.TrustRoot.parse(realm)
+ actual_discovery_url = realm_obj.buildDiscoveryURL(return_to)
+ self.failUnlessEqual(expected_discovery_url, actual_discovery_url)
+
+ def test_trivial(self):
+ """There is no wildcard and the realm is the same as the return_to URL
+ """
+ self.failUnlessDiscoURL('http://example.com/foo',
+ 'http://example.com/foo',
+ 'http://example.com/foo')
+
+ def test_wildcard(self):
+ """There is a wildcard, but there is no difference in the path"""
+ self.failUnlessDiscoURL('http://*.example.com/foo',
+ 'http://example.com/foo',
+ 'http://example.com/foo')
+
+ def test_wildcardSibling(self):
+ """There is a wildcard, there is no difference in the path,
+ and the domain name on the return_to URL has more subdomains
+ in it than segments in the realm"""
+ self.failUnlessDiscoURL('http://*.example.com/foo',
+ 'http://strong.types.example.com/foo',
+ 'http://strong.types.example.com/foo')
+
+ def test_pathDifference(self):
+ """There is no wildcard and the return_to URL's path is not
+ the same as the realm
+ """
+ self.failUnlessDiscoURL('http://example.com/foo',
+ 'http://example.com/foo/bar',
+ 'http://example.com/foo')
+
+ def test_queryAdded(self):
+ """There is no a wildcard and the return_to URL has a query is not
+ the same as the realm
+ """
+ self.failUnlessDiscoURL('http://example.com/foo',
+ 'http://example.com/foo?x=y',
+ 'http://example.com/foo')
+
+ def test_pathDifference_wild(self):
+ """There is a wildcard and the return_to URL's path is not
+ the same as the realm
+ """
+ self.failUnlessDiscoURL('http://*.example.com/foo',
+ 'http://example.com/foo/bar',
+ 'http://example.com/foo')
+
+ def test_queryAdded_wild(self):
+ """There is a wildcard and the return_to URL has a query is not
+ the same as the realm
+ """
+ self.failUnlessDiscoURL('http://*.example.com/foo',
+ 'http://example.com/foo?x=y',
+ 'http://example.com/foo')
+
+if __name__ == '__main__':
+ unittest.main()
Fri May 25 15:28:29 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.consumer.GenericConsumer.complete: dispatch mode-specific logic to individual methods
because I need to override one of them and don't want to duplicate all of it.
hunk ./openid/consumer/consumer.py 576
- if mode == 'cancel':
- return CancelResponse(endpoint)
- elif mode == 'error':
- error = message.getArg(OPENID_NS, 'error')
- contact = message.getArg(OPENID_NS, 'contact')
- reference = message.getArg(OPENID_NS, 'reference')
+ modeMethod = getattr(self, '_complete_' + mode,
+ self._completeInvalid)
+ [_$_]
+ return modeMethod(message, endpoint)
hunk ./openid/consumer/consumer.py 581
- return FailureResponse(endpoint, error, contact=contact,
- reference=reference)
- elif message.isOpenID2() and mode == 'setup_needed':
- return SetupNeededResponse(endpoint)
+ def _complete_cancel(self, message, endpoint):
+ return CancelResponse(endpoint)
hunk ./openid/consumer/consumer.py 584
- elif mode == 'id_res':
- try:
- self._checkSetupNeeded(message)
- except SetupNeededError, why:
- return SetupNeededResponse(endpoint, why.user_setup_url)
- else:
- try:
- return self._doIdRes(message, endpoint)
- except (ProtocolError, DiscoveryFailure), why:
- return FailureResponse(endpoint, why[0])
+ def _complete_error(self, message, endpoint):
+ error = message.getArg(OPENID_NS, 'error')
+ contact = message.getArg(OPENID_NS, 'contact')
+ reference = message.getArg(OPENID_NS, 'reference')
+
+ return FailureResponse(endpoint, error, contact=contact,
+ reference=reference)
+
+ def _complete_setup_needed(self, message, endpoint):
+ if not message.isOpenID2():
+ return self._completeInvalid(message, endpoint)
+
+ return SetupNeededResponse(endpoint)
+
+ def _complete_id_res(self, message, endpoint):
+ try:
+ self._checkSetupNeeded(message)
+ except SetupNeededError, why:
+ return SetupNeededResponse(endpoint, why.user_setup_url)
hunk ./openid/consumer/consumer.py 604
- return FailureResponse(endpoint,
- 'Invalid openid.mode: %r' % (mode,))
+ try:
+ return self._doIdRes(message, endpoint)
+ except (ProtocolError, DiscoveryFailure), why:
+ return FailureResponse(endpoint, why[0])
+
+ def _completeInvalid(self, message, endpoint):
+ mode = message.getArg(OPENID_NS, 'mode', '<No mode set>')
+ return FailureResponse(endpoint,
+ 'Invalid openid.mode: %r' % (mode,))
Fri May 25 14:02:15 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.discover.OpenIDServiceEndpoint.from*: docstrings
hunk ./openid/consumer/discover.py 167
+
+ @raises L{XRDSError}: When the XRDS does not parse.
hunk ./openid/consumer/discover.py 176
- if discovery_doc.isXRDS():
+ """Create endpoints from a DiscoveryResult.
+ [_$_]
+ @type discoveryResult: L{DiscoveryResult}
+
+ @rtype: list of L{OpenIDServiceEndpoint}
+
+ @raises L{XRDSError}: When the XRDS does not parse.
+ """
+ if discoveryResult.isXRDS():
Fri May 25 13:00:09 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.discover.discoverYadis: simplify a bit, use .fromXRDS
hunk ./openid/consumer/discover.py 348
+ body = response.response_text
hunk ./openid/consumer/discover.py 350
- openid_services = extractServices(
- response.normalized_uri, response.response_text,
- OpenIDServiceEndpoint)
+ openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body)
hunk ./openid/consumer/discover.py 363
- else:
- body = response.response_text
Fri May 25 12:52:26 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.discover.OpenIDServiceEndpoint.fromXRDS, fromDiscoveryResult: added siblings for .fromHTML()
hunk ./openid/consumer/discover.py 162
+
+ def fromXRDS(cls, uri, xrds):
+ """Parse the given document as XRDS looking for OpenID services.
+
+ @rtype: [OpenIDServiceEndpoint]
+ """
+ return extractServices(uri, xrds, cls)
+
+ fromXRDS = classmethod(fromXRDS)
+
+
+ def fromDiscoveryResult(cls, discoveryResult):
+ if discovery_doc.isXRDS():
+ method = cls.fromXRDS
+ else:
+ method = cls.fromHTML
+ return method(discoveryResult.normalized_uri,
+ discoveryResult.response_text)
+
+ fromDiscoveryResult = classmethod(fromDiscoveryResult)
+
+
Fri May 11 14:24:05 PDT 2007 Kevin Turner <http://kevin.janrain.com/>
* store.*: remove getExpired [#3667]
hunk ./openid/store/filestore.py 428
- def getExpired(self):
- """Return the server URL for all expired associations"""
- urls = []
- for _, assoc in self._allAssocs():
- if assoc.getExpiresIn() <= 0:
- urls.append(assoc.server_url)
- return urls
-
hunk ./openid/store/interface.py 162
-
- def getExpired(self):
- """Return all server URLs that have expired associations.
-
- @rtype: C{[str]}
- """
- raise NotImplementedError
hunk ./openid/store/memstore.py 93
-
- def getExpired(self):
- expired = []
- for server_url, assocs in self.server_assocs.iteritems():
- best = assocs.best()
- if best is None or best.getExpiresIn() == 0:
- print best
- expired.append(server_url)
-
- return expired
hunk ./openid/store/sqlstore.py 265
-
- def txn_getExpired(self):
- """Get the server URLs for all associations that have expired"""
- self.db_get_expired(int(time.time()))
- rows = self.cur.fetchall()
- return [row[0] for row in rows]
-
- getExpired = _inTxn(txn_getExpired)
hunk ./openid/test/storetest.py 40
- # Empty store has no associations (this just makes sure that all
- # of the stores have a getExpired method and that it doesn't just
- # blow up)
- assert store.getExpired() == [], store.getExpired()
-
Wed May 9 19:33:05 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.interface.OpenIDStore.cleanupAssociations,cleanup: added
hunk ./openid/store/interface.py 15
- to support one-way nonces.
+ to support one-way nonces. It added C{L{cleanupNonces}},
+ C{L{cleanupAssociations}}, and C{L{cleanup}}.
hunk ./openid/store/interface.py 185
+ def cleanupAssociations(self):
+ """Remove expired associations from the store.
+
+ This method is not called in the normal operation of the
+ library. It provides a way for store admins to keep
+ their storage from filling up with expired data.
+
+ @return: the number of associations expired.
+ @returntype: int
+ """
+ raise NotImplementedError
+
+ def cleanup(self):
+ """Shortcut for C{L{cleanupNonces}()}, C{L{cleanupAssociations}()}.
+
+ This method is not called in the normal operation of the
+ library. It provides a way for store admins to keep
+ their storage from filling up with expired data.
+ """
+ return self.cleanupNonces(), self.cleanupAssociations()
+
Wed May 9 19:31:44 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.sqlstore.SQLStore.cleanupAssociations: added
hunk ./openid/store/sqlstore.py 280
+ def txn_cleanupAssociations(self):
+ self.db_clean_assoc(int(time.time()))
+ return self.cur.rowcount
+
+ cleanupAssociations = _inTxn(txn_cleanupAssociations)
+
+
hunk ./openid/store/sqlstore.py 346
+ clean_assoc_sql = 'DELETE FROM %(associations)s WHERE issued + lifetime < ?;'
+
hunk ./openid/store/sqlstore.py 433
+ clean_assoc_sql = 'DELETE FROM %(associations)s WHERE issued + lifetime < %%s;'
+
hunk ./openid/store/sqlstore.py 524
+ clean_assoc_sql = 'DELETE FROM %(associations)s WHERE issued + lifetime < %%s;'
+
Wed May 9 19:05:42 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.filestore.FileOpenIDStore.cleanupAssociations: added
hunk ./openid/store/filestore.py 395
- def clean(self):
+ def cleanup(self):
hunk ./openid/store/filestore.py 402
+ cleanupAssociations()
+ cleanupNonces()
+
+ def cleanupAssociations(self):
+ removed = 0
hunk ./openid/store/filestore.py 410
- cleanupNonces()
+ removed += 1
+ return removed
Wed May 9 19:03:03 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.memstore.MemoryStore.cleanupAssociations: added
hunk ./openid/store/memstore.py 37
+ def cleanup(self):
+ """Remove expired associations.
+
+ @return: tuple of (removed associations, remaining associations)
+ """
+ remove = []
+ for handle, assoc in self.assocs.iteritems():
+ if assoc.getExpiresIn() == 0:
+ remove.append(handle)
+ for handle in remove:
+ del self.assocs[handle]
+ return len(remove), len(self.assocs)
+
+
+
hunk ./openid/store/memstore.py 116
+ def cleanupAssociations(self):
+ remove_urls = []
+ removed_assocs = 0
+ for server_url, assocs in self.server_assocs.iteritems():
+ removed, remaining = assocs.cleanup()
+ removed_assocs += removed
+ if not remaining:
+ remove_urls.append(server_url)
+
+ # Remove entries from server_assocs that had none remaining.
+ for server_url in remove_urls:
+ del self.server_assocs[server_url]
+ return removed_assocs
+
Wed May 9 18:39:54 PDT 2007 Kevin Turner <kevin@janrain.com>
* test.storetest: add test for cleanupAssociations()
hunk ./openid/test/storetest.py 152
+
+ ### test expired associations
+ # assoc 1: server 1, valid
+ # assoc 2: server 1, expired
+ # assoc 3: server 2, expired
+ # assoc 4: server 3, valid
+ assocValid1 = genAssoc(issued=-3600,lifetime=7200)
+ assocValid2 = genAssoc(issued=-5)
+ assocExpired1 = genAssoc(issued=-7200,lifetime=3600)
+ assocExpired2 = genAssoc(issued=-7200,lifetime=3600)
+
+ store.cleanupAssociations()
+ store.storeAssociation(server_url + '1', assocValid1)
+ store.storeAssociation(server_url + '1', assocExpired1)
+ store.storeAssociation(server_url + '2', assocExpired2)
+ store.storeAssociation(server_url + '3', assocValid2)
+
+ cleaned = store.cleanupAssociations()
+ assert cleaned == 2, cleaned
Wed May 9 18:14:11 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.filestore.FileStore._allAssocs: filename must include directory
hunk ./openid/store/filestore.py 366
- association_filenames = os.listdir(self.association_dir)
+ association_filenames = map(
+ lambda filename: os.path.join(self.association_dir, filename),
+ os.listdir(self.association_dir))
Wed May 9 18:03:13 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.interface.OpenIDStore.cleanupNonces: simpifly docstring
hunk ./openid/store/interface.py 170
- """Run garbage collection on expired nonces.
+ """Remove expired nonces from the store.
hunk ./openid/store/interface.py 180
+ @returntype: int
Wed May 9 17:57:20 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.interface.OpenIDStore.getAssociation: change docstring to match test
storetest says:
# More recent, and expires earlier than assoc2 or assoc. Make sure
# that we're picking the one with the latest issued date and not
# taking into account the expiration.
and that seems like as reasonable a policy as the other.
hunk ./openid/store/interface.py 62
- method is the one that will remain valid for the longest
- duration.
+ method is the one most recently issued.
Wed May 9 17:46:37 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.filestore.FileOpenIDStore._allAssocs: log error instead of silently passing.
this except/pass was masking serious bugs.
hunk ./openid/store/filestore.py 372
- pass
+ oidutil.log("%s disappeared during %s._allAssocs" % (
+ association_filename, self.__class__.__name__))
Wed May 9 15:26:55 PDT 2007 Kevin Turner <kevin@janrain.com>
* test.storetest: more comments
hunk ./openid/test/storetest.py 198
+ # A roundabout method of checking that the old nonces were cleaned is
+ # to see if we're allowed to add them again.
hunk ./openid/test/storetest.py 202
+ # The recent nonce wasn't cleaned, so it should still fail.
Wed May 9 15:24:02 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.sqlstore.SQLStore.cleanupNonces: added
hunk ./openid/store/sqlstore.py 274
+ def txn_cleanupNonces(self):
+ self.db_clean_nonce(int(time.time()) - nonce.SKEW)
+ return self.cur.rowcount
+
+ cleanupNonces = _inTxn(txn_cleanupNonces)
+
hunk ./openid/store/sqlstore.py 341
+ clean_nonce_sql = 'DELETE FROM %(nonces)s WHERE timestamp < ?;'
+
hunk ./openid/store/sqlstore.py 426
+ clean_nonce_sql = 'DELETE FROM %(nonces)s WHERE timestamp < %%s;'
+
hunk ./openid/store/sqlstore.py 515
+ clean_nonce_sql = 'DELETE FROM %(nonces)s WHERE timestamp < %%s;'
+
Wed May 9 15:14:14 PDT 2007 Kevin Turner <kevin@janrain.com>
* openid.filestore.FileOpenIDStore.cleanupNonces: added
hunk ./openid/store/filestore.py 399
+ for assoc_filename, assoc in self._allAssocs():
+ if assoc.getExpiresIn() == 0:
+ _removeIfPresent(assoc_filename)
+ cleanupNonces()
+
+ def cleanupNonces(self):
hunk ./openid/store/filestore.py 408
+ removed = 0
hunk ./openid/store/filestore.py 411
- if not nonce.checkTimestamp(nonce_fname, now=now):
+ timestamp = nonce_fname.split('-', 1)[0]
+ timestamp = int(timestamp, 16)
+ if abs(timestamp - now) > nonce.SKEW:
hunk ./openid/store/filestore.py 416
-
- for assoc_filename, assoc in self._allAssocs():
- if assoc.getExpiresIn() == 0:
- _removeIfPresent(assoc_filename)
+ removed += 1
+ return removed
Wed May 9 15:01:46 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.interface.OpenIDStore.cleanupNonces: added
hunk ./openid/store/interface.py 170
+ def cleanupNonces(self):
+ """Run garbage collection on expired nonces.
+
+ Discards any nonce from storage that is old enough that its
+ timestamp would not pass L{useNonce}.
+
+ This method is not called in the normal operation of the
+ library. It provides a way for store admins to keep
+ their storage from filling up with expired data.
+
+ @return: the number of nonces expired.
+ """
+ raise NotImplementedError
+
hunk ./openid/test/storetest.py 177
+
+ old_nonce1 = mkNonce(now - 20000)
+ old_nonce2 = mkNonce(now - 10000)
+ recent_nonce = mkNonce(now - 600)
+
+ from openid.store import nonce as nonceModule
+ orig_skew = nonceModule.SKEW
+ try:
+ nonceModule.SKEW = 0
+ store.cleanupNonces()
+ # Set SKEW high so stores will keep our nonces.
+ nonceModule.SKEW = 100000
+ assert store.useNonce(server_url, *split(old_nonce1))
+ assert store.useNonce(server_url, *split(old_nonce2))
+ assert store.useNonce(server_url, *split(recent_nonce))
+
+ nonceModule.SKEW = 3600
+ cleaned = store.cleanupNonces()
+ assert cleaned == 2, "Cleaned %r nonces." % (cleaned,)
+
+ nonceModule.SKEW = 100000
+ assert store.useNonce(server_url, *split(old_nonce1))
+ assert store.useNonce(server_url, *split(old_nonce2))
+ assert not store.useNonce(server_url, *split(recent_nonce))
+ finally:
+ nonceModule.SKEW = orig_skew
+
Wed May 9 12:44:03 PDT 2007 Kevin Turner <kevin@janrain.com>
* openid.server.server.OpenIDResponse.addExtension: more docstring
hunk ./openid/server/server.py 963
+ @type extension_response: L{openid.extension}
+
+ @returntype: None
Wed May 2 17:09:52 PDT 2007 Kevin Turner <kevin@janrain.com>
* test.storetest.testStore: test useNonce for timestamp checking
hunk ./openid/test/storetest.py 157
- def checkUseNonce(nonce, expected, server_url):
+ def checkUseNonce(nonce, expected, server_url, msg=''):
hunk ./openid/test/storetest.py 160
- assert bool(actual) == bool(expected)
+ assert bool(actual) == bool(expected), "%r != %r: %s" % (actual, expected,
+ msg)
hunk ./openid/test/storetest.py 175
+ # Nonces from when the universe was an hour old should not pass these days.
+ old_nonce = mkNonce(3600)
+ checkUseNonce(old_nonce, False, url, "Old nonce (%r) passed." % (old_nonce,))
+
+
Wed May 2 16:57:00 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.sqlstore.SQLStore.txn_useNonce: check timestamp
hunk ./openid/store/sqlstore.py 14
+from openid.store import nonce
hunk ./openid/store/sqlstore.py 252
+ if abs(timestamp - time.time()) > nonce.SKEW:
+ return False
+
Wed May 2 16:51:32 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.filestore.FileOpenIDStore.useNonce: add timestamp check
hunk ./openid/store/filestore.py 334
+ if abs(timestamp - time.time()) > nonce.SKEW:
+ return False
+
Wed May 2 16:50:54 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.filestore.FileOpenIDStore.clean: fix bad reference to checkTimestamp
hunk ./openid/store/filestore.py 38
+from openid.store import nonce
hunk ./openid/store/filestore.py 400
- for nonce in nonces:
- if not checkTimestamp(nonce, now=now):
- filename = os.path.join(self.nonce_dir, nonce)
+ for nonce_fname in nonces:
+ if not nonce.checkTimestamp(nonce_fname, now=now):
+ filename = os.path.join(self.nonce_dir, nonce_fname)
Wed May 2 16:45:23 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.interface.OpenIDStore.useNonce: should return False if the timestamp is not current.
hunk ./openid/store/interface.py 133
- been used, return C{False}.
+ been used or the timestamp is not current, return C{False}.
+
+ You may use L{openid.store.nonce.SKEW} for your timestamp window.
Tue May 8 15:30:59 PDT 2007 cygnus@janrain.com
* Encode indirect OpenID 2 responses as POSTs when length exceeds OpenID 1 limit
hunk ./openid/message.py 43
+
+# Limit, in bytes, of identity provider and return_to URLs, including
+# response payload. See OpenID 1.1 specification, Appendix D.
+OPENID1_URL_LIMIT = 2047
hunk ./openid/server/server.py 110
- OPENID2_NS, IDENTIFIER_SELECT
+ OPENID2_NS, IDENTIFIER_SELECT, OPENID1_URL_LIMIT
hunk ./openid/server/server.py 120
+ENCODE_HTML_FORM = ('HTML form',)
hunk ./openid/server/server.py 902
+ def toFormMarkup(self):
+ """Returns the form markup for this response.
+
+ @returntype: str
+ """
+ return self.fields.toFormMarkup(
+ self.fields.getArg(OPENID_NS, 'return_to'))
+
+
+ def renderAsForm(self):
+ """Returns True if this response's encoding is
+ ENCODE_HTML_FORM. Convenience method for server authors.
+
+ @returntype: bool
+ """
+ return self.whichEncoding() == ENCODE_HTML_FORM
+
+
hunk ./openid/server/server.py 936
- return ENCODE_URL
+ if self.fields.getOpenIDNamespace() == OPENID2_NS and \
+ len(self.encodeToURL()) > OPENID1_URL_LIMIT:
+ return ENCODE_HTML_FORM
+ else:
+ return ENCODE_URL
hunk ./openid/server/server.py 1225
+ elif encode_as == ENCODE_HTML_FORM:
+ wr = self.responseFactory(code=HTTP_OK,
+ body=response.toFormMarkup())
hunk ./openid/server/server.py 1571
+ def toFormMarkup(self):
+ return self.toMessage().toFormMarkup(self.getReturnTo())
+
hunk ./openid/server/server.py 1582
- return ENCODE_URL
+ if self.openid_message.getOpenIDNamespace() == OPENID2_NS and \
+ len(self.encodeToURL()) > OPENID1_URL_LIMIT:
+ return ENCODE_HTML_FORM
+ else:
+ return ENCODE_URL
hunk ./openid/test/test_server.py 6
- IDENTIFIER_SELECT, no_default
+ IDENTIFIER_SELECT, no_default, OPENID1_URL_LIMIT
hunk ./openid/test/test_server.py 56
+ def test_browserWithReturnTo_OpenID2_GET(self):
+ return_to = "http://rp.unittest/consumer"
+ # will be a ProtocolError raised by Decode or CheckIDRequest.answer
+ args = Message.fromPostArgs({
+ 'openid.ns': OPENID2_NS,
+ 'openid.mode': 'monkeydance',
+ 'openid.identity': 'http://wagu.unittest/',
+ 'openid.claimed_id': 'http://wagu.unittest/',
+ 'openid.return_to': return_to,
+ })
+ e = server.ProtocolError(args, "plucky")
+ self.failUnless(e.hasReturnTo())
+ expected_args = {
+ 'openid.mode': ['error'],
+ 'openid.error': ['plucky'],
+ }
+
+ rt_base, result_args = e.encodeToURL().split('?', 1)
+ result_args = cgi.parse_qs(result_args)
+ self.failUnlessEqual(result_args, expected_args)
+
+ def test_browserWithReturnTo_OpenID2_GET(self):
+ return_to = "http://rp.unittest/consumer"
+ # will be a ProtocolError raised by Decode or CheckIDRequest.answer
+ args = Message.fromPostArgs({
+ 'openid.ns': OPENID2_NS,
+ 'openid.mode': 'monkeydance',
+ 'openid.identity': 'http://wagu.unittest/',
+ 'openid.claimed_id': 'http://wagu.unittest/',
+ 'openid.return_to': return_to,
+ })
+ e = server.ProtocolError(args, "plucky")
+ self.failUnless(e.hasReturnTo())
+ expected_args = {
+ 'openid.ns': [OPENID2_NS],
+ 'openid.mode': ['error'],
+ 'openid.error': ['plucky'],
+ }
+
+ rt_base, result_args = e.encodeToURL().split('?', 1)
+ result_args = cgi.parse_qs(result_args)
+ self.failUnlessEqual(result_args, expected_args)
+
+ def test_browserWithReturnTo_OpenID2_POST(self):
+ return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT)
+ # will be a ProtocolError raised by Decode or CheckIDRequest.answer
+ args = Message.fromPostArgs({
+ 'openid.ns': OPENID2_NS,
+ 'openid.mode': 'monkeydance',
+ 'openid.identity': 'http://wagu.unittest/',
+ 'openid.claimed_id': 'http://wagu.unittest/',
+ 'openid.return_to': return_to,
+ })
+ e = server.ProtocolError(args, "plucky")
+ self.failUnless(e.hasReturnTo())
+ expected_args = {
+ 'openid.ns': [OPENID2_NS],
+ 'openid.mode': ['error'],
+ 'openid.error': ['plucky'],
+ }
+
+ self.failUnless(e.whichEncoding() == server.ENCODE_HTML_FORM)
+ self.failUnless(e.toFormMarkup() == e.toMessage().toFormMarkup(
+ args.getArg(OPENID_NS, 'return_to')))
+
+ def test_browserWithReturnTo_OpenID1_exceeds_limit(self):
+ return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT)
+ # will be a ProtocolError raised by Decode or CheckIDRequest.answer
+ args = Message.fromPostArgs({
+ 'openid.mode': 'monkeydance',
+ 'openid.identity': 'http://wagu.unittest/',
+ 'openid.return_to': return_to,
+ })
+ e = server.ProtocolError(args, "plucky")
+ self.failUnless(e.hasReturnTo())
+ expected_args = {
+ 'openid.mode': ['error'],
+ 'openid.error': ['plucky'],
+ }
+
+ self.failUnless(e.whichEncoding() == server.ENCODE_URL)
+
+ rt_base, result_args = e.encodeToURL().split('?', 1)
+ result_args = cgi.parse_qs(result_args)
+ self.failUnlessEqual(result_args, expected_args)
+
hunk ./openid/test/test_server.py 531
+ def test_id_res_OpenID2_GET(self):
+ """
+ Check that when an OpenID 2 response does not exceed the
+ OpenID 1 message size, a GET response (i.e., redirect) is
+ issued.
+ """
+ request = server.CheckIDRequest(
+ identity = 'http://bombom.unittest/',
+ trust_root = 'http://burr.unittest/',
+ return_to = 'http://burr.unittest/999',
+ immediate = False,
+ op_endpoint = self.server.op_endpoint,
+ )
+ response = server.OpenIDResponse(request)
+ response.fields = Message.fromOpenIDArgs({
+ 'ns': OPENID2_NS,
+ 'mode': 'id_res',
+ 'identity': request.identity,
+ 'claimed_id': request.identity,
+ 'return_to': request.return_to,
+ })
+
+ self.failIf(response.renderAsForm())
+ self.failUnless(response.whichEncoding() == server.ENCODE_URL)
+ webresponse = self.encode(response)
+ self.failUnless(webresponse.headers.has_key('location'))
+
+ def test_id_res_OpenID2_POST(self):
+ """
+ Check that when an OpenID 2 response exceeds the OpenID 1
+ message size, a POST response (i.e., an HTML form) is
+ returned.
+ """
+ request = server.CheckIDRequest(
+ identity = 'http://bombom.unittest/',
+ trust_root = 'http://burr.unittest/',
+ return_to = 'http://burr.unittest/999',
+ immediate = False,
+ op_endpoint = self.server.op_endpoint,
+ )
+ response = server.OpenIDResponse(request)
+ response.fields = Message.fromOpenIDArgs({
+ 'ns': OPENID2_NS,
+ 'mode': 'id_res',
+ 'identity': request.identity,
+ 'claimed_id': request.identity,
+ 'return_to': 'x' * OPENID1_URL_LIMIT,
+ })
+
+ self.failUnless(response.renderAsForm())
+ self.failUnless(len(response.encodeToURL()) > OPENID1_URL_LIMIT)
+ self.failUnless(response.whichEncoding() == server.ENCODE_HTML_FORM)
+ webresponse = self.encode(response)
+ self.failUnlessEqual(webresponse.body, response.toFormMarkup())
+
+ def test_id_res_OpenID1_exceeds_limit(self):
+ """
+ Check that when an OpenID 1 response exceeds the OpenID 1
+ message size, a GET response is issued. Technically, this
+ shouldn't be permitted by the library, but this test is in
+ place to preserve the status quo for OpenID 1.
+ """
+ request = server.CheckIDRequest(
+ identity = 'http://bombom.unittest/',
+ trust_root = 'http://burr.unittest/',
+ return_to = 'http://burr.unittest/999',
+ immediate = False,
+ op_endpoint = self.server.op_endpoint,
+ )
+ response = server.OpenIDResponse(request)
+ response.fields = Message.fromOpenIDArgs({
+ 'mode': 'id_res',
+ 'identity': request.identity,
+ 'return_to': 'x' * OPENID1_URL_LIMIT,
+ })
+
+ self.failIf(response.renderAsForm())
+ self.failUnless(len(response.encodeToURL()) > OPENID1_URL_LIMIT)
+ self.failUnless(response.whichEncoding() == server.ENCODE_URL)
+ webresponse = self.encode(response)
+ self.failUnlessEqual(webresponse.headers['location'], response.encodeToURL())
+
Mon May 7 12:32:16 PDT 2007 cygnus@janrain.com
* Removed sendSRegFields, added OpenIDResponse.addExtension
hunk ./examples/server.py 197
- def approved(self, request, identifier=None):
+ def addSRegResponse(self, response):
hunk ./examples/server.py 208
- response = request.answer(True, identity=identifier)
- sreg_resp.toMessage(response.fields)
+ response.addExtension(sreg_resp)
hunk ./examples/server.py 210
+ def approved(self, request, identifier=None):
+ response = request.answer(True, identity=identifier)
+ self.addSRegResponse(response)
hunk ./openid/server/server.py 932
+
+ def addExtension(self, extension_response):
+ """
+ Add an extension response to this response message.
+
+ @param extension_response: An object that implements the
+ extension interface for adding arguments to an OpenID
+ message.
+ """
+ extension_response.toMessage(self.fields)
+
hunk ./openid/sreg.py 206
- def fromOpenIDRequest(cls, message):
+ def fromOpenIDRequest(cls, request):
hunk ./openid/sreg.py 211
- @param message: The arguments that were given for this OpenID
- authentication request
- @type message: {str:unicode}
+ @param request: The OpenID request
+ @type request: openid.server.CheckIDRequest
hunk ./openid/sreg.py 221
- message = message.copy()
+ message = request.message.copy()
hunk ./openid/sreg.py 516
-def sendSRegFields(openid_request, data, openid_response):
- """Convenience function for copying all the sreg data that was
- requested from a supplied set of sreg data into the response
- message. If no data were requested, no data will be sent.
-
- @param openid_request: The OpenID (checkid_*) request that may be
- requesting sreg data.
- @type openid_request: C{L{openid.server.server.OpenIDRequest}}
-
- @param data: The simple registration data to send. All
- requested fields that are present in this dictionary will be
- added to the response message.
- @type data: {str:str}
-
- @param openid_response: The OpenID C{id_res} response to which the
- simple registration data should be added
- @type openid_response: openid.server.OpenIDResponse
-
- @returns: Does not return a value; updates the openid_response
- instead.
- """
- sreg_request = SRegRequest.fromOpenIDRequest(openid_request.message)
- sreg_response = SRegResponse.extractResponse(sreg_request, data)
- sreg_response.toMessage(openid_response.fields)
-
hunk ./openid/test/test_sreg.py 151
+ def __init__(self):
+ self.message = Message()
+
hunk ./openid/test/test_sreg.py 169
+ openid_req = OpenIDRequest()
+
hunk ./openid/test/test_sreg.py 172
- req = TestingReq.fromOpenIDRequest(msg)
+ openid_req.message = msg
+
+ req = TestingReq.fromOpenIDRequest(openid_req)
hunk ./openid/test/test_sreg.py 469
- sreg.sendSRegFields(req, data, resp)
+ sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, data)
+ resp.addExtension(sreg_resp)
Mon Apr 9 15:27:31 PDT 2007 cygnus@janrain.com
* Add eu to trustroot TLD list
hunk ./openid/server/trustroot.py 16
- 'cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|fi|fj|fk|fm|fo|'
+ 'cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|'
Thu May 3 15:12:33 PDT 2007 Josh Hoyt <josh@janrain.com>
* Added test clarifying that the wildcard is only in the authority section of the trust root
hunk ./openid/test/trustroot.txt 108
-23: does not match
+24: does not match
hunk ./openid/test/trustroot.txt 133
-
+http://foo.com/* http://foo.com/anything
Wed May 2 14:17:24 PDT 2007 Kevin Turner <kevin@janrain.com>
* NEWS: mention store API changes
more people have custom stores than I thought.
hunk ./NEWS 30
- [_$_]
+
+If you've written your own custom store or code that interacts directly with it,
+you'll need to review the change notes in openid.store.interface.
+
Wed May 2 14:12:50 PDT 2007 Kevin Turner <kevin@janrain.com>
* store.interface: note removal of isDumb in @change notes
hunk ./openid/store/interface.py 13
- @change: Version 2.0 removed the C{storeNonce} and C{getAuthKey} methods,
- and changed the behavior of the C{L{useNonce}} method to support
- one-way nonces.
+ @change: Version 2.0 removed the C{storeNonce}, C{getAuthKey}, and C{isDumb}
+ methods, and changed the behavior of the C{L{useNonce}} method
+ to support one-way nonces.
hunk ./openid/store/interface.py 140
- longer part of the interface.
+ longer part of the interface.)
Mon Apr 16 16:04:51 PDT 2007 cygnus@janrain.com
* Do not send openid.ns values in OpenID 1 messages
hunk ./openid/message.py 248
- ns_key = 'openid.ns.' + alias
- args[ns_key] = ns_uri
+ if self.getOpenIDNamespace() != OPENID1_NS:
+ ns_key = 'openid.ns.' + alias
+ args[ns_key] = ns_uri
hunk ./openid/test/test_auth_request.py 94
- self.failUnlessEqual('bag:', post_args['openid.ns.ext0'])
Mon Apr 16 14:16:08 PDT 2007 cygnus@janrain.com
* Fix simple registration API to use toMessage()
hunk ./examples/server.py 209
- sreg_resp.addToOpenIDResponse(response.fields)
+ sreg_resp.toMessage(response.fields)
hunk ./openid/sreg.py 22
- sreg_resp.addToOpenIDResponse(openid_response)
+ sreg_resp.toMessage(openid_response.fields)
hunk ./openid/sreg.py 386
- @group Server: extractResponse, addToOpenIDResponse
+ @group Server: extractResponse
hunk ./openid/sreg.py 466
- def addToOpenIDResponse(self, response_message):
- """Add the data fields contained in this simple registration
- response to the supplied message, in the appropriate
- namespace.
-
- @param response_message: The OpenID id_res response message
- that will be returned to the relying party
- @type response_message: C{L{openid.message.Message}}
-
- @returns: Nothing; updates the response_message
+ def getExtensionArgs(self):
+ """
+ Return the sreg data to be sent in the response.
hunk ./openid/sreg.py 470
- response_message.updateArgs(self.ns_uri, self.data)
+ return self.data
hunk ./openid/sreg.py 529
- sreg_response.addToOpenIDResponse(openid_response.fields)
+ sreg_response.toMessage(openid_response.fields)
Tue Apr 3 13:59:48 PDT 2007 Kevin Turner <kevin@janrain.com>
* consumer.discover.OpenIDServiceEndpoint.supportsType: clarification
hunk ./openid/consumer/discover.py 64
- """Does this endpoint support this type?"""
- return ((type_uri == OPENID_2_0_TYPE and
- OPENID_IDP_2_0_TYPE in self.type_uris) or
- self.usesExtension(type_uri))
+ """Does this endpoint support this type?
+
+ I consider C{/server} endpoints to implicitly support C{/signon}.
+ """
+ return (
+ (type_uri in self.type_uris) or [_$_]
+ (type_uri == OPENID_2_0_TYPE and self.isOPIdentifier())
+ )
Tue Apr 3 13:55:36 PDT 2007 Kevin Turner <kevin@janrain.com>
* test.test_consumer.IDPDrivenTest: update mock verifyDiscoveryResults
(the sig of the real implementation changed)
hunk ./openid/test/test_consumer.py 1544
- endpoint = OpenIDServiceEndpoint()
- endpoint.claimed_id = identifier
- endpoint.server_url = self.endpoint.server_url
- endpoint.local_id = identifier
+ discovered_endpoint = OpenIDServiceEndpoint()
+ discovered_endpoint.claimed_id = identifier
+ discovered_endpoint.server_url = self.endpoint.server_url
+ discovered_endpoint.local_id = identifier
hunk ./openid/test/test_consumer.py 1549
- def verifyDiscoveryResults(identifier, server_url):
- iverified.append(endpoint)
- return endpoint
+ def verifyDiscoveryResults(identifier, endpoint):
+ self.failUnless(endpoint is self.endpoint)
+ iverified.append(discovered_endpoint)
+ return discovered_endpoint
hunk ./openid/test/test_consumer.py 1561
- self.failUnlessEqual(iverified, [endpoint])
+ self.failUnlessEqual(iverified, [discovered_endpoint])
hunk ./openid/test/test_consumer.py 1573
- def verifyDiscoveryResults(identifier, server_url):
+ def verifyDiscoveryResults(identifier, endpoint):
Tue Apr 3 12:28:56 PDT 2007 Kevin Turner <kevin@janrain.com>
* openid.message, openid.consumer.discover: update 2.0 URIs for spec rev 295
Also correct a number of places in the code that confused OPENID_2_0_TYPE and
OPENID2_NS. These used to be the same string, but they are no longer.
hunk ./openid/consumer/discover.py 20
-OPENID_IDP_2_0_TYPE = 'http://openid.net/server/2.0'
-OPENID_2_0_TYPE = 'http://openid.net/signon/2.0'
+OPENID_IDP_2_0_TYPE = 'http://specs.openid.net/auth/2.0/server'
+OPENID_2_0_TYPE = 'http://specs.openid.net/auth/2.0/signon'
hunk ./openid/consumer/discover.py 65
- return ((type_uri == OPENID_2_0_MESSAGE_NS and
+ return ((type_uri == OPENID_2_0_TYPE and
hunk ./openid/message.py 21
-IDENTIFIER_SELECT = "http://openid.net/identifier_select/2.0"
+IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select'
hunk ./openid/message.py 31
-OPENID2_NS = 'http://openid.net/signon/2.0'
+OPENID2_NS = 'http://specs.openid.net/auth/2.0'
hunk ./openid/test/data/test_discover/openid2_xrds.xml 7
- <Type>http://openid.net/signon/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
hunk ./openid/test/data/test_discover/openid2_xrds_no_local_id.xml 7
- <Type>http://openid.net/signon/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
hunk ./openid/test/data/test_discover/openid_1_and_2_xrds.xml 9
- <Type>http://openid.net/signon/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
hunk ./openid/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml 9
- <Type>http://openid.net/signon/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
hunk ./openid/test/data/test_discover/yadis_2_bad_local_id.xml 9
- <Type>http://openid.net/signon/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
hunk ./openid/test/data/test_discover/yadis_2entries_idp.xml 10
- <Type>http://openid.net/signon/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
hunk ./openid/test/data/test_discover/yadis_2entries_idp.xml 16
- <Type>http://openid.net/server/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
hunk ./openid/test/data/test_discover/yadis_idp.xml 8
- <Type>http://openid.net/server/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
hunk ./openid/test/data/test_discover/yadis_idp_delegate.xml 8
- <Type>http://openid.net/server/2.0</Type>
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
hunk ./openid/test/test_consumer.py 1610
- endpoint.type_uris = [OPENID2_NS]
+ endpoint.type_uris = [OPENID_2_0_TYPE]
hunk ./openid/test/test_discover.py 187
- discover.OPENID_2_0_TYPE)
+ discover.OPENID_2_0_MESSAGE_NS)
hunk ./openid/test/test_discover.py 655
- self.failUnless(self.endpoint.supportsType(t))
+ self.failUnless(self.endpoint.supportsType(t),
+ "Must support %r" % (t,))
hunk ./openid/test/test_discover.py 658
- self.failIf(self.endpoint.supportsType(t))
+ self.failIf(self.endpoint.supportsType(t),
+ "Shouldn't support %r" % (t,))
hunk ./openid/test/test_negotiation.py 8
-from openid.consumer.discover import OpenIDServiceEndpoint
+from openid.consumer.discover import OpenIDServiceEndpoint, OPENID_2_0_TYPE
hunk ./openid/test/test_negotiation.py 40
- self.endpoint.type_uris = [OPENID2_NS]
+ self.endpoint.type_uris = [OPENID_2_0_TYPE]
Tue Apr 3 13:51:13 PDT 2007 Kevin Turner <kevin@janrain.com>
* openid/test/data/test_discover/yadis_idp_last.xml: remove unused test data file
hunk ./openid/test/data/test_discover/yadis_idp_last.xml 1
-<?xml version="1.0" encoding="UTF-8"?>
-<xrds:XRDS xmlns:xrds="xri://$xrds"
- xmlns="xri://$xrd*($v*2.0)"
- xmlns:openid="http://openid.net/xmlns/1.0"
- >
- <XRD>
- <Service priority="10">
- <Type>http://openid.net/signon/2.0</Type>
- <URI>http://www.myopenid.com/server2_0</URI>
- </Service>
-
- <Service priority="20">
- <Type>http://openid.net/server/2.0</Type>
- <URI>http://www.myopenid.com/server_id</URI>
- </Service>
- </XRD>
-</xrds:XRDS>
rmfile ./openid/test/data/test_discover/yadis_idp_last.xml
Tue Apr 3 13:49:52 PDT 2007 Kevin Turner <kevin@janrain.com>
* test.test_consumer.IDPDrivenTest.setUp: remove obsolete type_uri
This value is outdated and the test still passed.
Turns out the tests still pass if I remove it entirely.
hunk ./openid/test/test_consumer.py 1527
- self.endpoint.type_uris = ['http://openid.net/server/2.0']
Tue Apr 3 13:48:20 PDT 2007 Kevin Turner <kevin@janrain.com>
* test.test_consumer.TestDiscoveryVerification: fix OPENID2_NS/TYPE confusion
These tests were still passing, but they were passing because
failUnlessRaises(ProtocolError) was too broad.
hunk ./openid/test/test_consumer.py 1623
- endpoint.type_uris = [OPENID2_NS]
+ endpoint.type_uris = [OPENID_2_0_TYPE]
hunk ./openid/test/test_consumer.py 1628
- self.failUnlessRaises(ProtocolError,
- self.consumer._verifyDiscoveryResults,
- self.message, endpoint)
-
+ try:
+ r = self.consumer._verifyDiscoveryResults(self.message, endpoint)
+ except ProtocolError, e:
+ # Should we make more ProtocolError subclasses?
+ self.failUnless('OP Endpoint mismatch' in str(e), e)
+ else:
+ self.fail("expected ProtocolError, %r returned." % (r,))
+ [_$_]
hunk ./openid/test/test_consumer.py 1640
- endpoint.type_uris = [OPENID2_NS]
+ endpoint.type_uris = [OPENID_2_0_TYPE]
hunk ./openid/test/test_consumer.py 1644
- self.failUnlessRaises(ProtocolError,
- self.consumer._verifyDiscoveryResults,
- self.message, endpoint)
+ try:
+ r = self.consumer._verifyDiscoveryResults(self.message, endpoint)
+ except ProtocolError, e:
+ self.failUnless('local_id mismatch' in str(e), e)
+ else:
+ self.fail("expected ProtocolError, %r returned." % (r,))
Mon Apr 2 17:07:13 PDT 2007 Kevin Turner <kevin@janrain.com>
* openid.message.OPENID1_NS: 'signon', not 'sso'
This agrees with spec rev 294, LJ yadis, 2idi XRDS, and PIP.
Not sure how we got openid.net/sso/1.0 in there.
hunk ./openid/message.py 28
-OPENID1_NS = 'http://openid.net/sso/1.0'
+OPENID1_NS = 'http://openid.net/signon/1.0'