PasswordableBehaviorTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. <?php
  2. namespace Tools\Test\TestCase\Model\Behavior;
  3. use Cake\Auth\PasswordHasherFactory;
  4. use Cake\Core\Configure;
  5. use Cake\Network\Request;
  6. use Cake\ORM\TableRegistry;
  7. use Cake\Routing\Router;
  8. use Tools\TestSuite\TestCase;
  9. class PasswordableBehaviorTest extends TestCase {
  10. /**
  11. * @var array
  12. */
  13. public $fixtures = [
  14. 'plugin.tools.tools_users', 'plugin.tools.roles',
  15. ];
  16. /**
  17. * SetUp method
  18. *
  19. * @return void
  20. */
  21. public function setUp() {
  22. parent::setUp();
  23. Configure::write('App.namespace', 'TestApp');
  24. Configure::delete('Passwordable');
  25. Configure::write('Passwordable.auth', 'AuthTest');
  26. $this->Users = TableRegistry::get('ToolsUsers');
  27. $this->hasher = PasswordHasherFactory::build('Default');
  28. Router::setRequestInfo(new Request());
  29. }
  30. public function tearDown() {
  31. TableRegistry::clear();
  32. parent::tearDown();
  33. }
  34. /**
  35. * Make sure validation is triggered correctly
  36. *
  37. * @return void
  38. */
  39. public function testValidate() {
  40. $this->Users->addBehavior('Tools.Passwordable', []);
  41. $user = $this->Users->newEntity();
  42. $data = [
  43. 'pwd' => '123456',
  44. ];
  45. $this->Users->patchEntity($user, $data);
  46. $is = $this->Users->save($user);
  47. $this->assertFalse($is);
  48. $this->assertEquals(['pwd_repeat'], array_keys($user->errors()));
  49. $user = $this->Users->newEntity();
  50. $data = [
  51. 'pwd' => '1234ab',
  52. 'pwd_repeat' => '123456'
  53. ];
  54. $this->Users->patchEntity($user, $data);
  55. $is = $this->Users->save($user);
  56. $this->assertFalse($is);
  57. $this->assertEquals(['validateIdentical' => __d('tools', 'valErrPwdNotMatch')], $user->errors()['pwd_repeat']);
  58. $user = $this->Users->newEntity();
  59. $data = [
  60. 'pwd' => '123456',
  61. 'pwd_repeat' => '123456'
  62. ];
  63. $this->Users->patchEntity($user, $data);
  64. $is = $this->Users->save($user);
  65. $this->assertTrue(!empty($is));
  66. }
  67. /**
  68. * Test that confirm false does not require confirmation
  69. *
  70. * @return void
  71. */
  72. public function testValidateNoConfirm() {
  73. $this->Users->addBehavior('Tools.Passwordable', ['confirm' => false]);
  74. $user = $this->Users->newEntity();
  75. $data = [
  76. 'pwd' => '123456',
  77. ];
  78. $this->Users->patchEntity($user, $data);
  79. $is = $this->Users->save($user);
  80. //debug($is);
  81. $this->assertTrue(!empty($is));
  82. }
  83. /**
  84. * Trigger validation and update process if no values are entered but are required
  85. *
  86. * @return void
  87. */
  88. public function testValidateRequired() {
  89. $this->Users->addBehavior('Tools.Passwordable');
  90. $user = $this->Users->newEntity();
  91. $data = [
  92. 'pwd' => '',
  93. 'pwd_repeat' => ''
  94. ];
  95. $this->Users->patchEntity($user, $data);
  96. $is = $this->Users->save($user);
  97. $this->assertFalse($is);
  98. $this->assertEquals(['pwd', 'pwd_repeat'], array_keys($user->errors()));
  99. }
  100. /**
  101. * Validation and update process gets skipped if no values are entered
  102. *
  103. * @return void
  104. */
  105. public function testValidateNotRequired() {
  106. $this->Users->addBehavior('Tools.Passwordable', ['require' => false]);
  107. $user = $this->Users->newEntity();
  108. $data = [
  109. 'name' => 'foo', // we need at least one field besides the password on CREATE
  110. 'pwd' => '',
  111. 'pwd_repeat' => ''
  112. ];
  113. $this->Users->patchEntity($user, $data);
  114. $is = $this->Users->save($user);
  115. $this->assertTrue((bool)$is);
  116. $this->assertEquals(['name', 'id'], $is->visibleProperties());
  117. $id = $user->id;
  118. $user = $this->Users->newEntity();
  119. $data = [
  120. 'id' => $id,
  121. 'pwd' => '',
  122. 'pwd_repeat' => ''
  123. ];
  124. $this->Users->patchEntity($user, $data);
  125. $is = $this->Users->save($user);
  126. $this->assertTrue((bool)$is);
  127. $this->assertEquals(['id'], $is->visibleProperties());
  128. }
  129. /**
  130. * PasswordableBehaviorTest::testValidateEmptyWithCurrentPassword()
  131. *
  132. * @return void
  133. */
  134. public function testValidateEmptyWithCurrentPassword() {
  135. $this->Users->addBehavior('Tools.Passwordable', ['current' => true]);
  136. $user = $this->Users->newEntity();
  137. $data = [
  138. 'id' => '123',
  139. 'pwd' => '',
  140. 'pwd_repeat' => '',
  141. 'pwd_current' => '123456',
  142. ];
  143. $this->Users->patchEntity($user, $data);
  144. $is = $this->Users->save($user);
  145. //debug($user->errors());
  146. $this->assertFalse($is);
  147. $this->assertEquals(['pwd', 'pwd_repeat', 'pwd_current'], array_keys($user->errors()));
  148. $this->tearDown();
  149. $this->setUp();
  150. $this->Users->removeBehavior('Passwordable');
  151. $this->Users->addBehavior('Tools.Passwordable', ['require' => false, 'current' => true]);
  152. $user = $this->Users->newEntity();
  153. $data = [
  154. 'name' => 'foo',
  155. 'pwd' => '',
  156. 'pwd_repeat' => '',
  157. 'pwd_current' => '',
  158. ];
  159. $this->Users->patchEntity($user, $data);
  160. $is = $this->Users->save($user);
  161. $this->assertTrue(!empty($is));
  162. }
  163. /**
  164. * Test aliases for field names
  165. *
  166. * @return void
  167. */
  168. public function testDifferentFieldNames() {
  169. $this->Users->addBehavior('Tools.Passwordable', [
  170. 'formField' => 'passw',
  171. 'formFieldRepeat' => 'passw_repeat',
  172. 'formFieldCurrent' => 'passw_current',
  173. ]);
  174. $user = $this->Users->newEntity();
  175. $data = [
  176. 'passw' => '123456',
  177. 'passw_repeat' => '123456'
  178. ];
  179. $this->Users->patchEntity($user, $data);
  180. //debug($this->Users->data);
  181. $is = $this->Users->save($user);
  182. $this->assertTrue(!empty($is));
  183. }
  184. /**
  185. * Assert that allowSame false does not allow storing the same password as previously entered
  186. *
  187. * @return void
  188. */
  189. public function testNotSame() {
  190. $user = $this->Users->newEntity();
  191. $data = [
  192. 'name' => 'admin',
  193. 'password' => $this->hasher->hash('somepwd'),
  194. 'role_id' => '1'
  195. ];
  196. $this->Users->patchEntity($user, $data);
  197. $result = $this->Users->save($user);
  198. $this->assertTrue((bool)$result);
  199. $userCopy = clone($user);
  200. $this->Users->addBehavior('Tools.Passwordable', [
  201. 'formField' => 'passw',
  202. 'formFieldRepeat' => 'passw_repeat',
  203. 'formFieldCurrent' => 'passw_current',
  204. 'allowSame' => false,
  205. 'current' => true,
  206. ]);
  207. $user = clone($userCopy);
  208. $data = [
  209. 'passw_current' => 'something',
  210. 'passw' => 'somepwd',
  211. 'passw_repeat' => 'somepwd'
  212. ];
  213. $this->Users->patchEntity($user, $data);
  214. $is = $this->Users->save($user);
  215. $this->assertFalse($is);
  216. $user = clone($userCopy);
  217. $data = [
  218. 'passw_current' => 'somepwd',
  219. 'passw' => 'newpwd',
  220. 'passw_repeat' => 'newpwd'
  221. ];
  222. $this->Users->patchEntity($user, $data);
  223. $is = $this->Users->save($user);
  224. $this->assertTrue(!empty($is));
  225. }
  226. /**
  227. * Assert that allowSame false does not allow storing the same password as previously entered
  228. *
  229. * @return void
  230. */
  231. public function testNotSameWithoutCurrentField() {
  232. $this->Users->addBehavior('Tools.Passwordable', [
  233. 'formField' => 'passw',
  234. 'formFieldRepeat' => 'passw_repeat',
  235. 'allowSame' => false,
  236. 'current' => false
  237. ]);
  238. $user = $this->Users->newEntity();
  239. $data = [
  240. 'passw' => 'somepwd',
  241. 'passw_repeat' => 'somepwd'
  242. ];
  243. $this->Users->patchEntity($user, $data);
  244. $is = $this->Users->save($user);
  245. $this->assertTrue((bool)$is);
  246. $userCopy = clone($user);
  247. $uid = $user->id;
  248. $user = clone($userCopy);
  249. $data = [
  250. 'passw' => 'somepwd',
  251. 'passw_repeat' => 'somepwd'
  252. ];
  253. $this->Users->patchEntity($user, $data);
  254. $is = $this->Users->save($user);
  255. $this->assertFalse((bool)$is);
  256. $user = clone($userCopy);
  257. $data = [
  258. 'passw' => 'newpwd',
  259. 'passw_repeat' => 'newpwd'
  260. ];
  261. $this->Users->patchEntity($user, $data);
  262. $is = $this->Users->save($user);
  263. $this->assertTrue((bool)$is);
  264. }
  265. /**
  266. * Assert that on edit it does not wrongly pass validation (require => false)
  267. *
  268. * @return void
  269. */
  270. public function testRequireFalse() {
  271. $this->Users->addBehavior('Tools.Passwordable', [
  272. 'formField' => 'passw',
  273. 'formFieldRepeat' => 'passw_repeat',
  274. 'require' => false
  275. ]);
  276. $user = $this->Users->newEntity();
  277. $data = [
  278. 'passw' => 'somepwd',
  279. 'passw_repeat' => 'somepwd'
  280. ];
  281. $this->Users->patchEntity($user, $data);
  282. $is = $this->Users->save($user);
  283. $this->assertTrue((bool)$is);
  284. $userCopy = clone($user);
  285. $user = clone($userCopy);
  286. $data = [
  287. 'passw' => '',
  288. 'passw_repeat' => ''
  289. ];
  290. $this->Users->patchEntity($user, $data);
  291. $is = $this->Users->save($user);
  292. $this->assertTrue((bool)$is);
  293. //debug($user->errors());
  294. $user = clone($userCopy);
  295. $data = [
  296. 'passw' => 'somepwd2',
  297. 'passw_repeat' => ''
  298. ];
  299. $this->Users->patchEntity($user, $data);
  300. $is = $this->Users->save($user);
  301. $this->assertFalse((bool)$is);
  302. $user = clone($userCopy);
  303. $data = [
  304. 'passw' => 'somepwd2',
  305. 'passw_repeat' => 'somepwd2'
  306. ];
  307. $this->Users->patchEntity($user, $data);
  308. $is = $this->Users->save($user);
  309. $this->assertTrue((bool)$is);
  310. }
  311. /**
  312. * Needs faking of pwd check...
  313. *
  314. * @return void
  315. */
  316. public function testValidateCurrent() {
  317. $this->assertFalse($this->Users->behaviors()->has('Passwordable'));
  318. $user = $this->Users->newEntity();
  319. $data = [
  320. 'name' => 'xyz',
  321. 'password' => $this->hasher->hash('somepwd')];
  322. $this->Users->patchEntity($user, $data);
  323. $result = $this->Users->save($user);
  324. $this->assertTrue(!empty($result));
  325. $userCopy = clone($user);
  326. $uid = $user->id;
  327. $this->Users->removeBehavior('Passwordable');
  328. $this->Users->addBehavior('Tools.Passwordable', ['current' => true]);
  329. $user = clone($userCopy);
  330. $data = [
  331. 'pwd' => '123456',
  332. 'pwd_repeat' => '12345678',
  333. ];
  334. $this->Users->patchEntity($user, $data);
  335. $this->assertTrue($this->Users->behaviors()->has('Passwordable'));
  336. $is = $this->Users->save($user);
  337. $this->assertFalse($is);
  338. $user = clone($userCopy);
  339. $data = [
  340. 'pwd_current' => 'somepwdx',
  341. 'pwd' => '123456',
  342. 'pwd_repeat' => '123456'
  343. ];
  344. $this->Users->patchEntity($user, $data);
  345. $is = $this->Users->save($user);
  346. $this->assertFalse($is);
  347. $user = clone($userCopy);
  348. $data = [
  349. 'name' => 'Yeah',
  350. 'pwd_current' => 'somepwd',
  351. 'pwd' => '123456',
  352. 'pwd_repeat' => '123456'
  353. ];
  354. $user->accessible('*', false); // Mark all properties as protected
  355. $user->accessible(['id', 'pwd', 'pwd_repeat', 'pwd_current'], true);
  356. $this->Users->patchEntity($user, $data);
  357. // Test whitelist setting - only "password" needs to gets auto-added
  358. $options = ['validate' => true, 'fieldList' => ['id', 'pwd', 'pwd_repeat', 'pwd_current']];
  359. $is = $this->Users->save($user, $options);
  360. $this->assertTrue(!empty($is));
  361. $user = $this->Users->get($uid);
  362. // The password is updated, the name not
  363. $this->assertSame($is['password'], $user['password']);
  364. $this->assertSame('xyz', $user['name']);
  365. // Proof that we manually need to add pwd, pwd_repeat etc due to a bug in CakePHP<=2.4 allowing behaviors to only modify saving,
  366. // not validating of additional whitelist fields. Validation for those will be just skipped, no matter what the behavior tries
  367. // to set.
  368. $user = clone($userCopy);
  369. $data = [
  370. 'name' => 'Yeah',
  371. 'pwd_current' => '123', // Obviously wrong
  372. 'pwd' => 'some', // Too short
  373. 'pwd_repeat' => 'somex' // Don't match
  374. ];
  375. $user->accessible('*', false); // Mark all properties as protected
  376. $user->accessible(['id', 'name'], true);
  377. $this->Users->patchEntity($user, $data);
  378. // Test whitelist setting - only "password" gets auto-added, pwd, pwd_repeat etc need to be added manually
  379. // NOTE that I had to remove the code for adding those fields from the behavior (as it was not functional)
  380. // So of course, this won't work now as expected. But feel free to try to add them in the behavior. Results will be the same.
  381. $options = ['validate' => true, 'fieldList' => ['id', 'name']];
  382. $is = $this->Users->save($user, $options);
  383. // Validation errors triggered - as expected
  384. $this->assertFalse($is);
  385. $this->assertSame(['pwd', 'pwd_repeat', 'pwd_current'], array_keys($user->errors()));
  386. }
  387. /**
  388. * Test cake2.4 passwordHasher feature
  389. *
  390. * @return void
  391. */
  392. public function testPasswordHasher() {
  393. $this->skipIf((float)Configure::version() < 2.4, 'Needs 2.4 and above');
  394. $this->Users->addBehavior('Tools.Passwordable', [
  395. 'formField' => 'pwd',
  396. 'formFieldRepeat' => 'pwd_repeat',
  397. 'allowSame' => false,
  398. 'current' => false,
  399. 'passwordHasher' => 'Complex',
  400. ]);
  401. $user = $this->Users->newEntity();
  402. $data = [
  403. 'pwd' => 'somepwd',
  404. 'pwd_repeat' => 'somepwd'
  405. ];
  406. $this->Users->patchEntity($user, $data);
  407. $result = $this->Users->save($user);
  408. $this->assertTrue((bool)$result);
  409. $userCopy = clone($user);
  410. $this->Users->removeBehavior('Passwordable');
  411. $this->Users->addBehavior('Tools.Passwordable', ['current' => true]);
  412. $user = clone($userCopy);
  413. $data = [
  414. 'pwd' => '123456',
  415. 'pwd_repeat' => '12345678',
  416. ];
  417. $this->Users->patchEntity($user, $data);
  418. $this->assertTrue($this->Users->behaviors()->has('Passwordable'));
  419. $is = $this->Users->save($user);
  420. $this->assertFalse($is);
  421. $user = clone($userCopy);
  422. $data = [
  423. 'pwd_current' => 'somepwdx',
  424. 'pwd' => '123456',
  425. 'pwd_repeat' => '123456'
  426. ];
  427. $this->Users->patchEntity($user, $data);
  428. $is = $this->Users->save($user);
  429. $this->assertFalse($is);
  430. $user = clone($userCopy);
  431. $data = [
  432. 'pwd_current' => 'somepwd',
  433. 'pwd' => '123456',
  434. 'pwd_repeat' => '123456'
  435. ];
  436. $this->Users->patchEntity($user, $data);
  437. $is = $this->Users->save($user);
  438. $this->assertTrue(!empty($is));
  439. }
  440. /**
  441. * PasswordableBehaviorTest::testBlowfish()
  442. *
  443. * @return void
  444. */
  445. public function testBlowfish() {
  446. $this->Users->addBehavior('Tools.Passwordable', [
  447. 'allowSame' => false,
  448. 'current' => false,
  449. 'authType' => 'Blowfish',
  450. ]);
  451. $user = $this->Users->newEntity();
  452. $data = [
  453. 'pwd' => 'somepwd',
  454. 'pwd_repeat' => 'somepwd'
  455. ];
  456. $this->Users->patchEntity($user, $data);
  457. $result = $this->Users->save($user);
  458. $this->assertTrue((bool)$result);
  459. $userCopy = clone($user);
  460. $uid = (string)$user->id;
  461. $this->Users->removeBehavior('Passwordable');
  462. $this->Users->addBehavior('Tools.Passwordable', ['current' => true]);
  463. // Without the current password it will not continue
  464. $data = [
  465. 'pwd' => '123456',
  466. 'pwd_repeat' => '12345678',
  467. ];
  468. $this->Users->patchEntity($user, $data);
  469. $this->assertTrue($this->Users->behaviors()->has('Passwordable'));
  470. $result = $this->Users->save($user);
  471. $this->assertFalse($result);
  472. // Without the correct current password it will not continue
  473. $user = clone($userCopy);
  474. $data = [
  475. 'pwd_current' => 'somepwdx',
  476. 'pwd' => '123456',
  477. 'pwd_repeat' => '123456'
  478. ];
  479. $this->Users->patchEntity($user, $data);
  480. $result = $this->Users->save($user);
  481. $this->assertFalse($result);
  482. // Now it will
  483. $user = clone($userCopy);
  484. $data = [
  485. 'pwd_current' => 'somepwd',
  486. 'pwd' => '123456',
  487. 'pwd_repeat' => '123456'
  488. ];
  489. $this->Users->patchEntity($user, $data);
  490. $result = $this->Users->save($user);
  491. $this->assertTrue((bool)$result);
  492. }
  493. /**
  494. * Tests needsPasswordRehash()
  495. *
  496. * @return void
  497. */
  498. public function testNeedsPasswordRehash() {
  499. $this->Users->addBehavior('Tools.Passwordable', [
  500. 'allowSame' => false,
  501. 'current' => false,
  502. 'authType' => 'Blowfish',
  503. 'passwordHasher' => 'Default'
  504. ]);
  505. $hash = password_hash('foobar', PASSWORD_BCRYPT);
  506. $result = $this->Users->needsPasswordRehash($hash);
  507. $this->assertFalse($result);
  508. $hash = sha1('foobar');
  509. $result = $this->Users->needsPasswordRehash($hash);
  510. $this->assertTrue($result);
  511. }
  512. /**
  513. * Tests needsPasswordRehash()
  514. *
  515. * @return void
  516. */
  517. public function testNeedsPasswordRehashWithNotSupportedHasher() {
  518. $this->Users->addBehavior('Tools.Passwordable', [
  519. 'allowSame' => false,
  520. 'current' => false,
  521. 'authType' => 'Blowfish',
  522. ]);
  523. $hash = password_hash('foobar', PASSWORD_BCRYPT);
  524. $result = $this->Users->needsPasswordRehash($hash);
  525. $this->assertFalse($result);
  526. $this->Users->removeBehavior('Passwordable');
  527. $this->Users->addBehavior('Tools.Passwordable', [
  528. 'allowSame' => false,
  529. 'current' => false,
  530. 'authType' => 'Blowfish',
  531. 'passwordHasher' => 'Default'
  532. ]);
  533. $hash = password_hash('foobar', PASSWORD_BCRYPT);
  534. $result = $this->Users->needsPasswordRehash($hash);
  535. $this->assertFalse($result);
  536. }
  537. /**
  538. * PasswordableBehaviorTest::testSettings()
  539. *
  540. * @return void
  541. */
  542. public function testSettings() {
  543. // Pwd min and max length
  544. $this->Users->addBehavior('Tools.Passwordable', [
  545. 'allowSame' => false,
  546. 'current' => false,
  547. 'minLength' => 3,
  548. 'maxLength' => 6,
  549. ]);
  550. $user = $this->Users->newEntity();
  551. $data = [
  552. 'pwd' => '123',
  553. 'pwd_repeat' => '123'
  554. ];
  555. $this->Users->patchEntity($user, $data);
  556. $result = $this->Users->save($user);
  557. $this->assertTrue((bool)$result);
  558. $user = $this->Users->newEntity();
  559. $data = [
  560. 'pwd' => '12345678',
  561. 'pwd_repeat' => '12345678'
  562. ];
  563. $this->Users->patchEntity($user, $data);
  564. $result = $this->Users->save($user);
  565. $this->assertFalse($result);
  566. $expected = [
  567. 'pwd' => ['between' => __d('tools', 'valErrBetweenCharacters {0} {1}', 3, 6)],
  568. ];
  569. $this->assertEquals($expected, $user->errors());
  570. }
  571. /**
  572. * Test that validate false also works.
  573. *
  574. * @return void
  575. */
  576. public function testSaveWithValidateFalse() {
  577. $this->Users->addBehavior('Tools.Passwordable');
  578. $user = $this->Users->newEntity();
  579. $data = [
  580. 'pwd' => '123',
  581. ];
  582. $this->Users->patchEntity($user, $data, ['validate' => false]);
  583. $result = $this->Users->save($user);
  584. $this->assertTrue((bool)$result);
  585. $uid = (string)$user->id;
  586. $hash = $user['password'];
  587. $data = [
  588. 'pwd' => '1234'
  589. ];
  590. $this->Users->patchEntity($user, $data, ['validate' => false]);
  591. $result2 = $this->Users->save($user);
  592. $this->assertTrue((bool)$result2);
  593. $hash2 = $user['password'];
  594. $this->assertTrue($hash !== $hash2);
  595. }
  596. /**
  597. * PasswordableBehaviorTest::testValidateCustomRule()
  598. *
  599. * @return void
  600. */
  601. public function testValidateCustomRule() {
  602. $rules = [
  603. 'validateCustom' => [
  604. 'rule' => ['custom', '#^[a-z0-9]+$#'], // Just a test example, never use this regexp!
  605. 'message' => 'Foo Bar',
  606. 'last' => true,
  607. ],
  608. 'validateCustomExt' => [
  609. 'rule' => ['custom', '#^[a-z]+$#'], // Just a test example, never use this regexp!
  610. 'message' => 'Foo Bar Ext',
  611. 'last' => true,
  612. ]
  613. ];
  614. $this->Users->addBehavior('Tools.Passwordable', [
  615. 'customValidation' => $rules]);
  616. $user = $this->Users->newEntity();
  617. $data = [
  618. 'pwd' => '%123456',
  619. 'pwd_repeat' => '%123456'
  620. ];
  621. $this->Users->patchEntity($user, $data);
  622. $is = $this->Users->save($user);
  623. $this->assertFalse($is);
  624. $result = $user->errors();
  625. $expected = ['pwd' => ['validateCustom' => 'Foo Bar']];
  626. $this->assertSame($expected, $result);
  627. $user = $this->Users->newEntity();
  628. $data = [
  629. 'pwd' => 'abc123',
  630. 'pwd_repeat' => 'abc123'
  631. ];
  632. $this->Users->patchEntity($user, $data);
  633. $is = $this->Users->save($user);
  634. $this->assertFalse($is);
  635. $result = $user->errors();
  636. $expected = ['pwd' => ['validateCustomExt' => 'Foo Bar Ext']];
  637. $this->assertSame($expected, $result);
  638. $user = $this->Users->newEntity();
  639. $data = [
  640. 'pwd' => 'abcdef',
  641. 'pwd_repeat' => 'abcdef'
  642. ];
  643. $this->Users->patchEntity($user, $data);
  644. $is = $this->Users->save($user);
  645. $this->assertTrue((bool)$is);
  646. }
  647. }