diff -u b/core/modules/user/src/MigratePassword.php /dev/null --- b/core/modules/user/src/MigratePassword.php +++ /dev/null @@ -1,128 +0,0 @@ -D7 upgrade path. - */ -class MigratePassword implements PasswordInterface { - - /** - * The original password service. - * - * @var \Drupal\Core\Password\PasswordInterface - */ - protected $originalPassword; - - /** - * Indicates if MD5 password prefixing is enabled. - */ - protected $md5Prefixing = FALSE; - - /** - * Indicates if password needs hashing. - * - * Setting to false allows passwords that have already been hashed to be - * migrated. - */ - protected $needsHashing = TRUE; - - /** - * Builds the replacement password service class. - * - * @param \Drupal\Core\Password\PasswordInterface $original_password - * The password object. - */ - public function __construct(PasswordInterface $original_password) { - $this->originalPassword = $original_password; - } - - /** - * {@inheritdoc} - */ - public function check($password, $hash) { - return $this->originalPassword->check($password, $hash); - } - - /** - * {@inheritdoc} - */ - public function needsRehash($hash) { - return $this->originalPassword->needsRehash($hash); - } - - /** - * {@inheritdoc} - */ - public function hash($password) { - if ($this->needsHashing) { - $hash = $this->originalPassword->hash($password); - } - else { - $hash = $password; - } - - // Allow prefixing only if the service was asked to prefix. Check also if - // the $password pattern is conforming to a MD5 result. - if ($this->md5Prefixing && preg_match('/^[0-9a-f]{32}$/', $password)) { - $hash = 'U' . $hash; - } - - return $hash; - } - - /** - * Enables the MD5 password prefixing. - */ - public function enableMd5Prefixing() { - $this->md5Prefixing = TRUE; - } - - /** - * Disables the MD5 password prefixing. - */ - public function disableMd5Prefixing() { - $this->md5Prefixing = FALSE; - } - - /** - * Enables hashing of passwords. - * - * @return $this - * - * @see \Drupal\user\MigratePassword::disableHashing() - */ - public function enableHashing() { - $this->needsHashing = TRUE; - return $this; - } - - /** - * Disables hashing of passwords. - * - * This is useful if passwords have already been hashed and don't require - * hashing on save. For example, Drupal 7 passwords have already been hashed - * and do not need to be hashed again during user migration. - * - * @return $this - */ - public function disableHashing() { - $this->needsHashing = FALSE; - return $this; - } - - /** - * Implements the PhpassHashedPassword::getCountLog2() method. - * - * @todo: Revisit this whole alternate password service: - * https://www.drupal.org/node/2540594. - */ - public function getCountLog2($setting) { - return $this->originalPassword->getCountLog2($setting); - } - -} diff -u b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php --- b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php +++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php @@ -3,14 +3,13 @@ namespace Drupal\user\Plugin\migrate\destination; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem; use Drupal\Core\Password\PasswordInterface; use Drupal\migrate\Plugin\MigrationInterface; -use Drupal\migrate\MigrateException; -use Drupal\user\MigratePassword; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; use Drupal\migrate\Row; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -25,7 +24,7 @@ /** * The password service class. * - * @var \Drupal\user\MigratePassword + * @var \Drupal\Core\Password\PasswordInterface */ protected $password; @@ -56,9 +55,6 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, PasswordInterface $password) { parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager); $this->password = $password; - if (!($this->password instanceof MigratePassword)) { - throw new MigrateException('Password service has been altered by another module, aborting.'); - } } /** @@ -84,27 +80,26 @@ * @throws \Drupal\migrate\MigrateException */ public function import(Row $row, array $old_destination_id_values = array()) { - if (isset($this->configuration['md5_passwords'])) { - $this->password->enableMd5Prefixing(); - } - else { - $this->password->disableHashing(); - } - // Do not overwrite the root account password. if ($row->getDestinationProperty('uid') == 1) { $row->removeDestinationProperty('pass'); } - $ids = parent::import($row, $old_destination_id_values); + return parent::import($row, $old_destination_id_values); + } + /** + * {@inheritdoc} + */ + protected function save(ContentEntityInterface $entity, array $old_destination_id_values = array()) { + // Set the pre_hashed password so that the PasswordItem field does not hash + // already hashed passwords. If the md5_passwords configuration option is + // set we need to rehash the password and prefix with a U. + // @see \Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem::preSave() + $entity->pass->pre_hashed = TRUE; if (isset($this->configuration['md5_passwords'])) { - $this->password->disableMd5Prefixing(); + $entity->pass->value = 'U' . $this->password->hash($entity->pass->value); } - else { - $this->password->enableHashing(); - } - - return $ids; + return parent::save($entity, $old_destination_id_values); } /** only in patch2: unchanged: --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php @@ -28,6 +28,8 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel ->setSetting('case_sensitive', TRUE); $properties['existing'] = DataDefinition::create('string') ->setLabel(new TranslatableMarkup('Existing password')); + $properties['pre_hashed'] = DataDefinition::create('boolean') + ->setLabel(new TranslatableMarkup('Determines if a password needs hashing')); return $properties; } @@ -40,8 +42,11 @@ public function preSave() { $entity = $this->getEntity(); - // Update the user password if it has changed. - if ($entity->isNew() || (strlen(trim($this->value)) > 0 && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) { + if ($this->pre_hashed) { + // Reset the pre_hashed value since it has now been used. + $this->pre_hashed = FALSE; + } + 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)); // Abort if the hashing failed and returned FALSE. only in patch2: unchanged: --- a/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php @@ -3,6 +3,7 @@ namespace Drupal\migrate_drupal_ui\Tests\d6; use Drupal\migrate_drupal_ui\Tests\MigrateUpgradeTestBase; +use Drupal\user\Entity\User; /** * Tests Drupal 6 upgrade using the migrate UI. @@ -70,4 +71,16 @@ protected function getEntityCounts() { ]; } + /** + * Executes all steps of migrations upgrade. + */ + protected function testMigrateUpgrade() { + parent::testMigrateUpgrade(); + + // Ensure migrated users can log in. + $user = User::load(2); + $user->pass_raw = 'john.doe_pass'; + $this->drupalLogin($user); + } + } only in patch2: unchanged: --- a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php @@ -3,6 +3,7 @@ namespace Drupal\migrate_drupal_ui\Tests\d7; use Drupal\migrate_drupal_ui\Tests\MigrateUpgradeTestBase; +use Drupal\user\Entity\User; /** * Tests Drupal 7 upgrade using the migrate UI. @@ -70,4 +71,16 @@ protected function getEntityCounts() { ]; } + /** + * Executes all steps of migrations upgrade. + */ + protected function testMigrateUpgrade() { + parent::testMigrateUpgrade(); + + // Ensure migrated users can log in. + $user = User::load(2); + $user->pass_raw = 'a password'; + $this->drupalLogin($user); + } + } only in patch2: unchanged: --- a/core/modules/user/src/UserServiceProvider.php +++ /dev/null @@ -1,25 +0,0 @@ -setDefinition('password_original', $container->getDefinition('password')); - $container->setDefinition('password', $container->getDefinition('password_migrate')); - } - -} only in patch2: unchanged: --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -66,9 +66,6 @@ services: arguments: ['@current_user', '@entity.manager'] tags: - { name: 'context_provider' } - password_migrate: - class: Drupal\user\MigratePassword - arguments: ['@password_original'] parameters: user.tempstore.expire: 604800