diff options
author | tailor <cygnus@janrain.com> | 2007-03-07 00:28:02 +0000 |
---|---|---|
committer | tailor <cygnus@janrain.com> | 2007-03-07 00:28:02 +0000 |
commit | 9807f88ec4e58e4fa6a34614f50a3b4b6f554b55 (patch) | |
tree | 17f73cea8b63bd63ac035b8138cf150bc5274df7 | |
parent | eb308abef06912899f7b1d4bb02b1877b028ee14 (diff) | |
download | php-openid-9807f88ec4e58e4fa6a34614f50a3b4b6f554b55.zip php-openid-9807f88ec4e58e4fa6a34614f50a3b4b6f554b55.tar.gz php-openid-9807f88ec4e58e4fa6a34614f50a3b4b6f554b55.tar.bz2 |
[project @ Add session negotiation tests and fix consumer]
-rw-r--r-- | Auth/OpenID/Consumer.php | 59 | ||||
-rw-r--r-- | Tests/Auth/OpenID/Consumer.php | 3 | ||||
-rw-r--r-- | Tests/Auth/OpenID/Negotiation.php | 338 | ||||
-rw-r--r-- | Tests/TestDriver.php | 1 |
4 files changed, 387 insertions, 14 deletions
diff --git a/Auth/OpenID/Consumer.php b/Auth/OpenID/Consumer.php index 2ab5696..014f6d5 100644 --- a/Auth/OpenID/Consumer.php +++ b/Auth/OpenID/Consumer.php @@ -1122,7 +1122,8 @@ class Auth_OpenID_GenericConsumer { } $resp_message = $this->_makeKVPost($request, $server_url); - if ($resp_message == null) { + if (($resp_message === null) || + (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) { return false; } @@ -1207,19 +1208,19 @@ class Auth_OpenID_GenericConsumer { $resp = $this->fetcher->post($server_url, $body); if ($resp === null) { - return null; + return Auth_OpenID_ServerErrorContainer::fromMessage(''); } - $response = Auth_OpenID_KVForm::toArray($resp->body); + $response_message = Auth_OpenID_Message::fromKVForm($resp->body); if ($resp->status == 400) { - return null; + return Auth_OpenID_ServerErrorContainer::fromMessage( + $response_message); } else if ($resp->status != 200) { return null; } - $response = Auth_OpenID_Message::fromKVForm($resp->body); - return $response; + return $response_message; } /** @@ -1248,13 +1249,15 @@ class Auth_OpenID_GenericConsumer { function _negotiateAssociation($endpoint) { - # Get our preferred session/association type from the negotiatior. + // Get our preferred session/association type from the negotiatior. list($assoc_type, $session_type) = $this->negotiator->getAllowedType(); $assoc = $this->_requestAssociation( $endpoint, $assoc_type, $session_type); - if (is_a($assoc, 'Auth_OpenID_ServerError')) { + if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { + $why = $assoc; + // Any error message whose code is not 'unsupported-type' // should be considered a total failure. if (($why->error_code != 'unsupported-type') || @@ -1284,7 +1287,7 @@ class Auth_OpenID_GenericConsumer { // oidutil.log('Server responded with unsupported association ' // 'session but did not supply a fallback.') return null; - } else if (!$self->negotiator->isAllowed($assoc_type, + } else if (!$this->negotiator->isAllowed($assoc_type, $session_type)) { // fmt = ('Server sent unsupported session/association type: ' // 'session_type=%s, assoc_type=%s') @@ -1294,10 +1297,10 @@ class Auth_OpenID_GenericConsumer { // Attempt to create an association from the assoc_type // and session_type that the server told us it // supported. - $assoc = $self->_requestAssociation( + $assoc = $this->_requestAssociation( $endpoint, $assoc_type, $session_type); - if (is_a($assoc, 'Auth_OpenID_ServerError')) { + if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { // Do not keep trying, since it rejected the // association type that it told us to use. // oidutil.log('Server %s refused its suggested association @@ -1319,14 +1322,17 @@ class Auth_OpenID_GenericConsumer { list($assoc_session, $args) = $this->_createAssociateRequest( $endpoint, $assoc_type, $session_type); - $response = $this->_makeKVPost($args, $endpoint->server_url); + $response_message = $this->_makeKVPost($args, $endpoint->server_url); - if ($response === null) { + if ($response_message === null) { // oidutil.log('openid.associate request failed: %s' % (why[0],)) return null; + } else if (is_a($response_message, + 'Auth_OpenID_ServerErrorContainer')) { + return $response_message; } - return $this->_extractAssociation($response, $assoc_session); + return $this->_extractAssociation($response_message, $assoc_session); } function _extractAssociation($assoc_response, $assoc_session) @@ -1806,6 +1812,31 @@ class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse { } /** + * Exception that is raised when the server returns a 400 response + * code to a direct request. + */ +class Auth_OpenID_ServerErrorContainer { + function Auth_OpenID_ServerErrorContainer($error_text, + $error_code, + $message) + { + $this->error_text = $error_text; + $this->error_code = $error_code; + $this->message = $message; + } + + function fromMessage($message) + { + $error_text = $message->getArg( + Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>'); + $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code'); + return new Auth_OpenID_ServerErrorContainer($error_text, + $error_code, + $message); + } +} + +/** * A response with a status of Auth_OpenID_CANCEL. Indicates that the * user cancelled the OpenID authentication request. This has two * relevant attributes: diff --git a/Tests/Auth/OpenID/Consumer.php b/Tests/Auth/OpenID/Consumer.php index 9e86547..4e784e9 100644 --- a/Tests/Auth/OpenID/Consumer.php +++ b/Tests/Auth/OpenID/Consumer.php @@ -1278,6 +1278,9 @@ class _ExceptionRaisingMockFetcher { function post($url, $body) { __raiseError(E_MOCK_FETCHER_EXCEPTION); + + return new Services_Yadis_HTTPResponse($url, 400, + array(), ''); } } diff --git a/Tests/Auth/OpenID/Negotiation.php b/Tests/Auth/OpenID/Negotiation.php new file mode 100644 index 0000000..147ac06 --- /dev/null +++ b/Tests/Auth/OpenID/Negotiation.php @@ -0,0 +1,338 @@ +<?php + +require_once "PHPUnit.php"; +require_once "Tests/Auth/OpenID/TestUtil.php"; +require_once "Tests/Auth/OpenID/MemStore.php"; + +require_once "Auth/OpenID/Message.php"; +require_once "Auth/OpenID/Consumer.php"; + +/** + * A consumer whose _requestAssocation will return predefined results + * instead of trying to actually perform association requests. + */ +class ErrorRaisingConsumer extends Auth_OpenID_GenericConsumer { + // The list of objects to be returned by successive calls to + // _requestAssocation. Each call will pop the first element from + // this list and return it to _negotiateAssociation. If the + // element is a Message object, it will be wrapped in a + // ServerErrorContainer exception. Otherwise it will be returned + // as-is. + var $return_messages = array(); + + function _requestAssociation($endpoint, $assoc_type, $session_type) + { + $m = array_pop($this->return_messages); + if (is_a($m, 'Auth_OpenID_Message')) { + return Auth_OpenID_ServerErrorContainer::fromMessage($m); + } else { + return $m; + } + } +} + +/** + * Test the session type negotiation behavior of an OpenID 2 consumer. + */ +class TestOpenID2SessionNegotiation extends PHPUnit_TestCase { + function setUp() + { + $dumb = null; + $this->consumer = new ErrorRaisingConsumer($dumb); + $this->endpoint = new Auth_OpenID_ServiceEndpoint(); + $this->endpoint->type_uris = array(Auth_OpenID_OPENID2_NS); + $this->endpoint->server_url = 'bogus'; + } + + /** + * Test the case where the response to an associate request is a + * server error or is otherwise undecipherable. + */ + function testBadResponse() + { + $this->consumer->return_messages = array( + new Auth_OpenID_Message($this->endpoint->preferredNamespace())); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + // $this->failUnlessLogMatches('Server error when requesting an association') + } + + /** + * Test the case where the association type (assoc_type) returned + * in an unsupported-type response is absent. + */ + function testEmptyAssocType() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', null); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'new-session-type'); + + $this->consumer->return_messages = array($msg); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Unsupported association type', + // 'Server responded with unsupported association ' + + // 'session but did not supply a fallback.') + } + + /** + * Test the case where the session type (session_type) returned in + * an unsupported-type response is absent. + */ + function testEmptySessionType() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'new-assoc-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', null); + + $this->consumer->return_messages = array($msg); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Unsupported association type', + // 'Server responded with unsupported association ' + + // 'session but did not supply a fallback.') + } + + /** + * Test the case where an unsupported-type response specifies a + * preferred (assoc_type, session_type) combination that is not + * allowed by the consumer's SessionNegotiator. + */ + function testNotAllowed() + { + $allowed_types = array( + array('assoc_bogus', 'session_bogus') + ); + + $negotiator = new Auth_OpenID_SessionNegotiator($allowed_types); + $this->consumer->negotiator = $negotiator; + + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'not-allowed'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'not-allowed'); + + $this->consumer->return_messages = array($msg); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Unsupported association type', + // 'Server sent unsupported session/association type:') + } + + /** + * Test the case where an unsupported-type response triggers a + * retry to get an association with the new preferred type. + */ + function testUnsupportedWithRetry() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'HMAC-SHA1'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'DH-SHA1'); + + $assoc = new Auth_OpenID_Association( + 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1'); + + $this->consumer->return_messages = array($msg, $assoc); + $this->assertTrue($this->consumer->_negotiateAssociation($this->endpoint) === $assoc); + + // $this->failUnlessLogMatches('Unsupported association type'); + } + + /** + * Test the case where an unsupported-typ response triggers a + * retry, but the retry fails and None is returned instead. + */ + function testUnsupportedWithRetryAndFail() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'HMAC-SHA1'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'DH-SHA1'); + + $this->consumer->return_messages = array($msg, + new Auth_OpenID_Message($this->endpoint->preferredNamespace())); + + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Unsupported association type', + // 'Server %s refused' % ($this->endpoint.server_url)) + } + + /** + * Test the valid case, wherein an association is returned on the + * first attempt to get one. + */ + function testValid() + { + $assoc = new Auth_OpenID_Association( + 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1'); + + $this->consumer->return_messages = array($assoc); + $this->assertTrue($this->consumer->_negotiateAssociation($this->endpoint) === $assoc); + // $this->failUnlessLogEmpty() + } +} + +/** + * Tests for the OpenID 1 consumer association session behavior. See + * the docs for TestOpenID2SessionNegotiation. Notice that this class + * is not a subclass of the OpenID 2 tests. Instead, it uses many of + * the same inputs but inspects the log messages logged with + * oidutil.log. See the calls to $this->failUnlessLogMatches. Some + * of these tests pass openid2-style messages to the openid 1 + * association processing logic to be sure it ignores the extra data. + */ +class TestOpenID1SessionNegotiation extends PHPUnit_TestCase { + function setUp() + { + $dumb = null; + $this->consumer = new ErrorRaisingConsumer($dumb); + + $this->endpoint = new Auth_OpenID_ServiceEndpoint(); + $this->endpoint->type_uris = array(Auth_OpenID_OPENID1_NS); + $this->endpoint->server_url = 'bogus'; + } + + function testBadResponse() + { + $this->consumer->return_messages = + array(new Auth_OpenID_Message($this->endpoint->preferredNamespace())); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + // $this->failUnlessLogMatches('Server error when requesting an association') + } + + function testEmptyAssocType() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', null); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'new-session-type'); + + $this->consumer->return_messages = array($msg); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Server error when requesting an association') + } + + function testEmptySessionType() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'new-assoc-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', null); + + $this->consumer->return_messages = array($msg); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Server error when requesting an association'); + } + + function testNotAllowed() + { + $allowed_types = array( + array('assoc_bogus', 'session_bogus') + ); + + $negotiator = new Auth_OpenID_SessionNegotiator($allowed_types); + $this->consumer->negotiator = $negotiator; + + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'not-allowed'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'not-allowed'); + + $this->consumer->return_messages = array($msg); + $this->assertEquals($this->consumer->_negotiateAssociation($this->endpoint), null); + + // $this->failUnlessLogMatches('Server error when requesting an association') + } + + function testUnsupportedWithRetry() + { + $msg = new Auth_OpenID_Message($this->endpoint->preferredNamespace()); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error', 'Unsupported type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'error_code', 'unsupported-type'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'assoc_type', 'HMAC-SHA1'); + $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type', 'DH-SHA1'); + + $assoc = new Auth_OpenID_Association( + 'handle', 'secretxx', 'issued', 10000, 'HMAC-SHA1'); + + $this->consumer->return_messages = array($assoc, $msg); + + $result = $this->consumer->_negotiateAssociation($this->endpoint); + $this->assertTrue($result === null); + + // $this->failUnlessLogMatches('Server error when requesting an association') + } + + function testValid() + { + $assoc = new Auth_OpenID_Association( + 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1'); + + $this->consumer->return_messages = array($assoc); + $this->assertTrue($this->consumer->_negotiateAssociation($this->endpoint) === $assoc); + // $this->failUnlessLogEmpty() + } +} + +class TestNegotiatorBehaviors extends PHPUnit_TestCase { + function setUp() + { + $this->allowed_types = array( + array('assoc1', 'session1'), + array('assoc2', 'session2') + ); + + $this->n = new Auth_OpenID_SessionNegotiator($this->allowed_types); + } + + function testAddAllowedTypeNoSessionTypes() + { + $this->assertFalse($this->n->addAllowedType('invalid')); + } + + function testAddAllowedTypeBadSessionType() + { + $this->assertFalse($this->n->addAllowedType('assoc1', 'invalid')); + } + + function testAddAllowedTypeContents() + { + $assoc_type = 'HMAC-SHA1'; + $this->assertTrue($this->n->addAllowedType($assoc_type)); + + foreach (Auth_OpenID_getSessionTypes($assoc_type) as $typ) { + $this->assertTrue(in_array(array($assoc_type, $typ), + $this->n->allowed_types)); + } + } +} + +class Tests_Auth_OpenID_Negotiation extends PHPUnit_TestSuite { + + function getName() + { + return 'Tests_Auth_OpenID_Negotiation'; + } + + function Tests_Auth_OpenID_Negotiation() + { + $this->addTestSuite('TestNegotiatorBehaviors'); + $this->addTestSuite('TestOpenID1SessionNegotiation'); + $this->addTestSuite('TestOpenID2SessionNegotiation'); + } +} + +?>
\ No newline at end of file diff --git a/Tests/TestDriver.php b/Tests/TestDriver.php index 88ca1fc..7d5efb4 100644 --- a/Tests/TestDriver.php +++ b/Tests/TestDriver.php @@ -124,6 +124,7 @@ $_tests = array( 'HMACSHA1', 'KVForm', 'Message', + 'Negotiation', 'Nonce', 'OpenID_Yadis', 'Parse', |