PageRenderTime 67ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/cake/tests/cases/libs/controller/components/security.test.php

https://bitbucket.org/meLego/snelcms
PHP | 1481 lines | 890 code | 156 blank | 435 comment | 5 complexity | 273ae9355527fd796c9e4b8a8a2a5a09 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * SecurityComponentTest file
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
  8. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice
  12. *
  13. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
  15. * @package cake.tests.cases.libs.controller.components
  16. * @since CakePHP(tm) v 1.2.0.5435
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::import('Controller', 'Controller', false);
  20. App::import('Component', 'Security');
  21. /**
  22. * TestSecurityComponent
  23. *
  24. * @package cake
  25. * @package cake.tests.cases.libs.controller.components
  26. */
  27. class TestSecurityComponent extends SecurityComponent {
  28. /**
  29. * validatePost method
  30. *
  31. * @param Controller $controller
  32. * @return unknown
  33. */
  34. function validatePost($controller) {
  35. return $this->_validatePost($controller);
  36. }
  37. }
  38. /**
  39. * SecurityTestController
  40. *
  41. * @package cake
  42. * @package cake.tests.cases.libs.controller.components
  43. */
  44. class SecurityTestController extends Controller {
  45. /**
  46. * name property
  47. *
  48. * @var string 'SecurityTest'
  49. * @access public
  50. */
  51. public $name = 'SecurityTest';
  52. /**
  53. * components property
  54. *
  55. * @var array
  56. * @access public
  57. */
  58. public $components = array('Session', 'TestSecurity');
  59. /**
  60. * failed property
  61. *
  62. * @var bool false
  63. * @access public
  64. */
  65. public $failed = false;
  66. /**
  67. * Used for keeping track of headers in test
  68. *
  69. * @var array
  70. * @access public
  71. */
  72. public $testHeaders = array();
  73. /**
  74. * fail method
  75. *
  76. * @access public
  77. * @return void
  78. */
  79. function fail() {
  80. $this->failed = true;
  81. }
  82. /**
  83. * redirect method
  84. *
  85. * @param mixed $option
  86. * @param mixed $code
  87. * @param mixed $exit
  88. * @access public
  89. * @return void
  90. */
  91. function redirect($url, $status = null, $exit = true) {
  92. return $status;
  93. }
  94. /**
  95. * Conveinence method for header()
  96. *
  97. * @param string $status
  98. * @return void
  99. */
  100. public function header($status) {
  101. $this->testHeaders[] = $status;
  102. }
  103. }
  104. /**
  105. * SecurityComponentTest class
  106. *
  107. * @package cake.tests.cases.libs.controller.components
  108. */
  109. class SecurityComponentTest extends CakeTestCase {
  110. /**
  111. * Controller property
  112. *
  113. * @var SecurityTestController
  114. * @access public
  115. */
  116. public $Controller;
  117. /**
  118. * oldSalt property
  119. *
  120. * @var string
  121. * @access public
  122. */
  123. public $oldSalt;
  124. /**
  125. * setUp method
  126. *
  127. * @access public
  128. * @return void
  129. */
  130. function setUp() {
  131. parent::setUp();
  132. $request = new CakeRequest('posts/index', false);
  133. $request->addParams(array('controller' => 'posts', 'action' => 'index'));
  134. $this->Controller = new SecurityTestController($request);
  135. $this->Controller->Components->init($this->Controller);
  136. $this->Controller->Security = $this->Controller->TestSecurity;
  137. $this->Controller->Security->blackHoleCallback = 'fail';
  138. $this->Security = $this->Controller->Security;
  139. $this->Security->csrfCheck = false;
  140. Configure::write('Security.salt', 'foo!');
  141. }
  142. /**
  143. * Tear-down method. Resets environment state.
  144. *
  145. * @return void
  146. */
  147. function tearDown() {
  148. parent::tearDown();
  149. $this->Controller->Session->delete('_Token');
  150. unset($this->Controller->Security);
  151. unset($this->Controller->Component);
  152. unset($this->Controller);
  153. }
  154. /**
  155. * test that initalize can set properties.
  156. *
  157. * @return void
  158. */
  159. function testConstructorSettingProperties() {
  160. $settings = array(
  161. 'requirePost' => array('edit', 'update'),
  162. 'requireSecure' => array('update_account'),
  163. 'requireGet' => array('index'),
  164. 'validatePost' => false,
  165. 'loginUsers' => array(
  166. 'mark' => 'password'
  167. ),
  168. 'requireLogin' => array('login'),
  169. );
  170. $Security = new SecurityComponent($this->Controller->Components, $settings);
  171. $this->Controller->Security->initialize($this->Controller, $settings);
  172. $this->assertEqual($Security->requirePost, $settings['requirePost']);
  173. $this->assertEqual($Security->requireSecure, $settings['requireSecure']);
  174. $this->assertEqual($Security->requireGet, $settings['requireGet']);
  175. $this->assertEqual($Security->validatePost, $settings['validatePost']);
  176. $this->assertEqual($Security->loginUsers, $settings['loginUsers']);
  177. $this->assertEqual($Security->requireLogin, $settings['requireLogin']);
  178. }
  179. /**
  180. * testStartup method
  181. *
  182. * @access public
  183. * @return void
  184. */
  185. function testStartup() {
  186. $this->Controller->Security->startup($this->Controller);
  187. $result = $this->Controller->params['_Token']['key'];
  188. $this->assertNotNull($result);
  189. $this->assertTrue($this->Controller->Session->check('_Token'));
  190. }
  191. /**
  192. * testRequirePostFail method
  193. *
  194. * @access public
  195. * @return void
  196. */
  197. function testRequirePostFail() {
  198. $_SERVER['REQUEST_METHOD'] = 'GET';
  199. $this->Controller->request['action'] = 'posted';
  200. $this->Controller->Security->requirePost(array('posted'));
  201. $this->Controller->Security->startup($this->Controller);
  202. $this->assertTrue($this->Controller->failed);
  203. }
  204. /**
  205. * testRequirePostSucceed method
  206. *
  207. * @access public
  208. * @return void
  209. */
  210. function testRequirePostSucceed() {
  211. $_SERVER['REQUEST_METHOD'] = 'POST';
  212. $this->Controller->request['action'] = 'posted';
  213. $this->Controller->Security->requirePost('posted');
  214. $this->Security->startup($this->Controller);
  215. $this->assertFalse($this->Controller->failed);
  216. }
  217. /**
  218. * testRequireSecureFail method
  219. *
  220. * @access public
  221. * @return void
  222. */
  223. function testRequireSecureFail() {
  224. $_SERVER['HTTPS'] = 'off';
  225. $_SERVER['REQUEST_METHOD'] = 'POST';
  226. $this->Controller->request['action'] = 'posted';
  227. $this->Controller->Security->requireSecure(array('posted'));
  228. $this->Controller->Security->startup($this->Controller);
  229. $this->assertTrue($this->Controller->failed);
  230. }
  231. /**
  232. * testRequireSecureSucceed method
  233. *
  234. * @access public
  235. * @return void
  236. */
  237. function testRequireSecureSucceed() {
  238. $_SERVER['REQUEST_METHOD'] = 'Secure';
  239. $this->Controller->request['action'] = 'posted';
  240. $_SERVER['HTTPS'] = 'on';
  241. $this->Controller->Security->requireSecure('posted');
  242. $this->Controller->Security->startup($this->Controller);
  243. $this->assertFalse($this->Controller->failed);
  244. }
  245. /**
  246. * testRequireAuthFail method
  247. *
  248. * @access public
  249. * @return void
  250. */
  251. function testRequireAuthFail() {
  252. $_SERVER['REQUEST_METHOD'] = 'AUTH';
  253. $this->Controller->request['action'] = 'posted';
  254. $this->Controller->request->data = array('username' => 'willy', 'password' => 'somePass');
  255. $this->Controller->Security->requireAuth(array('posted'));
  256. $this->Controller->Security->startup($this->Controller);
  257. $this->assertTrue($this->Controller->failed);
  258. $this->Controller->Session->write('_Token', array('allowedControllers' => array()));
  259. $this->Controller->request->data = array('username' => 'willy', 'password' => 'somePass');
  260. $this->Controller->request['action'] = 'posted';
  261. $this->Controller->Security->requireAuth('posted');
  262. $this->Controller->Security->startup($this->Controller);
  263. $this->assertTrue($this->Controller->failed);
  264. $this->Controller->Session->write('_Token', array(
  265. 'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted2')
  266. ));
  267. $this->Controller->request->data = array('username' => 'willy', 'password' => 'somePass');
  268. $this->Controller->request['action'] = 'posted';
  269. $this->Controller->Security->requireAuth('posted');
  270. $this->Controller->Security->startup($this->Controller);
  271. $this->assertTrue($this->Controller->failed);
  272. }
  273. /**
  274. * testRequireAuthSucceed method
  275. *
  276. * @access public
  277. * @return void
  278. */
  279. function testRequireAuthSucceed() {
  280. $_SERVER['REQUEST_METHOD'] = 'AUTH';
  281. $this->Controller->request['action'] = 'posted';
  282. $this->Controller->Security->requireAuth('posted');
  283. $this->Controller->Security->startup($this->Controller);
  284. $this->assertFalse($this->Controller->failed);
  285. $this->Controller->Security->Session->write('_Token', array(
  286. 'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted')
  287. ));
  288. $this->Controller->request['controller'] = 'SecurityTest';
  289. $this->Controller->request['action'] = 'posted';
  290. $this->Controller->request->data = array(
  291. 'username' => 'willy', 'password' => 'somePass', '_Token' => ''
  292. );
  293. $this->Controller->action = 'posted';
  294. $this->Controller->Security->requireAuth('posted');
  295. $this->Controller->Security->startup($this->Controller);
  296. $this->assertFalse($this->Controller->failed);
  297. }
  298. /**
  299. * testRequirePostSucceedWrongMethod method
  300. *
  301. * @access public
  302. * @return void
  303. */
  304. function testRequirePostSucceedWrongMethod() {
  305. $_SERVER['REQUEST_METHOD'] = 'GET';
  306. $this->Controller->request['action'] = 'getted';
  307. $this->Controller->Security->requirePost('posted');
  308. $this->Controller->Security->startup($this->Controller);
  309. $this->assertFalse($this->Controller->failed);
  310. }
  311. /**
  312. * testRequireGetFail method
  313. *
  314. * @access public
  315. * @return void
  316. */
  317. function testRequireGetFail() {
  318. $_SERVER['REQUEST_METHOD'] = 'POST';
  319. $this->Controller->request['action'] = 'getted';
  320. $this->Controller->Security->requireGet(array('getted'));
  321. $this->Controller->Security->startup($this->Controller);
  322. $this->assertTrue($this->Controller->failed);
  323. }
  324. /**
  325. * testRequireGetSucceed method
  326. *
  327. * @access public
  328. * @return void
  329. */
  330. function testRequireGetSucceed() {
  331. $_SERVER['REQUEST_METHOD'] = 'GET';
  332. $this->Controller->request['action'] = 'getted';
  333. $this->Controller->Security->requireGet('getted');
  334. $this->Controller->Security->startup($this->Controller);
  335. $this->assertFalse($this->Controller->failed);
  336. }
  337. /**
  338. * testRequireLogin method
  339. *
  340. * @access public
  341. * @return void
  342. */
  343. function testRequireLogin() {
  344. $this->Controller->request['action'] = 'posted';
  345. $this->Controller->Security->requireLogin(
  346. 'posted',
  347. array('type' => 'basic', 'users' => array('admin' => 'password'))
  348. );
  349. $_SERVER['PHP_AUTH_USER'] = 'admin';
  350. $_SERVER['PHP_AUTH_PW'] = 'password';
  351. $this->Controller->Security->startup($this->Controller);
  352. $this->assertFalse($this->Controller->failed);
  353. $this->Controller->request['action'] = 'posted';
  354. $this->Controller->Security->requireLogin(
  355. array('posted'),
  356. array('type' => 'basic', 'users' => array('admin' => 'password'))
  357. );
  358. $_SERVER['PHP_AUTH_USER'] = 'admin2';
  359. $_SERVER['PHP_AUTH_PW'] = 'password';
  360. $this->Controller->Security->startup($this->Controller);
  361. $this->assertTrue($this->Controller->failed);
  362. $this->Controller->request['action'] = 'posted';
  363. $this->Controller->Security->requireLogin(
  364. 'posted',
  365. array('type' => 'basic', 'users' => array('admin' => 'password'))
  366. );
  367. $_SERVER['PHP_AUTH_USER'] = 'admin';
  368. $_SERVER['PHP_AUTH_PW'] = 'password2';
  369. $this->Controller->Security->startup($this->Controller);
  370. $this->assertTrue($this->Controller->failed);
  371. }
  372. /**
  373. * testDigestAuth method
  374. *
  375. * @access public
  376. * @return void
  377. */
  378. function testDigestAuth() {
  379. $skip = $this->skipIf((version_compare(PHP_VERSION, '5.1') == -1) XOR (!function_exists('apache_request_headers')),
  380. "%s Cannot run Digest Auth test for PHP versions < 5.1"
  381. );
  382. if ($skip) {
  383. return;
  384. }
  385. $this->Controller->request['action'] = 'posted';
  386. $_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST
  387. Digest username="Mufasa",
  388. realm="testrealm@host.com",
  389. nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
  390. uri="/dir/index.html",
  391. qop=auth,
  392. nc=00000001,
  393. cnonce="0a4f113b",
  394. response="460d0d3c6867c2f1ab85b1ada1aece48",
  395. opaque="5ccc069c403ebaf9f0171e9517f40e41"
  396. DIGEST;
  397. $this->Controller->Security->requireLogin('posted', array(
  398. 'type' => 'digest', 'users' => array('Mufasa' => 'password'),
  399. 'realm' => 'testrealm@host.com'
  400. ));
  401. $this->Controller->Security->startup($this->Controller);
  402. $this->assertFalse($this->Controller->failed);
  403. }
  404. /**
  405. * testRequireGetSucceedWrongMethod method
  406. *
  407. * @access public
  408. * @return void
  409. */
  410. function testRequireGetSucceedWrongMethod() {
  411. $_SERVER['REQUEST_METHOD'] = 'POST';
  412. $this->Controller->request['action'] = 'posted';
  413. $this->Security->requireGet('getted');
  414. $this->Security->startup($this->Controller);
  415. $this->assertFalse($this->Controller->failed);
  416. }
  417. /**
  418. * testRequirePutFail method
  419. *
  420. * @access public
  421. * @return void
  422. */
  423. function testRequirePutFail() {
  424. $_SERVER['REQUEST_METHOD'] = 'POST';
  425. $this->Controller->request['action'] = 'putted';
  426. $this->Controller->Security->requirePut(array('putted'));
  427. $this->Controller->Security->startup($this->Controller);
  428. $this->assertTrue($this->Controller->failed);
  429. }
  430. /**
  431. * testRequirePutSucceed method
  432. *
  433. * @access public
  434. * @return void
  435. */
  436. function testRequirePutSucceed() {
  437. $_SERVER['REQUEST_METHOD'] = 'PUT';
  438. $this->Controller->request['action'] = 'putted';
  439. $this->Controller->Security->requirePut('putted');
  440. $this->Controller->Security->startup($this->Controller);
  441. $this->assertFalse($this->Controller->failed);
  442. }
  443. /**
  444. * testRequirePutSucceedWrongMethod method
  445. *
  446. * @access public
  447. * @return void
  448. */
  449. function testRequirePutSucceedWrongMethod() {
  450. $_SERVER['REQUEST_METHOD'] = 'POST';
  451. $this->Controller->request['action'] = 'posted';
  452. $this->Controller->Security->requirePut('putted');
  453. $this->Controller->Security->startup($this->Controller);
  454. $this->assertFalse($this->Controller->failed);
  455. }
  456. /**
  457. * testRequireDeleteFail method
  458. *
  459. * @access public
  460. * @return void
  461. */
  462. function testRequireDeleteFail() {
  463. $_SERVER['REQUEST_METHOD'] = 'POST';
  464. $this->Controller->request['action'] = 'deleted';
  465. $this->Controller->Security->requireDelete(array('deleted', 'other_method'));
  466. $this->Controller->Security->startup($this->Controller);
  467. $this->assertTrue($this->Controller->failed);
  468. }
  469. /**
  470. * testRequireDeleteSucceed method
  471. *
  472. * @access public
  473. * @return void
  474. */
  475. function testRequireDeleteSucceed() {
  476. $_SERVER['REQUEST_METHOD'] = 'DELETE';
  477. $this->Controller->request['action'] = 'deleted';
  478. $this->Controller->Security->requireDelete('deleted');
  479. $this->Controller->Security->startup($this->Controller);
  480. $this->assertFalse($this->Controller->failed);
  481. }
  482. /**
  483. * testRequireDeleteSucceedWrongMethod method
  484. *
  485. * @access public
  486. * @return void
  487. */
  488. function testRequireDeleteSucceedWrongMethod() {
  489. $_SERVER['REQUEST_METHOD'] = 'POST';
  490. $this->Controller->request['action'] = 'posted';
  491. $this->Controller->Security->requireDelete('deleted');
  492. $this->Controller->Security->startup($this->Controller);
  493. $this->assertFalse($this->Controller->failed);
  494. }
  495. /**
  496. * testRequireLoginSettings method
  497. *
  498. * @access public
  499. * @return void
  500. */
  501. function testRequireLoginSettings() {
  502. $this->Controller->Security->requireLogin(
  503. 'add', 'edit',
  504. array('type' => 'basic', 'users' => array('admin' => 'password'))
  505. );
  506. $this->assertEqual($this->Controller->Security->requireLogin, array('add', 'edit'));
  507. $this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password'));
  508. }
  509. /**
  510. * testRequireLoginAllActions method
  511. *
  512. * @access public
  513. * @return void
  514. */
  515. function testRequireLoginAllActions() {
  516. $this->Controller->Security->requireLogin(
  517. array('type' => 'basic', 'users' => array('admin' => 'password'))
  518. );
  519. $this->assertEqual($this->Controller->Security->requireLogin, array('*'));
  520. $this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password'));
  521. }
  522. /**
  523. * Simple hash validation test
  524. *
  525. * @access public
  526. * @return void
  527. */
  528. function testValidatePost() {
  529. $this->Controller->Security->startup($this->Controller);
  530. $key = $this->Controller->request->params['_Token']['key'];
  531. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
  532. $this->Controller->request->data = array(
  533. 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
  534. '_Token' => compact('key', 'fields')
  535. );
  536. $this->assertTrue($this->Controller->Security->validatePost($this->Controller));
  537. }
  538. /**
  539. * test that validatePost fails if any of its required fields are missing.
  540. *
  541. * @return void
  542. */
  543. function testValidatePostFormHacking() {
  544. $this->Controller->Security->startup($this->Controller);
  545. $key = $this->Controller->params['_Token']['key'];
  546. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
  547. $this->Controller->request->data = array(
  548. 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
  549. '_Token' => compact('key')
  550. );
  551. $result = $this->Controller->Security->validatePost($this->Controller);
  552. $this->assertFalse($result, 'validatePost passed when fields were missing. %s');
  553. }
  554. /**
  555. * Test that objects can't be passed into the serialized string. This was a vector for RFI and LFI
  556. * attacks. Thanks to Felix Wilhelm
  557. *
  558. * @return void
  559. */
  560. function testValidatePostObjectDeserialize() {
  561. $this->Controller->Security->startup($this->Controller);
  562. $key = $this->Controller->request->params['_Token']['key'];
  563. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877';
  564. // a corrupted serialized object, so we can see if it ever gets to deserialize
  565. $attack = 'O:3:"App":1:{s:5:"__map";a:1:{s:3:"foo";s:7:"Hacked!";s:1:"fail"}}';
  566. $fields .= urlencode(':' . str_rot13($attack));
  567. $this->Controller->request->data = array(
  568. 'Model' => array('username' => 'mark', 'password' => 'foo', 'valid' => '0'),
  569. '_Token' => compact('key', 'fields')
  570. );
  571. $result = $this->Controller->Security->validatePost($this->Controller);
  572. $this->assertFalse($result, 'validatePost passed when key was missing. %s');
  573. }
  574. /**
  575. * Tests validation of checkbox arrays
  576. *
  577. * @access public
  578. * @return void
  579. */
  580. function testValidatePostArray() {
  581. $this->Controller->Security->startup($this->Controller);
  582. $key = $this->Controller->request->params['_Token']['key'];
  583. $fields = 'f7d573650a295b94e0938d32b323fde775e5f32b%3A';
  584. $this->Controller->request->data = array(
  585. 'Model' => array('multi_field' => array('1', '3')),
  586. '_Token' => compact('key', 'fields')
  587. );
  588. $this->assertTrue($this->Controller->Security->validatePost($this->Controller));
  589. }
  590. /**
  591. * testValidatePostNoModel method
  592. *
  593. * @access public
  594. * @return void
  595. */
  596. function testValidatePostNoModel() {
  597. $this->Controller->Security->startup($this->Controller);
  598. $key = $this->Controller->request->params['_Token']['key'];
  599. $fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3A';
  600. $this->Controller->request->data = array(
  601. 'anything' => 'some_data',
  602. '_Token' => compact('key', 'fields')
  603. );
  604. $result = $this->Controller->Security->validatePost($this->Controller);
  605. $this->assertTrue($result);
  606. }
  607. /**
  608. * testValidatePostSimple method
  609. *
  610. * @access public
  611. * @return void
  612. */
  613. function testValidatePostSimple() {
  614. $this->Controller->Security->startup($this->Controller);
  615. $key = $this->Controller->request->params['_Token']['key'];
  616. $fields = '69f493434187b867ea14b901fdf58b55d27c935d%3A';
  617. $this->Controller->request->data = $data = array(
  618. 'Model' => array('username' => '', 'password' => ''),
  619. '_Token' => compact('key', 'fields')
  620. );
  621. $result = $this->Controller->Security->validatePost($this->Controller);
  622. $this->assertTrue($result);
  623. }
  624. /**
  625. * Tests hash validation for multiple records, including locked fields
  626. *
  627. * @access public
  628. * @return void
  629. */
  630. function testValidatePostComplex() {
  631. $this->Controller->Security->startup($this->Controller);
  632. $key = $this->Controller->request->params['_Token']['key'];
  633. $fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3AAddresses.0.id%7CAddresses.1.id';
  634. $this->Controller->request->data = array(
  635. 'Addresses' => array(
  636. '0' => array(
  637. 'id' => '123456', 'title' => '', 'first_name' => '', 'last_name' => '',
  638. 'address' => '', 'city' => '', 'phone' => '', 'primary' => ''
  639. ),
  640. '1' => array(
  641. 'id' => '654321', 'title' => '', 'first_name' => '', 'last_name' => '',
  642. 'address' => '', 'city' => '', 'phone' => '', 'primary' => ''
  643. )
  644. ),
  645. '_Token' => compact('key', 'fields')
  646. );
  647. $result = $this->Controller->Security->validatePost($this->Controller);
  648. $this->assertTrue($result);
  649. }
  650. /**
  651. * test ValidatePost with multiple select elements.
  652. *
  653. * @return void
  654. */
  655. function testValidatePostMultipleSelect() {
  656. $this->Controller->Security->startup($this->Controller);
  657. $key = $this->Controller->request->params['_Token']['key'];
  658. $fields = '422cde416475abc171568be690a98cad20e66079%3A';
  659. $this->Controller->request->data = array(
  660. 'Tag' => array('Tag' => array(1, 2)),
  661. '_Token' => compact('key', 'fields'),
  662. );
  663. $result = $this->Controller->Security->validatePost($this->Controller);
  664. $this->assertTrue($result);
  665. $this->Controller->request->data = array(
  666. 'Tag' => array('Tag' => array(1, 2, 3)),
  667. '_Token' => compact('key', 'fields'),
  668. );
  669. $result = $this->Controller->Security->validatePost($this->Controller);
  670. $this->assertTrue($result);
  671. $this->Controller->request->data = array(
  672. 'Tag' => array('Tag' => array(1, 2, 3, 4)),
  673. '_Token' => compact('key', 'fields'),
  674. );
  675. $result = $this->Controller->Security->validatePost($this->Controller);
  676. $this->assertTrue($result);
  677. $fields = '19464422eafe977ee729c59222af07f983010c5f%3A';
  678. $this->Controller->request->data = array(
  679. 'User.password' => 'bar', 'User.name' => 'foo', 'User.is_valid' => '1',
  680. 'Tag' => array('Tag' => array(1)), '_Token' => compact('key', 'fields'),
  681. );
  682. $result = $this->Controller->Security->validatePost($this->Controller);
  683. $this->assertTrue($result);
  684. }
  685. /**
  686. * testValidatePostCheckbox method
  687. *
  688. * First block tests un-checked checkbox
  689. * Second block tests checked checkbox
  690. *
  691. * @access public
  692. * @return void
  693. */
  694. function testValidatePostCheckbox() {
  695. $this->Controller->Security->startup($this->Controller);
  696. $key = $this->Controller->request->params['_Token']['key'];
  697. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
  698. $this->Controller->request->data = array(
  699. 'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
  700. '_Token' => compact('key', 'fields')
  701. );
  702. $result = $this->Controller->Security->validatePost($this->Controller);
  703. $this->assertTrue($result);
  704. $fields = '874439ca69f89b4c4a5f50fb9c36ff56a28f5d42%3A';
  705. $this->Controller->request->data = array(
  706. 'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
  707. '_Token' => compact('key', 'fields')
  708. );
  709. $result = $this->Controller->Security->validatePost($this->Controller);
  710. $this->assertTrue($result);
  711. $this->Controller->request->data = array();
  712. $this->Controller->Security->startup($this->Controller);
  713. $key = $this->Controller->request->params['_Token']['key'];
  714. $this->Controller->request->data = $data = array(
  715. 'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
  716. '_Token' => compact('key', 'fields')
  717. );
  718. $result = $this->Controller->Security->validatePost($this->Controller);
  719. $this->assertTrue($result);
  720. }
  721. /**
  722. * testValidatePostHidden method
  723. *
  724. * @access public
  725. * @return void
  726. */
  727. function testValidatePostHidden() {
  728. $this->Controller->Security->startup($this->Controller);
  729. $key = $this->Controller->request->params['_Token']['key'];
  730. $fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3AModel.hidden%7CModel.other_hidden';
  731. $fields .= '';
  732. $this->Controller->request->data = array(
  733. 'Model' => array(
  734. 'username' => '', 'password' => '', 'hidden' => '0',
  735. 'other_hidden' => 'some hidden value'
  736. ),
  737. '_Token' => compact('key', 'fields')
  738. );
  739. $result = $this->Controller->Security->validatePost($this->Controller);
  740. $this->assertTrue($result);
  741. }
  742. /**
  743. * testValidatePostWithDisabledFields method
  744. *
  745. * @access public
  746. * @return void
  747. */
  748. function testValidatePostWithDisabledFields() {
  749. $this->Controller->Security->disabledFields = array('Model.username', 'Model.password');
  750. $this->Controller->Security->startup($this->Controller);
  751. $key = $this->Controller->request->params['_Token']['key'];
  752. $fields = 'ef1082968c449397bcd849f963636864383278b1%3AModel.hidden';
  753. $this->Controller->request->data = array(
  754. 'Model' => array(
  755. 'username' => '', 'password' => '', 'hidden' => '0'
  756. ),
  757. '_Token' => compact('fields', 'key')
  758. );
  759. $result = $this->Controller->Security->validatePost($this->Controller);
  760. $this->assertTrue($result);
  761. }
  762. /**
  763. * testValidateHiddenMultipleModel method
  764. *
  765. * @access public
  766. * @return void
  767. */
  768. function testValidateHiddenMultipleModel() {
  769. $this->Controller->Security->startup($this->Controller);
  770. $key = $this->Controller->request->params['_Token']['key'];
  771. $fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3AModel.valid%7CModel2.valid%7CModel3.valid';
  772. $this->Controller->request->data = array(
  773. 'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
  774. 'Model2' => array('valid' => '0'),
  775. 'Model3' => array('valid' => '0'),
  776. '_Token' => compact('key', 'fields')
  777. );
  778. $result = $this->Controller->Security->validatePost($this->Controller);
  779. $this->assertTrue($result);
  780. }
  781. /**
  782. * testValidateHasManyModel method
  783. *
  784. * @access public
  785. * @return void
  786. */
  787. function testValidateHasManyModel() {
  788. $this->Controller->Security->startup($this->Controller);
  789. $key = $this->Controller->request->params['_Token']['key'];
  790. $fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3AModel.0.hidden%7CModel.0.valid';
  791. $fields .= '%7CModel.1.hidden%7CModel.1.valid';
  792. $this->Controller->request->data = array(
  793. 'Model' => array(
  794. array(
  795. 'username' => 'username', 'password' => 'password',
  796. 'hidden' => 'value', 'valid' => '0'
  797. ),
  798. array(
  799. 'username' => 'username', 'password' => 'password',
  800. 'hidden' => 'value', 'valid' => '0'
  801. )
  802. ),
  803. '_Token' => compact('key', 'fields')
  804. );
  805. $result = $this->Controller->Security->validatePost($this->Controller);
  806. $this->assertTrue($result);
  807. }
  808. /**
  809. * testValidateHasManyRecordsPass method
  810. *
  811. * @access public
  812. * @return void
  813. */
  814. function testValidateHasManyRecordsPass() {
  815. $this->Controller->Security->startup($this->Controller);
  816. $key = $this->Controller->request->params['_Token']['key'];
  817. $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C';
  818. $fields .= 'Address.1.id%7CAddress.1.primary';
  819. $this->Controller->request->data = array(
  820. 'Address' => array(
  821. 0 => array(
  822. 'id' => '123',
  823. 'title' => 'home',
  824. 'first_name' => 'Bilbo',
  825. 'last_name' => 'Baggins',
  826. 'address' => '23 Bag end way',
  827. 'city' => 'the shire',
  828. 'phone' => 'N/A',
  829. 'primary' => '1',
  830. ),
  831. 1 => array(
  832. 'id' => '124',
  833. 'title' => 'home',
  834. 'first_name' => 'Frodo',
  835. 'last_name' => 'Baggins',
  836. 'address' => '50 Bag end way',
  837. 'city' => 'the shire',
  838. 'phone' => 'N/A',
  839. 'primary' => '1'
  840. )
  841. ),
  842. '_Token' => compact('key', 'fields')
  843. );
  844. $result = $this->Controller->Security->validatePost($this->Controller);
  845. $this->assertTrue($result);
  846. }
  847. /**
  848. * testValidateHasManyRecords method
  849. *
  850. * validatePost should fail, hidden fields have been changed.
  851. *
  852. * @access public
  853. * @return void
  854. */
  855. function testValidateHasManyRecordsFail() {
  856. $this->Controller->Security->startup($this->Controller);
  857. $key = $this->Controller->request->params['_Token']['key'];
  858. $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C';
  859. $fields .= 'Address.1.id%7CAddress.1.primary';
  860. $this->Controller->request->data = array(
  861. 'Address' => array(
  862. 0 => array(
  863. 'id' => '123',
  864. 'title' => 'home',
  865. 'first_name' => 'Bilbo',
  866. 'last_name' => 'Baggins',
  867. 'address' => '23 Bag end way',
  868. 'city' => 'the shire',
  869. 'phone' => 'N/A',
  870. 'primary' => '5',
  871. ),
  872. 1 => array(
  873. 'id' => '124',
  874. 'title' => 'home',
  875. 'first_name' => 'Frodo',
  876. 'last_name' => 'Baggins',
  877. 'address' => '50 Bag end way',
  878. 'city' => 'the shire',
  879. 'phone' => 'N/A',
  880. 'primary' => '1'
  881. )
  882. ),
  883. '_Token' => compact('key', 'fields')
  884. );
  885. $result = $this->Controller->Security->validatePost($this->Controller);
  886. $this->assertFalse($result);
  887. }
  888. /**
  889. * testLoginRequest method
  890. *
  891. * @access public
  892. * @return void
  893. */
  894. function testLoginRequest() {
  895. $this->Controller->Security->startup($this->Controller);
  896. $realm = 'cakephp.org';
  897. $options = array('realm' => $realm, 'type' => 'basic');
  898. $result = $this->Controller->Security->loginRequest($options);
  899. $expected = 'WWW-Authenticate: Basic realm="'.$realm.'"';
  900. $this->assertEqual($result, $expected);
  901. $this->Controller->Security->startup($this->Controller);
  902. $options = array('realm' => $realm, 'type' => 'digest');
  903. $result = $this->Controller->Security->loginRequest($options);
  904. $this->assertPattern('/realm="'.$realm.'"/', $result);
  905. $this->assertPattern('/qop="auth"/', $result);
  906. }
  907. /**
  908. * testGenerateDigestResponseHash method
  909. *
  910. * @access public
  911. * @return void
  912. */
  913. function testGenerateDigestResponseHash() {
  914. $this->Controller->Security->startup($this->Controller);
  915. $realm = 'cakephp.org';
  916. $loginData = array('realm' => $realm, 'users' => array('Willy Smith' => 'password'));
  917. $this->Controller->Security->requireLogin($loginData);
  918. $data = array(
  919. 'username' => 'Willy Smith',
  920. 'password' => 'password',
  921. 'nonce' => String::uuid(),
  922. 'nc' => 1,
  923. 'cnonce' => 1,
  924. 'realm' => $realm,
  925. 'uri' => 'path_to_identifier',
  926. 'qop' => 'testme'
  927. );
  928. $_SERVER['REQUEST_METHOD'] = 'POST';
  929. $result = $this->Controller->Security->generateDigestResponseHash($data);
  930. $expected = md5(
  931. md5($data['username'] . ':' . $loginData['realm'] . ':' . $data['password']) . ':' .
  932. $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
  933. md5(env('REQUEST_METHOD') . ':' . $data['uri'])
  934. );
  935. $this->assertIdentical($result, $expected);
  936. }
  937. /**
  938. * testLoginCredentials method
  939. *
  940. * @access public
  941. * @return void
  942. */
  943. function testLoginCredentials() {
  944. $this->Controller->Security->startup($this->Controller);
  945. $_SERVER['PHP_AUTH_USER'] = $user = 'Willy Test';
  946. $_SERVER['PHP_AUTH_PW'] = $pw = 'some password for the nice test';
  947. $result = $this->Controller->Security->loginCredentials('basic');
  948. $expected = array('username' => $user, 'password' => $pw);
  949. $this->assertIdentical($result, $expected);
  950. if (version_compare(PHP_VERSION, '5.1') != -1) {
  951. $_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST
  952. Digest username="Mufasa",
  953. realm="testrealm@host.com",
  954. nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
  955. uri="/dir/index.html",
  956. qop=auth,
  957. nc=00000001,
  958. cnonce="0a4f113b",
  959. response="6629fae49393a05397450978507c4ef1",
  960. opaque="5ccc069c403ebaf9f0171e9517f40e41"
  961. DIGEST;
  962. $expected = array(
  963. 'username' => 'Mufasa',
  964. 'realm' => 'testrealm@host.com',
  965. 'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
  966. 'uri' => '/dir/index.html',
  967. 'qop' => 'auth',
  968. 'nc' => '00000001',
  969. 'cnonce' => '0a4f113b',
  970. 'response' => '6629fae49393a05397450978507c4ef1',
  971. 'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
  972. );
  973. $result = $this->Controller->Security->loginCredentials('digest');
  974. $this->assertIdentical($result, $expected);
  975. }
  976. }
  977. /**
  978. * testParseDigestAuthData method
  979. *
  980. * @access public
  981. * @return void
  982. */
  983. function testParseDigestAuthData() {
  984. $this->Controller->Security->startup($this->Controller);
  985. $digest = <<<DIGEST
  986. Digest username="Mufasa",
  987. realm="testrealm@host.com",
  988. nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
  989. uri="/dir/index.html",
  990. qop=auth,
  991. nc=00000001,
  992. cnonce="0a4f113b",
  993. response="6629fae49393a05397450978507c4ef1",
  994. opaque="5ccc069c403ebaf9f0171e9517f40e41"
  995. DIGEST;
  996. $expected = array(
  997. 'username' => 'Mufasa',
  998. 'realm' => 'testrealm@host.com',
  999. 'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
  1000. 'uri' => '/dir/index.html',
  1001. 'qop' => 'auth',
  1002. 'nc' => '00000001',
  1003. 'cnonce' => '0a4f113b',
  1004. 'response' => '6629fae49393a05397450978507c4ef1',
  1005. 'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
  1006. );
  1007. $result = $this->Controller->Security->parseDigestAuthData($digest);
  1008. $this->assertIdentical($result, $expected);
  1009. $result = $this->Controller->Security->parseDigestAuthData('');
  1010. $this->assertNull($result);
  1011. }
  1012. /**
  1013. * test parsing digest information with email addresses
  1014. *
  1015. * @return void
  1016. */
  1017. function testParseDigestAuthEmailAddress() {
  1018. $this->Controller->Security->startup($this->Controller);
  1019. $digest = <<<DIGEST
  1020. Digest username="mark@example.com",
  1021. realm="testrealm@host.com",
  1022. nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
  1023. uri="/dir/index.html",
  1024. qop=auth,
  1025. nc=00000001,
  1026. cnonce="0a4f113b",
  1027. response="6629fae49393a05397450978507c4ef1",
  1028. opaque="5ccc069c403ebaf9f0171e9517f40e41"
  1029. DIGEST;
  1030. $expected = array(
  1031. 'username' => 'mark@example.com',
  1032. 'realm' => 'testrealm@host.com',
  1033. 'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
  1034. 'uri' => '/dir/index.html',
  1035. 'qop' => 'auth',
  1036. 'nc' => '00000001',
  1037. 'cnonce' => '0a4f113b',
  1038. 'response' => '6629fae49393a05397450978507c4ef1',
  1039. 'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
  1040. );
  1041. $result = $this->Controller->Security->parseDigestAuthData($digest);
  1042. $this->assertIdentical($result, $expected);
  1043. }
  1044. /**
  1045. * testFormDisabledFields method
  1046. *
  1047. * @access public
  1048. * @return void
  1049. */
  1050. function testFormDisabledFields() {
  1051. $this->Controller->Security->startup($this->Controller);
  1052. $key = $this->Controller->request->params['_Token']['key'];
  1053. $fields = '11842060341b9d0fc3808b90ba29fdea7054d6ad%3An%3A0%3A%7B%7D';
  1054. $this->Controller->request->data = array(
  1055. 'MyModel' => array('name' => 'some data'),
  1056. '_Token' => compact('key', 'fields')
  1057. );
  1058. $result = $this->Controller->Security->validatePost($this->Controller);
  1059. $this->assertFalse($result);
  1060. $this->Controller->Security->startup($this->Controller);
  1061. $this->Controller->Security->disabledFields = array('MyModel.name');
  1062. $key = $this->Controller->request->params['_Token']['key'];
  1063. $this->Controller->request->data = array(
  1064. 'MyModel' => array('name' => 'some data'),
  1065. '_Token' => compact('key', 'fields')
  1066. );
  1067. $result = $this->Controller->Security->validatePost($this->Controller);
  1068. $this->assertTrue($result);
  1069. }
  1070. /**
  1071. * testRadio method
  1072. *
  1073. * @access public
  1074. * @return void
  1075. */
  1076. function testRadio() {
  1077. $this->Controller->Security->startup($this->Controller);
  1078. $key = $this->Controller->request->params['_Token']['key'];
  1079. $fields = '575ef54ca4fc8cab468d6d898e9acd3a9671c17e%3An%3A0%3A%7B%7D';
  1080. $this->Controller->request->data = array(
  1081. '_Token' => compact('key', 'fields')
  1082. );
  1083. $result = $this->Controller->Security->validatePost($this->Controller);
  1084. $this->assertFalse($result);
  1085. $this->Controller->request->data = array(
  1086. '_Token' => compact('key', 'fields'),
  1087. 'Test' => array('test' => '')
  1088. );
  1089. $result = $this->Controller->Security->validatePost($this->Controller);
  1090. $this->assertTrue($result);
  1091. $this->Controller->request->data = array(
  1092. '_Token' => compact('key', 'fields'),
  1093. 'Test' => array('test' => '1')
  1094. );
  1095. $result = $this->Controller->Security->validatePost($this->Controller);
  1096. $this->assertTrue($result);
  1097. $this->Controller->request->data = array(
  1098. '_Token' => compact('key', 'fields'),
  1099. 'Test' => array('test' => '2')
  1100. );
  1101. $result = $this->Controller->Security->validatePost($this->Controller);
  1102. $this->assertTrue($result);
  1103. }
  1104. /**
  1105. * testInvalidAuthHeaders method
  1106. *
  1107. * @access public
  1108. * @return void
  1109. */
  1110. function testInvalidAuthHeaders() {
  1111. $this->Controller->Security->blackHoleCallback = null;
  1112. $_SERVER['PHP_AUTH_USER'] = 'admin';
  1113. $_SERVER['PHP_AUTH_PW'] = 'password';
  1114. $realm = 'cakephp.org';
  1115. $loginData = array('type' => 'basic', 'realm' => $realm);
  1116. $this->Controller->Security->requireLogin($loginData);
  1117. $this->Controller->Security->startup($this->Controller);
  1118. $expected = 'WWW-Authenticate: Basic realm="'.$realm.'"';
  1119. $this->assertEqual(count($this->Controller->testHeaders), 1);
  1120. $this->assertEqual(current($this->Controller->testHeaders), $expected);
  1121. }
  1122. /**
  1123. * test that a requestAction's controller will have the _Token appended to
  1124. * the params.
  1125. *
  1126. * @return void
  1127. * @see http://cakephp.lighthouseapp.com/projects/42648/tickets/68
  1128. */
  1129. function testSettingTokenForRequestAction() {
  1130. $this->Controller->Security->startup($this->Controller);
  1131. $key = $this->Controller->request->params['_Token']['key'];
  1132. $this->Controller->params['requested'] = 1;
  1133. unset($this->Controller->request->params['_Token']);
  1134. $this->Controller->Security->startup($this->Controller);
  1135. $this->assertEqual($this->Controller->request->params['_Token']['key'], $key);
  1136. }
  1137. /**
  1138. * test that blackhole doesn't delete the _Token session key so repeat data submissions
  1139. * stay blackholed.
  1140. *
  1141. * @link http://cakephp.lighthouseapp.com/projects/42648/tickets/214
  1142. * @return void
  1143. */
  1144. function testBlackHoleNotDeletingSessionInformation() {
  1145. $this->Controller->Security->startup($this->Controller);
  1146. $this->Controller->Security->blackHole($this->Controller, 'auth');
  1147. $this->assertTrue($this->Controller->Security->Session->check('_Token'), '_Token was deleted by blackHole %s');
  1148. }
  1149. /**
  1150. * test setting
  1151. *
  1152. * @return void
  1153. */
  1154. function testCsrfSettings() {
  1155. $this->Security->validatePost = false;
  1156. $this->Security->csrfCheck = true;
  1157. $this->Security->csrfExpires = '+10 minutes';
  1158. $this->Security->startup($this->Controller);
  1159. $token = $this->Security->Session->read('_Token');
  1160. $this->assertEquals(count($token['csrfTokens']), 1, 'Missing the csrf token.');
  1161. $this->assertEquals(strtotime('+10 minutes'), current($token['csrfTokens']), 'Token expiry does not match');
  1162. }
  1163. /**
  1164. * Test setting multiple nonces, when startup() is called more than once, (ie more than one request.)
  1165. *
  1166. * @return void
  1167. */
  1168. function testCsrfSettingMultipleNonces() {
  1169. $this->Security->validatePost = false;
  1170. $this->Security->csrfCheck = true;
  1171. $this->Security->csrfExpires = '+10 minutes';
  1172. $this->Security->startup($this->Controller);
  1173. $this->Security->startup($this->Controller);
  1174. $token = $this->Security->Session->read('_Token');
  1175. $this->assertEquals(count($token['csrfTokens']), 2, 'Missing the csrf token.');
  1176. foreach ($token['csrfTokens'] as $key => $expires) {
  1177. $this->assertEquals(strtotime('+10 minutes'), $expires, 'Token expiry does not match');
  1178. }
  1179. }
  1180. /**
  1181. * test that nonces are consumed by form submits.
  1182. *
  1183. * @return void
  1184. */
  1185. function testCsrfNonceConsumption() {
  1186. $this->Security->validatePost = false;
  1187. $this->Security->csrfCheck = true;
  1188. $this->Security->csrfExpires = '+10 minutes';
  1189. $this->Security->Session->write('_Token.csrfTokens', array('nonce1' => strtotime('+10 minutes')));
  1190. $this->Controller->request = $this->getMock('CakeRequest', array('is'));
  1191. $this->Controller->request->expects($this->once())->method('is')
  1192. ->with('post')
  1193. ->will($this->returnValue(true));
  1194. $this->Controller->request->params['action'] = 'index';
  1195. $this->Controller->request->data = array(
  1196. '_Token' => array(
  1197. 'key' => 'nonce1'
  1198. ),
  1199. 'Post' => array(
  1200. 'title' => 'Woot'
  1201. )
  1202. );
  1203. $this->Security->startup($this->Controller);
  1204. $token = $this->Security->Session->read('_Token');
  1205. $this->assertFalse(isset($token['csrfTokens']['nonce1']), 'Token was not consumed');
  1206. }
  1207. /**
  1208. * test that expired values in the csrfTokens are cleaned up.
  1209. *
  1210. * @return void
  1211. */
  1212. function testCsrfNonceVacuum() {
  1213. $this->Security->validatePost = false;
  1214. $this->Security->csrfCheck = true;
  1215. $this->Security->csrfExpires = '+10 minutes';
  1216. $this->Security->Session->write('_Token.csrfTokens', array(
  1217. 'valid' => strtotime('+30 minutes'),
  1218. 'poof' => strtotime('-11 minutes'),
  1219. 'dust' => strtotime('-20 minutes')
  1220. ));
  1221. $this->Security->startup($this->Controller);
  1222. $tokens = $this->Security->Session->read('_Token.csrfTokens');
  1223. $this->assertEquals(2, count($tokens), 'Too many tokens left behind');
  1224. $this->assertNotEmpty('valid', $tokens, 'Valid token was removed.');
  1225. }
  1226. /**
  1227. * test that when the key is missing the request is blackHoled
  1228. *
  1229. * @return void
  1230. */
  1231. function testCsrfBlackHoleOnKeyMismatch() {
  1232. $this->Security->validatePost = false;
  1233. $this->Security->csrfCheck = true;
  1234. $this->Security->csrfExpires = '+10 minutes';
  1235. $this->Security->Session->write('_Token.csrfTokens', array('nonce1' => strtotime('+10 minutes')));
  1236. $this->Controller->request = $this->getMock('CakeRequest', array('is'));
  1237. $this->Controller->request->expects($this->once())->method('is')
  1238. ->with('post')
  1239. ->will($this->returnValue(true));
  1240. $this->Controller->request->params['action'] = 'index';
  1241. $this->Controller->request->data = array(
  1242. '_Token' => array(
  1243. 'key' => 'not the right value'
  1244. ),
  1245. 'Post' => array(
  1246. 'title' => 'Woot'
  1247. )
  1248. );
  1249. $this->Security->startup($this->Controller);
  1250. $this->assertTrue($this->Controller->failed, 'fail() was not called.');
  1251. }
  1252. /**
  1253. * test that when the key is missing the request is blackHoled
  1254. *
  1255. * @return void
  1256. */
  1257. function testCsrfBlackHoleOnExpiredKey() {
  1258. $this->Security->validatePost = false;
  1259. $this->Security->csrfCheck = true;
  1260. $this->Security->csrfExpires = '+10 minutes';
  1261. $this->Security->Session->write('_Token.csrfTokens', array('nonce1' => strtotime('-5 minutes')));
  1262. $this->Controller->request = $this->getMock('CakeRequest', array('is'));
  1263. $this->Controller->request->expects($this->once())->method('is')
  1264. ->with('post')
  1265. ->will($this->returnValue(true));
  1266. $this->Controller->request->params['action'] = 'index';
  1267. $this->Controller->request->data = array(
  1268. '_Token' => array(
  1269. 'key' => 'nonce1'
  1270. ),
  1271. 'Post' => array(
  1272. 'title' => 'Woot'
  1273. )
  1274. );
  1275. $this->Security->startup($this->Controller);
  1276. $this->assertTrue($this->Controller->failed, 'fail() was not called.');
  1277. }
  1278. /**
  1279. * test that csrfUseOnce = false works.
  1280. *
  1281. * @return void
  1282. */
  1283. function testCsrfNotUseOnce() {
  1284. $this->Security->validatePost = false;
  1285. $this->Security->csrfCheck = true;
  1286. $this->Security->csrfUseOnce = false;
  1287. $this->Security->csrfExpires = '+10 minutes';
  1288. // Generate one token
  1289. $this->Security->startup($this->Controller);
  1290. $token = $this->Security->Session->read('_Token.csrfTokens');
  1291. $this->assertEquals(1, count($token), 'Should only be one token.');
  1292. $this->Security->startup($this->Controller);
  1293. $token2 = $this->Security->Session->read('_Token.csrfTokens');
  1294. $this->assertEquals(1, count($token2), 'Should only be one token.');
  1295. $this->assertEquals($token, $token2, 'Tokens should not be different.');
  1296. }
  1297. /**
  1298. * ensure that longer session tokens are not consumed
  1299. *
  1300. * @return void
  1301. */
  1302. function testCsrfNotUseOnceValidationLeavingToken() {
  1303. $this->Security->validatePost = false;
  1304. $this->Security->csrfCheck = true;
  1305. $this->Security->csrfUseOnce = false;
  1306. $this->Security->csrfExpires = '+10 minutes';
  1307. $this->Security->Session->write('_Token.csrfTokens', array('nonce1' => strtotime('+10 minutes')));
  1308. $this->Controller->request = $this->getMock('CakeRequest', array('is'));
  1309. $this->Controller->request->expects($this->once())->method('is')
  1310. ->with('post')
  1311. ->will($this->returnValue(true));
  1312. $this->Controller->request->params['action'] = 'index';
  1313. $this->Controller->request->data = array(
  1314. '_Token' => array(
  1315. 'key' => 'nonce1'
  1316. ),
  1317. 'Post' => array(
  1318. 'title' => 'Woot'
  1319. )
  1320. );
  1321. $this->Security->startup($this->Controller);
  1322. $token = $this->Security->Session->read('_Token');
  1323. $this->assertTrue(isset($token['csrfTokens']['nonce1']), 'Token was consumed');
  1324. }
  1325. }