AssociationResponse.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <?php
  2. require_once "Tests/Auth/OpenID/TestUtil.php";
  3. require_once "Tests/Auth/OpenID/MemStore.php";
  4. require_once "Auth/OpenID/Message.php";
  5. require_once "Auth/OpenID/Server.php";
  6. require_once "Auth/OpenID/Consumer.php";
  7. require_once "Auth/OpenID/Association.php";
  8. // Some values we can use for convenience (see mkAssocResponse)
  9. global $association_response_values;
  10. $association_response_values = array(
  11. 'expires_in' => '1000',
  12. 'assoc_handle' => 'a handle',
  13. 'assoc_type' => 'a type',
  14. 'session_type' => 'a session type',
  15. 'ns' => Auth_OpenID_OPENID2_NS
  16. );
  17. /**
  18. * Build an association response message that contains the specified
  19. * subset of keys. The values come from association_response_values.
  20. *
  21. * This is useful for testing for missing keys and other times that we
  22. * don't care what the values are.
  23. */
  24. function mkAssocResponse($keys)
  25. {
  26. global $association_response_values;
  27. $args = array();
  28. foreach ($keys as $key) {
  29. $args[$key] = $association_response_values[$key];
  30. }
  31. return Auth_OpenID_Message::fromOpenIDArgs($args);
  32. }
  33. class Tests_Auth_OpenID_AssociationResponse extends PHPUnit_Framework_TestCase {
  34. function setUp()
  35. {
  36. $this->store = new Tests_Auth_OpenID_MemStore();
  37. $this->consumer = new Auth_OpenID_GenericConsumer($this->store);
  38. $this->endpoint = new Auth_OpenID_ServiceEndpoint();
  39. }
  40. function failUnlessProtocolError($thing)
  41. {
  42. $this->assertTrue(Auth_OpenID::isFailure($thing));
  43. }
  44. function _run($keys)
  45. {
  46. $msg = mkAssocResponse($keys);
  47. $dumb = null;
  48. $this->assertTrue(Auth_OpenID::isFailure($this->consumer->_extractAssociation($msg, $dumb)));
  49. }
  50. }
  51. /**
  52. * Test for returning an error upon missing fields in association
  53. * responses for OpenID 2
  54. */
  55. class TestExtractAssociationMissingFieldsOpenID2 extends Tests_Auth_OpenID_AssociationResponse {
  56. function test_noFields_openid2()
  57. {
  58. $this->_run(array('ns'));
  59. }
  60. function test_missingExpires_openid2()
  61. {
  62. $this->_run(array('assoc_handle', 'assoc_type', 'session_type', 'ns'));
  63. }
  64. function test_missingHandle_openid2()
  65. {
  66. $this->_run(array('expires_in', 'assoc_type', 'session_type', 'ns'));
  67. }
  68. function test_missingAssocType_openid2()
  69. {
  70. $this->_run(array('expires_in', 'assoc_handle', 'session_type', 'ns'));
  71. }
  72. function test_missingSessionType_openid2()
  73. {
  74. $this->_run(array('expires_in', 'assoc_handle', 'assoc_type', 'ns'));
  75. }
  76. }
  77. /**
  78. * Test for returning an error upon missing fields in association
  79. * responses for OpenID 2
  80. */
  81. class TestExtractAssociationMissingFieldsOpenID1 extends Tests_Auth_OpenID_AssociationResponse {
  82. function test_noFields_openid1()
  83. {
  84. $this->_run(array());
  85. }
  86. function test_missingExpires_openid1()
  87. {
  88. $this->_run(array('assoc_handle', 'assoc_type'));
  89. }
  90. function test_missingHandle_openid1()
  91. {
  92. $this->_run(array('expires_in', 'assoc_type'));
  93. }
  94. function test_missingAssocType_openid1()
  95. {
  96. $this->_run(array('expires_in', 'assoc_handle'));
  97. }
  98. }
  99. class DummyAssocationSession {
  100. function DummyAssocationSession($session_type, $allowed_assoc_types=array())
  101. {
  102. $this->session_type = $session_type;
  103. $this->allowed_assoc_types = $allowed_assoc_types;
  104. }
  105. }
  106. class ExtractAssociationSessionTypeMismatch extends Tests_Auth_OpenID_AssociationResponse {
  107. function _run($requested_session_type, $response_session_type, $openid1=false)
  108. {
  109. global $association_response_values;
  110. $assoc_session = new DummyAssocationSession($requested_session_type);
  111. $keys = array_keys($association_response_values);
  112. if ($openid1) {
  113. if (in_array('ns', $keys)) {
  114. unset($keys[array_search('ns', $keys)]);
  115. }
  116. }
  117. $msg = mkAssocResponse($keys);
  118. $msg->setArg(Auth_OpenID_OPENID_NS, 'session_type',
  119. $response_session_type);
  120. $this->assertTrue(
  121. $this->consumer->_extractAssociation($msg, $assoc_session) === null);
  122. }
  123. function test_typeMismatchNoEncBlank_openid2()
  124. {
  125. $this->_run('no-encryption', '');
  126. }
  127. function test_typeMismatchDHSHA1NoEnc_openid2()
  128. {
  129. $this->_run('DH-SHA1', 'no-encryption');
  130. }
  131. function test_typeMismatchDHSHA256NoEnc_openid2()
  132. {
  133. $this->_run('DH-SHA256', 'no-encryption');
  134. }
  135. function test_typeMismatchNoEncDHSHA1_openid2()
  136. {
  137. $this->_run('no-encryption', 'DH-SHA1');
  138. }
  139. function test_typeMismatchDHSHA1NoEnc_openid1()
  140. {
  141. $this->_run('DH-SHA1', 'DH-SHA256', true);
  142. }
  143. function test_typeMismatchDHSHA256NoEnc_openid1()
  144. {
  145. $this->_run('DH-SHA256', 'DH-SHA1', true);
  146. }
  147. function test_typeMismatchNoEncDHSHA1_openid1()
  148. {
  149. $this->_run('no-encryption', 'DH-SHA1', true);
  150. }
  151. }
  152. class TestOpenID1AssociationResponseSessionType extends Tests_Auth_OpenID_AssociationResponse {
  153. function _run($expected_session_type, $session_type_value)
  154. {
  155. // Create a Message with just 'session_type' in it, since
  156. // that's all this function will use. 'session_type' may be
  157. // absent if it's set to None.
  158. $args = array();
  159. if ($session_type_value !== null) {
  160. $args['session_type'] = $session_type_value;
  161. }
  162. $message = Auth_OpenID_Message::fromOpenIDArgs($args);
  163. $this->assertTrue($message->isOpenID1());
  164. $actual_session_type = $this->consumer->_getOpenID1SessionType($message);
  165. $error_message = sprintf('Returned sesion type parameter %s was expected ' .
  166. 'to yield session type %s, but yielded %s',
  167. $session_type_value, $expected_session_type,
  168. $actual_session_type);
  169. $this->assertEquals(
  170. $expected_session_type,
  171. $actual_session_type,
  172. $error_message);
  173. }
  174. function test_none()
  175. {
  176. $this->_run('no-encryption', null);
  177. }
  178. function test_empty()
  179. {
  180. $this->_run('no-encryption', '');
  181. }
  182. function test_explicitNoEncryption()
  183. {
  184. $this->_run('no-encryption', 'no-encryption');
  185. }
  186. function test_dhSHA1()
  187. {
  188. $this->_run('DH-SHA1', 'DH-SHA1');
  189. }
  190. // DH-SHA256 is not a valid session type for OpenID1, but this
  191. // function does not test that. This is mostly just to make sure
  192. // that it will pass-through stuff that is not explicitly handled,
  193. // so it will get handled the same way as it is handled for OpenID
  194. // 2
  195. function test_dhSHA256()
  196. {
  197. $this->_run('DH-SHA256', 'DH-SHA256');
  198. }
  199. }
  200. class DummyAssociationSession {
  201. var $secret = "shh! don't tell!";
  202. var $extract_secret_called = false;
  203. var $session_type = null;
  204. var $allowed_assoc_types = null;
  205. function extractSecret($message)
  206. {
  207. $this->extract_secret_called = true;
  208. return $this->secret;
  209. }
  210. }
  211. class TestInvalidFields extends Tests_Auth_OpenID_AssociationResponse {
  212. function setUp()
  213. {
  214. parent::setUp();
  215. $this->session_type = 'testing-session';
  216. // This must something that works for Association.fromExpiresIn
  217. $this->assoc_type = 'HMAC-SHA1';
  218. $this->assoc_handle = 'testing-assoc-handle';
  219. // These arguments should all be valid
  220. $this->assoc_response = Auth_OpenID_Message::fromOpenIDArgs(array(
  221. 'expires_in' => '1000',
  222. 'assoc_handle' => $this->assoc_handle,
  223. 'assoc_type' => $this->assoc_type,
  224. 'session_type' => $this->session_type,
  225. 'ns' => Auth_OpenID_OPENID2_NS,
  226. ));
  227. $this->assoc_session = new DummyAssociationSession();
  228. // Make the session for the response's session type
  229. $this->assoc_session->session_type = $this->session_type;
  230. $this->assoc_session->allowed_assoc_types = array($this->assoc_type);
  231. }
  232. function test_worksWithGoodFields()
  233. {
  234. // Handle a full successful association response
  235. $assoc = $this->consumer->_extractAssociation(
  236. $this->assoc_response, $this->assoc_session);
  237. $this->assertTrue($this->assoc_session->extract_secret_called);
  238. $this->assertEquals($this->assoc_session->secret, $assoc->secret);
  239. $this->assertEquals(1000, $assoc->lifetime);
  240. $this->assertEquals($this->assoc_handle, $assoc->handle);
  241. $this->assertEquals($this->assoc_type, $assoc->assoc_type);
  242. }
  243. function test_badAssocType()
  244. {
  245. // Make sure that the assoc type in the response is not valid
  246. // for the given session.
  247. $this->assoc_session->allowed_assoc_types = array();
  248. $this->assertTrue(
  249. $this->consumer->_extractAssociation($this->assoc_response,
  250. $this->assoc_session) === null);
  251. }
  252. function test_badExpiresIn()
  253. {
  254. // Invalid value for expires_in should cause failure
  255. $this->assoc_response->setArg(Auth_OpenID_OPENID_NS, 'expires_in', 'forever');
  256. $assoc = $this->consumer->_extractAssociation($this->assoc_response,
  257. $this->assoc_session);
  258. $this->assertTrue(Auth_OpenID::isFailure($assoc));
  259. }
  260. }
  261. class TestExtractAssociationDiffieHellman extends Tests_Auth_OpenID_AssociationResponse {
  262. var $secret = 'xxxxxxxxxxxxxxxxxxxx';
  263. function _setUpDH()
  264. {
  265. list($sess, $message) = $this->consumer->_createAssociateRequest(
  266. $this->endpoint, 'HMAC-SHA1', 'DH-SHA1');
  267. // XXX: this is testing _createAssociateRequest
  268. $this->assertEquals($this->endpoint->compatibilityMode(),
  269. $message->isOpenID1());
  270. $server_sess = Auth_OpenID_DiffieHellmanSHA1ServerSession::fromMessage($message);
  271. $server_resp = $server_sess->answer($this->secret);
  272. $server_resp['assoc_type'] = 'HMAC-SHA1';
  273. $server_resp['assoc_handle'] = 'handle';
  274. $server_resp['expires_in'] = '1000';
  275. $server_resp['session_type'] = 'DH-SHA1';
  276. return array($sess, Auth_OpenID_Message::fromOpenIDArgs($server_resp));
  277. }
  278. function test_success()
  279. {
  280. list($sess, $server_resp) = $this->_setUpDH();
  281. $ret = $this->consumer->_extractAssociation($server_resp, $sess);
  282. $this->assertTrue($ret !== null);
  283. $this->assertEquals($ret->assoc_type, 'HMAC-SHA1');
  284. $this->assertEquals($ret->secret, $this->secret);
  285. $this->assertEquals($ret->handle, 'handle');
  286. $this->assertEquals($ret->lifetime, 1000);
  287. }
  288. function test_openid2success()
  289. {
  290. // Use openid 2 type in endpoint so _setUpDH checks
  291. // compatibility mode state properly
  292. $this->endpoint->type_uris = array(Auth_OpenID_TYPE_2_0,
  293. Auth_OpenID_TYPE_1_1);
  294. $this->test_success();
  295. }
  296. /**
  297. * Can't run this test because the base64 decoder is broken.
  298. */
  299. /*
  300. function test_badDHValues()
  301. {
  302. list($sess, $server_resp) = $this->_setUpDH();
  303. $server_resp->setArg(Auth_OpenID_OPENID_NS, 'enc_mac_key', "\x00\x00\x00");
  304. $this->assertTrue($this->consumer->_extractAssociation($server_resp, $sess) === null);
  305. }
  306. */
  307. }
  308. global $Tests_Auth_OpenID_AssociationResponse_other;
  309. $Tests_Auth_OpenID_AssociationResponse_other = array(
  310. new TestInvalidFields(),
  311. new TestOpenID1AssociationResponseSessionType(),
  312. new ExtractAssociationSessionTypeMismatch(),
  313. new TestExtractAssociationMissingFieldsOpenID1(),
  314. new TestExtractAssociationMissingFieldsOpenID2()
  315. );
  316. if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
  317. $Tests_Auth_OpenID_AssociationResponse_other[] = new TestExtractAssociationDiffieHellman();
  318. }