Loading core/config/schema/core.data_types.schema.yml +10 −0 Original line number Diff line number Diff line Loading @@ -523,6 +523,16 @@ field.value.string_long: type: text label: 'Value' # Schema for the configuration of the Password field type. field.storage_settings.password: type: field.storage_settings.string label: 'Password settings' field.field_settings.password: type: mapping label: 'Password settings' # Schema for the configuration of the URI field type. field.storage_settings.uri: Loading core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php +5 −7 Original line number Diff line number Diff line Loading @@ -46,6 +46,11 @@ public function preSave() { // Reset the pre_hashed value since it has now been used. $this->pre_hashed = FALSE; } elseif (!$entity->isNew() && empty($this->value)) { // If the password is empty, that means it was not changed, so use the // original password. $this->value = $entity->original->{$this->getFieldDefinition()->getName()}->value; } elseif ($entity->isNew() || (strlen(trim($this->value)) > 0 && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) { // Allow alternate password hashing schemes. $this->value = \Drupal::service('password')->hash(trim($this->value)); Loading @@ -55,13 +60,6 @@ public function preSave() { } } if (!$entity->isNew()) { // If the password is empty, that means it was not changed, so use the // original password. if (empty($this->value)) { $this->value = $entity->original->{$this->getFieldDefinition()->getName()}->value; } } // Ensure that the existing password is unset to minimise risks of it // getting serialized and stored somewhere. $this->existing = NULL; Loading core/tests/Drupal/KernelTests/Core/Field/FieldType/PasswordItemTest.php 0 → 100644 +189 −0 Original line number Diff line number Diff line <?php namespace Drupal\KernelTests\Core\Field\FieldType; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Password\PasswordInterface; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\Tests\field\Kernel\FieldKernelTestBase; /** * @coversDefaultClass \Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem * @group Field */ class PasswordItemTest extends FieldKernelTestBase { /** * A field storage to use in this test class. * * @var \Drupal\field\Entity\FieldStorageConfig */ protected $fieldStorage; /** * The field used in this test class. * * @var \Drupal\field\Entity\FieldConfig */ protected $field; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->fieldStorage = FieldStorageConfig::create([ 'field_name' => 'test_field', 'entity_type' => 'entity_test', 'type' => 'password', ]); $this->fieldStorage->save(); $this->field = FieldConfig::create([ 'field_storage' => $this->fieldStorage, 'bundle' => 'entity_test', 'required' => TRUE, ]); $this->field->save(); } /** * @covers ::preSave */ public function testPreSavePreHashed() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = 'this_is_not_a_real_hash'; $entity->test_field->pre_hashed = TRUE; $entity->save(); $this->assertSame('this_is_not_a_real_hash', $entity->test_field->value); $this->assertFalse($entity->test_field->pre_hashed); } /** * @covers ::preSave */ public function testPreSaveNewNull() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = NULL; $entity->save(); $this->assertNull($entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveNewEmptyString() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = ''; $entity->save(); // The string starts with the portable password string and is a hash of an // empty string. $this->assertStringStartsWith('$S$', $entity->test_field->value); $this->assertTrue($this->container->get('password')->check('', $entity->test_field->value)); } /** * @covers ::preSave */ public function testPreSaveNewMultipleSpacesString() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = ' '; $entity->save(); // The string starts with the portable password string and is a hash of an // empty string. $this->assertStringStartsWith('$S$', $entity->test_field->value); $this->assertTrue($this->container->get('password')->check('', $entity->test_field->value)); } /** * @covers ::preSave */ public function testPreSaveExistingNull() { $entity = EntityTest::create(); $entity->test_field = $this->randomString(); $entity->save(); $this->assertNotNull($entity->test_field->value); $entity->test_field = NULL; $entity->save(); $this->assertNull($entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveExistingEmptyString() { $entity = EntityTest::create(); $entity->test_field = $this->randomString(); $entity->save(); $hashed_password = $entity->test_field->value; $entity->test_field = ''; $entity->save(); $this->assertSame($hashed_password, $entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveExistingMultipleSpacesString() { $entity = EntityTest::create(); $entity->test_field = $this->randomString(); $entity->save(); $entity->test_field = ' '; $entity->save(); // @todo Fix this bug in https://www.drupal.org/project/i/3238399. $this->assertSame(' ', $entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveExceptionNew() { $entity = EntityTest::create(); $entity->test_field = str_repeat('a', PasswordInterface::PASSWORD_MAX_LENGTH + 1); $this->expectException(EntityStorageException::class); $this->expectExceptionMessage('The entity does not have a password'); $entity->save(); } /** * @covers ::preSave */ public function testPreSaveExceptionExisting() { $entity = EntityTest::create(); $entity->test_field = 'will_be_hashed'; $entity->save(); $this->assertNotEquals('will_be_hashed', $entity->test_field->value); $this->expectException(EntityStorageException::class); $this->expectExceptionMessage('The entity does not have a password'); $entity->test_field = str_repeat('a', PasswordInterface::PASSWORD_MAX_LENGTH + 1); $entity->save(); } } Loading
core/config/schema/core.data_types.schema.yml +10 −0 Original line number Diff line number Diff line Loading @@ -523,6 +523,16 @@ field.value.string_long: type: text label: 'Value' # Schema for the configuration of the Password field type. field.storage_settings.password: type: field.storage_settings.string label: 'Password settings' field.field_settings.password: type: mapping label: 'Password settings' # Schema for the configuration of the URI field type. field.storage_settings.uri: Loading
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php +5 −7 Original line number Diff line number Diff line Loading @@ -46,6 +46,11 @@ public function preSave() { // Reset the pre_hashed value since it has now been used. $this->pre_hashed = FALSE; } elseif (!$entity->isNew() && empty($this->value)) { // If the password is empty, that means it was not changed, so use the // original password. $this->value = $entity->original->{$this->getFieldDefinition()->getName()}->value; } elseif ($entity->isNew() || (strlen(trim($this->value)) > 0 && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) { // Allow alternate password hashing schemes. $this->value = \Drupal::service('password')->hash(trim($this->value)); Loading @@ -55,13 +60,6 @@ public function preSave() { } } if (!$entity->isNew()) { // If the password is empty, that means it was not changed, so use the // original password. if (empty($this->value)) { $this->value = $entity->original->{$this->getFieldDefinition()->getName()}->value; } } // Ensure that the existing password is unset to minimise risks of it // getting serialized and stored somewhere. $this->existing = NULL; Loading
core/tests/Drupal/KernelTests/Core/Field/FieldType/PasswordItemTest.php 0 → 100644 +189 −0 Original line number Diff line number Diff line <?php namespace Drupal\KernelTests\Core\Field\FieldType; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Password\PasswordInterface; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\Tests\field\Kernel\FieldKernelTestBase; /** * @coversDefaultClass \Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem * @group Field */ class PasswordItemTest extends FieldKernelTestBase { /** * A field storage to use in this test class. * * @var \Drupal\field\Entity\FieldStorageConfig */ protected $fieldStorage; /** * The field used in this test class. * * @var \Drupal\field\Entity\FieldConfig */ protected $field; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->fieldStorage = FieldStorageConfig::create([ 'field_name' => 'test_field', 'entity_type' => 'entity_test', 'type' => 'password', ]); $this->fieldStorage->save(); $this->field = FieldConfig::create([ 'field_storage' => $this->fieldStorage, 'bundle' => 'entity_test', 'required' => TRUE, ]); $this->field->save(); } /** * @covers ::preSave */ public function testPreSavePreHashed() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = 'this_is_not_a_real_hash'; $entity->test_field->pre_hashed = TRUE; $entity->save(); $this->assertSame('this_is_not_a_real_hash', $entity->test_field->value); $this->assertFalse($entity->test_field->pre_hashed); } /** * @covers ::preSave */ public function testPreSaveNewNull() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = NULL; $entity->save(); $this->assertNull($entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveNewEmptyString() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = ''; $entity->save(); // The string starts with the portable password string and is a hash of an // empty string. $this->assertStringStartsWith('$S$', $entity->test_field->value); $this->assertTrue($this->container->get('password')->check('', $entity->test_field->value)); } /** * @covers ::preSave */ public function testPreSaveNewMultipleSpacesString() { $entity = EntityTest::create([ 'name' => $this->randomString(), ]); $entity->test_field = ' '; $entity->save(); // The string starts with the portable password string and is a hash of an // empty string. $this->assertStringStartsWith('$S$', $entity->test_field->value); $this->assertTrue($this->container->get('password')->check('', $entity->test_field->value)); } /** * @covers ::preSave */ public function testPreSaveExistingNull() { $entity = EntityTest::create(); $entity->test_field = $this->randomString(); $entity->save(); $this->assertNotNull($entity->test_field->value); $entity->test_field = NULL; $entity->save(); $this->assertNull($entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveExistingEmptyString() { $entity = EntityTest::create(); $entity->test_field = $this->randomString(); $entity->save(); $hashed_password = $entity->test_field->value; $entity->test_field = ''; $entity->save(); $this->assertSame($hashed_password, $entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveExistingMultipleSpacesString() { $entity = EntityTest::create(); $entity->test_field = $this->randomString(); $entity->save(); $entity->test_field = ' '; $entity->save(); // @todo Fix this bug in https://www.drupal.org/project/i/3238399. $this->assertSame(' ', $entity->test_field->value); } /** * @covers ::preSave */ public function testPreSaveExceptionNew() { $entity = EntityTest::create(); $entity->test_field = str_repeat('a', PasswordInterface::PASSWORD_MAX_LENGTH + 1); $this->expectException(EntityStorageException::class); $this->expectExceptionMessage('The entity does not have a password'); $entity->save(); } /** * @covers ::preSave */ public function testPreSaveExceptionExisting() { $entity = EntityTest::create(); $entity->test_field = 'will_be_hashed'; $entity->save(); $this->assertNotEquals('will_be_hashed', $entity->test_field->value); $this->expectException(EntityStorageException::class); $this->expectExceptionMessage('The entity does not have a password'); $entity->test_field = str_repeat('a', PasswordInterface::PASSWORD_MAX_LENGTH + 1); $entity->save(); } }