diff --git a/core/modules/user/config/install/user.settings.yml b/core/modules/user/config/install/user.settings.yml index 8372ccdb73..a1b87e05d2 100644 --- a/core/modules/user/config/install/user.settings.yml +++ b/core/modules/user/config/install/user.settings.yml @@ -14,3 +14,4 @@ cancel_method: user_cancel_block password_reset_timeout: 86400 password_strength: true langcode: en +password_reset_text: 'If an account exists with these credentials, password reset instructions will be sent to the email address associated with the account.' diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 2f9bda44f2..6b9257db24 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -50,6 +50,9 @@ user.settings: password_strength: type: boolean label: 'Enable password strength indicator' + password_reset_text: + type: string + label: 'Default password reset user message' user.mail: type: config_object diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php index bd80dd7e29..ac519baa89 100644 --- a/core/modules/user/src/AccountSettingsForm.php +++ b/core/modules/user/src/AccountSettingsForm.php @@ -192,6 +192,20 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#maxlength' => 180, ]; + // Privacy settings. + $form['privacy'] = [ + '#type' => 'details', + '#title' => $this->t('Privacy settings'), + '#open' => TRUE, + ]; + $form['privacy']['user_password_reset_text'] = [ + '#type' => 'textarea', + '#title' => $this->t('Password reset text'), + '#description' => $this->t('The message that appears when a user successfully submits the password reset form. Due to privacy concerns, it should not contain any information about previously registered users.'), + '#default_value' => $config->get('password_reset_text'), + '#required' => TRUE, + ]; + $form['email'] = [ '#type' => 'vertical_tabs', '#title' => $this->t('Emails'), @@ -436,6 +450,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { ->set('notify.status_activated', $form_state->getValue('user_mail_status_activated_notify')) ->set('notify.status_blocked', $form_state->getValue('user_mail_status_blocked_notify')) ->set('notify.status_canceled', $form_state->getValue('user_mail_status_canceled_notify')) + ->set('password_reset_text', $form_state->getValue('user_password_reset_text')) ->save(); $this->config('user.mail') ->set('cancel_confirm.body', $form_state->getValue('user_mail_cancel_confirm_body')) diff --git a/core/modules/user/src/Form/UserPasswordForm.php b/core/modules/user/src/Form/UserPasswordForm.php index a7ae862bb5..ca07c78aee 100644 --- a/core/modules/user/src/Form/UserPasswordForm.php +++ b/core/modules/user/src/Form/UserPasswordForm.php @@ -116,17 +116,11 @@ public function validateForm(array &$form, FormStateInterface $form_state) { } $account = reset($users); if ($account && $account->id()) { - // Blocked accounts cannot request a new password. - if (!$account->isActive()) { - $form_state->setErrorByName('name', $this->t('%name is blocked or has not been activated yet.', ['%name' => $name])); - } - else { + // The account is valid and active. + if ($account->isActive()) { $form_state->setValueForElement(['#parents' => ['account']], $account); } } - else { - $form_state->setErrorByName('name', $this->t('%name is not recognized as a username or an email address.', ['%name' => $name])); - } } /** @@ -136,13 +130,21 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $langcode = $this->languageManager->getCurrentLanguage()->getId(); $account = $form_state->getValue('account'); - // Mail one time login URL and instructions using current language. - $mail = _user_mail_notify('password_reset', $account, $langcode); - if (!empty($mail)) { - $this->logger('user')->notice('Password reset instructions mailed to %name at %email.', ['%name' => $account->getUsername(), '%email' => $account->getEmail()]); - drupal_set_message($this->t('Further instructions have been sent to your email address.')); + if ($account && $account->id()) { + // Mail one time login URL and instructions using current language. + $mail = _user_mail_notify('password_reset', $account, $langcode); + if (!empty($mail)) { + $this->logger('user')->notice('Password reset instructions mailed to %name at %email.', ['%name' => $account->getUsername(), '%email' => $account->getEmail()]); + } + } + else { + $name = $form_state->getValue('name'); + $this->logger('user')->notice('Password reset form was submitted with an unknown or inactive account: %name.', ['%name' => $name]); } + $message = \Drupal::config('user.settings')->get('password_reset_text'); + drupal_set_message($message); + $form_state->setRedirect('user.page'); } diff --git a/core/modules/user/src/Tests/UserPasswordResetTest.php b/core/modules/user/src/Tests/UserPasswordResetTest.php index b940878f2d..2d8b4c3307 100644 --- a/core/modules/user/src/Tests/UserPasswordResetTest.php +++ b/core/modules/user/src/Tests/UserPasswordResetTest.php @@ -70,7 +70,10 @@ protected function setUp() { * Tests password reset functionality. */ public function testUserPasswordReset() { - // Verify that accessing the password reset form without having the session + // Arrange constants to be used in the tests. + $password_reset_message = $this->config('user.settings')->get('password_reset_text'); + + // Verify that accessing the password reset form without having the session. // variables set results in an access denied message. $this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()])); $this->assertResponse(403); @@ -81,10 +84,11 @@ public function testUserPasswordReset() { $edit = ['name' => $this->randomMachineName(32)]; $this->drupalPostForm(NULL, $edit, t('Submit')); - $this->assertText(t('@name is not recognized as a username or an email address.', ['@name' => $edit['name']]), 'Validation error message shown when trying to request password for invalid account.'); + $this->assertText($password_reset_message, 'Validation error message shown when trying to request password for invalid account.'); $this->assertEqual(count($this->drupalGetMails(['id' => 'user_password_reset'])), 0, 'No email was sent when requesting a password for an invalid account.'); // Reset the password by username via the password reset page. + $this->drupalGet('user/password'); $edit['name'] = $this->account->getUsername(); $this->drupalPostForm(NULL, $edit, t('Submit')); @@ -176,7 +180,7 @@ public function testUserPasswordReset() { $before = count($this->drupalGetMails(['id' => 'user_password_reset'])); $edit = ['name' => $blocked_account->getUsername()]; $this->drupalPostForm(NULL, $edit, t('Submit')); - $this->assertRaw(t('%name is blocked or has not been activated yet.', ['%name' => $blocked_account->getUsername()]), 'Notified user blocked accounts can not request a new password'); + $this->assertText($password_reset_message, 'Validation error shown when trying to request password for blocked account.'); $this->assertTrue(count($this->drupalGetMails(['id' => 'user_password_reset'])) === $before, 'No email was sent when requesting password reset for a blocked account'); // Verify a password reset link is invalidated when the user's email address changes. diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 0af797a43f..84bf5462bb 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -98,3 +98,12 @@ function user_update_8100() { $config->set('status_blocked', $mail)->save(TRUE); } } + +/** + * Add password reset notification text. + */ +function user_update_8601() { + $config = \Drupal::configFactory()->getEditable('user.settings'); + $default_value = \Drupal::config('user.settings')->get('password_reset_text'); + $config->set('password_reset_text', $default_value)->save(TRUE); +}