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

/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php

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