Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0

This commit is contained in:
Pantheon Automation 2016-10-06 15:16:20 -07:00 committed by Greg Anderson
parent 2f563ab520
commit f1c8716f57
1732 changed files with 52334 additions and 11780 deletions

View file

@ -705,7 +705,7 @@ display:
group_type: group
admin_label: ''
operator: '='
value: true
value: '1'
group: 1
exposed: true
expose:
@ -753,7 +753,7 @@ display:
group_type: group
admin_label: ''
operator: '='
value: true
value: '1'
group: 1
exposed: false
expose:

View file

@ -89,7 +89,7 @@ display:
entity_field: name
filters:
status:
value: true
value: '1'
table: users_field_data
field: status
id: status

View file

@ -96,7 +96,7 @@ display:
entity_field: name
filters:
status:
value: true
value: '1'
table: users_field_data
field: status
id: status

View file

@ -6,21 +6,38 @@ source:
plugin: d6_user_picture_file
constants:
is_public: true
# source_base_path must be set by the tool configuring this migration. It
# represents the fully qualified path relative to which URIs in the files
# table are specified, and must end with a /.
source_base_path: ''
process:
filename: filename
uid: uid
uri:
source_full_path:
-
plugin: concat
delimiter: /
source:
- constants/source_base_path
- picture
-
plugin: urlencode
destination_full_path:
plugin: file_uri
source:
- picture
- file_directory_path
- temp_directory_path
- 'constants/is_public'
uri:
plugin: file_copy
source:
- '@source_full_path'
- '@destination_full_path'
destination:
plugin: entity:file
source_path_property: picture
migration_dependencies:
# Every migration that saves into {file_managed} must have the d6_file
# migration as an optional dependency to ensure it runs first.
# Every migration that references a file by Drupal 6 fid should specify d6_file as an
# optional dependency.
optional:
- d6_file

View file

@ -41,3 +41,4 @@ migration_dependencies:
- user_picture_field_instance
- user_picture_entity_display
- user_picture_entity_form_display
- d7_field_instance

View file

@ -62,7 +62,7 @@ class Cookie implements AuthenticationProviderInterface {
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* The session.
*
* @return \Drupal\Core\Session\AccountInterface|NULL
* @return \Drupal\Core\Session\AccountInterface|null
* The UserSession object for the current user, or NULL if this is an
* anonymous session.
*/

View file

@ -0,0 +1,345 @@
<?php
namespace Drupal\user\Controller;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\user\UserAuthInterface;
use Drupal\user\UserInterface;
use Drupal\user\UserStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Serializer;
/**
* Provides controllers for login, login status and logout via HTTP requests.
*/
class UserAuthenticationController extends ControllerBase implements ContainerInjectionInterface {
/**
* String sent in responses, to describe the user as being logged in.
*
* @var string
*/
const LOGGED_IN = 1;
/**
* String sent in responses, to describe the user as being logged out.
*
* @var string
*/
const LOGGED_OUT = 0;
/**
* The flood controller.
*
* @var \Drupal\Core\Flood\FloodInterface
*/
protected $flood;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* The CSRF token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfToken;
/**
* The user authentication.
*
* @var \Drupal\user\UserAuthInterface
*/
protected $userAuth;
/**
* The route provider.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* The available serialization formats.
*
* @var array
*/
protected $serializerFormats = [];
/**
* Constructs a new UserAuthenticationController object.
*
* @param \Drupal\Core\Flood\FloodInterface $flood
* The flood controller.
* @param \Drupal\user\UserStorageInterface $user_storage
* The user storage.
* @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
* The CSRF token generator.
* @param \Drupal\user\UserAuthInterface $user_auth
* The user authentication.
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
* @param \Symfony\Component\Serializer\Serializer $serializer
* The serializer.
* @param array $serializer_formats
* The available serialization formats.
*/
public function __construct(FloodInterface $flood, UserStorageInterface $user_storage, CsrfTokenGenerator $csrf_token, UserAuthInterface $user_auth, RouteProviderInterface $route_provider, Serializer $serializer, array $serializer_formats) {
$this->flood = $flood;
$this->userStorage = $user_storage;
$this->csrfToken = $csrf_token;
$this->userAuth = $user_auth;
$this->serializer = $serializer;
$this->serializerFormats = $serializer_formats;
$this->routeProvider = $route_provider;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
if ($container->hasParameter('serializer.formats') && $container->has('serializer')) {
$serializer = $container->get('serializer');
$formats = $container->getParameter('serializer.formats');
}
else {
$formats = ['json'];
$encoders = [new JsonEncoder()];
$serializer = new Serializer([], $encoders);
}
return new static(
$container->get('flood'),
$container->get('entity_type.manager')->getStorage('user'),
$container->get('csrf_token'),
$container->get('user.auth'),
$container->get('router.route_provider'),
$serializer,
$formats
);
}
/**
* Logs in a user.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @return \Symfony\Component\HttpFoundation\Response
* A response which contains the ID and CSRF token.
*/
public function login(Request $request) {
$format = $this->getRequestFormat($request);
$content = $request->getContent();
$credentials = $this->serializer->decode($content, $format);
if (!isset($credentials['name']) && !isset($credentials['pass'])) {
throw new BadRequestHttpException('Missing credentials.');
}
if (!isset($credentials['name'])) {
throw new BadRequestHttpException('Missing credentials.name.');
}
if (!isset($credentials['pass'])) {
throw new BadRequestHttpException('Missing credentials.pass.');
}
$this->floodControl($request, $credentials['name']);
if ($this->userIsBlocked($credentials['name'])) {
throw new BadRequestHttpException('The user has not been activated or is blocked.');
}
if ($uid = $this->userAuth->authenticate($credentials['name'], $credentials['pass'])) {
$this->flood->clear('user.http_login', $this->getLoginFloodIdentifier($request, $credentials['name']));
/** @var \Drupal\user\UserInterface $user */
$user = $this->userStorage->load($uid);
$this->userLoginFinalize($user);
// Send basic metadata about the logged in user.
$response_data = [];
if ($user->get('uid')->access('view', $user)) {
$response_data['current_user']['uid'] = $user->id();
}
if ($user->get('roles')->access('view', $user)) {
$response_data['current_user']['roles'] = $user->getRoles();
}
if ($user->get('name')->access('view', $user)) {
$response_data['current_user']['name'] = $user->getAccountName();
}
$response_data['csrf_token'] = $this->csrfToken->get('rest');
$logout_route = $this->routeProvider->getRouteByName('user.logout.http');
// Trim '/' off path to match \Drupal\Core\Access\CsrfAccessCheck.
$logout_path = ltrim($logout_route->getPath(), '/');
$response_data['logout_token'] = $this->csrfToken->get($logout_path);
$encoded_response_data = $this->serializer->encode($response_data, $format);
return new Response($encoded_response_data);
}
$flood_config = $this->config('user.flood');
if ($identifier = $this->getLoginFloodIdentifier($request, $credentials['name'])) {
$this->flood->register('user.http_login', $flood_config->get('user_window'), $identifier);
}
// Always register an IP-based failed login event.
$this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
throw new BadRequestHttpException('Sorry, unrecognized username or password.');
}
/**
* Verifies if the user is blocked.
*
* @param string $name
* The username.
*
* @return bool
* TRUE if the user is blocked, otherwise FALSE.
*/
protected function userIsBlocked($name) {
return user_is_blocked($name);
}
/**
* Finalizes the user login.
*
* @param \Drupal\user\UserInterface $user
* The user.
*/
protected function userLoginFinalize(UserInterface $user) {
user_login_finalize($user);
}
/**
* Logs out a user.
*
* @return \Drupal\rest\ResourceResponse
* The response object.
*/
public function logout() {
$this->userLogout();
return new Response(NULL, 204);
}
/**
* Logs the user out.
*/
protected function userLogout() {
user_logout();
}
/**
* Checks whether a user is logged in or not.
*
* @return \Symfony\Component\HttpFoundation\Response
* The response.
*/
public function loginStatus() {
if ($this->currentUser()->isAuthenticated()) {
$response = new Response(self::LOGGED_IN);
}
else {
$response = new Response(self::LOGGED_OUT);
}
$response->headers->set('Content-Type', 'text/plain');
return $response;
}
/**
* Gets the format of the current request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return string
* The format of the request.
*/
protected function getRequestFormat(Request $request) {
$format = $request->getRequestFormat();
if (!in_array($format, $this->serializerFormats)) {
throw new BadRequestHttpException("Unrecognized format: $format.");
}
return $format;
}
/**
* Enforces flood control for the current login request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param string $username
* The user name sent for login credentials.
*/
protected function floodControl(Request $request, $username) {
$flood_config = $this->config('user.flood');
if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
throw new AccessDeniedHttpException('Access is blocked because of IP based flood prevention.', NULL, Response::HTTP_TOO_MANY_REQUESTS);
}
if ($identifier = $this->getLoginFloodIdentifier($request, $username)) {
// Don't allow login if the limit for this user has been reached.
// Default is to allow 5 failed attempts every 6 hours.
if (!$this->flood->isAllowed('user.http_login', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
if ($flood_config->get('uid_only')) {
$error_message = sprintf('There have been more than %s failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', $flood_config->get('user_limit'));
}
else {
$error_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
}
throw new AccessDeniedHttpException($error_message, NULL, Response::HTTP_TOO_MANY_REQUESTS);
}
}
}
/**
* Gets the login identifier for user login flood control.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param string $username
* The username supplied in login credentials.
*
* @return string
* The login identifier or if the user does not exist an empty string.
*/
protected function getLoginFloodIdentifier(Request $request, $username) {
$flood_config = $this->config('user.flood');
$accounts = $this->userStorage->loadByProperties(['name' => $username, 'status' => 1]);
if ($account = reset($accounts)) {
if ($flood_config->get('uid_only')) {
// Register flood events based on the uid only, so they apply for any
// IP address. This is the most secure option.
$identifier = $account->id();
}
else {
// The default identifier is a combination of uid and IP address. This
// is less secure but more resistant to denial-of-service attacks that
// could lock out all users with public user names.
$identifier = $account->id() . '-' . $request->getClientIp();
}
return $identifier;
}
return '';
}
}

View file

@ -425,21 +425,17 @@ class User extends ContentEntityBase implements UserInterface {
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['uid'] = BaseFieldDefinition::create('integer')
->setLabel(t('User ID'))
->setDescription(t('The user ID.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = parent::baseFieldDefinitions($entity_type);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The user UUID.'))
->setReadOnly(TRUE);
$fields['uid']->setLabel(t('User ID'))
->setDescription(t('The user ID.'));
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language code'))
$fields['uuid']->setDescription(t('The user UUID.'));
$fields['langcode']->setLabel(t('Language code'))
->setDescription(t('The user language code.'))
->setTranslatable(TRUE);
->setDisplayOptions('form', ['type' => 'hidden']);
$fields['preferred_langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Preferred language code'))

View file

@ -232,7 +232,7 @@ class UserLoginForm extends FormBase {
// handlers that ran earlier than this one.
$user_input = $form_state->getUserInput();
$query = isset($user_input['name']) ? array('name' => $user_input['name']) : array();
$form_state->setErrorByName('name', $this->t('Unrecognized username or password. <a href=":password">Have you forgotten your password?</a>', array(':password' => $this->url('user.pass', [], array('query' => $query)))));
$form_state->setErrorByName('name', $this->t('Unrecognized username or password. <a href=":password">Forgot your password?</a>', array(':password' => $this->url('user.pass', [], array('query' => $query)))));
$accounts = $this->userStorage->loadByProperties(array('name' => $form_state->getValue('name')));
if (!empty($accounts)) {
$this->logger('user')->notice('Login attempt failed for %user.', array('%user' => $form_state->getValue('name')));

View file

@ -42,7 +42,7 @@ class UserPermissionsForm extends FormBase {
* The permission handler.
* @param \Drupal\user\RoleStorageInterface $role_storage
* The role storage.
* @param \Drupal\Core\Extension\ModuleHandlerInterface
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(PermissionHandlerInterface $permission_handler, RoleStorageInterface $role_storage, ModuleHandlerInterface $module_handler) {
@ -100,7 +100,6 @@ class UserPermissionsForm extends FormBase {
'#value' => $role_names,
);
// Render role/permission overview:
$options = array();
$hide_descriptions = system_admin_compact_mode();
$form['system_compact_link'] = array(
@ -145,7 +144,6 @@ class UserPermissionsForm extends FormBase {
'restrict access' => FALSE,
'warning' => !empty($perm_item['restrict access']) ? $this->t('Warning: Give to trusted roles only; this permission has security implications.') : '',
);
$options[$perm] = $perm_item['title'];
$form['permissions'][$perm]['description'] = array(
'#type' => 'inline_template',
'#template' => '<div class="permission"><span class="title">{{ title }}</span>{% if description or warning %}<div class="description">{% if warning %}<em class="permission-warning">{{ warning }}</em> {% endif %}{{ description }}</div>{% endif %}</div>',
@ -158,7 +156,6 @@ class UserPermissionsForm extends FormBase {
$form['permissions'][$perm]['description']['#context']['description'] = $perm_item['description'];
$form['permissions'][$perm]['description']['#context']['warning'] = $perm_item['warning'];
}
$options[$perm] = '';
foreach ($role_names as $rid => $name) {
$form['permissions'][$perm][$rid] = array(
'#title' => $name . ': ' . $perm_item['title'],

View file

@ -2,7 +2,7 @@
namespace Drupal\user;
use Drupal\Component\Discovery\YamlDiscovery;
use Drupal\Core\Discovery\YamlDiscovery;
use Drupal\Core\Controller\ControllerResolverInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@ -59,7 +59,7 @@ class PermissionHandler implements PermissionHandlerInterface {
/**
* The YAML discovery class to find all .permissions.yml files.
*
* @var \Drupal\Component\Discovery\YamlDiscovery
* @var \Drupal\Core\Discovery\YamlDiscovery
*/
protected $yamlDiscovery;
@ -91,7 +91,7 @@ class PermissionHandler implements PermissionHandlerInterface {
/**
* Gets the YAML discovery.
*
* @return \Drupal\Component\Discovery\YamlDiscovery
* @return \Drupal\Core\Discovery\YamlDiscovery
* The YAML discovery.
*/
protected function getYamlDiscovery() {

View file

@ -29,7 +29,7 @@ class UserNameConstraintValidator extends ConstraintValidator {
if (strpos($name, ' ') !== FALSE) {
$this->context->addViolation($constraint->multipleSpacesMessage);
}
if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)
if (preg_match('/[^\x{80}-\x{F7} a-z0-9@+_.\'-]/i', $name)
|| preg_match(
// Non-printable ISO-8859-1 + NBSP
'/[\x{80}-\x{A0}' .

View file

@ -25,11 +25,11 @@ class UserUpdate7002 extends ProcessPluginBase implements ContainerFactoryPlugin
*/
protected static $timezones;
/**
* Contains the system.theme configuration object.
*
* @var \Drupal\Core\Config\Config
*/
/**
* Contains the system.theme configuration object.
*
* @var \Drupal\Core\Config\Config
*/
protected $dateConfig;
/**

View file

@ -9,7 +9,7 @@ use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
/**
* Default argument plugin to extract the current user
*
* This plugin actually has no options so it odes not need to do a great deal.
* This plugin actually has no options so it does not need to do a great deal.
*
* @ViewsArgumentDefault(
* id = "current_user",

View file

@ -112,16 +112,4 @@ class Permissions extends PrerenderList {
return $item['permission'];
}
/*
protected function documentSelfTokens(&$tokens) {
$tokens['[' . $this->options['id'] . '-role' . ']'] = $this->t('The name of the role.');
$tokens['[' . $this->options['id'] . '-rid' . ']'] = $this->t('The role ID of the role.');
}
protected function addSelfTokens(&$tokens, $item) {
$tokens['[' . $this->options['id'] . '-role' . ']'] = $item['role'];
$tokens['[' . $this->options['id'] . '-rid' . ']'] = $item['rid'];
}
*/
}

View file

@ -96,9 +96,9 @@ class Name extends InOperator {
// prevent array filter from removing our anonymous user.
}
/**
* {@inheritdoc}
*/
/**
* {@inheritdoc}
*/
public function getValueOptions() {
return $this->valueOptions;
}
@ -108,7 +108,8 @@ class Name extends InOperator {
$this->valueOptions = array();
if ($this->value) {
$result = entity_load_multiple_by_properties('user', array('uid' => $this->value));
$result = \Drupal::entityTypeManager()->getStorage('user')
->loadByProperties(['uid' => $this->value]);
foreach ($result as $account) {
if ($account->id()) {
$this->valueOptions[$account->id()] = $account->label();

View file

@ -100,7 +100,7 @@ class UserAdminTest extends WebTestBase {
$config
->set('notify.status_blocked', TRUE)
->save();
$this->drupalPostForm('admin/people', $edit, t('Apply'), array(
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'), array(
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
'query' => array('order' => 'name', 'sort' => 'asc')
@ -121,7 +121,7 @@ class UserAdminTest extends WebTestBase {
$editunblock = array();
$editunblock['action'] = 'user_unblock_user_action';
$editunblock['user_bulk_form[4]'] = TRUE;
$this->drupalPostForm('admin/people', $editunblock, t('Apply'), array(
$this->drupalPostForm('admin/people', $editunblock, t('Apply to selected items'), array(
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
'query' => array('order' => 'name', 'sort' => 'asc')
@ -174,8 +174,8 @@ class UserAdminTest extends WebTestBase {
->save();
// Register a new user account.
$edit = array();
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$edit['name'] = $this->randomMachineName();
$edit['mail'] = $edit['name'] . '@example.com';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$subject = 'Account details for ' . $edit['name'] . ' at ' . $system->get('name') . ' (pending admin approval)';
// Ensure that admin notification mail is sent to the configured

View file

@ -34,9 +34,9 @@ class UserBlocksTest extends WebTestBase {
$this->drupalLogout($this->adminUser);
}
/**
* Tests that user login block is hidden from user/login.
*/
/**
* Tests that user login block is hidden from user/login.
*/
function testUserLoginBlockVisibility() {
// Array keyed list where key being the URL address and value being expected
// visibility as boolean type.
@ -51,7 +51,7 @@ class UserBlocksTest extends WebTestBase {
$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');
}
@ -87,6 +87,17 @@ class UserBlocksTest extends WebTestBase {
$this->drupalPostForm('http://example.com/', $edit, t('Log in'), array('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 = array();
$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?'));
}
/**

View file

@ -124,7 +124,7 @@ class UserCancelTest extends WebTestBase {
'action' => 'user_cancel_user_action',
'user_bulk_form[0]' => TRUE,
);
$this->drupalPostForm('admin/people', $edit, t('Apply'));
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'));
// Verify that uid 1's account was not cancelled.
$user_storage->resetCache(array(1));
@ -353,7 +353,7 @@ class UserCancelTest extends WebTestBase {
$test_node = $node_storage->load($node->id());
$this->assertTrue(($test_node->getOwnerId() == 0 && $test_node->isPublished()), 'Node of the user has been attributed to anonymous user.');
$test_node = node_revision_load($revision, TRUE);
$this->assertTrue(($test_node->getRevisionAuthor()->id() == 0 && $test_node->isPublished()), 'Node revision of the user has been attributed to anonymous user.');
$this->assertTrue(($test_node->getRevisionUser()->id() == 0 && $test_node->isPublished()), 'Node revision of the user has been attributed to anonymous user.');
$node_storage->resetCache(array($revision_node->id()));
$test_node = $node_storage->load($revision_node->id());
$this->assertTrue(($test_node->getOwnerId() != 0 && $test_node->isPublished()), "Current revision of the user's node was not attributed to anonymous user.");
@ -567,7 +567,7 @@ class UserCancelTest extends WebTestBase {
for ($i = 0; $i <= 4; $i++) {
$edit['user_bulk_form[' . $i . ']'] = TRUE;
}
$this->drupalPostForm('admin/people', $edit, t('Apply'));
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'));
$this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
$this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
$this->assertText(t('Require email confirmation to cancel account'), 'Allows to send confirmation mail.');

View file

@ -171,7 +171,7 @@ class UserLoginTest extends WebTestBase {
}
}
else {
$this->assertText(t('Unrecognized username or password. Have you forgotten your password?'));
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
}
}

View file

@ -88,7 +88,7 @@ class UserPasswordResetTest extends PageCacheTagsTestBase {
$edit['name'] = $this->account->getUsername();
$this->drupalPostForm(NULL, $edit, t('Submit'));
// Verify that the user was sent an email.
// 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', array('@username' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')));
$this->assertMail('subject', $subject, 'Password reset email subject is correct.');
@ -287,7 +287,7 @@ class UserPasswordResetTest extends PageCacheTagsTestBase {
'pass' => $this->randomMachineName(),
);
$this->drupalPostForm('user/login', $edit, t('Log in'));
$this->assertRaw(t('Unrecognized username or password. <a href=":password">Have you forgotten your password?</a>',
$this->assertRaw(t('Unrecognized username or password. <a href=":password">Forgot your password?</a>',
array(':password' => \Drupal::url('user.pass', [], array('query' => array('name' => $edit['name']))))));
unset($edit['pass']);
$this->drupalGet('user/password', array('query' => array('name' => $edit['name'])));
@ -332,6 +332,6 @@ class UserPasswordResetTest extends PageCacheTagsTestBase {
$this->assertNoText($user2->getUsername(), 'The invalid password reset page does not show the user name.');
$this->assertUrl('user/password', array(), '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.');
}
}
}

View file

@ -40,7 +40,10 @@ class UserRegistrationTest extends WebTestBase {
$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.');
$accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
/** @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);
@ -54,7 +57,7 @@ class UserRegistrationTest extends WebTestBase {
$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 = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
$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.');
}
@ -83,7 +86,8 @@ class UserRegistrationTest extends WebTestBase {
$edit['pass[pass2]'] = $new_pass;
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->container->get('entity.manager')->getStorage('user')->resetCache();
$accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
$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.');
@ -108,7 +112,8 @@ class UserRegistrationTest extends WebTestBase {
$this->assertText(t('The username @name has not been activated or is blocked.', array('@name' => $name)), 'User cannot log in yet.');
// Activate the new account.
$accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$admin_user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($admin_user);
@ -248,7 +253,8 @@ class UserRegistrationTest extends WebTestBase {
$this->drupalPostForm(NULL, $edit, t('Create new account'));
// Check user fields.
$accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
$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.');
@ -338,7 +344,8 @@ class UserRegistrationTest extends WebTestBase {
$edit['test_user_field[0][value]'] = $value;
$this->drupalPostForm(NULL, $edit, t('Create new account'));
// Check user fields.
$accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
$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.');
@ -367,7 +374,8 @@ class UserRegistrationTest extends WebTestBase {
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$this->drupalPostForm(NULL, $edit, t('Create new account'));
// Check user fields.
$accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail));
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(array('name' => $name, 'mail' => $mail));
$new_user = reset($accounts);
$this->assertEqual($new_user->test_user_field[0]->value, $value, format_string('@js : The field value was correctly saved.', array('@js' => $js)));
$this->assertEqual($new_user->test_user_field[1]->value, $value + 1, format_string('@js : The field value was correctly saved.', array('@js' => $js)));

View file

@ -113,7 +113,7 @@ class UserRoleAdminTest extends WebTestBase {
$saved_rids[] = $role->id();
$weight--;
}
$this->drupalPostForm('admin/people/roles', $edit, t('Save order'));
$this->drupalPostForm('admin/people/roles', $edit, t('Save'));
$this->assertText(t('The role settings have been updated.'), 'The role settings form submitted successfully.');
// Load up the user roles with the new weights.

View file

@ -21,8 +21,9 @@ class UserSearchTest extends WebTestBase {
function testUserSearch() {
// Verify that a user without 'administer users' permission cannot search
// for users by email address.
$user1 = $this->drupalCreateUser(array('access user profiles', 'search content'));
// for users by email address. Additionally, ensure that the username has a
// plus sign to ensure searching works with that.
$user1 = $this->drupalCreateUser(array('access user profiles', 'search content'), "foo+bar");
$this->drupalLogin($user1);
$keys = $user1->getEmail();
$edit = array('keys' => $keys);

View file

@ -2,6 +2,7 @@
namespace Drupal\user\Tests;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\simpletest\WebTestBase;
/**
@ -27,7 +28,7 @@ class UserTimeZoneTest extends WebTestBase {
->set('timezone.user.configurable', 1)
->set('timezone.default', 'America/Los_Angeles')
->save();
entity_load('date_format', 'medium')
DateFormat::load('medium')
->setPattern('Y-m-d H:i T')
->save();

View file

@ -53,7 +53,10 @@ class UserTranslationUITest extends ContentTranslationUITestBase {
* {@inheritdoc}
*/
protected function doTestTranslationEdit() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {

View file

@ -50,7 +50,7 @@ class BulkFormAccessTest extends UserTestBase {
'user_bulk_form[' . ($no_edit_user->id() - 1) . ']' => TRUE,
'action' => 'user_block_user_action',
);
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply'));
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$this->assertResponse(200);
$this->assertRaw(SafeMarkup::format('No access to execute %action on the @entity_type_label %entity_label.', [
@ -71,7 +71,7 @@ class BulkFormAccessTest extends UserTestBase {
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
'action' => 'user_block_user_action',
);
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply'));
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$normal_user = User::load($normal_user->id());
$this->assertTrue($normal_user->isBlocked(), 'The user is blocked.');
@ -83,7 +83,7 @@ class BulkFormAccessTest extends UserTestBase {
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
'action' => 'user_unblock_user_action',
);
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply'));
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
// Re-load the normal user and ensure it is still blocked.
$normal_user = User::load($normal_user->id());
@ -114,7 +114,7 @@ class BulkFormAccessTest extends UserTestBase {
'user_bulk_form[' . ($account2->id() - 1) . ']' => TRUE,
'action' => 'user_cancel_user_action',
);
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply'));
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$edit = array(
'user_cancel_method' => 'user_cancel_delete',
);

View file

@ -2,6 +2,7 @@
namespace Drupal\user\Tests\Views;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
use Drupal\views\Views;
@ -45,7 +46,7 @@ class BulkFormTest extends UserTestBase {
$edit = array(
'action' => 'user_block_user_action',
);
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply'));
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$this->assertText(t('No users selected.'));
// Assign a role to a user.
@ -59,7 +60,7 @@ class BulkFormTest extends UserTestBase {
'user_bulk_form[1]' => TRUE,
'action' => 'user_add_role_action.' . $role,
);
$this->drupalPostForm(NULL, $edit, t('Apply'));
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Re-load the user and check their roles.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
@ -69,7 +70,7 @@ class BulkFormTest extends UserTestBase {
'user_bulk_form[1]' => TRUE,
'action' => 'user_remove_role_action.' . $role,
);
$this->drupalPostForm(NULL, $edit, t('Apply'));
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Re-load the user and check their roles.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
@ -82,7 +83,7 @@ class BulkFormTest extends UserTestBase {
'user_bulk_form[1]' => TRUE,
'action' => 'user_block_user_action',
);
$this->drupalPostForm(NULL, $edit, t('Apply'));
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Re-load the user and check their status.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
@ -103,7 +104,7 @@ class BulkFormTest extends UserTestBase {
'user_bulk_form[0]' => TRUE,
'action' => 'user_block_user_action',
);
$this->drupalPostForm(NULL, $edit, t('Apply'));
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$anonymous_account = $user_storage->load(0);
$this->assertTrue($anonymous_account->isBlocked(), 'Ensure the anonymous user got blocked.');
@ -130,7 +131,7 @@ class BulkFormTest extends UserTestBase {
*/
public function testBulkFormCombineFilter() {
// Add a user.
$account = entity_load('user', $this->users[0]->id());
User::load($this->users[0]->id());
$view = Views::getView('test_user_bulk_form_combine_filter');
$errors = $view->validate();
$this->assertEqual(reset($errors['default']), t('Field %field set in %filter is not usable for this filter type. Combined field filter only works for simple fields.', array('%field' => 'User: Bulk update', '%filter' => 'Global: Combine fields filter')));

View file

@ -14,8 +14,8 @@ $connection = Database::getConnection();
// already.
$connection->delete('config')->condition('name', 'user.mail')->execute();
$connection->insert('config')
->fields(array('collection', 'name', 'data'))
->values(array(
->fields(array('collection', 'name', 'data'))
->values(array(
'collection' => '',
'name' => 'user.mail',
'data' => "a:10:{s:14:\"cancel_confirm\";a:2:{s:4:\"body\";s:369:\"[user:name],\n\nA request to cancel your account has been made at [site:name].\n\nYou may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:\n\n[user:cancel-url]\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n-- [site:name] team\";s:7:\"subject\";s:59:\"Account cancellation request for [user:name] at [site:name]\";}s:14:\"password_reset\";a:2:{s:4:\"body\";s:397:\"[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.\n\n-- [site:name] team\";s:7:\"subject\";s:60:\"Replacement login information for [user:name] at [site:name]\";}s:22:\"register_admin_created\";a:2:{s:4:\"body\";s:463:\"[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:58:\"An administrator created an account for you at [site:name]\";}s:29:\"register_no_approval_required\";a:2:{s:4:\"body\";s:437:\"[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:46:\"Account details for [user:name] at [site:name]\";}s:25:\"register_pending_approval\";a:2:{s:4:\"body\";s:281:\"[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:31:\"register_pending_approval_admin\";a:2:{s:4:\"body\";s:56:\"[user:name] has applied for an account.\n\n[user:edit-url]\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:16:\"status_activated\";a:2:{s:4:\"body\";s:446:\"[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (approved)\";}s:14:\"status_blocked\";a:2:{s:4:\"body\";s:89:\"[user:name],\n\nYour account on [site:account-name] has been blocked.\n\n-- [site:name] team\";s:7:\"subject\";s:56:\"Account details for [user:name] at [site:name] (blocked)\";}s:15:\"status_canceled\";a:2:{s:4:\"body\";s:82:\"[user:name],\n\nYour account on [site:name] has been canceled.\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (canceled)\";}s:8:\"langcode\";s:2:\"en\";}"

View file

@ -48,7 +48,7 @@ display:
group: 1
id: status
table: users_field_data
value: true
value: '1'
plugin_id: boolean
entity_type: user
entity_field: status

View file

@ -52,7 +52,7 @@ display:
table: users_field_data
field: status
operator: '='
value: true
value: '1'
plugin_id: boolean
entity_type: user
entity_field: status

View file

@ -126,7 +126,7 @@ display:
field_api_classes: false
filters:
status:
value: true
value: '1'
table: users_field_data
field: status
plugin_id: boolean

View file

@ -142,7 +142,7 @@ display:
plugin_id: user_roles
filters:
status:
value: true
value: '1'
table: users_field_data
field: status
id: status

View file

@ -0,0 +1,421 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Flood\DatabaseBackend;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Controller\UserAuthenticationController;
use GuzzleHttp\Cookie\CookieJar;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Serializer;
/**
* Tests login via direct HTTP.
*
* @group user
*/
class UserLoginHttpTest extends BrowserTestBase {
/**
* The cookie jar.
*
* @var \GuzzleHttp\Cookie\CookieJar
*/
protected $cookies;
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->cookies = new CookieJar();
$encoders = [new JsonEncoder(), new XmlEncoder()];
$this->serializer = new Serializer([], $encoders);
}
/**
* Executes a login HTTP request.
*
* @param string $name
* The username.
* @param string $pass
* The user password.
* @param string $format
* The format to use to make the request.
*
* @return \Psr\Http\Message\ResponseInterface The HTTP response.
* The HTTP response.
*/
protected function loginRequest($name, $pass, $format = 'json') {
$user_login_url = Url::fromRoute('user.login.http')
->setRouteParameter('_format', $format)
->setAbsolute();
$request_body = [];
if (isset($name)) {
$request_body['name'] = $name;
}
if (isset($pass)) {
$request_body['pass'] = $pass;
}
$result = \Drupal::httpClient()->post($user_login_url->toString(), [
'body' => $this->serializer->encode($request_body, $format),
'headers' => [
'Accept' => "application/$format",
],
'http_errors' => FALSE,
'cookies' => $this->cookies,
]);
return $result;
}
/**
* Tests user session life cycle.
*/
public function testLogin() {
$client = \Drupal::httpClient();
foreach ([FALSE, TRUE] as $serialization_enabled_option) {
if ($serialization_enabled_option) {
/** @var \Drupal\Core\Extension\ModuleInstaller $module_installer */
$module_installer = $this->container->get('module_installer');
$module_installer->install(['serialization']);
$formats = ['json', 'xml'];
}
else {
// Without the serialization module only JSON is supported.
$formats = ['json'];
}
foreach ($formats as $format) {
// Create new user for each iteration to reset flood.
// Grant the user administer users permissions to they can see the
// 'roles' field.
$account = $this->drupalCreateUser(['administer users']);
$name = $account->getUsername();
$pass = $account->passRaw;
$login_status_url = $this->getLoginStatusUrlString($format);
$response = $client->get($login_status_url);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
// Flooded.
$this->config('user.flood')
->set('user_limit', 3)
->save();
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 403, 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.', $format);
// After testing the flood control we can increase the limit.
$this->config('user.flood')
->set('user_limit', 100)
->save();
$response = $this->loginRequest(NULL, NULL, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.', $format);
$response = $this->loginRequest(NULL, $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.name.', $format);
$response = $this->loginRequest($name, NULL, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.pass.', $format);
// Blocked.
$account
->block()
->save();
$response = $this->loginRequest($name, $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
$account
->activate()
->save();
$response = $this->loginRequest($name, 'garbage', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest('garbage', $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, $pass, $format);
$this->assertEquals(200, $response->getStatusCode());
$result_data = $this->serializer->decode($response->getBody(), $format);
$this->assertEquals($name, $result_data['current_user']['name']);
$this->assertEquals($account->id(), $result_data['current_user']['uid']);
$this->assertEquals($account->getRoles(), $result_data['current_user']['roles']);
$logout_token = $result_data['logout_token'];
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
$response = $this->logoutRequest($format, $logout_token);
$this->assertEquals(204, $response->getStatusCode());
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
$this->resetFlood();
}
}
}
/**
* Gets a value for a given key from the response.
*
* @param \Psr\Http\Message\ResponseInterface $response
* The response object.
* @param string $key
* The key for the value.
* @param string $format
* The encoded format.
*
* @return mixed
* The value for the key.
*/
protected function getResultValue(ResponseInterface $response, $key, $format) {
$decoded = $this->serializer->decode((string) $response->getBody(), $format);
if (is_array($decoded)) {
return $decoded[$key];
}
else {
return $decoded->{$key};
}
}
/**
* Resets all flood entries.
*/
protected function resetFlood() {
$this->container->get('database')->delete(DatabaseBackend::TABLE_NAME)->execute();
}
/**
* Tests the global login flood control.
*
* @see \Drupal\basic_auth\Tests\Authentication\BasicAuthTest::testGlobalLoginFloodControl
* @see \Drupal\user\Tests\UserLoginTest::testGlobalLoginFloodControl
*/
public function testGlobalLoginFloodControl() {
$this->config('user.flood')
->set('ip_limit', 2)
// Set a high per-user limit out so that it is not relevant in the test.
->set('user_limit', 4000)
->save();
$user = $this->drupalCreateUser([]);
$incorrect_user = clone $user;
$incorrect_user->passRaw .= 'incorrect';
// Try 2 failed logins.
for ($i = 0; $i < 2; $i++) {
$response = $this->loginRequest($incorrect_user->getUsername(), $incorrect_user->passRaw);
$this->assertEquals('400', $response->getStatusCode());
}
// IP limit has reached to its limit. Even valid user credentials will fail.
$response = $this->loginRequest($user->getUsername(), $user->passRaw);
$this->assertHttpResponseWithMessage($response, '403', 'Access is blocked because of IP based flood prevention.');
}
/**
* Checks a response for status code and body.
*
* @param \Psr\Http\Message\ResponseInterface $response
* The response object.
* @param int $expected_code
* The expected status code.
* @param mixed $expected_body
* The expected response body.
*/
protected function assertHttpResponse(ResponseInterface $response, $expected_code, $expected_body) {
$this->assertEquals($expected_code, $response->getStatusCode());
$this->assertEquals($expected_body, (string) $response->getBody());
}
/**
* Checks a response for status code and message.
*
* @param \Psr\Http\Message\ResponseInterface $response
* The response object.
* @param int $expected_code
* The expected status code.
* @param string $expected_message
* The expected message encoded in response.
* @param string $format
* The format that the response is encoded in.
*/
protected function assertHttpResponseWithMessage(ResponseInterface $response, $expected_code, $expected_message, $format = 'json') {
$this->assertEquals($expected_code, $response->getStatusCode());
$this->assertEquals($expected_message, $this->getResultValue($response, 'message', $format));
}
/**
* Test the per-user login flood control.
*
* @see \Drupal\user\Tests\UserLoginTest::testPerUserLoginFloodControl
* @see \Drupal\basic_auth\Tests\Authentication\BasicAuthTest::testPerUserLoginFloodControl
*/
public function testPerUserLoginFloodControl() {
foreach ([TRUE, FALSE] as $uid_only_setting) {
$this->config('user.flood')
// Set a high global limit out so that it is not relevant in the test.
->set('ip_limit', 4000)
->set('user_limit', 3)
->set('uid_only', $uid_only_setting)
->save();
$user1 = $this->drupalCreateUser([]);
$incorrect_user1 = clone $user1;
$incorrect_user1->passRaw .= 'incorrect';
$user2 = $this->drupalCreateUser([]);
// Try 2 failed logins.
for ($i = 0; $i < 2; $i++) {
$response = $this->loginRequest($incorrect_user1->getUsername(), $incorrect_user1->passRaw);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.');
}
// A successful login will reset the per-user flood control count.
$response = $this->loginRequest($user1->getUsername(), $user1->passRaw);
$result_data = $this->serializer->decode($response->getBody(), 'json');
$this->logoutRequest('json', $result_data['logout_token']);
// Try 3 failed logins for user 1, they will not trigger flood control.
for ($i = 0; $i < 3; $i++) {
$response = $this->loginRequest($incorrect_user1->getUsername(), $incorrect_user1->passRaw);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.');
}
// Try one successful attempt for user 2, it should not trigger any
// flood control.
$this->drupalLogin($user2);
$this->drupalLogout();
// Try one more attempt for user 1, it should be rejected, even if the
// correct password has been used.
$response = $this->loginRequest($user1->getUsername(), $user1->passRaw);
// Depending on the uid_only setting the error message will be different.
if ($uid_only_setting) {
$excepted_message = 'There have been more than 3 failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.';
}
else {
$excepted_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
}
$this->assertHttpResponseWithMessage($response, 403, $excepted_message);
}
}
/**
* Executes a logout HTTP request.
*
* @param string $format
* The format to use to make the request.
* @param string $logout_token
* The csrf token for user logout.
*
* @return \Psr\Http\Message\ResponseInterface The HTTP response.
* The HTTP response.
*/
protected function logoutRequest($format = 'json', $logout_token = '') {
/** @var \GuzzleHttp\Client $client */
$client = $this->container->get('http_client');
$user_logout_url = Url::fromRoute('user.logout.http')
->setRouteParameter('_format', $format)
->setAbsolute();
if ($logout_token) {
$user_logout_url->setOption('query', ['token' => $logout_token]);
}
$post_options = [
'headers' => [
'Accept' => "application/$format",
],
'http_errors' => FALSE,
'cookies' => $this->cookies,
];
$response = $client->post($user_logout_url->toString(), $post_options);
return $response;
}
/**
* Test csrf protection of User Logout route.
*/
public function testLogoutCsrfProtection() {
$client = \Drupal::httpClient();
$login_status_url = $this->getLoginStatusUrlString();
$account = $this->drupalCreateUser();
$name = $account->getUsername();
$pass = $account->passRaw;
$response = $this->loginRequest($name, $pass);
$this->assertEquals(200, $response->getStatusCode());
$result_data = $this->serializer->decode($response->getBody(), 'json');
$logout_token = $result_data['logout_token'];
// Test third party site posting to current site with logout request.
// This should not logout the current user because it lacks the CSRF
// token.
$response = $this->logoutRequest('json');
$this->assertEquals(403, $response->getStatusCode());
// Ensure still logged in.
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
// Try with an incorrect token.
$response = $this->logoutRequest('json', 'not-the-correct-token');
$this->assertEquals(403, $response->getStatusCode());
// Ensure still logged in.
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
// Try a logout request with correct token.
$response = $this->logoutRequest('json', $logout_token);
$this->assertEquals(204, $response->getStatusCode());
// Ensure actually logged out.
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
}
/**
* Gets the URL string for checking login.
*
* @param string $format
* The format to use to make the request.
*
* @return string
* The URL string.
*/
protected function getLoginStatusUrlString($format = 'json') {
$user_login_status_url = Url::fromRoute('user.login_status.http');
$user_login_status_url->setRouteParameter('_format', $format);
$user_login_status_url->setAbsolute();
return $user_login_status_url->toString();
}
}

View file

@ -3,6 +3,7 @@
namespace Drupal\Tests\user\Kernel\Migrate\d6;
use Drupal\file\Entity\File;
use Drupal\Tests\file\Kernel\Migrate\d6\FileMigrationTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
@ -12,6 +13,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateUserPictureFileTest extends MigrateDrupal6TestBase {
use FileMigrationTestTrait;
/**
* {@inheritdoc}
*/
@ -19,13 +22,7 @@ class MigrateUserPictureFileTest extends MigrateDrupal6TestBase {
parent::setUp();
$this->installEntitySchema('file');
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->getMigration('d6_user_picture_file');
$source = $migration->getSourceConfiguration();
$source['site_path'] = 'core/modules/simpletest';
$migration->set('source', $source);
$this->executeMigration($migration);
$this->executeMigration('d6_user_picture_file');
}
/**

View file

@ -3,6 +3,7 @@
namespace Drupal\Tests\user\Kernel\Migrate\d6;
use Drupal\migrate\MigrateExecutable;
use Drupal\Tests\file\Kernel\Migrate\d6\FileMigrationTestTrait;
use Drupal\user\Entity\User;
use Drupal\file\Entity\File;
use Drupal\Core\Database\Database;
@ -16,6 +17,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateUserTest extends MigrateDrupal6TestBase {
use FileMigrationTestTrait;
/**
* {@inheritdoc}
*/

View file

@ -29,7 +29,7 @@ class MigrateUserRoleTest extends MigrateDrupal7TestBase {
* The role ID.
* @param string $label
* The role's expected label.
* @param int|NULL $original_rid
* @param int|null $original_rid
* The original (integer) ID of the role, to check permissions.
*/
protected function assertEntity($id, $label, $original_rid) {

View file

@ -2,6 +2,9 @@
namespace Drupal\Tests\user\Kernel\Migrate\d7;
use Drupal\comment\Entity\CommentType;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
@ -17,7 +20,18 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['file', 'image'];
public static $modules = [
'comment',
'datetime',
'file',
'image',
'link',
'node',
'system',
'taxonomy',
'telephone',
'text',
];
/**
* {@inheritdoc}
@ -27,14 +41,42 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
// Prepare to migrate user pictures as well.
$this->installEntitySchema('file');
$this->createType('page');
$this->createType('article');
$this->createType('blog');
$this->createType('book');
$this->createType('forum');
$this->createType('test_content_type');
Vocabulary::create(['vid' => 'test_vocabulary'])->save();
$this->executeMigrations([
'user_picture_field',
'user_picture_field_instance',
'd7_user_role',
'd7_field',
'd7_field_instance',
'd7_user',
]);
}
/**
* Creates a node type with a corresponding comment type.
*
* @param string $id
* The node type ID.
*/
protected function createType($id) {
NodeType::create([
'type' => $id,
'label' => $this->randomString(),
])->save();
CommentType::create([
'id' => 'comment_node_' . $id,
'label' => $this->randomString(),
'target_entity_type_id' => 'node',
])->save();
}
/**
* Asserts various aspects of a user account.
*
@ -60,8 +102,10 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* Role IDs the user account is expected to have.
* @param bool $has_picture
* Whether the user is expected to have a picture attached.
* @param int $field_integer
* The value of the integer field.
*/
protected function assertEntity($id, $label, $mail, $password, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE) {
protected function assertEntity($id, $label, $mail, $password, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE, $field_integer = NULL) {
/** @var \Drupal\user\UserInterface $user */
$user = User::load($id);
$this->assertTrue($user instanceof UserInterface);
@ -80,6 +124,10 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
$this->assertIdentical($roles, $user->getRoles());
$this->assertIdentical($has_picture, !$user->user_picture->isEmpty());
$this->assertIdentical($password, $user->getPassword());
if (!is_null($field_integer)) {
$this->assertTrue($user->hasField('field_integer'));
$this->assertEquals($field_integer, $user->field_integer->value);
}
}
/**
@ -87,7 +135,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
*/
public function testUser() {
$password = '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ';
$this->assertEntity(2, 'Odo', 'odo@local.host', $password, '0', '0', FALSE, '', 'odo@local.host');
$this->assertEntity(2, 'Odo', 'odo@local.host', $password, '0', '0', FALSE, 'en', 'odo@local.host', [RoleInterface::AUTHENTICATED_ID], FALSE, 99);
// Ensure that the user can authenticate.
$this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password'));

View file

@ -0,0 +1,72 @@
<?php
namespace Drupal\Tests\user\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests the d7_user_role source plugin.
*
* @covers \Drupal\user\Plugin\migrate\source\d7\Role
* @group user
*/
class RoleTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['migrate_drupal', 'user'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$expected = [
[
'rid' => 1,
'name' => 'anonymous user',
'permissions' => [
'access content',
],
],
[
'rid' => 2,
'name' => 'authenticated user',
'permissions' => [
'access comments',
'access content',
'post comments',
'post comments without approval',
],
],
[
'rid' => 3,
'name' => 'administrator',
'permissions' => [
'access comments',
'administer comments',
'post comments',
'post comments without approval',
'access content',
'administer content types',
'administer nodes',
],
],
];
$data = [
[[], $expected],
];
foreach ($expected as $row) {
foreach ($row['permissions'] as $permission) {
$data[0][0]['role_permission'][] = [
'permission' => $permission,
'rid' => $row['rid'],
];
}
unset($row['permissions']);
$data[0][0]['role'][] = $row;
}
return $data;
}
}

View file

@ -135,7 +135,7 @@ class UserAccountFormFieldsTest extends KernelTestBase {
$entity = $this->container->get('entity.manager')
->getStorage($entity_type)
->create($fields);
$form_object = $this->container->get('entity.manager')
$this->container->get('entity.manager')
->getFormObject($entity_type, $operation)
->setEntity($entity);

View file

@ -48,6 +48,7 @@ class UserValidationTest extends KernelTestBase {
'foo@example.com' => array('Valid username', 'assertNull'),
'foo@-example.com' => array('Valid username', 'assertNull'), // invalid domains are allowed in usernames
'þòøÇߪř€' => array('Valid username', 'assertNull'),
'foo+bar' => array('Valid username', 'assertNull'), // '+' symbol is allowed
'ᚠᛇᚻ᛫ᛒᛦᚦ' => array('Valid UTF8 username', 'assertNull'), // runes
' foo' => array('Invalid username that starts with a space', 'assertNotNull'),
'foo ' => array('Invalid username that ends with a space', 'assertNotNull'),

View file

@ -105,25 +105,25 @@ class PermissionHandlerTest extends UnitTestCase {
$url = vfsStream::url('modules');
mkdir($url . '/module_a');
file_put_contents($url . '/module_a/module_a.permissions.yml',
"access_module_a: single_description"
);
file_put_contents($url . '/module_a/module_a.permissions.yml', "access_module_a: single_description");
mkdir($url . '/module_b');
file_put_contents($url . '/module_b/module_b.permissions.yml',
"'access module b':
file_put_contents($url . '/module_b/module_b.permissions.yml', <<<EOF
'access module b':
title: 'Access B'
description: 'bla bla'
'access module a via module b':
title: 'Access A via B'
provider: 'module_a'
");
EOF
);
mkdir($url . '/module_c');
file_put_contents($url . '/module_c/module_c.permissions.yml',
"'access_module_c':
file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
'access_module_c':
title: 'Access C'
description: 'bla bla'
'restrict access': TRUE
");
EOF
);
$modules = array('module_a', 'module_b', 'module_c');
$extensions = array(
'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
@ -187,9 +187,10 @@ class PermissionHandlerTest extends UnitTestCase {
$url = vfsStream::url('modules');
mkdir($url . '/module_a');
file_put_contents($url . '/module_a/module_a.permissions.yml',
"access_module_a2: single_description2
access_module_a1: single_description1"
file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
access_module_a2: single_description2
access_module_a1: single_description1
EOF
);
mkdir($url . '/module_b');
file_put_contents($url . '/module_b/module_b.permissions.yml',
@ -234,21 +235,24 @@ access_module_a1: single_description1"
$url = vfsStream::url('modules');
mkdir($url . '/module_a');
file_put_contents($url . '/module_a/module_a.permissions.yml',
"permission_callbacks:
file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
permission_callbacks:
- 'Drupal\\user\\Tests\\TestPermissionCallbacks::singleDescription'
");
EOF
);
mkdir($url . '/module_b');
file_put_contents($url . '/module_b/module_b.permissions.yml',
"permission_callbacks:
file_put_contents($url . '/module_b/module_b.permissions.yml', <<<EOF
permission_callbacks:
- 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
- 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider'
");
EOF
);
mkdir($url . '/module_c');
file_put_contents($url . '/module_c/module_c.permissions.yml',
"permission_callbacks:
file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
permission_callbacks:
- 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescriptionRestrictAccess'
");
EOF
);
$modules = array('module_a', 'module_b', 'module_c');
$extensions = array(
@ -309,13 +313,14 @@ access_module_a1: single_description1"
$url = vfsStream::url('modules');
mkdir($url . '/module_a');
file_put_contents($url . '/module_a/module_a.permissions.yml',
"'access module a':
file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
'access module a':
title: 'Access A'
description: 'bla bla'
permission_callbacks:
- 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
");
EOF
);
$modules = array('module_a');
$extensions = array(

View file

@ -5,7 +5,7 @@ namespace Drupal\Tests\user\Unit\Plugin\Core\Entity;
use Drupal\Tests\Core\Session\UserSessionTest;
use Drupal\user\RoleInterface;
/**
/**
* @coversDefaultClass \Drupal\user\Entity\User
* @group user
*/

View file

@ -1,73 +0,0 @@
<?php
namespace Drupal\Tests\user\Unit\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests D7 role source plugin.
*
* @group user
*/
class RoleTest extends MigrateSqlSourceTestCase {
const PLUGIN_CLASS = 'Drupal\user\Plugin\migrate\source\d7\Role';
protected $migrationConfiguration = array(
'id' => 'test',
'source' => array(
'plugin' => 'd7_user_role',
),
);
protected $expectedResults = array(
array(
'rid' => 1,
'name' => 'anonymous user',
'permissions' => array(
'access content',
),
),
array(
'rid' => 2,
'name' => 'authenticated user',
'permissions' => array(
'access comments',
'access content',
'post comments',
'post comments without approval',
),
),
array(
'rid' => 3,
'name' => 'administrator',
'permissions' => array(
'access comments',
'administer comments',
'post comments',
'post comments without approval',
'access content',
'administer content types',
'administer nodes',
),
),
);
/**
* {@inheritdoc}
*/
protected function setUp() {
foreach ($this->expectedResults as $row) {
foreach ($row['permissions'] as $permission) {
$this->databaseContents['role_permission'][] = array(
'permission' => $permission,
'rid' => $row['rid'],
);
}
unset($row['permissions']);
$this->databaseContents['role'][] = $row;
}
parent::setUp();
}
}

View file

@ -232,7 +232,8 @@ function user_load($uid, $reset = FALSE) {
* @see \Drupal\user\Entity\User::loadMultiple()
*/
function user_load_by_mail($mail) {
$users = entity_load_multiple_by_properties('user', array('mail' => $mail));
$users = \Drupal::entityTypeManager()->getStorage('user')
->loadByProperties(['mail' => $mail]);
return $users ? reset($users) : FALSE;
}
@ -248,7 +249,8 @@ function user_load_by_mail($mail) {
* @see \Drupal\user\Entity\User::loadMultiple()
*/
function user_load_by_name($name) {
$users = entity_load_multiple_by_properties('user', array('name' => $name));
$users = \Drupal::entityTypeManager()->getStorage('user')
->loadByProperties(['name' => $name]);
return $users ? reset($users) : FALSE;
}
@ -984,7 +986,7 @@ function user_user_role_insert(RoleInterface $role) {
}
$add_id = 'user_add_role_action.' . $role->id();
if (!entity_load('action', $add_id)) {
if (!Action::load($add_id)) {
$action = Action::create(array(
'id' => $add_id,
'type' => 'user',
@ -997,7 +999,7 @@ function user_user_role_insert(RoleInterface $role) {
$action->trustData()->save();
}
$remove_id = 'user_remove_role_action.' . $role->id();
if (!entity_load('action', $remove_id)) {
if (!Action::load($remove_id)) {
$action = Action::create(array(
'id' => $remove_id,
'type' => 'user',
@ -1024,10 +1026,10 @@ function user_user_role_delete(RoleInterface $role) {
return;
}
$actions = entity_load_multiple('action', array(
$actions = Action::loadMultiple([
'user_add_role_action.' . $role->id(),
'user_remove_role_action.' . $role->id(),
));
]);
foreach ($actions as $action) {
$action->delete();
}
@ -1247,7 +1249,7 @@ function user_form_process_password_confirm($element) {
$password_settings['showStrengthIndicator'] = TRUE;
$password_settings += array(
'strengthTitle' => t('Password strength:'),
'hasWeaknesses' => t('To make your password stronger:'),
'hasWeaknesses' => t('Recommendations to make your password stronger:'),
'tooShort' => t('Make it at least 12 characters'),
'addLowerCase' => t('Add lowercase letters'),
'addUpperCase' => t('Add uppercase letters'),
@ -1332,7 +1334,7 @@ function user_toolbar() {
$links_cache_contexts[] = 'user';
}
else {
$links = array(
$links = array(
'login' => array(
'title' => t('Log in'),
'url' => Url::fromRoute('user.page'),

View file

@ -129,6 +129,34 @@ user.login:
options:
_maintenance_access: TRUE
user.login.http:
path: '/user/login'
defaults:
_controller: \Drupal\user\Controller\UserAuthenticationController::login
methods: [POST]
requirements:
_user_is_logged_in: 'FALSE'
_format: 'json'
user.login_status.http:
path: '/user/login_status'
defaults:
_controller: \Drupal\user\Controller\UserAuthenticationController::loginStatus
methods: [GET]
requirements:
_access: 'TRUE'
_format: 'json'
user.logout.http:
path: '/user/logout'
defaults:
_controller: \Drupal\user\Controller\UserAuthenticationController::logout
methods: [POST]
requirements:
_user_is_logged_in: 'TRUE'
_format: 'json'
_csrf_token: 'TRUE'
user.cancel_confirm:
path: '/user/{user}/cancel/confirm/{timestamp}/{hashed_pass}'
defaults: