incubator-heraldry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ket...@apache.org
Subject svn commit: r493403 - in /incubator/heraldry/libraries/python/openid/trunk/openid: consumer/consumer.py test/test_consumer.py
Date Sat, 06 Jan 2007 05:28:12 GMT
Author: keturn
Date: Fri Jan  5 21:28:12 2007
New Revision: 493403

URL: http://svn.apache.org/viewvc?view=rev&rev=493403
Log:
[python-to-heraldry @ Move nonce checking into _idRes]
various other small changes happened in consumer, including:

 * Remove getNonce from response, since it's not necessary

 * Parameterize the name of the query argument that is used for the
   OpenID 1 nonce

 * Update tests to test new function

Original author: Josh Hoyt <josh@janrain.com>
Date: 2006-12-28 01:18:51+00:00

Modified:
    incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_consumer.py

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py?view=diff&rev=493403&r1=493402&r2=493403
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py Fri Jan 
5 21:28:12 2007
@@ -439,6 +439,14 @@
     running.
     """
 
+    # The name of the query parameter that gets added to the return_to
+    # URL when using OpenID1. You can change this value if you want or
+    # need a different name, but don't make it start with openid,
+    # because it's not a standard protocol thing for OpenID1. For
+    # OpenID2, the library will take care of the nonce using standard
+    # OpenID query parameter names.
+    openid1_nonce_query_arg_name = 'janrain_nonce'
+
     session_types = {
         'DH-SHA1':DiffieHellmanSHA1ConsumerSession,
         'DH-SHA256':DiffieHellmanSHA256ConsumerSession,
@@ -452,7 +460,7 @@
     def begin(self, service_endpoint):
         assoc = self._getAssociation(service_endpoint)
         request = AuthRequest(service_endpoint, assoc)
-        request.return_to_args['nonce'] = mkNonce()
+        request.return_to_args[self.openid1_nonce_query_arg_name] = mkNonce()
         return request
 
     def complete(self, message, endpoint):
@@ -472,57 +480,12 @@
                 self._checkSetupNeeded(message)
             except SetupNeededError, why:
                 return SetupNeededResponse(endpoint, why.user_setup_url)
-
-            try:
-                response = self._doIdRes(message, endpoint)
-            except fetchers.HTTPFetchingError, why:
-                message = 'HTTP request failed: %s' % (str(why),)
-                return FailureResponse(endpoint, message)
             else:
-                if response.status == 'success':
-                    return self._checkNonce(endpoint.server_url, response)
-                else:
-                    return response
+                return self._doIdRes(message, endpoint)
         else:
             return FailureResponse(endpoint,
                                    'Invalid openid.mode: %r' % (mode,))
 
-    def _checkNonce(self, server_url, response):
-        nonce = response.getNonce()
-        if nonce is None:
-            if response.isOpenID1():
-                # Assume that this is an OpenID 1.X response and
-                # use/extract the nonce that we generated.
-                return_to = response.getReturnTo()
-                parsed_url = urlparse(return_to)
-                query = parsed_url[4]
-                for k, v in cgi.parse_qsl(query):
-                    if k == 'nonce':
-                        server_url = '' # came from us
-                        nonce = v
-                        break
-                else:
-                    msg = 'Nonce missing from return_to: %r' % (
-                        response.getReturnTo())
-                    return FailureResponse(response.endpoint, msg)
-            else:
-                msg = 'Nonce missing from response'
-                return FailureResponse(response.endpoint, msg)
-
-        # The nonce matches the signed nonce in the openid.return_to
-        # response parameter
-        try:
-            timestamp, salt = splitNonce(nonce)
-        except ValueError:
-            return FailureResponse(response.endpoint, 'Malformed nonce')
-        if not self.store.useNonce(server_url, timestamp, salt):
-            return FailureResponse(response.endpoint,
-                                   'Nonce missing from store')
-
-        # If the nonce check succeeded, return the original success
-        # response
-        return response
-
     def _makeKVPost(self, request_message, server_url):
         """Make a Direct Request to an OpenID Provider and return the
         result as a Message object.
@@ -592,19 +555,63 @@
         # discovery:
         if endpoint.isIdentifierSelect():
             try:
-                endpoint = self._verifyDiscoveryResults(
-                    endpoint, message)
+                endpoint = self._verifyDiscoveryResults(endpoint, message)
             except DiscoveryFailure, exc:
                 return FailureResponse(endpoint, exc.args[0])
         elif endpoint.getLocalID() != response_identity:
             fmt = 'Mismatch between delegate (%r) and server (%r) response'
             return FailureResponse(
-                endpoint, fmt % (endpoint.getLocalID(),
-                                 response_identity))
+                endpoint, fmt % (endpoint.getLocalID(), response_identity))
+
+        if self._idResCheckNonce(message, endpoint):
+            signed_fields = ['openid.' + f for f in signed_list]
+            return SuccessResponse(endpoint, message, signed_fields)
+        else:
+            return FailureResponse(endpoint, 'Nonce missing, old or used')
+
+    def _idResGetNonceOpenID1(self, message, endpoint):
+        """Extract the nonce from an OpenID 1 response
+
+        See the openid1_nonce_query_arg_name class variable
 
-        signed_fields = ['openid.' + f for f in signed_list]
-        return SuccessResponse(endpoint, message, signed_fields)
+        @returns: The nonce as a string or None
+        """
+        return_to = message.getArg(OPENID1_NS, 'return_to', None)
+        if return_to is None:
+            return None
 
+        parsed_url = urlparse(return_to)
+        query = parsed_url[4]
+        for k, v in cgi.parse_qsl(query):
+            if k == self.openid1_nonce_query_arg_name:
+                return v
+
+        return None
+
+    def _idResCheckNonce(self, message, endpoint):
+        if message.isOpenID1():
+            # This indicates that the nonce was generated by the consumer
+            nonce = self._idResGetNonceOpenID1(message, endpoint)
+            server_url = ''
+        else:
+            nonce = message.getArg(OPENID2_NS, 'response_nonce')
+            server_url = endpoint.server_url
+
+        if nonce is None:
+            oidutil.log('Nonce missing from response')
+            return False
+
+        try:
+            timestamp, salt = splitNonce(nonce)
+        except ValueError:
+            oidutil.log('Malformed nonce')
+            return False
+
+        if self.store.useNonce(server_url, timestamp, salt):
+            return True
+        else:
+            oidutil.log('Nonce already used or out of range')
+            return False
 
     def _idResCheckSignature(self, message, server_url):
         assoc_handle = message.getArg(OPENID_NS, 'assoc_handle')
@@ -634,7 +641,6 @@
 
 
     def _idResCheckForFields(self, message, signed_list):
-        # XXX: whether or not 'signed' is required depends on the assoc type.
         basic_fields = ['return_to', 'assoc_handle', 'sig']
         basic_sig_fields = ['return_to', 'identity',]
 
@@ -1292,18 +1298,6 @@
         @returntype: str
         """
         return self.getSigned(OPENID_NS, 'return_to')
-
-    def getNonce(self):
-        """Get the openid.nonce argument from this response.
-
-        This is useful for preventing replay attacks.
-
-        @returns: The nonce generated by the server, or C{None} if the
-            response did not contain an C{openid.nonce} argument.
-
-        @returntype: str
-        """
-        return self.getSigned(OPENID2_NS, 'response_nonce')
 
 
 

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/test/test_consumer.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/test/test_consumer.py?view=diff&rev=493403&r1=493402&r2=493403
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/test/test_consumer.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/test/test_consumer.py Fri Jan
 5 21:28:12 2007
@@ -172,8 +172,11 @@
         assert new_return_to.startswith(return_to)
         assert redirect_url.startswith(server_url)
 
+        nonce_key = consumer.openid1_nonce_query_arg_name
+        nonce = request.return_to_args[nonce_key]
+
         query = {
-            'nonce':request.return_to_args['nonce'],
+            nonce_key:nonce,
             'openid.mode':'id_res',
             'openid.return_to':new_return_to,
             'openid.identity':delegate_url,
@@ -423,17 +426,17 @@
 
         claimed_id = 'bogus.claimed'
 
-        self.message = Message.fromPostArgs(
-            {'openid.mode': 'id_res',
-             'openid.return_to': 'return_to (just anything)',
-             'openid.identity': claimed_id,
-             'openid.assoc_handle': 'does not matter',
-             'openid.sig': GOODSIG,
-             'openid.response_nonce': mkNonce(),
-             'openid.signed': 'identity,return_to,response_nonce,assoc_handle,claimed_id',
-             'openid.claimed_id': claimed_id,
-             'openid.op_endpoint': self.server_url,
-             'openid.ns':OPENID2_NS,
+        self.message = Message.fromOpenIDArgs(
+            {'mode': 'id_res',
+             'return_to': 'return_to (just anything)',
+             'identity': claimed_id,
+             'assoc_handle': 'does not matter',
+             'sig': GOODSIG,
+             'response_nonce': mkNonce(),
+             'signed': 'identity,return_to,response_nonce,assoc_handle,claimed_id',
+             'claimed_id': claimed_id,
+             'op_endpoint': self.server_url,
+             'ns':OPENID2_NS,
              })
 
         self.endpoint = OpenIDServiceEndpoint()
@@ -558,22 +561,6 @@
         self.failUnless(
             self.consumer.store.getAssociation(self.server_url) is None)
 
-class IdResFetchFailingConsumer(GenericConsumer):
-    message = 'fetch failed'
-
-    def _doIdRes(self, *args, **kwargs):
-        raise HTTPFetchingError(self.message)
-
-class TestFetchErrorInIdRes(TestIdRes):
-    consumer_class = IdResFetchFailingConsumer
-
-    def test_idResFailure(self):
-        message = Message.fromPostArgs({'openid.mode': 'id_res'})
-        r = self.consumer.complete(message, self.endpoint)
-        self.failUnlessEqual(r.status, FAILURE)
-        self.failUnlessEqual(r.identity_url, self.consumer_id)
-        r.message.index(IdResFetchFailingConsumer.message)
-
 class TestSetupNeeded(TestIdRes):
     def failUnlessSetupNeeded(self, expected_setup_url, message):
         try:
@@ -634,95 +621,86 @@
 
 class CheckAuthHappened(Exception): pass
 
-class CheckAuthDetectingConsumer(GenericConsumer):
-    def _checkAuth(self, *args):
-        raise CheckAuthHappened(args)
-
-
-class CheckNonceTest(TestIdRes, CatchLogs):
+class CheckNonceVerifyTest(TestIdRes, CatchLogs):
     def setUp(self):
         CatchLogs.setUp(self)
         TestIdRes.setUp(self)
+        self.consumer.openid1_nonce_query_arg_name = 'nonce'
 
     def tearDown(self):
         CatchLogs.tearDown(self)
 
-    def test_consumerNonce(self):
+    def test_openid1Success(self):
         """use consumer-generated nonce"""
         self.return_to = 'http://rt.unittest/?nonce=%s' % (mkNonce(),)
-        self.response = mkSuccess(self.endpoint,
-                                  {'return_to': self.return_to})
-        ret = self.consumer._checkNonce(None, self.response)
-        self.failUnlessEqual(ret.status, SUCCESS)
-        self.failUnlessEqual(ret.identity_url, self.consumer_id)
+        self.response = Message.fromOpenIDArgs({'return_to': self.return_to})
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failUnless(ret)
+        self.failUnlessLogEmpty()
 
     def test_consumerNonceOpenID2(self):
         """OpenID 2 does not use consumer-generated nonce"""
         self.return_to = 'http://rt.unittest/?nonce=%s' % (mkNonce(),)
-        self.response = mkSuccess(self.endpoint,
-                                  {'return_to': self.return_to,
-                                   'ns':OPENID2_NS})
-        ret = self.consumer._checkNonce(None, self.response)
-        self.failUnlessEqual(ret.status, FAILURE)
-        self.failUnless(ret.message.startswith('Nonce missing from response'))
+        self.response = Message.fromOpenIDArgs(
+            {'return_to': self.return_to, 'ns':OPENID2_NS})
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failIf(ret)
+        self.failUnlessLogMatches('Nonce missing from response')
 
     def test_serverNonce(self):
         """use server-generated nonce"""
-        self.response = mkSuccess(self.endpoint,
-                                  {'ns':OPENID2_NS,
-                                   'response_nonce': mkNonce(),})
-        ret = self.consumer._checkNonce(self.server_url, self.response)
-        self.failUnlessEqual(ret.status, SUCCESS)
-        self.failUnlessEqual(ret.identity_url, self.consumer_id)
-
+        self.response = Message.fromOpenIDArgs(
+            {'ns':OPENID2_NS, 'response_nonce': mkNonce(),})
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failUnless(ret)
+        self.failUnlessLogEmpty()
 
     def test_serverNonceOpenID1(self):
         """OpenID 1 does not use server-generated nonce"""
-        self.response = mkSuccess(self.endpoint,
-                                  {'ns':OPENID1_NS,
-                                   'return_to': 'http://return.to/',
-                                   'response_nonce': mkNonce(),})
-        ret = self.consumer._checkNonce(self.server_url, self.response)
-        self.failUnlessEqual(ret.status, FAILURE)
-        self.failUnless(ret.message.startswith('Nonce missing from return_to'))
+        self.response = Message.fromOpenIDArgs(
+            {'ns':OPENID1_NS,
+             'return_to': 'http://return.to/',
+             'response_nonce': mkNonce(),})
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failIf(ret)
+        self.failUnlessLogMatches('Nonce missing from response')
 
     def test_badNonce(self):
         """remove the nonce from the store"""
         nonce = mkNonce()
         stamp, salt = splitNonce(nonce)
         self.store.useNonce(self.server_url, stamp, salt)
-        self.response = mkSuccess(self.endpoint,
+        self.response = Message.fromOpenIDArgs(
                                   {'response_nonce': nonce,
                                    'ns':OPENID2_NS,
                                    })
-        ret = self.consumer._checkNonce(self.server_url, self.response)
-        self.failUnlessEqual(ret.status, FAILURE)
-        self.failUnlessEqual(ret.identity_url, self.consumer_id)
-        self.failUnless(ret.message.startswith('Nonce missing from store'),
-                        ret.message)
-
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failIf(ret)
+        self.failUnlessLogMatches('Nonce already used or out of range')
 
     def test_tamperedNonce(self):
         """Malformed nonce"""
-        self.response = mkSuccess(self.endpoint,
+        self.response = Message.fromOpenIDArgs(
                                   {'ns':OPENID2_NS,
                                    'response_nonce':'malformed'})
-        ret = self.consumer._checkNonce(self.server_url, self.response)
-        self.failUnlessEqual(ret.status, FAILURE)
-        self.failUnlessEqual(ret.identity_url, self.consumer_id)
-        self.failUnless(ret.message.startswith('Malformed nonce'), ret.message)
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failIf(ret)
 
     def test_missingNonce(self):
         """no nonce parameter on the return_to"""
-        self.response = mkSuccess(self.endpoint,
+        self.response = Message.fromOpenIDArgs(
                                   {'return_to': self.return_to})
-        ret = self.consumer._checkNonce(self.server_url, self.response)
-        self.failUnlessEqual(ret.status, FAILURE)
-        self.failUnlessEqual(ret.identity_url, self.consumer_id)
-        self.failUnless(ret.message.startswith('Nonce missing from return_to'))
-
+        ret = self.consumer._idResCheckNonce(self.response, self.endpoint)
+        self.failIf(ret)
 
+class CheckAuthDetectingConsumer(GenericConsumer):
+    def _checkAuth(self, *args):
+        raise CheckAuthHappened(args)
 
+    def _idResCheckNonce(self, *args):
+        """We're not testing nonce-checking, so just return success
+        when it asks."""
+        return True
 class TestCheckAuthTriggered(TestIdRes, CatchLogs):
     consumer_class = CheckAuthDetectingConsumer
 
@@ -809,12 +787,12 @@
         self.store.storeAssociation(self.server_url, bad_assoc)
 
         query = {
-            'openid.return_to':self.return_to,
-            'openid.identity':self.server_id,
-            'openid.assoc_handle':good_handle,
+            'return_to':self.return_to,
+            'identity':self.server_id,
+            'assoc_handle':good_handle,
             }
 
-        message = Message.fromPostArgs(query)
+        message = Message.fromOpenIDArgs(query)
         message = good_assoc.signMessage(message)
         info = self.consumer._doIdRes(message, self.endpoint)
         self.failUnlessEqual(info.status, SUCCESS, info.message)
@@ -1224,6 +1202,7 @@
             iverified.append(endpoint)
             return endpoint
         self.consumer._verifyDiscoveryResults = verifyDiscoveryResults
+        self.consumer._idResCheckNonce = lambda *args: True
         response = self.consumer._doIdRes(message, self.endpoint)
 
         self.failUnlessSuccess(response)



Mime
View raw message