AssociationResponse.php 12 KB

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