diff --git a/core/modules/user/config/install/user.settings.yml b/core/modules/user/config/install/user.settings.yml index fbb0d98..742a6b3 100644 --- a/core/modules/user/config/install/user.settings.yml +++ b/core/modules/user/config/install/user.settings.yml @@ -1,6 +1,7 @@ admin_role: '' anonymous: Anonymous verify_mail: true +verify_email_match: true notify: cancel_confirm: true password_reset: true diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 70d8581..03edaeb 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -13,6 +13,9 @@ user.settings: verify_mail: type: boolean label: 'Require email verification when a visitor creates an account.' + verify_email_match: + type: boolean + label: 'Require e-mail and username match when e-mail is used as username.' notify: type: mapping label: 'Notify user' diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php index d715e61..ec0fbfc 100644 --- a/core/modules/user/src/AccountForm.php +++ b/core/modules/user/src/AccountForm.php @@ -328,9 +328,13 @@ public function validate(array $form, array &$form_state) { parent::validate($form, $form_state); $account = $this->entity; + $mail = $form_state['values']['mail']; + // Validate new or changing username. if (isset($form_state['values']['name'])) { - if ($error = user_validate_name($form_state['values']['name'])) { + $name = $form_state['values']['name']; + + if ($error = user_validate_name($name)) { $this->setFormError('name', $form_state, $error); } // Cast the user ID as an integer. It might have been set to NULL, which @@ -338,19 +342,23 @@ public function validate(array $form, array &$form_state) { else { $name_taken = (bool) $this->entityQuery->get('user') ->condition('uid', (int) $account->id(), '<>') - ->condition('name', $form_state['values']['name']) + ->condition('name', $name) ->range(0, 1) ->count() ->execute(); if ($name_taken) { - $this->setFormError('name', $form_state, $this->t('The name %name is already taken.', array('%name' => $form_state['values']['name']))); + $this->setFormError('name', $form_state, $this->t('The name %name is already taken.', array('%name' => $name))); + } + + // Check whether the user name provided is an email address, if so, make + // sure it matches the mail value. + if (\Drupal::config('user.settings')->get('verify_email_match') && (filter_var($name, FILTER_VALIDATE_EMAIL) && ($name !== $mail))) { + $this->setFormError('name', $form_state, $this->t('If your username is an email address, it must match your email.')); } } } - $mail = $form_state['values']['mail']; - if (!empty($mail)) { $mail_taken = (bool) $this->entityQuery->get('user') ->condition('uid', (int) $account->id(), '<>') diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php index 94c4bae..a005f04 100644 --- a/core/modules/user/src/AccountSettingsForm.php +++ b/core/modules/user/src/AccountSettingsForm.php @@ -130,6 +130,12 @@ public function buildForm(array $form, array &$form_state) { '#default_value' => $config->get('verify_mail'), '#description' => $this->t('New users will be required to validate their email address prior to logging into the site, and will be assigned a system-generated password. With this setting disabled, users will be logged in immediately upon registering, and may select their own passwords during registration.') ); + $form['registration_cancellation']['user_email_match_verification'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Require e-mail and username match when e-mail is used as username.'), + '#default_value' => $config->get('verify_email_match'), + '#description' => $this->t('Users will be required to have matching e-mail address and username if an email address is used as username.') + ); $form['registration_cancellation']['user_password_strength'] = array( '#type' => 'checkbox', '#title' => $this->t('Enable password strength indicator'), @@ -416,6 +422,7 @@ public function submitForm(array &$form, array &$form_state) { ->set('register', $form_state['values']['user_register']) ->set('password_strength', $form_state['values']['user_password_strength']) ->set('verify_mail', $form_state['values']['user_email_verification']) + ->set('verify_email_match', $form_state['values']['user_email_match_verification']) ->set('signatures', $form_state['values']['user_signatures']) ->set('cancel_method', $form_state['values']['user_cancel_method']) ->set('notify.status_activated', $form_state['values']['user_mail_status_activated_notify']) diff --git a/core/modules/user/src/Tests/UserEditedOwnAccountTest.php b/core/modules/user/src/Tests/UserEditedOwnAccountTest.php index 2437aec..ed97bf5 100644 --- a/core/modules/user/src/Tests/UserEditedOwnAccountTest.php +++ b/core/modules/user/src/Tests/UserEditedOwnAccountTest.php @@ -42,5 +42,28 @@ function testUserEditedOwnAccount() { // Set the new name on the user account and attempt to log back in. $account->name = $edit['name']; $this->drupalLogin($account); + + // Attempt to change username to an email other than my own. + $edit['name'] = $this->randomName() . '@example.com'; + $this->drupalPostForm('user/' . $account->id() . '/edit', $edit, t('Save')); + $this->assertText(t('If your username is an email address, it must match your email.'), 'Error message found when an email username does not match user email.'); + $this->assertNoText(t('The changes have been saved.'), 'The user account was not saved.'); + + // Lookup user by name to make sure we didn't actually change the name. + $accounts = \Drupal::entityManager()->getStorage('user')->loadByProperties(array('name' => $edit['name'])); + $this->assertTrue(empty($accounts), 'Username was not changed to email address other than my own.'); + + // Change username to my email address. + $edit['name'] = $account->getEmail(); + $this->drupalPostForm('user/' . $account->id() . '/edit', $edit, t('Save')); + $this->assertText(t('The changes have been saved.'), 'The user account was saved.'); + + // Test that 'verify_email_match' turned off allows emails that don't match. + \Drupal::config('user.settings')->set('verify_email_match', FALSE)->save(); + + // Change username to my email address. + $edit['name'] = $this->randomName() . '@example.com'; + $this->drupalPostForm('user/' . $account->id() . '/edit', $edit, t('Save')); + $this->assertText(t('The changes have been saved.'), 'The user account was saved.'); } } diff --git a/core/modules/user/src/Tests/UserRegistrationTest.php b/core/modules/user/src/Tests/UserRegistrationTest.php index 0a6916c..5c15c8a 100644 --- a/core/modules/user/src/Tests/UserRegistrationTest.php +++ b/core/modules/user/src/Tests/UserRegistrationTest.php @@ -153,6 +153,68 @@ function testRegistrationEmailDuplicates() { $this->assertText(t('The email address @email is already registered.', array('@email' => $duplicate_user->getEmail())), 'Supplying a duplicate email address with added whitespace displays an error message'); } + /** + * Tests new users username matches their email if username is an email. + */ + function testRegistrationEmailAsUsername() { + // Don't require e-mail verification. + // Allow registration by site visitors without administrator approval. + \Drupal::config('user.settings') + ->set('verify_mail', FALSE) + ->set('register', USER_REGISTER_VISITORS) + ->save(); + + $mail = $this->randomName() . '@example.com'; + $different = $this->randomName() . $mail; + + // Set up edit array. + $edit = array(); + $edit['mail'] = $mail; + $edit['name'] = $different; + $edit['pass[pass1]'] = $edit['pass[pass2]'] = $this->randomName(); + + // Attempt to create an account using an email that doesn't match the name. + $this->drupalPostForm('user/register', $edit, t('Create new account')); + $this->assertText(t('If your username is an email address, it must match your email.'), 'Email username does not match user email error message found.'); + $this->assertNoText(t('Registration successful. You are now logged in.'), 'The user was not created and logged in.'); + + // Attempt to create new account using matching email address. + $edit['name'] = $edit['mail'] = $this->randomName() . '@example.com'; + + $this->drupalPostForm('user/register', $edit, t('Create new account')); + $this->assertText(t('Registration successful. You are now logged in.'), 'The user was created and logged in with matching email.'); + + $accounts = \Drupal::entityManager()->getStorage('user')->loadByProperties(array('name' => $edit['name'])); + $new_user = reset($accounts); + $this->assertTrue(($new_user->getUsername() === $edit['name']) && ($new_user->getEmail() === $edit['mail']), 'Created user with matching username and email address.'); + } + + /** + * Tests new users username not matching their email if username is an email. + */ + function testRegistrationEmailAsUsernameDisabled() { + // Test that 'verify_email_match' turned off allows emails that don't match. + \Drupal::config('user.settings') + ->set('verify_email_match', FALSE) + ->set('verify_mail', FALSE) + ->set('register', USER_REGISTER_VISITORS) + ->save(); + + $mail = $this->randomName() . '@example.com'; + $different = $this->randomName() . $mail; + + $edit = array(); + $edit['mail'] = $mail; + $edit['name'] = $different; + $edit['pass[pass1]'] = $edit['pass[pass2]'] = $this->randomName(); + + // Attempt to create an account using an email that doesn't match the name. + // This should be OK, as 'verify_email_match' is disabled. + $this->drupalPostForm('user/register', $edit, t('Create new account')); + $this->assertNoText(t('If your username is an email address, it must match your email.'), 'Email username does not match user email error message found.'); + $this->assertText(t('Registration successful. You are now logged in.'), 'The user was not created and logged in.'); + } + function testRegistrationDefaultValues() { // Don't require email verification and allow registration by site visitors // without administrator approval.