diff --git a/core/composer.json b/core/composer.json index c975dc9..067f70c 100644 --- a/core/composer.json +++ b/core/composer.json @@ -31,7 +31,8 @@ "behat/mink": "~1.6", "behat/mink-goutte-driver": "~1.1", "fabpot/goutte": "^2.0.3", - "masterminds/html5": "~2.1" + "masterminds/html5": "~2.1", + "ircmaxell/password-compat": "~1.0" }, "autoload": { "psr-4": { diff --git a/core/core.services.yml b/core/core.services.yml index 6681e7d..8bb66c3 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -5,6 +5,7 @@ parameters: default: keyvalue.database factory.keyvalue.expirable: default: keyvalue.expirable.database + password_hash_cost: 10 services: cache_factory: class: Drupal\Core\Cache\CacheFactory @@ -662,13 +663,14 @@ services: class: Drupal\Core\Path\PathValidator arguments: ['@router', '@router.no_access_checks', '@current_user', '@path_processor_manager'] -# The argument to the hashing service defined in services.yml, to the -# constructor of PhpassHashedPassword is the log2 number of iterations for -# password stretching. +# The first argument of the hashing service (constructor of PhpPassword) is the +# 'cost' option of password_hash(). # @todo increase by 1 every Drupal version in order to counteract increases in -# the speed and power of computers available to crack the hashes. The current -# password hashing method was introduced in Drupal 7 with a log2 count of 15. +# the speed and power of computers available to crack the hashes. password: + class: Drupal\Core\Password\PhpPassword + arguments: ['%password_hash_cost%', '@drupal7_password'] + drupal7_password: class: Drupal\Core\Password\PhpassHashedPassword arguments: [16] accept_header_matcher: diff --git a/core/lib/Drupal/Core/Password/PhpPassword.php b/core/lib/Drupal/Core/Password/PhpPassword.php new file mode 100644 index 0000000..7b0968f --- /dev/null +++ b/core/lib/Drupal/Core/Password/PhpPassword.php @@ -0,0 +1,104 @@ +=5.5.0) password hashing + * functions. + * + * NOTE: Because password hashing functions are available only since PHP 5.5, on + * PHP 5.4 the compatibility is assured by the 'password_compat' library (see + * https://github.com/ircmaxell/password_compat). + * + * @see http://php.net/manual/en/ref.password.php + * @see https://github.com/ircmaxell/password_compat + * + * @todo Remove 'password_compat' library when Drupal will require PHP >= 5.5.0 + */ +class PhpPassword implements PasswordInterface { + + /** + * The algorithmic cost that should be used. + * + * @var int + */ + protected $cost; + + /** + * The Drupal 7 password hashing service. + * + * @var \Drupal\Core\Password\PhpassHashedPassword + */ + protected $drupal7Password; + + /** + * Constructs a new password hashing instance. + * + * @param int $cost + * The algorithmic cost that should be used. + * @param \Drupal\Core\Password\PasswordInterface $drupal7_password + * The Drupal7 password hashing service. + */ + function __construct($cost, PasswordInterface $drupal7_password) { + $this->cost = $cost; + $this->drupal7Password = $drupal7_password; + } + + /** + * {@inheritdoc} + */ + public function hash($password) { + // Prevent DoS attacks by refusing to hash large passwords. + if (strlen($password) > 512) { + return FALSE; + } + + return password_hash($password, PASSWORD_DEFAULT, $this->getOptions()); + } + + /** + * {@inheritdoc} + */ + public function check($password, UserInterface $account) { + $stored_hash = $account->getPassword(); + + // Password migrated from Drupal 7. + if (substr($stored_hash, 0, 5) == 'D7$S$') { + $salt = substr($stored_hash, 2, 12); + $stored_hash = substr($stored_hash, 14); + $password = $this->drupal7Password->crypt('sha512', $password, $salt); + } + // MD5 migrated password (Drupal 6). + elseif (substr($stored_hash, 0, 2) == 'U$') { + $stored_hash = substr($stored_hash, 1); + $password = md5($password); + } + + return password_verify($password, $stored_hash); + } + + /** + * {@inheritdoc} + */ + public function userNeedsNewHash(UserInterface $account) { + return password_needs_rehash($account->getPassword(), PASSWORD_DEFAULT, $this->getOptions()); + } + + /** + * Returns password options. + * + * @return array + * Associative array with password options. + */ + protected function getOptions() { + return ['cost' => $this->cost]; + } + +} diff --git a/core/lib/Drupal/Core/Password/PhpassHashedPassword.php b/core/lib/Drupal/Core/Password/PhpassHashedPassword.php index 17cafdd..e8cd3e9 100644 --- a/core/lib/Drupal/Core/Password/PhpassHashedPassword.php +++ b/core/lib/Drupal/Core/Password/PhpassHashedPassword.php @@ -158,7 +158,7 @@ protected function enforceLog2Boundaries($count_log2) { * A string containing the hashed password (and salt) or FALSE on failure. * The return string will be truncated at HASH_LENGTH characters max. */ - protected function crypt($algo, $password, $setting) { + public function crypt($algo, $password, $setting) { // Prevent DoS attacks by refusing to hash large passwords. if (strlen($password) > 512) { return FALSE; diff --git a/core/modules/migrate/src/MigratePassword.php b/core/modules/migrate/src/MigratePassword.php index ba42e8a..b412357 100644 --- a/core/modules/migrate/src/MigratePassword.php +++ b/core/modules/migrate/src/MigratePassword.php @@ -8,12 +8,13 @@ namespace Drupal\migrate; use Drupal\Core\Password\PasswordInterface; +use Drupal\migrate\Entity\MigrationInterface; use Drupal\user\UserInterface; /** - * Replaces the original 'password' service in order to prefix the MD5 re-hashed - * passwords with the 'U' flag. The new salted hash is recreated on first login - * similarly to the D6->D7 upgrade path. + * Replaces the original 'password' service in order to prefix the MD5 or + * Drupal 7 re-hashed passwords. The new salted hash will be recreated on first + * login similarly to the D6->D7 upgrade path. */ class MigratePassword implements PasswordInterface { @@ -26,10 +27,19 @@ class MigratePassword implements PasswordInterface { /** * Indicates if MD5 password prefixing is enabled. + * + * @var bool */ protected $enabled = FALSE; /** + * The current migration. + * + * @var \Drupal\migrate\Entity\MigrationInterface + */ + protected $migration; + + /** * Builds the replacement password service class. * * @param \Drupal\Core\Password\PasswordInterface $original_password @@ -59,27 +69,46 @@ public function userNeedsNewHash(UserInterface $account) { public function hash($password) { $hash = $this->originalPassword->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->enabled && preg_match('/^[0-9a-f]{32}$/', $password)) { - $hash = 'U' . $hash; + // Allow prefixing only if the service was asked to prefix. + if ($this->enabled) { + // If the $password pattern is conforming to a MD5 result this is most + // likely a Drupal 6 hashed password. + if (preg_match('/^[0-9a-f]{32}$/', $password)) { + $hash = 'U' . $hash; + } + // Prefix user hashed passwords coming from Drupal 7. + $source = $this->migration->getSourcePlugin()->getPluginId(); + if (substr($password, 0, 3) == '$S$' && $source == 'd7_user') { + $salt = substr($password, 0, 12); + $hash = 'D7' . $salt . $hash; + } } return $hash; } /** - * Enables the MD5 password prefixing. + * Enables password prefixing. */ - public function enableMd5Prefixing() { + public function enablePrefixing() { $this->enabled = TRUE; } /** - * Disables the MD5 password prefixing. + * Disables password prefixing. */ - public function disableMd5Prefixing() { + public function disablePrefixing() { $this->enabled = FALSE; } + /** + * Sets the current migration. + * + * @param \Drupal\migrate\Entity\MigrationInterface $migration + * The current migration. + */ + public function setMigration(MigrationInterface $migration) { + $this->migration = $migration; + } + } diff --git a/core/modules/migrate/src/MigrateServiceProvider.php b/core/modules/migrate/src/MigrateServiceProvider.php index 78a60bb..705f9d2 100644 --- a/core/modules/migrate/src/MigrateServiceProvider.php +++ b/core/modules/migrate/src/MigrateServiceProvider.php @@ -12,10 +12,10 @@ /** * Swaps the original 'password' service in order to handle password hashing for - * user migrations that have passwords hashed to MD5. + * user migrations that have passwords hashed with MD5 or Drupal 7 passwords. * * @see \Drupal\migrate\MigratePassword - * @see \Drupal\Core\Password\PhpassHashedPassword + * @see \Drupal\Core\Password\PhpPassword */ class MigrateServiceProvider implements ServiceModifierInterface { diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityUser.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityUser.php index 786988f..34fd890 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityUser.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityUser.php @@ -13,7 +13,6 @@ use Drupal\migrate\Entity\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\MigratePassword; -use Drupal\migrate\Plugin\MigratePluginManager; use Drupal\migrate\Row; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -79,12 +78,14 @@ public static function create(ContainerInterface $container, array $configuratio /** * {@inheritdoc} + * * @throws \Drupal\migrate\MigrateException */ public function import(Row $row, array $old_destination_id_values = array()) { if ($this->password) { if ($this->password instanceof MigratePassword) { - $this->password->enableMd5Prefixing(); + $this->password->setMigration($this->migration); + $this->password->enablePrefixing(); } else { throw new MigrateException('Password service has been altered by another module, aborting.'); @@ -92,7 +93,7 @@ public function import(Row $row, array $old_destination_id_values = array()) { } $ids = parent::import($row, $old_destination_id_values); if ($this->password) { - $this->password->disableMd5Prefixing(); + $this->password->disablePrefixing(); } return $ids; diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 14b839b..2237499 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -15,6 +15,7 @@ use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Language\Language; +use Drupal\Core\Password\PhpassHashedPassword; use Drupal\Core\Site\Settings; use Symfony\Component\DependencyInjection\Parameter; use Drupal\Core\StreamWrapper\StreamWrapperInterface; @@ -344,8 +345,9 @@ public function containerBuild(ContainerBuilder $container) { $definition->clearTag('path_processor_inbound')->clearTag('path_processor_outbound'); } - if ($container->hasDefinition('password')) { - $container->getDefinition('password')->setArguments(array(1)); + if ($container->hasDefinition('password') && $container->hasDefinition('drupal7_password')) { + $container->getDefinition('drupal7_password')->setArguments([1]); + $container->getDefinition('password')->setArguments([4, $container->get('drupal7_password')]); } // Register the stream wrapper manager. diff --git a/core/modules/user/src/Tests/UserLoginTest.php b/core/modules/user/src/Tests/UserLoginTest.php index 27ffa2a..2b75204 100644 --- a/core/modules/user/src/Tests/UserLoginTest.php +++ b/core/modules/user/src/Tests/UserLoginTest.php @@ -8,7 +8,8 @@ namespace Drupal\user\Tests; use Drupal\simpletest\WebTestBase; -use Drupal\Core\Password\PhpassHashedPassword; +use Drupal\user\Entity\User; +use Drupal\user\UserInterface; /** * Ensure that login works as expected. @@ -111,13 +112,10 @@ function testPerUserLoginFloodControl() { } /** - * Test that user password is re-hashed upon login after changing $count_log2. + * Test that user password is re-hashed upon login after changing the cost. */ function testPasswordRehashOnLogin() { - // Determine default log2 for phpass hashing algorithm - $default_count_log2 = 16; - - // Retrieve instance of password hashing algorithm + /** @var \Drupal\Core\Password\Password $password_hasher */ $password_hasher = $this->container->get('password'); // Create a new user and authenticate. @@ -125,22 +123,64 @@ function testPasswordRehashOnLogin() { $password = $account->pass_raw; $this->drupalLogin($account); $this->drupalLogout(); - // Load the stored user. The password hash should reflect $default_count_log2. + // Load the stored user. The password hash should reflect $default_cost. $account = user_load($account->id()); - $this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $default_count_log2); + $this->assertTrue($password_hasher->check($password, $account)); - // Change the required number of iterations by loading a test-module - // containing the necessary container builder code and then verify that the - // users password gets rehashed during the login. - $overridden_count_log2 = 19; - \Drupal::service('module_installer')->install(array('user_custom_phpass_params_test')); + // Change the required cost by loading a test-module containing the + // necessary container builder code and then verify that the users password + // gets rehashed during the login. + \Drupal::service('module_installer')->install(array('user_custom_pass_hash_params_test')); $this->resetAll(); $account->pass_raw = $password; $this->drupalLogin($account); // Load the stored user, which should have a different password hash now. $account = user_load($account->id(), TRUE); - $this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $overridden_count_log2); + $password_hasher = $this->container->get('password'); + $this->assertTrue($password_hasher->check($password, $account)); + } + + /** + * Test MD5 (Drupal 6) and Drupal 7 passwords rehashing. + */ + public function testMigratedPasswordRehashing() { + /** @var \Drupal\Core\Password\PasswordInterface $d7_hasher */ + $d7_hasher = $this->container->get('drupal7_password'); + /** @var \Drupal\Core\Password\PasswordInterface $hasher */ + $hasher = $this->container->get('password'); + + // Drupal 6 migrated password. + + $account = $this->drupalCreateUser(); + $plain = $account->pass_raw; + + // We pretend that the user was migrated from Drupal 6. + $md5_pass = md5($plain); + $migrated_pass = 'U' . $hasher->hash($md5_pass); + $this->storeHashedPassword($account, $migrated_pass); + + $this->drupalLogin($account); + $this->drupalLogout(); + + // After logging is the user password has been rehashed and is valid. + $this->assertTrue($hasher->check($plain, $account)); + + // Drupal 7 migrated password. + + $account2 = $this->drupalCreateUser(); + $plain = $account2->pass_raw; + + // We pretend that the user was migrated from Drupal 7. + $d7_pass = $d7_hasher->hash($plain); + $salt = substr($d7_pass, 0, 12); + $migrated_pass = 'D7' . $salt . $hasher->hash($d7_pass); + $this->storeHashedPassword($account2, $migrated_pass); + $this->drupalLogin($account2); + $this->drupalLogout(); + + // After logging is the user password has been rehashed and is valid. + $this->assertTrue($hasher->check($plain, $account2)); } /** @@ -172,4 +212,22 @@ function assertFailedLogin($account, $flood_trigger = NULL) { $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?')); } } + + /** + * Updates the hashed user password bypassing the API. We want to set an + * already hashed password. + * + * @param \Drupal\user\UserInterface $account + * The user account. + * @param string $hashed_password + * An already hashed password. + */ + protected function storeHashedPassword(UserInterface $account, $hashed_password) { + $account->setPassword($hashed_password); + db_update('users_field_data') + ->fields(['pass' => $hashed_password]) + ->condition('uid', $account->id()) + ->execute(); + } + } diff --git a/core/modules/user/tests/modules/user_custom_pass_hash_params_test/user_custom_pass_hash_params_test.info.yml b/core/modules/user/tests/modules/user_custom_pass_hash_params_test/user_custom_pass_hash_params_test.info.yml new file mode 100644 index 0000000..aca50c4 --- /dev/null +++ b/core/modules/user/tests/modules/user_custom_pass_hash_params_test/user_custom_pass_hash_params_test.info.yml @@ -0,0 +1,6 @@ +name: 'User custom password hash params test' +type: module +description: 'Support module for testing custom hashing password algorithm parameters.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/user/tests/modules/user_custom_pass_hash_params_test/user_custom_pass_hash_params_test.services.yml b/core/modules/user/tests/modules/user_custom_pass_hash_params_test/user_custom_pass_hash_params_test.services.yml new file mode 100644 index 0000000..ed9c05e --- /dev/null +++ b/core/modules/user/tests/modules/user_custom_pass_hash_params_test/user_custom_pass_hash_params_test.services.yml @@ -0,0 +1,7 @@ +services: + password: + class: Drupal\Core\Password\PhpPassword + arguments: [11, '@drupal7_password'] + drupal7_password: + class: Drupal\Core\Password\PhpassHashedPassword + arguments: [16] diff --git a/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml b/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml deleted file mode 100644 index 68b8ff9..0000000 --- a/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: 'User custom phpass params test' -type: module -description: 'Support module for testing custom phpass password algorithm parameters.' -package: Testing -version: VERSION -core: 8.x diff --git a/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.services.yml b/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.services.yml deleted file mode 100644 index 8950bfe..0000000 --- a/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.services.yml +++ /dev/null @@ -1,4 +0,0 @@ -services: - password: - class: Drupal\Core\Password\PhpassHashedPassword - arguments: [19] diff --git a/core/tests/Drupal/Tests/Core/Password/PasswordHashingTest.php b/core/tests/Drupal/Tests/Core/Password/PhpPasswordTest.php similarity index 62% copy from core/tests/Drupal/Tests/Core/Password/PasswordHashingTest.php copy to core/tests/Drupal/Tests/Core/Password/PhpPasswordTest.php index f304be0..6d0d4f3 100644 --- a/core/tests/Drupal/Tests/Core/Password/PasswordHashingTest.php +++ b/core/tests/Drupal/Tests/Core/Password/PhpPasswordTest.php @@ -2,21 +2,22 @@ /** * @file - * Contains Drupal\system\Tests\System\PasswordHashingTest. + * Contains Drupal\system\Tests\System\PhpPasswordTest. */ namespace Drupal\Tests\Core\Password; +use Drupal\Core\Password\PhpPassword; use Drupal\Core\Password\PhpassHashedPassword; use Drupal\Tests\UnitTestCase; /** * Unit tests for password hashing API. * - * @coversDefaultClass \Drupal\Core\Password\PhpassHashedPassword + * @coversDefaultClass \Drupal\Core\Password\PhpPassword * @group System */ -class PasswordHashingTest extends UnitTestCase { +class PhpPasswordTest extends UnitTestCase { /** * The user for testing. @@ -49,7 +50,7 @@ class PasswordHashingTest extends UnitTestCase { /** * The password hasher under test. * - * @var \Drupal\Core\Password\PhpassHashedPassword + * @var \Drupal\Core\Password\PasswordInterface */ protected $passwordHasher; @@ -61,21 +62,12 @@ protected function setUp() { $this->user = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->getMock(); - $this->passwordHasher = new PhpassHashedPassword(1); - } + $this->passwordHasher = new PhpPassword(4, new PhpassHashedPassword(1)); - /** - * Tests the hash count boundaries are enforced. - * - * @covers ::enforceLog2Boundaries - */ - public function testWithinBounds() { - $hasher = new FakePhpassHashedPassword(); - $this->assertEquals(PhpassHashedPassword::MIN_HASH_COUNT, $hasher->enforceLog2Boundaries(1), "Min hash count enforced"); - $this->assertEquals(PhpassHashedPassword::MAX_HASH_COUNT, $hasher->enforceLog2Boundaries(100), "Max hash count enforced"); + $this->password = $this->randomMachineName(); + $this->md5Password = md5($this->password); } - /** * Test a password needs update. * @@ -86,15 +78,13 @@ public function testPasswordNeedsUpdate() { ->method('getPassword') ->will($this->returnValue($this->md5Password)); // The md5 password should be flagged as needing an update. - $this->assertTrue($this->passwordHasher->userNeedsNewHash($this->user), 'User with md5 password needs a new hash.'); + $this->assertTrue($this->passwordHasher->userNeedsNewHash($this->user)); } /** * Test password hashing. * * @covers ::hash - * @covers ::getCountLog2 - * @covers ::check * @covers ::userNeedsNewHash */ public function testPasswordHashing() { @@ -102,45 +92,38 @@ public function testPasswordHashing() { $this->user->expects($this->any()) ->method('getPassword') ->will($this->returnValue($this->hashedPassword)); - $this->assertSame($this->passwordHasher->getCountLog2($this->hashedPassword), PhpassHashedPassword::MIN_HASH_COUNT, 'Hashed password has the minimum number of log2 iterations.'); - $this->assertNotEquals($this->hashedPassword, $this->md5Password, 'Password hash changed.'); - $this->assertTrue($this->passwordHasher->check($this->password, $this->user), 'Password check succeeds.'); - // Since the log2 setting hasn't changed and the user has a valid password, - // userNeedsNewHash() should return FALSE. - $this->assertFalse($this->passwordHasher->userNeedsNewHash($this->user), 'User does not need a new hash.'); + $this->assertNotEquals($this->hashedPassword, $this->md5Password); + $this->assertTrue($this->passwordHasher->check($this->password, $this->user)); + $this->assertFalse($this->passwordHasher->userNeedsNewHash($this->user)); } /** * Tests password rehashing. * * @covers ::hash - * @covers ::getCountLog2 - * @covers ::check * @covers ::userNeedsNewHash */ public function testPasswordRehashing() { - - // Increment the log2 iteration to MIN + 1. - $this->passwordHasher = new PhpassHashedPassword(PhpassHashedPassword::MIN_HASH_COUNT + 1); - $this->assertTrue($this->passwordHasher->userNeedsNewHash($this->user), 'User needs a new hash after incrementing the log2 count.'); + // Increment the cost from 4 to 5. + $this->passwordHasher = new PhpPassword(5, new PhpassHashedPassword(1)); + $this->assertTrue($this->passwordHasher->userNeedsNewHash($this->user)); // Re-hash the password. $rehashed_password = $this->passwordHasher->hash($this->password); $this->user->expects($this->any()) ->method('getPassword') ->will($this->returnValue($rehashed_password)); - $this->assertSame($this->passwordHasher->getCountLog2($rehashed_password), PhpassHashedPassword::MIN_HASH_COUNT + 1, 'Re-hashed password has the correct number of log2 iterations.'); - $this->assertNotEquals($rehashed_password, $this->hashedPassword, 'Password hash changed again.'); + $this->assertNotEquals($rehashed_password, $this->hashedPassword); // Now the hash should be OK. - $this->assertFalse($this->passwordHasher->userNeedsNewHash($this->user), 'Re-hashed password does not need a new hash.'); - $this->assertTrue($this->passwordHasher->check($this->password, $this->user), 'Password check succeeds with re-hashed password.'); + $this->assertFalse($this->passwordHasher->userNeedsNewHash($this->user)); + $this->assertTrue($this->passwordHasher->check($this->password, $this->user)); } /** * Verifies that passwords longer than 512 bytes are not hashed. * - * @covers ::crypt + * @covers ::hash * * @dataProvider providerLongPasswords */ @@ -178,19 +161,3 @@ public function providerLongPasswords() { } } - -/** - * A fake class for tests. - */ -class FakePhpassHashedPassword extends PhpassHashedPassword { - - function __construct() { - // Noop. - } - - // Expose this method as public for tests. - public function enforceLog2Boundaries($count_log2) { - return parent::enforceLog2Boundaries($count_log2); - } - -} diff --git a/core/tests/Drupal/Tests/Core/Password/PasswordHashingTest.php b/core/tests/Drupal/Tests/Core/Password/PhpassHashedPasswordTest.php similarity index 97% rename from core/tests/Drupal/Tests/Core/Password/PasswordHashingTest.php rename to core/tests/Drupal/Tests/Core/Password/PhpassHashedPasswordTest.php index f304be0..2a870ed 100644 --- a/core/tests/Drupal/Tests/Core/Password/PasswordHashingTest.php +++ b/core/tests/Drupal/Tests/Core/Password/PhpassHashedPasswordTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\system\Tests\System\PasswordHashingTest. + * Contains Drupal\system\Tests\System\PhpassHashedPasswordTest. */ namespace Drupal\Tests\Core\Password; @@ -16,7 +16,7 @@ * @coversDefaultClass \Drupal\Core\Password\PhpassHashedPassword * @group System */ -class PasswordHashingTest extends UnitTestCase { +class PhpassHashedPasswordTest extends UnitTestCase { /** * The user for testing.