diff --git a/core/core.services.yml b/core/core.services.yml index f4260e2..edcfb71 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -752,20 +752,13 @@ services: # The first argument of the hashing service (constructor of PhpPassword) is # the 'cost' option of password_hash(). In Drupal 8 the 'cost' has the default - # value used by password_hash() which is 10. Future versions of Drupal may + # value used by password_hash(), which is 10. Future versions of Drupal may # increase this value in order to counteract increases in the speed and power # of computers available to crack the hashes. Note that an increase of 1 will # double the time needed for password hashing. password: - class: Drupal\Core\Password\Drupal8Password - arguments: ['@password.php', '@password.drupal7'] - lazy: true - password.php: - class: Drupal\Core\Password\PhpPassword + class: Drupal\Core\Password\MultiFormatPassword arguments: ['%password_hash_cost%'] - password.drupal7: - class: Drupal\Core\Password\Drupal7Password - arguments: [16] lazy: true accept_header_matcher: class: Drupal\Core\Routing\AcceptHeaderMatcher diff --git a/core/lib/Drupal/Core/Password/Drupal7Password.php b/core/lib/Drupal/Core/Password/Drupal7Password.php deleted file mode 100644 index 5ff5280..0000000 --- a/core/lib/Drupal/Core/Password/Drupal7Password.php +++ /dev/null @@ -1,272 +0,0 @@ -countLog2 = $this->enforceLog2Boundaries($countLog2); - } - - /** - * Encodes bytes into printable base 64 using the *nix standard from crypt(). - * - * @param String $input - * The string containing bytes to encode. - * @param Integer $count - * The number of characters (bytes) to encode. - * - * @return String - * Encoded string - */ - protected function base64Encode($input, $count) { - $output = ''; - $i = 0; - do { - $value = ord($input[$i++]); - $output .= static::$ITOA64[$value & 0x3f]; - if ($i < $count) { - $value |= ord($input[$i]) << 8; - } - $output .= static::$ITOA64[($value >> 6) & 0x3f]; - if ($i++ >= $count) { - break; - } - if ($i < $count) { - $value |= ord($input[$i]) << 16; - } - $output .= static::$ITOA64[($value >> 12) & 0x3f]; - if ($i++ >= $count) { - break; - } - $output .= static::$ITOA64[($value >> 18) & 0x3f]; - } while ($i < $count); - - return $output; - } - - /** - * Generates a random base 64-encoded salt prefixed with settings for the hash. - * - * Proper use of salts may defeat a number of attacks, including: - * - The ability to try candidate passwords against multiple hashes at once. - * - The ability to use pre-hashed lists of candidate passwords. - * - The ability to determine whether two users have the same (or different) - * password without actually having to guess one of the passwords. - * - * @return String - * A 12 character string containing the iteration count and a random salt. - */ - protected function generateSalt() { - $output = '$S$'; - // We encode the final log2 iteration count in base 64. - $output .= static::$ITOA64[$this->countLog2]; - // 6 bytes is the standard salt for a portable phpass hash. - $output .= $this->base64Encode(Crypt::randomBytes(6), 6); - return $output; - } - - /** - * Ensures that $count_log2 is within set bounds. - * - * @param Integer $count_log2 - * Integer that determines the number of iterations used in the hashing - * process. A larger value is more secure, but takes more time to complete. - * - * @return Integer - * Integer within set bounds that is closest to $count_log2. - */ - protected function enforceLog2Boundaries($count_log2) { - if ($count_log2 < static::MIN_HASH_COUNT) { - return static::MIN_HASH_COUNT; - } - elseif ($count_log2 > static::MAX_HASH_COUNT) { - return static::MAX_HASH_COUNT; - } - - return (int) $count_log2; - } - - /** - * Hash a password using a secure stretched hash. - * - * By using a salt and repeated hashing the password is "stretched". Its - * security is increased because it becomes much more computationally costly - * for an attacker to try to break the hash by brute-force computation of the - * hashes of a large number of plain-text words or strings to find a match. - * - * @param String $algo - * The string name of a hashing algorithm usable by hash(), like 'sha256'. - * @param String $password - * Plain-text password up to 512 bytes (128 to 512 UTF-8 characters) to - * hash. - * @param String $setting - * An existing hash or the output of $this->generateSalt(). Must be - * at least 12 characters (the settings and salt). - * - * @return String - * 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) { - // Prevent DoS attacks by refusing to hash large passwords. - if (strlen($password) > 512) { - return FALSE; - } - - // The first 12 characters of an existing hash are its setting string. - $setting = substr($setting, 0, 12); - - if ($setting[0] != '$' || $setting[2] != '$') { - return FALSE; - } - $count_log2 = $this->getCountLog2($setting); - // Stored hashes may have been crypted with any iteration count. However we - // do not allow applying the algorithm for unreasonable low and high values - // respectively. - if ($count_log2 != $this->enforceLog2Boundaries($count_log2)) { - return FALSE; - } - $salt = substr($setting, 4, 8); - // Hashes must have an 8 character salt. - if (strlen($salt) != 8) { - return FALSE; - } - - // Convert the base 2 logarithm into an integer. - $count = 1 << $count_log2; - - // We rely on the hash() function being available in PHP 5.2+. - $hash = hash($algo, $salt . $password, TRUE); - do { - $hash = hash($algo, $hash . $password, TRUE); - } while (--$count); - - $len = strlen($hash); - $output = $setting . $this->base64Encode($hash, $len); - // $this->base64Encode() of a 16 byte MD5 will always be 22 characters. - // $this->base64Encode() of a 64 byte sha512 will always be 86 characters. - $expected = 12 + ceil((8 * $len) / 6); - return (strlen($output) == $expected) ? substr($output, 0, static::HASH_LENGTH) : FALSE; - } - - /** - * Parse the log2 iteration count from a stored hash or setting string. - * - * @param String $setting - * An existing hash or the output of $this->generateSalt(). Must be - * at least 12 characters (the settings and salt). - */ - public function getCountLog2($setting) { - return strpos(static::$ITOA64, $setting[3]); - } - - /** - * {@inheritdoc} - */ - public function hash($password) { - return $this->crypt('sha512', $password, $this->generateSalt()); - } - - /** - * {@inheritdoc} - */ - public function check($password, $hash) { - if (substr($hash, 0, 2) == 'U$') { - // This may be an updated password from user_update_7000(). Such hashes - // have 'U' added as the first character and need an extra md5() (see the - // Drupal 7 documentation). - $stored_hash = substr($hash, 1); - $password = md5($password); - } - else { - $stored_hash = $hash; - } - - $type = substr($stored_hash, 0, 3); - switch ($type) { - case '$S$': - // A normal Drupal 7 password using sha512. - $computed_hash = $this->crypt('sha512', $password, $stored_hash); - break; - case '$H$': - // phpBB3 uses "$H$" for the same thing as "$P$". - case '$P$': - // A phpass password generated using md5. This is an - // imported password or from an earlier Drupal version. - $computed_hash = $this->crypt('md5', $password, $stored_hash); - break; - default: - return FALSE; - } - return ($computed_hash && $stored_hash == $computed_hash); - } - - /** - * {@inheritdoc} - */ - public function needsRehash($hash) { - // Check whether this was an updated password. - if ((substr($hash, 0, 3) != '$S$') || (strlen($hash) != static::HASH_LENGTH)) { - return TRUE; - } - // Ensure that $count_log2 is within set bounds. - $count_log2 = $this->enforceLog2Boundaries($this->countLog2); - // Check whether the iteration count used differs from the standard number. - return ($this->getCountLog2($hash) !== $count_log2); - } - -} diff --git a/core/lib/Drupal/Core/Password/Drupal8Password.php b/core/lib/Drupal/Core/Password/Drupal8Password.php deleted file mode 100644 index 023e776..0000000 --- a/core/lib/Drupal/Core/Password/Drupal8Password.php +++ /dev/null @@ -1,75 +0,0 @@ -phpPassword = $php_password; - $this->drupal7Password = $drupal7_password; - } - - /** - * {@inheritdoc} - */ - public function hash($password) { - return $this->phpPassword->hash($password); - } - - /** - * {@inheritdoc} - */ - public function check($password, $hash) { - - // MD5 migrated password (Drupal 6). - if (substr($hash, 0, 2) == 'U$') { - $hash = substr($hash, 1); - $password = md5($password); - } - - switch (substr($hash, 0, 2)) { - case '$S': - case '$H': - case '$P': - return $this->drupal7Password->check($password, $hash); - - default: - return $this->phpPassword->check($password, $hash); - } - } - - /** - * {@inheritdoc} - */ - public function needsRehash($hash) { - return $this->phpPassword->needsRehash($hash); - } - -} diff --git a/core/lib/Drupal/Core/Password/MultiFormatPassword.php b/core/lib/Drupal/Core/Password/MultiFormatPassword.php new file mode 100644 index 0000000..700a522 --- /dev/null +++ b/core/lib/Drupal/Core/Password/MultiFormatPassword.php @@ -0,0 +1,79 @@ += 5.5.0) password_hash() function. + * + * @var int + * + * @see password_hash(). + * @see http://php.net/manual/en/ref.password.php + */ + protected $cost; + + /** + * The PHP password hashing instance. + * + * @var \Drupal\Core\Password\PHPPassword + */ + protected $phpPassword; + + /** + * Constructs a new password hashing instance. + * + * @param int $cost + * The algorithmic cost that should be used. + */ + function __construct($cost) { + $this->cost = $cost; + $this->phpPassword = new PHPPassword($cost); + } + + /** + * {@inheritdoc} + */ + public function hash($password) { + return $this->phpPassword->hash($password); + } + + /** + * {@inheritdoc} + */ + public function check($password, $hash) { + if (substr($hash, 0, 2) == 'U$') { + // A migrated password from Drupal 6. + $hash = substr($hash, 1); + $password = md5($password); + } + + switch (substr($hash, 0, 3)) { + case '$S$': + // A Drupal 7 password using sha512. + return PhpassHashedPassword::check('sha512', $password, $hash); + case '$H$': + // phpBB3 uses "$H$" for the same thing as "$P$". + case '$P$': + // A phpass password generated using md5. This is an + // imported password or from an earlier Drupal version. + return PhpassHashedPassword::check('md5', $password, $hash); + default: + return $this->phpPassword->check($password, $hash); + } + } + + /** + * {@inheritdoc} + */ + public function needsRehash($hash) { + return $this->phpPassword->needsRehash($hash); + } + +} diff --git a/core/lib/Drupal/Core/Password/PhpPassword.php b/core/lib/Drupal/Core/Password/PhpPassword.php index 7dc8869..05eadea 100644 --- a/core/lib/Drupal/Core/Password/PhpPassword.php +++ b/core/lib/Drupal/Core/Password/PhpPassword.php @@ -26,12 +26,12 @@ class PhpPassword implements PasswordInterface { * The algorithmic cost that should be used. This is the same 'cost' option as * is used by the PHP (>= 5.5.0) password_hash() function. * - * @var int + * @var array * * @see password_hash(). * @see http://php.net/manual/en/ref.password.php */ - protected $cost; + protected $options = []; /** * Constructs a new password hashing instance. @@ -40,7 +40,7 @@ class PhpPassword implements PasswordInterface { * The algorithmic cost that should be used. */ function __construct($cost) { - $this->cost = $cost; + $this->options = ['cost' => $cost]; } /** @@ -52,7 +52,9 @@ public function hash($password) { return FALSE; } - return password_hash($password, PASSWORD_DEFAULT, $this->getOptions()); + // Use PASSWORD_BCRYPT since we want the same format generated for + // all Drupal 8.x installations, regardless of changes to the PHP default. + return password_hash($password, PASSWORD_BCRYPT, $this->options); } /** @@ -67,24 +69,13 @@ public function check($password, $hash) { */ public function needsRehash($hash) { // The PHP 5.5 password_needs_rehash() will return TRUE in two cases: - // - The password is a Drupal 6 or 7 password and it has been rehashed - // during the migration. In this case the rehashed legacy hash is prefixed - // to indicate an old Drupal hash and will not comply with the expected - // password_needs_rehash() format. + // - The hash does not match the bcrypt signature, such as if this is a + // Drupal 7 password or a migrated Drupal 6 password. // - The parameters of hashing engine were changed. For example the // parameter 'password_hash_cost' (the hashing cost) has been increased in // core.services.yml. - return password_needs_rehash($hash, PASSWORD_DEFAULT, $this->getOptions()); + return password_needs_rehash($hash, PASSWORD_BCRYPT, $this->options); } - /** - * 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 new file mode 100644 index 0000000..15b8246 --- /dev/null +++ b/core/lib/Drupal/Core/Password/PhpassHashedPassword.php @@ -0,0 +1,160 @@ +> 6) & 0x3f]; + if ($i++ >= $count) { + break; + } + if ($i < $count) { + $value |= ord($input[$i]) << 16; + } + $output .= static::$ITOA64[($value >> 12) & 0x3f]; + if ($i++ >= $count) { + break; + } + $output .= static::$ITOA64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + /** + * Hash a password using a secure stretched hash. + * + * By using a salt and repeated hashing the password is "stretched". Its + * security is increased because it becomes much more computationally costly + * for an attacker to try to break the hash by brute-force computation of the + * hashes of a large number of plain-text words or strings to find a match. + * + * @param String $algo + * The string name of a hashing algorithm usable by hash(), like 'sha256'. + * @param String $password + * Plain-text password up to 512 bytes (128 to 512 UTF-8 characters) to + * hash. + * @param String $setting + * An existing hash or the output of $this->generateSalt(). Must be + * at least 12 characters (the settings and salt). + * + * @return String + * A string containing the hashed password (and salt) or FALSE on failure. + * The return string will be truncated at HASH_LENGTH characters max. + */ + protected static function crypt($algo, $password, $setting) { + // Prevent DoS attacks by refusing to hash large passwords. This value is + // the same as PasswordInterface::PASSWORD_MAX_LENGTH + if (strlen($password) > 512) { + return FALSE; + } + + // The first 12 characters of an existing hash are its setting string. + $setting = substr($setting, 0, 12); + if ($setting[0] != '$' || $setting[2] != '$') { + return FALSE; + } + $count_log2 = static::getCountLog2($setting); + // Stored hashes may have been crypted with any iteration count. However we + // do not allow applying the algorithm for unreasonably high values. + if ($count_log2 > static::MAX_HASH_COUNT) { + return FALSE; + } + $salt = substr($setting, 4, 8); + // Hashes must have an 8 character salt. + if (strlen($salt) != 8) { + return FALSE; + } + + // Convert the base 2 logarithm into an integer. + $count = 1 << $count_log2; + + // We rely on the hash() function being available in PHP 5.2+. + $hash = hash($algo, $salt . $password, TRUE); + do { + $hash = hash($algo, $hash . $password, TRUE); + } while (--$count); + + $len = strlen($hash); + $output = $setting . static::base64Encode($hash, $len); + // static::base64Encode() of a 16 byte MD5 will always be 22 characters. + // static::base64Encode() of a 64 byte sha512 will always be 86 characters. + $expected = 12 + ceil((8 * $len) / 6); + return (strlen($output) == $expected) ? substr($output, 0, static::HASH_LENGTH) : FALSE; + } + + /** + * Parse the log2 iteration count from a stored hash or setting string. + * + * @param String $setting + * An existing hash. Must be at least 12 characters (the settings and salt). + * @return int + */ + public static function getCountLog2($setting) { + return strpos(static::$ITOA64, $setting[3]); + } + + /** + * Check a plain text password against a stored hash. + * + * @param String $algo + * The string name of a hashing algorithm usable by hash(), like 'sha256'. + * @param String $password + * Plain-text password up to 512 bytes (128 to 512 UTF-8 characters) to + * be checked. + * @param String $stored_hash + * An existing hash. Must be at least 12 characters (the settings and salt). + * + * @return boolean + */ + public static function check($algo, $password, $stored_hash) { + $hash = static::crypt($algo, $password, $stored_hash); + return ($hash && $stored_hash === $hash); + } +} diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index fae6dc4..b259849 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -345,9 +345,8 @@ public function containerBuild(ContainerBuilder $container) { $definition->clearTag('path_processor_inbound')->clearTag('path_processor_outbound'); } - if ($container->hasDefinition('password') && $container->hasDefinition('drupal7_password')) { - $container->getDefinition('drupal7_password')->setArguments([1]); - $container->getDefinition('password')->setArguments([4, $container->get('drupal7_password')]); + if ($container->hasDefinition('password')) { + $container->getDefinition('password')->setArguments([4]); } // Register the stream wrapper manager. diff --git a/core/modules/user/src/Tests/UserLoginTest.php b/core/modules/user/src/Tests/UserLoginTest.php index fdbbc99..92b566e 100644 --- a/core/modules/user/src/Tests/UserLoginTest.php +++ b/core/modules/user/src/Tests/UserLoginTest.php @@ -26,13 +26,6 @@ class UserLoginTest extends WebTestBase { private $passwordHasher; /** - * Drupal 7 password hasher service. - * - * @var \Drupal\Core\Password\PasswordInterface - */ - private $drupal7PasswordHasher; - - /** * Tests login with destination. */ function testLoginCacheTagsAndDestination() { @@ -45,10 +38,9 @@ function testLoginCacheTagsAndDestination() { $this->drupalGet('user/login', array('query' => array('destination' => 'foo'))); $edit = array('name' => $user->getUserName(), 'pass' => $user->pass_raw); $this->drupalPostForm(NULL, $edit, t('Log in')); - $this->assertUrl('foo', [], 'Redirected to the correct URL'); + $this->assertUrl('foo', [], 'Redirected to the correct URL'); $this->passwordHasher = $this->container->get('password'); - $this->drupal7PasswordHasher = $this->container->get('drupal7_password'); } /** @@ -140,7 +132,7 @@ function testPasswordRehashOnLogin() { // Load the stored user. The password hash should reflect $default_cost. $user_storage = $this->container->get('entity.manager')->getStorage('user'); /** @var \Drupal\user\UserInterface $account */ - $account = User::load($account->id()); + $account = $user_storage->load($account->id()); $this->assertTrue($this->passwordHasher->check($password, $account->getPassword())); // Change the required cost by loading a test-module containing the @@ -173,30 +165,11 @@ public function testDrupal6MigratedPasswordRehashing() { // User first login after migration. $this->drupalLogin($account); $this->drupalLogout(); + // Re-load the account object. + $account = User::load($account->id()); // After logging in the user password has been rehashed and is valid. - $this->assertTrue($this->passwordHasher->check($plain, $account->getPassword())); - } - - /** - * Test Drupal 7 passwords rehashing. - */ - public function testDrupal7MigratedPasswordRehashing() { - /** @var \Drupal\user\UserInterface $account */ - $account = $this->drupalCreateUser(); - $plain = $account->pass_raw; - - // We pretend that the user was migrated from Drupal 7. - $d7_pass = $this->drupal7PasswordHasher->hash($plain); - $salt = substr($d7_pass, 0, 12); - $migrated_pass = 'D7' . $salt . $this->passwordHasher->hash($d7_pass); - $this->storeHashedPassword($account, $migrated_pass); - - // User first login after migration. - $this->drupalLogin($account); - $this->drupalLogout(); - - // After logging in the user password has been rehashed and is valid. + $this->assertFalse($this->passwordHasher->needsRehash($account->getPassword())); $this->assertTrue($this->passwordHasher->check($plain, $account->getPassword())); } 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 index 45c6bb0..1c0a8c9 100644 --- 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 @@ -1,7 +1,4 @@ services: password: class: Drupal\Core\Password\PhpPassword - arguments: [11, '@drupal7_password'] - drupal7_password: - class: Drupal\Core\Password\Drupal7Password - arguments: [19] + arguments: [9] diff --git a/core/tests/Drupal/Tests/Core/Password/Drupal7PasswordTest.php b/core/tests/Drupal/Tests/Core/Password/Drupal7PasswordTest.php deleted file mode 100644 index cb69edf..0000000 --- a/core/tests/Drupal/Tests/Core/Password/Drupal7PasswordTest.php +++ /dev/null @@ -1,194 +0,0 @@ -user = $this->getMockBuilder('Drupal\user\Entity\User') - ->disableOriginalConstructor() - ->getMock(); - $this->passwordHasher = new Drupal7Password(1); - } - - /** - * Tests the hash count boundaries are enforced. - * - * @covers ::enforceLog2Boundaries - */ - public function testWithinBounds() { - $hasher = new FakeDrupal7Password(); - $this->assertEquals(Drupal7Password::MIN_HASH_COUNT, $hasher->enforceLog2Boundaries(1)); - $this->assertEquals(Drupal7Password::MAX_HASH_COUNT, $hasher->enforceLog2Boundaries(100)); - } - - /** - * Test a password needs update. - * - * @covers ::needsRehash - */ - public function testPasswordNeedsUpdate() { - $this->user->expects($this->any()) - ->method('getPassword') - ->will($this->returnValue($this->md5Password)); - // The md5 password should be flagged as needing an update. - $this->assertTrue($this->passwordHasher->needsRehash($this->user->getPassword())); - } - - /** - * Test password hashing. - * - * @covers ::hash - * @covers ::getCountLog2 - * @covers ::check - * @covers ::needsRehash - */ - public function testPasswordHashing() { - $this->hashedPassword = $this->passwordHasher->hash($this->password); - $this->user->expects($this->any()) - ->method('getPassword') - ->will($this->returnValue($this->hashedPassword)); - $this->assertSame($this->passwordHasher->getCountLog2($this->hashedPassword), Drupal7Password::MIN_HASH_COUNT); - $this->assertNotEquals($this->hashedPassword, $this->md5Password); - $this->assertTrue($this->passwordHasher->check($this->password, $this->user->getPassword())); - // Since the log2 setting hasn't changed and the user has a valid password, - // needsRehash() should return FALSE. - $this->assertFalse($this->passwordHasher->needsRehash($this->user->getPassword())); - } - - /** - * Tests password rehashing. - * - * @covers ::hash - * @covers ::getCountLog2 - * @covers ::check - * @covers ::needsRehash - */ - public function testPasswordRehashing() { - - // Increment the log2 iteration to MIN + 1. - $this->passwordHasher = new Drupal7Password(Drupal7Password::MIN_HASH_COUNT + 1); - $this->assertTrue($this->passwordHasher->needsRehash($this->user->getPassword())); - // 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), Drupal7Password::MIN_HASH_COUNT + 1); - $this->assertNotEquals($rehashed_password, $this->hashedPassword); - - // Now the hash should be OK. - $this->assertFalse($this->passwordHasher->needsRehash($this->user->getPassword())); - $this->assertTrue($this->passwordHasher->check($this->password, $this->user->getPassword())); - } - - /** - * Verifies that passwords longer than 512 bytes are not hashed. - * - * @covers ::crypt - * - * @dataProvider providerLongPasswords - */ - public function testLongPassword($password, $allowed) { - - $hashed_password = $this->passwordHasher->hash($password); - - if ($allowed) { - $this->assertNotFalse($hashed_password); - } - else { - $this->assertFalse($hashed_password); - } - } - - /** - * Provides the test matrix for testLongPassword(). - */ - public function providerLongPasswords() { - // '512 byte long password is allowed.' - $passwords['allowed'] = array(str_repeat('x', 512), TRUE); - // 513 byte long password is not allowed. - $passwords['too_long'] = array(str_repeat('x', 513), FALSE); - - // Check a string of 3-byte UTF-8 characters, 510 byte long password is - // allowed. - $passwords['utf8'] = array(str_repeat('€', 170), TRUE); - // 512 byte long password is allowed. - $passwords['ut8_extended'] = array($passwords['utf8'][0] . 'xx', TRUE); - - // Check a string of 3-byte UTF-8 characters, 513 byte long password is - // allowed. - $passwords['utf8_too_long'] = array(str_repeat('€', 171), FALSE); - return $passwords; - } - -} - -/** - * A fake class for tests. - */ -class FakeDrupal7Password extends Drupal7Password { - - 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/PhpPasswordTest.php b/core/tests/Drupal/Tests/Core/Password/PhpPasswordTest.php index 923da55..5663d7a 100644 --- a/core/tests/Drupal/Tests/Core/Password/PhpPasswordTest.php +++ b/core/tests/Drupal/Tests/Core/Password/PhpPasswordTest.php @@ -8,7 +8,6 @@ namespace Drupal\Tests\Core\Password; use Drupal\Core\Password\PhpPassword; -use Drupal\Core\Password\Drupal7Password; use Drupal\Tests\UnitTestCase; /** @@ -20,13 +19,6 @@ class PhpPasswordTest extends UnitTestCase { /** - * The user for testing. - * - * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\user\UserInterface - */ - protected $user; - - /** * The raw password. * * @var string @@ -34,11 +26,11 @@ class PhpPasswordTest extends UnitTestCase { protected $password; /** - * The md5 password. + * The hashed password updated from Drupal 6 simple md5 hash. * * @var string */ - protected $md5Password; + protected $md5HashedPassword; /** * The hashed password. @@ -59,13 +51,11 @@ class PhpPasswordTest extends UnitTestCase { */ protected function setUp() { parent::setUp(); - $this->user = $this->getMockBuilder('Drupal\user\Entity\User') - ->disableOriginalConstructor() - ->getMock(); - $this->passwordHasher = new PhpPassword(4, new Drupal7Password(1)); + + $this->passwordHasher = new PhpPassword(4); $this->password = $this->randomMachineName(); - $this->md5Password = md5($this->password); + $this->md5HashedPassword = 'U' . $this->passwordHasher->hash(md5($this->password)); } /** @@ -74,11 +64,8 @@ protected function setUp() { * @covers ::needsRehash */ public function testPasswordNeedsUpdate() { - $this->user->expects($this->any()) - ->method('getPassword') - ->will($this->returnValue($this->md5Password)); // The md5 password should be flagged as needing an update. - $this->assertTrue($this->passwordHasher->needsRehash($this->user->getPassword())); + $this->assertTrue($this->passwordHasher->needsRehash($this->md5HashedPassword)); } /** @@ -89,12 +76,9 @@ public function testPasswordNeedsUpdate() { */ public function testPasswordHashing() { $this->hashedPassword = $this->passwordHasher->hash($this->password); - $this->user->expects($this->any()) - ->method('getPassword') - ->will($this->returnValue($this->hashedPassword)); - $this->assertNotEquals($this->hashedPassword, $this->md5Password); - $this->assertTrue($this->passwordHasher->check($this->password, $this->user->getPassword())); - $this->assertFalse($this->passwordHasher->needsRehash($this->user->getPassword())); + $this->assertNotEquals($this->hashedPassword, $this->md5HashedPassword); + $this->assertTrue($this->passwordHasher->check($this->password, $this->hashedPassword)); + $this->assertFalse($this->passwordHasher->needsRehash($this->hashedPassword)); } /** @@ -105,19 +89,16 @@ public function testPasswordHashing() { */ public function testPasswordRehashing() { // Increment the cost from 4 to 5. - $this->passwordHasher = new PhpPassword(5, new Drupal7Password(1)); - $this->assertTrue($this->passwordHasher->needsRehash($this->user->getPassword())); + $this->passwordHasher = new PhpPassword(5); + $this->assertTrue($this->passwordHasher->needsRehash($this->hashedPassword)); // Re-hash the password. $rehashed_password = $this->passwordHasher->hash($this->password); - $this->user->expects($this->any()) - ->method('getPassword') - ->will($this->returnValue($rehashed_password)); $this->assertNotEquals($rehashed_password, $this->hashedPassword); // Now the hash should be OK. - $this->assertFalse($this->passwordHasher->needsRehash($this->user->getPassword())); - $this->assertTrue($this->passwordHasher->check($this->password, $this->user->getPassword())); + $this->assertFalse($this->passwordHasher->needsRehash($rehashed_password)); + $this->assertTrue($this->passwordHasher->check($this->password, $rehashed_password)); } /**