diff options
-rw-r--r-- | Auth/OpenID/Message.php | 4 | ||||
-rw-r--r-- | Auth/OpenID/Server.php | 80 | ||||
-rw-r--r-- | Tests/Auth/OpenID/Server.php | 155 |
3 files changed, 222 insertions, 17 deletions
diff --git a/Auth/OpenID/Message.php b/Auth/OpenID/Message.php index 95d1103..6bfc5f8 100644 --- a/Auth/OpenID/Message.php +++ b/Auth/OpenID/Message.php @@ -42,6 +42,10 @@ define('Auth_OpenID_BARE_NS', 'Bare namespace'); // return null instead of returning a default. define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED'); +// Limit, in bytes, of identity provider and return_to URLs, including +// response payload. See OpenID 1.1 specification, Appendix D. +define('Auth_OpenID_OPENID1_URL_LIMIT', 2047); + // All OpenID protocol fields. Used to check namespace aliases. global $Auth_OpenID_OPENID_PROTOCOL_FIELDS; $Auth_OpenID_OPENID_PROTOCOL_FIELDS = array( diff --git a/Auth/OpenID/Server.php b/Auth/OpenID/Server.php index 58501cc..bd2619a 100644 --- a/Auth/OpenID/Server.php +++ b/Auth/OpenID/Server.php @@ -127,6 +127,11 @@ define(Auth_OpenID_ENCODE_URL, 'URL/redirect'); /** * @access private */ +define(Auth_OpenID_ENCODE_HTML_FORM, 'HTML form'); + +/** + * @access private + */ function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError') { return is_a($obj, $cls); @@ -152,18 +157,24 @@ class Auth_OpenID_ServerError { $this->reference = $reference; } + function getReturnTo() + { + if ($this->message && + $this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) { + return $this->message->getArg(Auth_OpenID_OPENID_NS, + 'return_to'); + } else { + return false; + } + } + /** * Returns the return_to URL for the request which caused this * error. */ function hasReturnTo() { - if ($this->message) { - return $this->message->hasKey(Auth_OpenID_OPENID_NS, - 'return_to'); - } else { - return false; - } + return $this->getReturnTo() !== false; } /** @@ -177,15 +188,7 @@ class Auth_OpenID_ServerError { return null; } - $return_to = $this->message->getArg(Auth_OpenID_OPENID_NS, - 'return_to'); - if (!$return_to) { - return null; - } - - return Auth_OpenID::appendArgs($return_to, - array('openid.mode' => 'error', - 'openid.error' => $this->toString())); + return $this->toMessage()->toURL($this->getReturnTo()); } /** @@ -201,6 +204,11 @@ class Auth_OpenID_ServerError { 'error' => $this->toString())); } + function toFormMarkup() + { + return $this->toMessage()->toFormMarkup($this->getReturnTo()); + } + function toMessage() { // Generate a Message object for sending to the relying party, @@ -232,7 +240,13 @@ class Auth_OpenID_ServerError { global $_Auth_OpenID_Request_Modes; if ($this->hasReturnTo()) { - return Auth_OpenID_ENCODE_URL; + if ($this->message->isOpenID2() && + (strlen($this->encodeToURL()) > + Auth_OpenID_OPENID1_URL_LIMIT)) { + return Auth_OpenID_ENCODE_HTML_FORM; + } else { + return Auth_OpenID_ENCODE_URL; + } } if (!$this->message) { @@ -1135,12 +1149,41 @@ class Auth_OpenID_ServerResponse { global $_Auth_OpenID_Request_Modes; if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) { - return Auth_OpenID_ENCODE_URL; + if ($this->fields->isOpenID2() && + (strlen($this->encodeToURL()) > + Auth_OpenID_OPENID1_URL_LIMIT)) { + return Auth_OpenID_ENCODE_HTML_FORM; + } else { + return Auth_OpenID_ENCODE_URL; + } } else { return Auth_OpenID_ENCODE_KVFORM; } } + /* + * Returns the form markup for this response. + * + * @return str + */ + function toFormMarkup() + { + return $this->fields->toFormMarkup( + $this->fields->getArg(Auth_OpenID_OPENID_NS, 'return_to')); + } + + /* + * Returns True if this response's encoding is ENCODE_HTML_FORM. + * Convenience method for server authors. + * + * @return bool + */ + function renderAsForm() + { + return $this->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM; + } + + function encodeToURL() { return $this->fields->toURL($this->request->return_to); @@ -1364,6 +1407,9 @@ class Auth_OpenID_Encoder { $location = $response->encodeToURL(); $wr = new $cls(AUTH_OPENID_HTTP_REDIRECT, array('location' => $location)); + } else if ($encode_as == Auth_OpenID_ENCODE_HTML_FORM) { + $wr = new $cls(AUTH_OPENID_HTTP_OK, array(), + $response->toFormMarkup()); } else { return new Auth_OpenID_EncodingError($response); } diff --git a/Tests/Auth/OpenID/Server.php b/Tests/Auth/OpenID/Server.php index 41cfcab..49e21a5 100644 --- a/Tests/Auth/OpenID/Server.php +++ b/Tests/Auth/OpenID/Server.php @@ -89,6 +89,78 @@ class Tests_Auth_OpenID_Test_ServerError extends PHPUnit_TestCase { $this->assertEquals($result_args, $expected_args); } + function test_browserWithReturnTo_OpenID2_GET() + { + $return_to = "http://rp.unittest/consumer"; + // will be a ProtocolError raised by Decode or + // CheckIDRequest.answer + $args = Auth_OpenID_Message::fromPostArgs(array( + 'openid.ns' => Auth_OpenID_OPENID2_NS, + 'openid.mode' => 'monkeydance', + 'openid.identity' => 'http://wagu.unittest/', + 'openid.claimed_id' => 'http://wagu.unittest/', + 'openid.return_to' => $return_to)); + + $e = new Auth_OpenID_ServerError($args, "plucky"); + $this->assertTrue($e->hasReturnTo()); + $expected_args = array('openid.ns' => Auth_OpenID_OPENID2_NS, + 'openid.mode' => 'error', + 'openid.error' => 'plucky'); + + list($rt_base, $result_args_s) = explode('?', $e->encodeToURL(), 2); + $result_args = Auth_OpenID::parse_str($result_args_s); + + $this->assertEquals($result_args, $expected_args); + } + + function test_browserWithReturnTo_OpenID2_POST() + { + $return_to = "http://rp.unittest/consumer" . str_repeat('x', Auth_OpenID_OPENID1_URL_LIMIT); + // will be a ProtocolError raised by Decode or + // CheckIDRequest.answer + $args = Auth_OpenID_Message::fromPostArgs(array( + 'openid.ns' => Auth_OpenID_OPENID2_NS, + 'openid.mode' => 'monkeydance', + 'openid.identity' => 'http://wagu.unittest/', + 'openid.claimed_id' => 'http://wagu.unittest/', + 'openid.return_to' => $return_to)); + + $e = new Auth_OpenID_ServerError($args, "plucky"); + $this->assertTrue($e->hasReturnTo()); + $expected_args = array('openid.ns' => Auth_OpenID_OPENID2_NS, + 'openid.mode' => 'error', + 'openid.error' => 'plucky'); + + $this->assertTrue($e->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM); + + $this->assertTrue($e->toFormMarkup() == + $e->toMessage()->toFormMarkup($args->getArg(Auth_OpenID_OPENID_NS, 'return_to'))); + } + + function test_browserWithReturnTo_OpenID1_exceeds_limit() + { + $return_to = "http://rp.unittest/consumer" . str_repeat('x', Auth_OpenID_OPENID1_URL_LIMIT); + // will be a ProtocolError raised by Decode or + // CheckIDRequest.answer + $args = Auth_OpenID_Message::fromPostArgs(array( + 'openid.mode' => 'monkeydance', + 'openid.identity' => 'http://wagu.unittest/', + 'openid.return_to' => $return_to)); + + $this->assertTrue($args->isOpenID1()); + + $e = new Auth_OpenID_ServerError($args, "plucky"); + $this->assertTrue($e->hasReturnTo()); + $expected_args = array('openid.mode' => 'error', + 'openid.error' => 'plucky'); + + $this->assertTrue($e->whichEncoding() == Auth_OpenID_ENCODE_URL); + + list($rt_base, $result_args_s) = explode('?', $e->encodeToURL(), 2); + $result_args = Auth_OpenID::parse_str($result_args_s); + $this->assertEquals($result_args, $expected_args); + } + function test_noReturnTo() { // will be a ProtocolError raised by Decode or CheckIDRequest.answer @@ -547,6 +619,89 @@ class Tests_Auth_OpenID_Test_Encode extends PHPUnit_TestCase { $this->op_endpoint); } + function encode($thing) { + return $this->encoder->encode($thing); + } + + function test_id_res_OpenID2_GET() + { + /* Check that when an OpenID 2 response does not exceed the + OpenID 1 message size, a GET response (i.e., redirect) is + issued. */ + $request = new Auth_OpenID_CheckIDRequest( + 'http://bombom.unittest/', + 'http://burr.unittest/999', + 'http://burr.unittest/', + false, + $this->server->op_endpoint); + + $response = new Auth_OpenID_ServerResponse($request); + $response->fields = Auth_OpenID_Message::fromOpenIDArgs(array( + 'ns' => Auth_OpenID_OPENID2_NS, + 'mode' => 'id_res', + 'identity' => $request->identity, + 'claimed_id' => $request->identity, + 'return_to' => $request->return_to)); + + $this->assertFalse($response->renderAsForm()); + $this->assertTrue($response->whichEncoding() == Auth_OpenID_ENCODE_URL); + $webresponse = $this->encode($response); + $this->assertTrue(array_key_exists('location', $webresponse->headers)); + } + + function test_id_res_OpenID2_POST() + { + /* Check that when an OpenID 2 response exceeds the OpenID 1 + message size, a POST response (i.e., an HTML form) is + returned. */ + $request = new Auth_OpenID_CheckIDRequest( + 'http://bombom.unittest/', + 'http://burr.unittest/999', + 'http://burr.unittest/', + false, + $this->server->op_endpoint); + + $response = new Auth_OpenID_ServerResponse($request); + $response->fields = Auth_OpenID_Message::fromOpenIDArgs(array( + 'ns' => Auth_OpenID_OPENID2_NS, + 'mode' => 'id_res', + 'identity' => $request->identity, + 'claimed_id' => $request->identity, + 'return_to' => str_repeat('x', Auth_OpenID_OPENID1_URL_LIMIT))); + + $this->assertTrue($response->renderAsForm()); + $this->assertTrue(strlen($response->encodeToURL()) > Auth_OpenID_OPENID1_URL_LIMIT); + $this->assertTrue($response->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM); + $webresponse = $this->encode($response); + $this->assertEquals($webresponse->body, $response->toFormMarkup()); + } + + function test_id_res_OpenID1_exceeds_limit() + { + /* 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 = new Auth_OpenID_CheckIDRequest( + 'http://bombom.unittest/', + 'http://burr.unittest/999', + 'http://burr.unittest/', + false, + $this->server->op_endpoint); + + $response = new Auth_OpenID_ServerResponse($request); + $response->fields = Auth_OpenID_Message::fromOpenIDArgs(array( + 'mode' => 'id_res', + 'identity' => $request->identity, + 'return_to' => str_repeat('x', Auth_OpenID_OPENID1_URL_LIMIT))); + + $this->assertFalse($response->renderAsForm()); + $this->assertTrue(strlen($response->encodeToURL()) > Auth_OpenID_OPENID1_URL_LIMIT); + $this->assertTrue($response->whichEncoding() == Auth_OpenID_ENCODE_URL); + $webresponse = $this->encode($response); + $this->assertEquals($webresponse->headers['location'], $response->encodeToURL()); + } + function test_id_res() { $request = new Auth_OpenID_CheckIDRequest( |