浏览代码

Make sure fieldList is updated for PasswordableBehavior

dereuromark 9 年之前
父节点
当前提交
94811263b9

+ 27 - 1
docs/Behavior/Passwordable.md

@@ -29,6 +29,7 @@ Also capable of:
 | maxLength         |   PWD_MAX_LENGTH      |   |   
 | validator         |   'default'           |   |
 | customValidation  |   null                |    Custom validation rule(s) for the formField on top     |
+| forceFieldList    |   false               |    Force field list to overwrite entity accessibility     |
 
 You can either pass those to the behavior at runtime, or globally via Configure and `app.php`:
 ```php
@@ -112,7 +113,7 @@ class UsersController extends Controller {
 
 		if ($this->request->is(['put', 'post'])) {
 			$options = [
-				'fieldList' => [ 'pwd', 'pwd_repeat', ... ]
+				'fieldList' => [...]
 			];
 			$user = $this->Users->patchEntity($user, $this->request->data, $options);
 			if ($this->Users->save($user)) {
@@ -188,3 +189,28 @@ Always allow [a-z], [0-9] and ALL special chars a user can possibly type in.
 Regex rules can be useful to assert that the password is strong enough, though. That means, that it contains not just letters/numbers, but
 also some special chars. This would be way more secure and useful. But also try to be reasonable here, some developers tend to overreach here,
 making it very annoying to set up passwords.
+
+### Field list and Accessibility
+The behavior will automatically add the internally needed fields to the `'fieldList'` options array, if you provided one on patching.
+So you only need to pass in the other non-password-related fields:
+```php
+$options = [
+	'fieldList' => ['id', 'name']
+];
+$user = $this->Users->patchEntity($user, $this->request->data, $options);
+```
+
+If the config `forceFieldList` is set to true, it will even create the fieldList for you on the fly.
+Otherwise it will use the entity accessible config to determine if the password can be assigned.
+So if you do not want to force it, make sure your entity has those fields not protected:
+```php
+// Inside the entity
+protected $_accessible = [
+	'*' => false,
+	'pwd' => true,
+	...
+];
+
+// Or from the outside before patching
+$user->accessible('*', false); // Mark all properties as protected
+$user->accessible(['pwd', ...], true); // Allow certain fields

+ 17 - 1
src/Model/Behavior/PasswordableBehavior.php

@@ -53,7 +53,8 @@ class PasswordableBehavior extends Behavior {
 		'minLength' => PWD_MIN_LENGTH,
 		'maxLength' => PWD_MAX_LENGTH,
 		'validator' => 'default',
-		'customValidation' => null // Custom validation rule(s) for the formField
+		'customValidation' => null, // Custom validation rule(s) for the formField
+		'forceFieldList' => false,
 	];
 
 	/**
@@ -221,6 +222,21 @@ class PasswordableBehavior extends Behavior {
 		$formFieldRepeat = $this->_config['formFieldRepeat'];
 		$formFieldCurrent = $this->_config['formFieldCurrent'];
 
+		if (!isset($options['fieldList']) && $this->_config['forceFieldList']) {
+			$options['fieldList'] = array_keys((array)$data);
+		}
+		if (isset($options['fieldList'])) {
+			if (!in_array($formField, $options['fieldList'])) {
+				$options['fieldList'][] = $formField;
+			}
+			if (!in_array($formFieldRepeat, $options['fieldList'])) {
+				$options['fieldList'][] = $formFieldRepeat;
+			}
+			if (!in_array($formFieldCurrent, $options['fieldList'])) {
+				$options['fieldList'][] = $formFieldCurrent;
+			}
+		}
+
 		// Make sure fields are set and validation rules are triggered - prevents tempering of form data
 		if (!isset($data[$formField])) {
 			$data[$formField] = '';

+ 41 - 41
tests/TestCase/Model/Behavior/PasswordableBehaviorTest.php

@@ -19,6 +19,11 @@ class PasswordableBehaviorTest extends TestCase {
 	];
 
 	/**
+	 * @var \TestApp\Model\Table\ToolsUsersTable
+	 */
+	public $Users;
+
+	/**
 	 * SetUp method
 	 *
 	 * @return void
@@ -391,11 +396,13 @@ class PasswordableBehaviorTest extends TestCase {
 			'pwd_repeat' => '123456'
 		];
 		$user->accessible('*', false); // Mark all properties as protected
-		$user->accessible(['id', 'pwd', 'pwd_repeat', 'pwd_current'], true);
-		$this->Users->patchEntity($user, $data);
-		// Test whitelist setting - only "password" needs to gets auto-added
-		$options = ['validate' => true, 'fieldList' => ['id', 'pwd', 'pwd_repeat', 'pwd_current']];
+		$user->accessible(['id'], true); // Allow id to be accessible by default
+		$user = $this->Users->patchEntity($user, $data, ['fieldList' => ['id']]);
 
+		$this->assertNotSame($is['password'], $user['password']);
+		$this->assertTrue($user->dirty('pwd'));
+
+		$options = ['validate' => true];
 		$is = $this->Users->save($user, $options);
 		$this->assertTrue(!empty($is));
 
@@ -416,11 +423,11 @@ class PasswordableBehaviorTest extends TestCase {
 		];
 		$user->accessible('*', false); // Mark all properties as protected
 		$user->accessible(['id', 'name'], true);
-		$this->Users->patchEntity($user, $data);
+		$this->Users->patchEntity($user, $data, ['fieldList' => ['id', 'name']]);
 		// Test whitelist setting - only "password" gets auto-added, pwd, pwd_repeat etc need to be added manually
 		// NOTE that I had to remove the code for adding those fields from the behavior (as it was not functional)
 		// 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.
-		$options = ['validate' => true, 'fieldList' => ['id', 'name']];
+		$options = ['validate' => true];
 		$is = $this->Users->save($user, $options);
 
 		// Validation errors triggered - as expected
@@ -429,17 +436,12 @@ class PasswordableBehaviorTest extends TestCase {
 	}
 
 	/**
-	 * Test cake2.4 passwordHasher feature
-	 *
 	 * @return void
 	 */
-	public function testPasswordHasher() {
-		$this->skipIf((float)Configure::version() < 2.4, 'Needs 2.4 and above');
-
+	public function testPatchWithFieldList() {
 		$this->Users->addBehavior('Tools.Passwordable', [
 			'formField' => 'pwd',
 			'formFieldRepeat' => 'pwd_repeat',
-			'allowSame' => false,
 			'current' => false,
 			'passwordHasher' => 'Complex',
 		]);
@@ -448,42 +450,40 @@ class PasswordableBehaviorTest extends TestCase {
 			'pwd' => 'somepwd',
 			'pwd_repeat' => 'somepwd'
 		];
-		$this->Users->patchEntity($user, $data);
+		$user->accessible('*', false); // Mark all properties as protected
+		$user->accessible(['id'], true);
+		$this->Users->patchEntity($user, $data, ['fieldList' => ['id']]);
 		$result = $this->Users->save($user);
 		$this->assertTrue((bool)$result);
-		$userCopy = clone($user);
+	}
 
-		$this->Users->removeBehavior('Passwordable');
-		$this->Users->addBehavior('Tools.Passwordable', ['current' => true]);
-		$user = clone($userCopy);
+	/**
+	 * @return void
+	 */
+	public function testPatchWithoutFieldList() {
+		$this->Users->addBehavior('Tools.Passwordable', [
+			'formField' => 'pwd',
+			'formFieldRepeat' => 'pwd_repeat',
+			'current' => false,
+			'passwordHasher' => 'Complex',
+			'forceFieldList' => true
+		]);
+		$user = $this->Users->newEntity();
 		$data = [
-			'pwd' => '123456',
-			'pwd_repeat' => '12345678',
+			'name' => 'x',
+			'pwd' => 'somepwd',
+			'pwd_repeat' => 'somepwd'
 		];
-		$this->Users->patchEntity($user, $data);
-		$this->assertTrue($this->Users->behaviors()->has('Passwordable'));
-		$is = $this->Users->save($user);
-		$this->assertFalse($is);
+		$user->accessible('*', false); // Mark all properties as protected
+		$user->accessible(['id'], true);
+		$user = $this->Users->patchEntity($user, $data);
+		$result = $this->Users->save($user);
+		$this->assertTrue((bool)$result);
 
-		$user = clone($userCopy);
-		$data = [
-			'pwd_current' => 'somepwdx',
-			'pwd' => '123456',
-			'pwd_repeat' => '123456'
-		];
-		$this->Users->patchEntity($user, $data);
-		$is = $this->Users->save($user);
-		$this->assertFalse($is);
+		$savedUser = $this->Users->get($user->id);
 
-		$user = clone($userCopy);
-		$data = [
-			'pwd_current' => 'somepwd',
-			'pwd' => '123456',
-			'pwd_repeat' => '123456'
-		];
-		$this->Users->patchEntity($user, $data);
-		$is = $this->Users->save($user);
-		$this->assertTrue(!empty($is));
+		$this->assertSame($data['name'], $savedUser->name);
+		$this->assertSame($user->password, $savedUser->password);
 	}
 
 	/**