diff -u b/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
--- b/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -407,8 +407,8 @@
// Make a request to the logout page, and redirect to the user page, the
// idea being if you were properly logged out you should be seeing a login
// screen.
- $this->drupalGetRoute('user.logout', array('destination' => 'user/login'));
$this->assertResponse(200, 'User was logged out.');
+ $this->drupalPostForm('user/logout', [], t('Confirm'), ['query' => ['destination' => 'user/login']]);
$pass = $this->assertField('name', 'Username field found.', 'Logout');
$pass = $pass && $this->assertField('pass', 'Password field found.', 'Logout');
@@ -1300,52 +1300,6 @@
return $out;
}
- /**
- * Retrieves a Drupal path by route.
- *
- * @param $path
- * Drupal path or URL to load into internal browser
- * @param $options
- * Options to be forwarded to the url generator.
- * @param $headers
- * An array containing additional HTTP request headers, each formatted as
- * "name: value".
- *
- * @return
- * The retrieved HTML string, also available as $this->getRawContent()
- */
- protected function drupalGetRoute($route_name, array $parameters = array(), array $options = array(), array $headers = array()) {
- $options['absolute'] = TRUE;
-
- // The URL generator service is not necessarily available yet; e.g., in
- // interactive installer tests.
- if ($this->container->has('url_generator')) {
- $url = $this->container->get('url_generator')->generateFromRoute($route_name, $parameters, $options);
- }
-
- // We re-using a CURL connection here. If that connection still has certain
- // options set, it might change the GET into a POST. Make sure we clear out
- // previous options.
- $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $url, CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers));
- // Ensure that any changes to variables in the other thread are picked up.
- $this->refreshVariables();
-
- // Replace original page output with new output from redirected page(s).
- if ($new = $this->checkForMetaRefresh()) {
- $out = $new;
- }
-
- $verbose = 'GET request to: ' . $url .
- '
Ending URL: ' . $this->getUrl();
- if ($this->dumpHeaders) {
- $verbose .= '
Headers: ' . String::checkPlain(var_export(array_map('trim', $this->headers), TRUE)) . '
';
- }
- $verbose .= '
' . $out;
-
- $this->verbose($verbose);
- return $out;
- }
-
/**
* Retrieves a Drupal path or an absolute path and JSON decodes the result.
*
diff -u b/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
--- b/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -12,7 +12,7 @@
_controller: '\Drupal\user\Controller\UserController::logout'
requirements:
_user_is_logged_in: 'TRUE'
- _csrf_token: 'TRUE'
+ _csrf_token_optional: 'TRUE'
user.admin_index:
path: '/admin/config/people'
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
+++ b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
@@ -32,7 +32,7 @@ function __construct(CsrfTokenGenerator $csrf_token) {
* {@inheritdoc}
*/
public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
- if ($route->hasRequirement('_csrf_token')) {
+ if ($route->hasRequirement('_csrf_token') || $route->hasRequirement('_csrf_token_optional')) {
$path = ltrim($route->getPath(), '/');
// Replace the path parameters with values from the parameters array.
foreach ($parameters as $param => $value) {
only in patch2:
unchanged:
--- a/core/modules/user/src/Controller/UserController.php
+++ b/core/modules/user/src/Controller/UserController.php
@@ -4,6 +4,7 @@
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Xss;
+use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\user\Form\UserPasswordResetForm;
@@ -49,6 +50,13 @@ class UserController extends ControllerBase {
protected $logger;
/**
+ * The srcrf token generator.
+ *
+ * @var \Drupal\Core\Access\CsrfTokenGenerator
+ */
+ protected $csrfToken;
+
+ /**
* Constructs a UserController object.
*
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
@@ -59,12 +67,15 @@ class UserController extends ControllerBase {
* The user data service.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
+ * @param \Drupal\Core\Access\CsrfTokenGenerator $token_generator
+ * The csrf token generator.
*/
- public function __construct(DateFormatterInterface $date_formatter, UserStorageInterface $user_storage, UserDataInterface $user_data, LoggerInterface $logger) {
+ public function __construct(DateFormatterInterface $date_formatter, UserStorageInterface $user_storage, UserDataInterface $user_data, LoggerInterface $logger, CsrfTokenGenerator $token_generator) {
$this->dateFormatter = $date_formatter;
$this->userStorage = $user_storage;
$this->userData = $user_data;
$this->logger = $logger;
+ $this->csrfToken = $token_generator;
}
/**
@@ -75,7 +86,8 @@ public static function create(ContainerInterface $container) {
$container->get('date.formatter'),
$container->get('entity.manager')->getStorage('user'),
$container->get('user.data'),
- $container->get('logger.factory')->get('user')
+ $container->get('logger.factory')->get('user'),
+ $container->get('csrf_token')
);
}
@@ -274,10 +286,25 @@ public function userTitle(UserInterface $user = NULL) {
/**
* Logs the current user out.
*
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object.
+ *
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirection to home page.
*/
- public function logout() {
+ public function logout(Request $request) {
+ $token = $request->query->get('token');
+
+ // Show confirm form when no csrf token is present.
+ if (!$token) {
+ return $this->formBuilder()
+ ->getForm('\Drupal\user\Form\UserLogoutConfirm');
+ }
+ if (!$this->csrfToken->validate($token, 'user/logout')) {
+ drupal_set_message($this->t('Invalid csrf token.'), 'error');
+
+ return $this->redirect('');
+ }
user_logout();
return $this->redirect('');
}
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/user/src/Form/UserLogoutConfirm.php
@@ -0,0 +1,51 @@
+t('Are you sure you want to log out?');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCancelUrl() {
+ return new Url('');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() {
+ return 'user_logout_confirm';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ user_logout();
+
+ $form_state->setRedirect('');
+ }
+
+}
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/user/tests/src/Functional/UserLogoutTest.php
@@ -0,0 +1,52 @@
+install(['bartik']);
+ $theme_config = \Drupal::configFactory()->getEditable('system.theme');
+ $theme_config->set('default', 'bartik');
+ $theme_config->save();
+
+ $this->placeBlock('system_menu_block:account');
+ }
+
+ /**
+ * Tests user logout functionality.
+ */
+ public function testLogout() {
+ $account = $this->createUser([]);
+ $this->drupalLogin($account);
+
+ // Test invalid csrf token.
+ $this->drupalGet('user/logout', ['query' => ['token' => '123']]);
+ $this->assertSession()->pageTextContains('Invalid csrf token.');
+
+ $this->drupalGet('user');
+ $this->getSession()->getPage()->clickLink(t('Log out'));
+ // Make sure user legs logged out.
+ $this->drupalGet('user/login');
+ $this->assertSession()->fieldExists('name');
+ }
+
+}