FormAuthenticateTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 2.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Auth;
  17. use Cake\Auth\FormAuthenticate;
  18. use Cake\Controller\ComponentRegistry;
  19. use Cake\Http\Response;
  20. use Cake\Http\ServerRequest;
  21. use Cake\I18n\Time;
  22. use Cake\ORM\Entity;
  23. use Cake\TestSuite\TestCase;
  24. /**
  25. * Test case for FormAuthentication
  26. */
  27. class FormAuthenticateTest extends TestCase
  28. {
  29. /**
  30. * Fixtures
  31. *
  32. * @var array
  33. */
  34. public $fixtures = ['core.AuthUsers', 'core.Users'];
  35. /**
  36. * setup
  37. *
  38. * @return void
  39. */
  40. public function setUp(): void
  41. {
  42. parent::setUp();
  43. $this->Collection = $this->getMockBuilder(ComponentRegistry::class)->getMock();
  44. $this->auth = new FormAuthenticate($this->Collection, [
  45. 'userModel' => 'Users',
  46. ]);
  47. $password = password_hash('password', PASSWORD_DEFAULT);
  48. $this->getTableLocator()->clear();
  49. $Users = $this->getTableLocator()->get('Users');
  50. $Users->updateAll(['password' => $password], []);
  51. $AuthUsers = $this->getTableLocator()->get('AuthUsers', [
  52. 'className' => 'TestApp\Model\Table\AuthUsersTable',
  53. ]);
  54. $AuthUsers->updateAll(['password' => $password], []);
  55. $this->response = $this->getMockBuilder(Response::class)->getMock();
  56. }
  57. /**
  58. * test applying settings in the constructor
  59. *
  60. * @return void
  61. */
  62. public function testConstructor(): void
  63. {
  64. $object = new FormAuthenticate($this->Collection, [
  65. 'userModel' => 'AuthUsers',
  66. 'fields' => ['username' => 'user', 'password' => 'password'],
  67. ]);
  68. $this->assertSame('AuthUsers', $object->getConfig('userModel'));
  69. $this->assertEquals(['username' => 'user', 'password' => 'password'], $object->getConfig('fields'));
  70. }
  71. /**
  72. * test the authenticate method
  73. *
  74. * @return void
  75. */
  76. public function testAuthenticateNoData()
  77. {
  78. $request = new ServerRequest([
  79. 'url' => 'posts/index',
  80. 'post' => [],
  81. ]);
  82. $this->assertFalse($this->auth->authenticate($request, $this->response));
  83. }
  84. /**
  85. * test the authenticate method
  86. *
  87. * @return void
  88. */
  89. public function testAuthenticateNoUsername(): void
  90. {
  91. $request = new ServerRequest([
  92. 'url' => 'posts/index',
  93. 'post' => ['password' => 'foobar'],
  94. ]);
  95. $this->assertFalse($this->auth->authenticate($request, $this->response));
  96. }
  97. /**
  98. * test the authenticate method
  99. *
  100. * @return void
  101. */
  102. public function testAuthenticateNoPassword(): void
  103. {
  104. $request = new ServerRequest([
  105. 'url' => 'posts/index',
  106. 'post' => ['username' => 'mariano'],
  107. ]);
  108. $this->assertFalse($this->auth->authenticate($request, $this->response));
  109. }
  110. /**
  111. * test authenticate password is false method
  112. *
  113. * @return void
  114. */
  115. public function testAuthenticatePasswordIsFalse(): void
  116. {
  117. $request = new ServerRequest([
  118. 'url' => 'posts/index',
  119. 'post' => [
  120. 'username' => 'mariano',
  121. 'password' => null,
  122. ],
  123. ]);
  124. $this->assertFalse($this->auth->authenticate($request, $this->response));
  125. }
  126. /**
  127. * Test for password as empty string with _checkFields() call skipped
  128. * Refs https://github.com/cakephp/cakephp/pull/2441
  129. *
  130. * @return void
  131. */
  132. public function testAuthenticatePasswordIsEmptyString(): void
  133. {
  134. $request = new ServerRequest([
  135. 'url' => 'posts/index',
  136. 'post' => [
  137. 'username' => 'mariano',
  138. 'password' => '',
  139. ],
  140. ]);
  141. $this->auth = $this->getMockBuilder(FormAuthenticate::class)
  142. ->setMethods(['_checkFields'])
  143. ->setConstructorArgs([
  144. $this->Collection,
  145. ['userModel' => 'Users'],
  146. ])
  147. ->getMock();
  148. // Simulate that check for ensuring password is not empty is missing.
  149. $this->auth->expects($this->once())
  150. ->method('_checkFields')
  151. ->will($this->returnValue(true));
  152. $this->assertFalse($this->auth->authenticate($request, $this->response));
  153. }
  154. /**
  155. * test authenticate field is not string
  156. *
  157. * @return void
  158. */
  159. public function testAuthenticateFieldsAreNotString(): void
  160. {
  161. $request = new ServerRequest([
  162. 'url' => 'posts/index',
  163. 'post' => [
  164. 'username' => ['mariano', 'phpnut'],
  165. 'password' => 'my password',
  166. ],
  167. ]);
  168. $this->assertFalse($this->auth->authenticate($request, $this->response));
  169. $request = new ServerRequest([
  170. 'url' => 'posts/index',
  171. 'post' => [
  172. 'username' => 'mariano',
  173. 'password' => ['password1', 'password2'],
  174. ],
  175. ]);
  176. $this->assertFalse($this->auth->authenticate($request, $this->response));
  177. }
  178. /**
  179. * test the authenticate method
  180. *
  181. * @return void
  182. */
  183. public function testAuthenticateInjection(): void
  184. {
  185. $request = new ServerRequest([
  186. 'url' => 'posts/index',
  187. 'post' => [
  188. 'username' => '> 1',
  189. 'password' => "' OR 1 = 1",
  190. ],
  191. ]);
  192. $this->assertFalse($this->auth->authenticate($request, $this->response));
  193. }
  194. /**
  195. * test authenticate success
  196. *
  197. * @return void
  198. */
  199. public function testAuthenticateSuccess(): void
  200. {
  201. $request = new ServerRequest([
  202. 'url' => 'posts/index',
  203. 'post' => [
  204. 'username' => 'mariano',
  205. 'password' => 'password',
  206. ],
  207. ]);
  208. $result = $this->auth->authenticate($request, $this->response);
  209. $expected = [
  210. 'id' => 1,
  211. 'username' => 'mariano',
  212. 'created' => new Time('2007-03-17 01:16:23'),
  213. 'updated' => new Time('2007-03-17 01:18:31'),
  214. ];
  215. $this->assertEquals($expected, $result);
  216. }
  217. /**
  218. * Test that authenticate() includes virtual fields.
  219. *
  220. * @return void
  221. */
  222. public function testAuthenticateIncludesVirtualFields(): void
  223. {
  224. $users = $this->getTableLocator()->get('Users');
  225. $users->setEntityClass('TestApp\Model\Entity\VirtualUser');
  226. $request = new ServerRequest([
  227. 'url' => 'posts/index',
  228. 'post' => [
  229. 'username' => 'mariano',
  230. 'password' => 'password',
  231. ],
  232. ]);
  233. $result = $this->auth->authenticate($request, $this->response);
  234. $expected = [
  235. 'id' => 1,
  236. 'username' => 'mariano',
  237. 'bonus' => 'bonus',
  238. 'created' => new Time('2007-03-17 01:16:23'),
  239. 'updated' => new Time('2007-03-17 01:18:31'),
  240. ];
  241. $this->assertEquals($expected, $result);
  242. }
  243. /**
  244. * test a model in a plugin.
  245. *
  246. * @return void
  247. */
  248. public function testPluginModel(): void
  249. {
  250. $this->loadPlugins(['TestPlugin']);
  251. $PluginModel = $this->getTableLocator()->get('TestPlugin.AuthUsers');
  252. $user['id'] = 1;
  253. $user['username'] = 'gwoo';
  254. $user['password'] = password_hash('cake', PASSWORD_BCRYPT);
  255. $PluginModel->save(new Entity($user));
  256. $this->auth->setConfig('userModel', 'TestPlugin.AuthUsers');
  257. $request = new ServerRequest([
  258. 'url' => 'posts/index',
  259. 'post' => [
  260. 'username' => 'gwoo',
  261. 'password' => 'cake',
  262. ],
  263. ]);
  264. $result = $this->auth->authenticate($request, $this->response);
  265. $expected = [
  266. 'id' => 1,
  267. 'username' => 'gwoo',
  268. 'created' => new Time('2007-03-17 01:16:23'),
  269. 'updated' => new Time('2007-03-17 01:18:31'),
  270. ];
  271. $this->assertEquals($expected, $result);
  272. $this->clearPlugins();
  273. }
  274. /**
  275. * Test using custom finder
  276. *
  277. * @return void
  278. */
  279. public function testFinder(): void
  280. {
  281. $request = new ServerRequest([
  282. 'url' => 'posts/index',
  283. 'post' => [
  284. 'username' => 'mariano',
  285. 'password' => 'password',
  286. ],
  287. ]);
  288. $this->auth->setConfig([
  289. 'userModel' => 'AuthUsers',
  290. 'finder' => 'auth',
  291. ]);
  292. $result = $this->auth->authenticate($request, $this->response);
  293. $expected = [
  294. 'id' => 1,
  295. 'username' => 'mariano',
  296. ];
  297. $this->assertEquals($expected, $result, 'Result should not contain "created" and "modified" fields');
  298. $this->auth->setConfig([
  299. 'finder' => ['auth' => ['return_created' => true]],
  300. ]);
  301. $result = $this->auth->authenticate($request, $this->response);
  302. $expected = [
  303. 'id' => 1,
  304. 'username' => 'mariano',
  305. 'created' => new Time('2007-03-17 01:16:23'),
  306. ];
  307. $this->assertEquals($expected, $result);
  308. }
  309. /**
  310. * Test using custom finder
  311. *
  312. * @return void
  313. */
  314. public function testFinderOptions(): void
  315. {
  316. $request = new ServerRequest([
  317. 'url' => 'posts/index',
  318. 'post' => [
  319. 'username' => 'mariano',
  320. 'password' => 'password',
  321. ],
  322. ]);
  323. $this->auth->setConfig([
  324. 'userModel' => 'AuthUsers',
  325. 'finder' => 'username',
  326. ]);
  327. $result = $this->auth->authenticate($request, $this->response);
  328. $expected = [
  329. 'id' => 1,
  330. 'username' => 'mariano',
  331. ];
  332. $this->assertEquals($expected, $result);
  333. $this->auth->setConfig([
  334. 'finder' => ['username' => ['username' => 'nate']],
  335. ]);
  336. $result = $this->auth->authenticate($request, $this->response);
  337. $expected = [
  338. 'id' => 5,
  339. 'username' => 'nate',
  340. ];
  341. $this->assertEquals($expected, $result);
  342. }
  343. /**
  344. * test password hasher settings
  345. *
  346. * @return void
  347. */
  348. public function testPasswordHasherSettings(): void
  349. {
  350. $this->auth->setConfig('passwordHasher', [
  351. 'className' => 'Default',
  352. 'hashType' => PASSWORD_BCRYPT,
  353. ]);
  354. $passwordHasher = $this->auth->passwordHasher();
  355. $result = $passwordHasher->getConfig();
  356. $this->assertEquals(PASSWORD_BCRYPT, $result['hashType']);
  357. $hash = password_hash('mypass', PASSWORD_BCRYPT);
  358. $User = $this->getTableLocator()->get('Users');
  359. $User->updateAll(
  360. ['password' => $hash],
  361. ['username' => 'mariano']
  362. );
  363. $request = new ServerRequest([
  364. 'url' => 'posts/index',
  365. 'post' => [
  366. 'username' => 'mariano',
  367. 'password' => 'mypass',
  368. ],
  369. ]);
  370. $result = $this->auth->authenticate($request, $this->response);
  371. $expected = [
  372. 'id' => 1,
  373. 'username' => 'mariano',
  374. 'created' => new Time('2007-03-17 01:16:23'),
  375. 'updated' => new Time('2007-03-17 01:18:31'),
  376. ];
  377. $this->assertEquals($expected, $result);
  378. $this->auth = new FormAuthenticate($this->Collection, [
  379. 'fields' => ['username' => 'username', 'password' => 'password'],
  380. 'userModel' => 'Users',
  381. ]);
  382. $this->auth->setConfig('passwordHasher', [
  383. 'className' => 'Default',
  384. ]);
  385. $this->assertEquals($expected, $this->auth->authenticate($request, $this->response));
  386. $User->updateAll(
  387. ['password' => '$2y$10$/G9GBQDZhWUM4w/WLes3b.XBZSK1hGohs5dMi0vh/oen0l0a7DUyK'],
  388. ['username' => 'mariano']
  389. );
  390. $this->assertFalse($this->auth->authenticate($request, $this->response));
  391. }
  392. /**
  393. * Tests that using default means password don't need to be rehashed
  394. *
  395. * @return void
  396. */
  397. public function testAuthenticateNoRehash(): void
  398. {
  399. $request = new ServerRequest([
  400. 'url' => 'posts/index',
  401. 'post' => [
  402. 'username' => 'mariano',
  403. 'password' => 'password',
  404. ],
  405. ]);
  406. $result = $this->auth->authenticate($request, $this->response);
  407. $this->assertNotEmpty($result);
  408. $this->assertFalse($this->auth->needsPasswordRehash());
  409. }
  410. /**
  411. * Tests that not using the Default password hasher means that the password
  412. * needs to be rehashed
  413. *
  414. * @return void
  415. */
  416. public function testAuthenticateRehash(): void
  417. {
  418. $this->auth = new FormAuthenticate($this->Collection, [
  419. 'userModel' => 'Users',
  420. 'passwordHasher' => 'Weak',
  421. ]);
  422. $password = $this->auth->passwordHasher()->hash('password');
  423. $this->getTableLocator()->get('Users')->updateAll(['password' => $password], []);
  424. $request = new ServerRequest([
  425. 'url' => 'posts/index',
  426. 'post' => [
  427. 'username' => 'mariano',
  428. 'password' => 'password',
  429. ],
  430. ]);
  431. $result = $this->auth->authenticate($request, $this->response);
  432. $this->assertNotEmpty($result);
  433. $this->assertTrue($this->auth->needsPasswordRehash());
  434. }
  435. }