updated core to 8.6.3
This commit is contained in:
@@ -161,7 +161,7 @@ abstract class AccountForm extends ContentEntityForm {
|
||||
|
||||
// When not building the user registration form, prevent web browsers from
|
||||
// autofilling/prefilling the email, username, and password fields.
|
||||
if ($this->getOperation() != 'register') {
|
||||
if (!$register) {
|
||||
foreach (['mail', 'name', 'pass'] as $key) {
|
||||
if (isset($form['account'][$key])) {
|
||||
$form['account'][$key]['#attributes']['autocomplete'] = 'off';
|
||||
|
||||
@@ -35,7 +35,7 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a DeleteNode object.
|
||||
* Constructs a CancelUser object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
|
||||
@@ -96,7 +96,7 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
|
||||
// will be replaced at the very last moment. This ensures forms with
|
||||
// dynamically generated action URLs don't have poor cacheability.
|
||||
// Use the proper API to generate the placeholder, when we have one. See
|
||||
// https://www.drupal.org/node/2562341. The placholder uses a fixed string
|
||||
// https://www.drupal.org/node/2562341. The placeholder uses a fixed string
|
||||
// that is
|
||||
// Crypt::hashBase64('\Drupal\user\Plugin\Block\UserLoginBlock::build');
|
||||
// This is based on the implementation in
|
||||
|
||||
@@ -242,7 +242,7 @@ class UserSelection extends DefaultSelection {
|
||||
$value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
|
||||
$value_part->compile($this->connection, $query);
|
||||
$or->condition((new Condition('AND'))
|
||||
->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + [':anonymous_name' => \Drupal::config('user.settings')->get('anonymous')])
|
||||
->where(str_replace($query->escapeField('anonymous_name'), ':anonymous_name', (string) $value_part), $value_part->arguments() + [':anonymous_name' => \Drupal::config('user.settings')->get('anonymous')])
|
||||
->condition('base_table.uid', 0)
|
||||
);
|
||||
$query->condition($or);
|
||||
|
||||
@@ -30,10 +30,9 @@ class LanguageNegotiationUser extends LanguageNegotiationMethodBase {
|
||||
|
||||
// User preference (only for authenticated users).
|
||||
if ($this->languageManager && $this->currentUser->isAuthenticated()) {
|
||||
$preferred_langcode = $this->currentUser->getPreferredLangcode();
|
||||
$default_langcode = $this->languageManager->getDefaultLanguage()->getId();
|
||||
$preferred_langcode = $this->currentUser->getPreferredLangcode(FALSE);
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
if (!empty($preferred_langcode) && $preferred_langcode != $default_langcode && isset($languages[$preferred_langcode])) {
|
||||
if (!empty($preferred_langcode) && isset($languages[$preferred_langcode])) {
|
||||
$langcode = $preferred_langcode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,10 +127,14 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase impleme
|
||||
$route_match = $this->stackedRouteMatch->getRouteMatchFromRequest($request);
|
||||
if ($route_match && !$route_object = $route_match->getRouteObject()) {
|
||||
try {
|
||||
// Some inbound path processors make changes to the request. Make a
|
||||
// copy as we're not actually routing the request so we do not want to
|
||||
// make changes.
|
||||
$cloned_request = clone $request;
|
||||
// Process the path as an inbound path. This will remove any language
|
||||
// prefixes and other path components that inbound processing would
|
||||
// clear out, so we can attempt to load the route clearly.
|
||||
$path = $this->pathProcessorManager->processInbound(urldecode(rtrim($request->getPathInfo(), '/')), $request);
|
||||
$path = $this->pathProcessorManager->processInbound(urldecode(rtrim($cloned_request->getPathInfo(), '/')), $cloned_request);
|
||||
$attributes = $this->router->match($path);
|
||||
}
|
||||
catch (ResourceNotFoundException $e) {
|
||||
|
||||
@@ -33,7 +33,7 @@ class UserLangcode extends ProcessPluginBase implements ContainerFactoryPluginIn
|
||||
* @param string $plugin_id
|
||||
* The plugin ID.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin definiiton.
|
||||
* The plugin definition.
|
||||
* @param \Drupal\Core\Language\LanguageManager $language_manager
|
||||
* The language manager service.
|
||||
*/
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\system\Functional\Cache\PageCacheTagsTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that password reset methods work as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPasswordResetTest extends PageCacheTagsTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user object to test password resetting.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:account');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Activate user by logging in.
|
||||
$this->drupalLogin($account);
|
||||
|
||||
$this->account = User::load($account->id());
|
||||
$this->account->passRaw = $account->passRaw;
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set the last login time that is used to generate the one-time link so
|
||||
// that it is definitely over a second ago.
|
||||
$account->login = REQUEST_TIME - mt_rand(10, 100000);
|
||||
db_update('users_field_data')
|
||||
->fields(['login' => $account->getLastLoginTime()])
|
||||
->condition('uid', $account->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset functionality.
|
||||
*/
|
||||
public function testUserPasswordReset() {
|
||||
// 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);
|
||||
|
||||
// Try to reset the password for an invalid account.
|
||||
$this->drupalGet('user/password');
|
||||
|
||||
$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->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.
|
||||
$edit['name'] = $this->account->getUsername();
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
|
||||
// Verify that the user was sent an email.
|
||||
$this->assertMail('to', $this->account->getEmail(), 'Password email sent to user.');
|
||||
$subject = t('Replacement login information for @username at @site', ['@username' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]);
|
||||
$this->assertMail('subject', $subject, 'Password reset email subject is correct.');
|
||||
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
// Ensure that the current url does not contain the hash and timestamp.
|
||||
$this->assertUrl(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
|
||||
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
|
||||
|
||||
// Ensure the password reset URL is not cached.
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
|
||||
|
||||
// Check the one-time login page.
|
||||
$this->assertText($this->account->getUsername(), 'One-time login page contains the correct username.');
|
||||
$this->assertText(t('This login can be used only once.'), 'Found warning about one-time login.');
|
||||
$this->assertTitle(t('Reset password | Drupal'), 'Page title is "Reset password".');
|
||||
|
||||
// Check successful login.
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
|
||||
|
||||
// Change the forgotten password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Forgotten password changed.');
|
||||
|
||||
// Verify that the password reset session has been destroyed.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t("Your current password is missing or incorrect; it's required to change the Password."), 'Password needed to make profile changes.');
|
||||
|
||||
// Log out, and try to log in again using the same one-time link.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
|
||||
|
||||
// Request a new password again, this time using the email address.
|
||||
$this->drupalGet('user/password');
|
||||
// Count email messages before to compare with after.
|
||||
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
|
||||
$edit = ['name' => $this->account->getEmail()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertTrue(count($this->drupalGetMails(['id' => 'user_password_reset'])) === $before + 1, 'Email sent when requesting password reset using email address.');
|
||||
|
||||
// Visit the user edit page without pass-reset-token and make sure it does
|
||||
// not cause an error.
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->drupalGet('user/' . $this->account->id() . '/edit');
|
||||
$this->assertNoText('Expected user_string to be a string, NULL given');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Create a password reset link as if the request time was 60 seconds older than the allowed limit.
|
||||
$timeout = $this->config('user.settings')->get('password_reset_timeout');
|
||||
$bogus_timestamp = REQUEST_TIME - $timeout - 60;
|
||||
$_uid = $this->account->id();
|
||||
$this->drupalGet("user/reset/$_uid/$bogus_timestamp/" . user_pass_rehash($this->account, $bogus_timestamp));
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
|
||||
|
||||
// Create a user, block the account, and verify that a login link is denied.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp));
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Verify a blocked user can not request a new password.
|
||||
$this->drupalGet('user/password');
|
||||
// Count email messages before to compare with after.
|
||||
$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->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.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getUsername()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$old_email_reset_link = $this->getResetURL();
|
||||
$this->account->setEmail("1" . $this->account->getEmail());
|
||||
$this->account->save();
|
||||
$this->drupalGet($old_email_reset_link);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
|
||||
|
||||
// Verify a password reset link will automatically log a user when /login is
|
||||
// appended.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getUsername()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$reset_url = $this->getResetURL();
|
||||
$this->drupalGet($reset_url . '/login');
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
|
||||
|
||||
// Ensure blocked and deleted accounts can't access the user.reset.login
|
||||
// route.
|
||||
$this->drupalLogout();
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$blocked_account->delete();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves password reset email and extracts the login link.
|
||||
*/
|
||||
public function getResetURL() {
|
||||
// Assume the most recent email.
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
|
||||
return $urls[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user password reset while logged in.
|
||||
*/
|
||||
public function testUserPasswordResetLoggedIn() {
|
||||
$another_account = $this->drupalCreateUser();
|
||||
$this->drupalLogin($another_account);
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, NULL, t('Submit'));
|
||||
|
||||
// Click the reset URL while logged and change our password.
|
||||
$resetURL = $this->getResetURL();
|
||||
// Log in as a different user.
|
||||
$this->drupalLogin($this->account);
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertRaw(new FormattableMarkup(
|
||||
'Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href=":logout">log out</a> and try using the link again.',
|
||||
['%other_user' => $this->account->getUsername(), '%resetting_user' => $another_account->getUsername(), ':logout' => Url::fromRoute('user.logout')->toString()]
|
||||
));
|
||||
|
||||
$another_account->delete();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertText('The one-time login link you clicked is invalid.');
|
||||
|
||||
// Log in.
|
||||
$this->drupalLogin($this->account);
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, NULL, t('Submit'));
|
||||
|
||||
// Click the reset URL while logged and change our password.
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
|
||||
// Change the password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Password changed.');
|
||||
|
||||
// Logged in users should not be able to access the user.reset.login or the
|
||||
// user.reset.form routes.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$this->drupalGet("user/reset/" . $this->account->id() . "/$timestamp/" . user_pass_rehash($this->account, $timestamp) . '/login');
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet("user/reset/" . $this->account->id());
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill the text box on incorrect login via link to password reset page.
|
||||
*/
|
||||
public function testUserResetPasswordTextboxFilled() {
|
||||
$this->drupalGet('user/login');
|
||||
$edit = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'pass' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('user/login', $edit, t('Log in'));
|
||||
$this->assertRaw(t('Unrecognized username or password. <a href=":password">Forgot your password?</a>',
|
||||
[':password' => \Drupal::url('user.pass', [], ['query' => ['name' => $edit['name']]])]));
|
||||
unset($edit['pass']);
|
||||
$this->drupalGet('user/password', ['query' => ['name' => $edit['name']]]);
|
||||
$this->assertFieldByName('name', $edit['name'], 'User name found.');
|
||||
// Ensure the name field value is not cached.
|
||||
$this->drupalGet('user/password');
|
||||
$this->assertNoFieldByName('name', $edit['name'], 'User name not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that users cannot forge password reset URLs of other users.
|
||||
*/
|
||||
public function testResetImpersonation() {
|
||||
// Create two identical user accounts except for the user name. They must
|
||||
// have the same empty password, so we can't use $this->drupalCreateUser().
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['status'] = 1;
|
||||
$user1 = User::create($edit);
|
||||
$user1->save();
|
||||
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$user2 = User::create($edit);
|
||||
$user2->save();
|
||||
|
||||
// Unique password hashes are automatically generated, the only way to
|
||||
// change that is to update it directly in the database.
|
||||
db_update('users_field_data')
|
||||
->fields(['pass' => NULL])
|
||||
->condition('uid', [$user1->id(), $user2->id()], 'IN')
|
||||
->execute();
|
||||
\Drupal::entityManager()->getStorage('user')->resetCache();
|
||||
$user1 = User::load($user1->id());
|
||||
$user2 = User::load($user2->id());
|
||||
|
||||
$this->assertEqual($user1->getPassword(), $user2->getPassword(), 'Both users have the same password hash.');
|
||||
|
||||
// The password reset URL must not be valid for the second user when only
|
||||
// the user ID is changed in the URL.
|
||||
$reset_url = user_pass_reset_url($user1);
|
||||
$attack_reset_url = str_replace("user/reset/{$user1->id()}", "user/reset/{$user2->id()}", $reset_url);
|
||||
$this->drupalGet($attack_reset_url);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertNoText($user2->getUsername(), 'The invalid password reset page does not show the user name.');
|
||||
$this->assertUrl('user/password', [], 'The user is redirected to the password reset request page.');
|
||||
$this->assertText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that password reset methods work as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPasswordResetTest extends WebDriverTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* This test uses the standard profile to test the password reset in
|
||||
* combination with an ajax request provided by the user picture configuration
|
||||
* in the standard profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* The user object to test password resetting.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Activate user by logging in.
|
||||
$this->drupalLogin($account);
|
||||
|
||||
$this->account = User::load($account->id());
|
||||
$this->account->pass_raw = $account->pass_raw;
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set the last login time that is used to generate the one-time link so
|
||||
// that it is definitely over a second ago.
|
||||
$account->login = REQUEST_TIME - mt_rand(10, 100000);
|
||||
db_update('users_field_data')
|
||||
->fields(['login' => $account->getLastLoginTime()])
|
||||
->condition('uid', $account->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset functionality with an AJAX form.
|
||||
*
|
||||
* Make sure the ajax request from uploading a user picture does not
|
||||
* invalidate the reset token.
|
||||
*/
|
||||
public function testUserPasswordResetWithAdditionalAjaxForm() {
|
||||
$this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
|
||||
// Try to reset the password for an invalid account.
|
||||
$this->drupalGet('user/password');
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$edit['name'] = $this->account->getUsername();
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
|
||||
// Login
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
|
||||
// Generate file.
|
||||
$image_file = current($this->drupalGetTestFiles('image'));
|
||||
$image_path = \Drupal::service('file_system')->realpath($image_file->uri);
|
||||
|
||||
// Upload file.
|
||||
$this->getSession()->getPage()->attachFileToField('Picture', $image_path);
|
||||
$this->assertSession()->waitForButton('Remove');
|
||||
|
||||
// Change the forgotten password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Verify that the password reset session has been destroyed.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
// Password needed to make profile changes.
|
||||
$this->assertSession()->pageTextContains("Your current password is missing or incorrect; it's required to change the Password.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves password reset email and extracts the login link.
|
||||
*/
|
||||
public function getResetURL() {
|
||||
// Assume the most recent email.
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
|
||||
return $urls[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests users' ability to change their own administration language.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserAdminLanguageTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* A user with permission to access admin pages and administer languages.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A non-administrator user for this test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $regularUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'language', 'language_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// User to add and remove language.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer languages', 'access administration pages']);
|
||||
// User to check non-admin access.
|
||||
$this->regularUser = $this->drupalCreateUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that admin language is not configurable in single language sites.
|
||||
*/
|
||||
public function testUserAdminLanguageConfigurationNotAvailableWithOnlyOneLanguage() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->setLanguageNegotiation();
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language settings widget is not available.
|
||||
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that admin language negotiation is configurable only if enabled.
|
||||
*/
|
||||
public function testUserAdminLanguageConfigurationAvailableWithAdminLanguageNegotiation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->addCustomLanguage();
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
|
||||
// Checks with user administration pages language negotiation disabled.
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language settings widget is not available.
|
||||
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
|
||||
|
||||
// Checks with user administration pages language negotiation enabled.
|
||||
$this->setLanguageNegotiation();
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language settings widget is available.
|
||||
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector is available.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the admin language is configurable only for administrators.
|
||||
*
|
||||
* If a user has the permission "access administration pages", they should
|
||||
* be able to see the setting to pick the language they want those pages in.
|
||||
*
|
||||
* If a user does not have that permission, it would confusing for them to
|
||||
* have a setting for pages they cannot access, so they should not be able to
|
||||
* set a language for those pages.
|
||||
*/
|
||||
public function testUserAdminLanguageConfigurationAvailableIfAdminLanguageNegotiationIsEnabled() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
// Adds a new language, because with only one language, setting won't show.
|
||||
$this->addCustomLanguage();
|
||||
$this->setLanguageNegotiation();
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language setting is visible for admin.
|
||||
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector available for admins.');
|
||||
|
||||
// Ensure administration pages language setting is hidden for non-admins.
|
||||
$this->drupalLogin($this->regularUser);
|
||||
$path = 'user/' . $this->regularUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available for regular user.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the actual language negotiation.
|
||||
*/
|
||||
public function testActualNegotiation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->addCustomLanguage();
|
||||
$this->setLanguageNegotiation();
|
||||
|
||||
// Even though we have admin language negotiation, so long as the user has
|
||||
// no preference set, negotiation will fall back further.
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-default');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-url');
|
||||
|
||||
// Set a preferred language code for the user.
|
||||
$edit = [];
|
||||
$edit['preferred_admin_langcode'] = 'xx';
|
||||
$this->drupalPostForm($path, $edit, t('Save'));
|
||||
|
||||
// Test negotiation with the URL method first. The admin method will only
|
||||
// be used if the URL method did not match.
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-user-admin');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-url');
|
||||
|
||||
// Test negotiation with the admin language method first. The admin method
|
||||
// will be used at all times.
|
||||
$this->setLanguageNegotiation(TRUE);
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-user-admin');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-user-admin');
|
||||
|
||||
// Unset the preferred language code for the user.
|
||||
$edit = [];
|
||||
$edit['preferred_admin_langcode'] = '';
|
||||
$this->drupalPostForm($path, $edit, t('Save'));
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-default');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-url');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the User interface negotiation detection method.
|
||||
*
|
||||
* Enables the "Account preference for administration pages" language
|
||||
* detection method for the User interface language negotiation type.
|
||||
*
|
||||
* @param bool $admin_first
|
||||
* Whether the admin negotiation should be first.
|
||||
*/
|
||||
public function setLanguageNegotiation($admin_first = FALSE) {
|
||||
$edit = [
|
||||
'language_interface[enabled][language-user-admin]' => TRUE,
|
||||
'language_interface[enabled][language-url]' => TRUE,
|
||||
'language_interface[weight][language-user-admin]' => ($admin_first ? -12 : -8),
|
||||
'language_interface[weight][language-url]' => -10,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for adding a custom language.
|
||||
*/
|
||||
public function addCustomLanguage() {
|
||||
$langcode = 'xx';
|
||||
// The English name for the language.
|
||||
$name = $this->randomMachineName(16);
|
||||
$edit = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\KernelTests\ConfigFormTestBase;
|
||||
use Drupal\user\AccountSettingsForm;
|
||||
|
||||
/**
|
||||
* Configuration object user.mail and user.settings save test.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserAdminSettingsFormTest extends ConfigFormTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->form = AccountSettingsForm::create($this->container);
|
||||
$this->values = [
|
||||
'anonymous' => [
|
||||
'#value' => $this->randomString(10),
|
||||
'#config_name' => 'user.settings',
|
||||
'#config_key' => 'anonymous',
|
||||
],
|
||||
'user_mail_cancel_confirm_body' => [
|
||||
'#value' => $this->randomString(),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'cancel_confirm.body',
|
||||
],
|
||||
'user_mail_cancel_confirm_subject' => [
|
||||
'#value' => $this->randomString(20),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'cancel_confirm.subject',
|
||||
],
|
||||
'register_pending_approval_admin_body' => [
|
||||
'#value' => $this->randomString(),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'register_pending_approval_admin.body',
|
||||
],
|
||||
'register_pending_approval_admin_subject' => [
|
||||
'#value' => $this->randomString(20),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'register_pending_approval_admin.subject',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests user blocks.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserBlocksTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'views'];
|
||||
|
||||
/**
|
||||
* A user with the 'administer blocks' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer blocks']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPlaceBlock('user_login_block');
|
||||
$this->drupalLogout($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user login block is hidden from user/login.
|
||||
*/
|
||||
public function testUserLoginBlockVisibility() {
|
||||
// Array keyed list where key being the URL address and value being expected
|
||||
// visibility as boolean type.
|
||||
$paths = [
|
||||
'node' => TRUE,
|
||||
'user/login' => FALSE,
|
||||
'user/register' => TRUE,
|
||||
'user/password' => TRUE,
|
||||
];
|
||||
foreach ($paths as $path => $expected_visibility) {
|
||||
$this->drupalGet($path);
|
||||
$elements = $this->xpath('//div[contains(@class,"block-user-login-block") and @role="form"]');
|
||||
if ($expected_visibility) {
|
||||
$this->assertTrue(!empty($elements), 'User login block in path "' . $path . '" should be visible');
|
||||
}
|
||||
else {
|
||||
$this->assertTrue(empty($elements), 'User login block in path "' . $path . '" should not be visible');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user login block.
|
||||
*/
|
||||
public function testUserLoginBlock() {
|
||||
// Create a user with some permission that anonymous users lack.
|
||||
$user = $this->drupalCreateUser(['administer permissions']);
|
||||
|
||||
// Log in using the block.
|
||||
$edit = [];
|
||||
$edit['name'] = $user->getUsername();
|
||||
$edit['pass'] = $user->pass_raw;
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
|
||||
// Check that we are still on the same page.
|
||||
$this->assertUrl(\Drupal::url('user.admin_permissions', [], ['absolute' => TRUE]), [], 'Still on the same page after login for access denied page');
|
||||
|
||||
// Now, log out and repeat with a non-403 page.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('filter/tips');
|
||||
$this->assertEqual('MISS', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
|
||||
|
||||
// Log out again and repeat with a non-403 page including query arguments.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('filter/tips', ['query' => ['foo' => 'bar']]);
|
||||
$this->assertEqual('HIT', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
|
||||
$this->assertTrue(strpos($this->getUrl(), '/filter/tips?foo=bar') !== FALSE, 'Correct query arguments are displayed after login');
|
||||
|
||||
// Repeat with different query arguments.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('filter/tips', ['query' => ['foo' => 'baz']]);
|
||||
$this->assertEqual('HIT', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
|
||||
$this->assertTrue(strpos($this->getUrl(), '/filter/tips?foo=baz') !== FALSE, 'Correct query arguments are displayed after login');
|
||||
|
||||
// Check that the user login block is not vulnerable to information
|
||||
// disclosure to third party sites.
|
||||
$this->drupalLogout();
|
||||
$this->drupalPostForm('http://example.com/', $edit, t('Log in'), ['external' => FALSE]);
|
||||
// Check that we remain on the site after login.
|
||||
$this->assertUrl($user->url('canonical', ['absolute' => TRUE]), [], 'Redirected to user profile page after login from the frontpage');
|
||||
|
||||
// Verify that form validation errors are displayed immediately for forms
|
||||
// in blocks and not on subsequent page requests.
|
||||
$this->drupalLogout();
|
||||
$edit = [];
|
||||
$edit['name'] = 'foo';
|
||||
$edit['pass'] = 'invalid password';
|
||||
$this->drupalPostForm('filter/tips', $edit, t('Log in'));
|
||||
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
|
||||
$this->drupalGet('filter/tips');
|
||||
$this->assertNoText(t('Unrecognized username or password. Forgot your password?'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the access column for a user.
|
||||
*/
|
||||
private function updateAccess($uid, $access = REQUEST_TIME) {
|
||||
db_update('users_field_data')
|
||||
->condition('uid', $uid)
|
||||
->fields(['access' => $access])
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the create user administration page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserCreateTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* Create a user through the administration interface and ensure that it
|
||||
* displays in the user list.
|
||||
*/
|
||||
public function testUserAdd() {
|
||||
$user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$this->assertEqual($user->getCreatedTime(), REQUEST_TIME, 'Creating a user sets default "created" timestamp.');
|
||||
$this->assertEqual($user->getChangedTime(), REQUEST_TIME, 'Creating a user sets default "changed" timestamp.');
|
||||
|
||||
// Create a field.
|
||||
$field_name = 'test_field';
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'user',
|
||||
'module' => 'image',
|
||||
'type' => 'image',
|
||||
'cardinality' => 1,
|
||||
'locked' => FALSE,
|
||||
'indexes' => ['target_id' => ['target_id']],
|
||||
'settings' => [
|
||||
'uri_scheme' => 'public',
|
||||
],
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'user',
|
||||
'label' => 'Picture',
|
||||
'bundle' => 'user',
|
||||
'description' => t('Your virtual face or picture.'),
|
||||
'required' => FALSE,
|
||||
'settings' => [
|
||||
'file_extensions' => 'png gif jpg jpeg',
|
||||
'file_directory' => 'pictures',
|
||||
'max_filesize' => '30 KB',
|
||||
'alt_field' => 0,
|
||||
'title_field' => 0,
|
||||
'max_resolution' => '85x85',
|
||||
'min_resolution' => '',
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Test user creation page for valid fields.
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertFieldbyId('edit-status-0', 0, 'The user status option Blocked exists.', 'User login');
|
||||
$this->assertFieldbyId('edit-status-1', 1, 'The user status option Active exists.', 'User login');
|
||||
$this->assertFieldByXPath('//input[@type="radio" and @id="edit-status-1" and @checked="checked"]', NULL, 'Default setting for user status is active.');
|
||||
|
||||
// Test that browser autocomplete behavior does not occur.
|
||||
$this->assertNoRaw('data-user-info-from-browser', 'Ensure form attribute, data-user-info-from-browser, does not exist.');
|
||||
|
||||
// Test that the password strength indicator displays.
|
||||
$config = $this->config('user.settings');
|
||||
|
||||
$config->set('password_strength', TRUE)->save();
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
|
||||
|
||||
$config->set('password_strength', FALSE)->save();
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
|
||||
|
||||
// We create two users, notifying one and not notifying the other, to
|
||||
// ensure that the tests work in both cases.
|
||||
foreach ([FALSE, TRUE] as $notify) {
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => $pass = $this->randomString(),
|
||||
'pass[pass2]' => $pass,
|
||||
'notify' => $notify,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
|
||||
if ($notify) {
|
||||
$this->assertText(t('A welcome message with further instructions has been emailed to the new user @name.', ['@name' => $edit['name']]), 'User created');
|
||||
$this->assertEqual(count($this->drupalGetMails()), 1, 'Notification email sent');
|
||||
}
|
||||
else {
|
||||
$this->assertText(t('Created a new user account for @name. No email has been sent.', ['@name' => $edit['name']]), 'User created');
|
||||
$this->assertEqual(count($this->drupalGetMails()), 0, 'Notification email not sent');
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/people');
|
||||
$this->assertText($edit['name'], 'User found in list of users');
|
||||
$user = user_load_by_name($name);
|
||||
$this->assertEqual($user->isActive(), 'User is not blocked');
|
||||
}
|
||||
|
||||
// Test that the password '0' is considered a password.
|
||||
// @see https://www.drupal.org/node/2563751.
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => 0,
|
||||
'pass[pass2]' => 0,
|
||||
'notify' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
$this->assertText("Created a new user account for $name. No email has been sent");
|
||||
$this->assertNoText('Password field is required');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests registration of user under different configurations.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRegistrationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_test'];
|
||||
|
||||
public function testRegistrationWithEmailVerification() {
|
||||
$config = $this->config('user.settings');
|
||||
// Require email verification.
|
||||
$config->set('verify_mail', TRUE)->save();
|
||||
|
||||
// Set registration to administrator only.
|
||||
$config->set('register', USER_REGISTER_ADMINISTRATORS_ONLY)->save();
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertResponse(403, 'Registration page is inaccessible when only administrators can create accounts.');
|
||||
|
||||
// Allow registration by site visitors without administrator approval.
|
||||
$config->set('register', USER_REGISTER_VISITORS)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('A welcome message with further instructions has been sent to your email address.'), 'User registered successfully.');
|
||||
|
||||
/** @var EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertTrue($new_user->isActive(), 'New account is active after registration.');
|
||||
$resetURL = user_pass_reset_url($new_user);
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertTitle(t('Set password | Drupal'), 'Page title is "Set password".');
|
||||
|
||||
// Allow registration by site visitors, but require administrator approval.
|
||||
$config->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->container->get('entity.manager')->getStorage('user')->resetCache();
|
||||
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertFalse($new_user->isActive(), 'New account is blocked until approved by an administrator.');
|
||||
}
|
||||
|
||||
public function testRegistrationWithoutEmailVerification() {
|
||||
$config = $this->config('user.settings');
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$config
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
|
||||
// Try entering a mismatching password.
|
||||
$edit['pass[pass1]'] = '99999.0';
|
||||
$edit['pass[pass2]'] = '99999';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.');
|
||||
|
||||
// Enter a correct password.
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->container->get('entity.manager')->getStorage('user')->resetCache();
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertNotNull($new_user, 'New account successfully created with matching passwords.');
|
||||
$this->assertText(t('Registration successful. You are now logged in.'), 'Users are logged in after registering.');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Allow registration by site visitors, but require administrator approval.
|
||||
$config->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass1]'] = $pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $pass;
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval');
|
||||
|
||||
// Try to log in before administrator approval.
|
||||
$auth = [
|
||||
'name' => $name,
|
||||
'pass' => $pass,
|
||||
];
|
||||
$this->drupalPostForm('user/login', $auth, t('Log in'));
|
||||
$this->assertText(t('The username @name has not been activated or is blocked.', ['@name' => $name]), 'User cannot log in yet.');
|
||||
|
||||
// Activate the new account.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
$edit = [
|
||||
'status' => 1,
|
||||
];
|
||||
$this->drupalPostForm('user/' . $new_user->id() . '/edit', $edit, t('Save'));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Log in after administrator approval.
|
||||
$this->drupalPostForm('user/login', $auth, t('Log in'));
|
||||
$this->assertText(t('Member for'), 'User can log in after administrator approval.');
|
||||
}
|
||||
|
||||
public function testRegistrationEmailDuplicates() {
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
// Set up a user to check for duplicates.
|
||||
$duplicate_user = $this->drupalCreateUser();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $duplicate_user->getEmail();
|
||||
|
||||
// Attempt to create a new account using an existing email address.
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying an exact duplicate email address displays an error message');
|
||||
|
||||
// Attempt to bypass duplicate email registration validation by adding spaces.
|
||||
$edit['mail'] = ' ' . $duplicate_user->getEmail() . ' ';
|
||||
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying a duplicate email address with added whitespace displays an error message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that UUID isn't cached in form state on register form.
|
||||
*
|
||||
* This is a regression test for https://www.drupal.org/node/2500527 to ensure
|
||||
* that the form is not cached on GET requests.
|
||||
*/
|
||||
public function testUuidFormState() {
|
||||
\Drupal::service('module_installer')->install(['image']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Add a picture field in order to ensure that no form cache is written,
|
||||
// which breaks registration of more than 1 user every 6 hours.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'image',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
$form_display = EntityFormDisplay::create([
|
||||
'targetEntityType' => 'user',
|
||||
'bundle' => 'user',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
]);
|
||||
$form_display->setComponent('user_picture', [
|
||||
'type' => 'image_image',
|
||||
]);
|
||||
$form_display->save();
|
||||
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
|
||||
|
||||
// Create one account.
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$user_storage = \Drupal::entityManager()->getStorage('user');
|
||||
|
||||
$this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Create a second account.
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
|
||||
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
|
||||
}
|
||||
|
||||
public function testRegistrationDefaultValues() {
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$config_user_settings = $this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
// Set the default timezone to Brussels.
|
||||
$config_system_date = $this->config('system.date')
|
||||
->set('timezone.user.configurable', 1)
|
||||
->set('timezone.default', 'Europe/Brussels')
|
||||
->save();
|
||||
|
||||
// Check the presence of expected cache tags.
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->getUsername(), $name, 'Username matches.');
|
||||
$this->assertEqual($new_user->getEmail(), $mail, 'Email address matches.');
|
||||
$this->assertTrue(($new_user->getCreatedTime() > REQUEST_TIME - 20), 'Correct creation time.');
|
||||
$this->assertEqual($new_user->isActive(), $config_user_settings->get('register') == USER_REGISTER_VISITORS ? 1 : 0, 'Correct status field.');
|
||||
$this->assertEqual($new_user->getTimezone(), $config_system_date->get('timezone.default'), 'Correct time zone field.');
|
||||
$this->assertEqual($new_user->langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct language field.');
|
||||
$this->assertEqual($new_user->preferred_langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct preferred language field.');
|
||||
$this->assertEqual($new_user->init->value, $mail, 'Correct init field.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests username and email field constraints on user registration.
|
||||
*
|
||||
* @see \Drupal\user\Plugin\Validation\Constraint\UserNameUnique
|
||||
* @see \Drupal\user\Plugin\Validation\Constraint\UserMailUnique
|
||||
*/
|
||||
public function testUniqueFields() {
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
$edit = ['mail' => 'test@example.com', 'name' => $account->getUsername()];
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertRaw(new FormattableMarkup('The username %value is already taken.', ['%value' => $account->getUsername()]));
|
||||
|
||||
$edit = ['mail' => $account->getEmail(), 'name' => $this->randomString()];
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertRaw(new FormattableMarkup('The email address %value is already taken.', ['%value' => $account->getEmail()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field API fields on user registration forms.
|
||||
*/
|
||||
public function testRegistrationWithUserFields() {
|
||||
// Create a field on 'user' entity type.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'test_user_field',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => 'Some user field',
|
||||
'bundle' => 'user',
|
||||
'required' => TRUE,
|
||||
]);
|
||||
$field->save();
|
||||
entity_get_form_display('user', 'user', 'default')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
entity_get_form_display('user', 'user', 'register')
|
||||
->save();
|
||||
|
||||
// Check that the field does not appear on the registration form.
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertNoText($field->label(), 'The field does not appear on user registration form');
|
||||
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
|
||||
// Have the field appear on the registration form.
|
||||
entity_get_form_display('user', 'user', 'register')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertText($field->label(), 'The field appears on user registration form');
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
|
||||
// Check that validation errors are correctly reported.
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
// Missing input in required field.
|
||||
$edit['test_user_field[0][value]'] = '';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
$this->assertRaw(t('@name field is required.', ['@name' => $field->label()]), 'Field validation error was correctly reported.');
|
||||
// Invalid input.
|
||||
$edit['test_user_field[0][value]'] = '-1';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
$this->assertRaw(t('%name does not accept the value -1.', ['%name' => $field->label()]), 'Field validation error was correctly reported.');
|
||||
|
||||
// Submit with valid data.
|
||||
$value = rand(1, 255);
|
||||
$edit['test_user_field[0][value]'] = $value;
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->test_user_field->value, $value, 'The field value was correctly saved.');
|
||||
|
||||
// Check that the 'add more' button works.
|
||||
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$field_storage->save();
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
// Add two inputs.
|
||||
$value = rand(1, 255);
|
||||
$edit = [];
|
||||
$edit['test_user_field[0][value]'] = $value;
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
// Submit with three values.
|
||||
$edit['test_user_field[1][value]'] = $value + 1;
|
||||
$edit['test_user_field[2][value]'] = $value + 2;
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->test_user_field[0]->value, $value, 'The field value was correctly saved.');
|
||||
$this->assertEqual($new_user->test_user_field[1]->value, $value + 1, 'The field value was correctly saved.');
|
||||
$this->assertEqual($new_user->test_user_field[2]->value, $value + 2, 'The field value was correctly saved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the presence of cache tags on registration form with user fields.
|
||||
*/
|
||||
protected function assertRegistrationFormCacheTagsWithUserFields() {
|
||||
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
|
||||
$this->assertCacheTag('config:field.field.user.user.test_user_field');
|
||||
$this->assertCacheTag('config:field.storage.user.test_user_field');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,17 @@
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
@trigger_error(__NAMESPACE__ . '\UserResetEmailTestTrait is deprecated and scheduled for removal before Drupal 9.0.0. Add the method to the test class instead, see https://www.drupal.org/node/2999766', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
|
||||
/**
|
||||
* Helper function for logging in from reset password email.
|
||||
*
|
||||
* @deprecated Scheduled for removal before Drupal 9.0.0.
|
||||
* Add the method to the test class instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2999766
|
||||
*/
|
||||
trait UserResetEmailTestTrait {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user