diff --git a/core/core.services.yml b/core/core.services.yml index 796fdb7..64be188 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 @@ -663,7 +664,10 @@ services: # the speed and power of computers available to crack the hashes. password: class: Drupal\Core\Password\Password - arguments: [10] + arguments: ['%password_hash_cost%', '@drupal7_password'] + drupal7_password: + class: Drupal\Core\Password\PhpassHashedPassword + arguments: [16] accept_header_matcher: class: Drupal\Core\Routing\AcceptHeaderMatcher arguments: ['@content_negotiation'] diff --git a/core/lib/Drupal/Core/Password/Password.php b/core/lib/Drupal/Core/Password/Password.php index bbb448c..99e90f2 100644 --- a/core/lib/Drupal/Core/Password/Password.php +++ b/core/lib/Drupal/Core/Password/Password.php @@ -25,14 +25,23 @@ class Password implements PasswordInterface { protected $cost; /** + * The Drupal 7 password hashing service. + * + * @var \Drupal\Core\Password\PasswordInterface + */ + 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) { - parent::__construct(16); + function __construct($cost, PasswordInterface $drupal7_password) { $this->cost = $cost; + $this->drupal7Password = $drupal7_password; } /** @@ -46,16 +55,20 @@ public function hash($password) { * {@inheritdoc} */ public function check($password, UserInterface $account) { - $info = password_get_info($account->getPassword()); - // If this the stored hash is not valid password_hash() hash, pass the check - // to Drupal 7 hash check. - if (!$info['algo']) { - // We are not injecting this class because it will never be swapped. - $d7_pass = new PhpassHashedPassword(16); - return $d7_pass->check($password, $account); + $stored_hash = $account->getPassword(); + + // Password migrated from Drupal 7. + if (substr($stored_hash, 0, 3) == 'D7$') { + $stored_hash = substr($stored_hash, 2); + $password = $this->drupal7Password->hash($password); + } + // Password migrated from Drupal 6. + elseif (substr($stored_hash, 0, 2) == 'U$') { + $stored_hash = substr($stored_hash, 1); + $password = md5($password); } - return password_verify($password, $account->getPassword()); + return password_verify($password, $stored_hash); } /** diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 14b839b..d608d05 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; @@ -345,7 +346,7 @@ public function containerBuild(ContainerBuilder $container) { } if ($container->hasDefinition('password')) { - $container->getDefinition('password')->setArguments(array(1)); + $container->getDefinition('password')->setArguments([4, new PhpassHashedPassword(1)]); } // 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..08b89b5 100644 --- a/core/modules/user/src/Tests/UserLoginTest.php +++ b/core/modules/user/src/Tests/UserLoginTest.php @@ -111,13 +111,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 +122,22 @@ 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)); } /** 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..770e7c4 --- /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\Password + 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]