diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php index 57d43ec..285b7fd 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php @@ -141,6 +141,26 @@ protected function getSiteMaintenanceMessage() { } /** + * Check if the user has cookies enabled. + * + * If the user was redirected to this page on an attempted login but the + * login didn't succeed, warn them about missing cookies. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The event to process. + * + * @see user_login_form_submit() + */ + public function checkUserCookies(GetResponseEvent $event) { + /** @var \Symfony\Component\HttpFoundation\Request $request */ + $request = $event->getRequest(); + if (($request->query->get('state') === 'loggedin') && !$this->account->isAuthenticated()) { + $domain = ini_get('session.cookie_domain') ? ltrim(ini_get('session.cookie_domain'), '.') : $request->server->get('HTTP_HOST'); + $this->drupalSetMessage($this->t('To log in to this site, your browser must accept cookies from the domain %domain.', ['%domain' => $domain]), 'error'); + } + } + + /** * Wraps the drupal_set_message function. */ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) { @@ -152,6 +172,7 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = */ public static function getSubscribedEvents() { $events[KernelEvents::REQUEST][] = array('onKernelRequestMaintenance', 30); + $events[KernelEvents::REQUEST][] = ['checkUserCookies']; $events[KernelEvents::EXCEPTION][] = array('onKernelRequestMaintenance'); return $events; } diff --git a/core/lib/Drupal/Core/Routing/RedirectDestination.php b/core/lib/Drupal/Core/Routing/RedirectDestination.php index 873694a..1945e33 100644 --- a/core/lib/Drupal/Core/Routing/RedirectDestination.php +++ b/core/lib/Drupal/Core/Routing/RedirectDestination.php @@ -64,7 +64,7 @@ public function get() { $this->destination = $query->get('destination'); } else { - $this->destination = $this->urlGenerator->generateFromRoute('', [], ['query' => UrlHelper::buildQuery(UrlHelper::filterQueryParameters($query->all()))]); + $this->destination = $this->urlGenerator->generateFromRoute('', [], ['query' => UrlHelper::filterQueryParameters($query->all())]); } } diff --git a/core/modules/big_pipe/src/Tests/BigPipeTest.php b/core/modules/big_pipe/src/Tests/BigPipeTest.php index a3f773e..51b8db1 100644 --- a/core/modules/big_pipe/src/Tests/BigPipeTest.php +++ b/core/modules/big_pipe/src/Tests/BigPipeTest.php @@ -6,6 +6,7 @@ use Drupal\big_pipe\Render\BigPipe; use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Logger\RfcLogLevel; use Drupal\Core\Url; use Drupal\simpletest\WebTestBase; @@ -86,7 +87,7 @@ public function testNoJsDetection() { $this->drupalLogin($this->rootUser); $this->assertSessionCookieExists(TRUE); $this->assertBigPipeNoJsCookieExists(FALSE); - $this->assertRaw(''); + $this->assertRaw(''); $this->assertNoRaw($no_js_to_js_markup); $this->assertBigPipeNoJsMetaRefreshRedirect(); $this->assertBigPipeNoJsCookieExists(TRUE); diff --git a/core/modules/system/src/Tests/Session/SessionHttpsTest.php b/core/modules/system/src/Tests/Session/SessionHttpsTest.php index 8e25fd2..b8a09e2 100644 --- a/core/modules/system/src/Tests/Session/SessionHttpsTest.php +++ b/core/modules/system/src/Tests/Session/SessionHttpsTest.php @@ -132,7 +132,12 @@ protected function loginHttp(AccountInterface $account) { // Follow the location header. $path = $this->getPathFromLocationHeader(FALSE); - $this->drupalGet($this->httpUrl($path)); + $parsed_path = parse_url($path); + $query = []; + if (isset($parsed_path['query'])) { + parse_str($parsed_path['query'], $query); + } + $this->drupalGet($this->httpUrl($parsed_path['path']), ['query' => $query]); $this->assertResponse(200); } @@ -172,7 +177,12 @@ protected function loginHttps(AccountInterface $account) { // Follow the location header. $path = $this->getPathFromLocationHeader(TRUE); - $this->drupalGet($this->httpsUrl($path)); + $parsed_path = parse_url($path); + $query = []; + if (isset($parsed_path['query'])) { + parse_str($parsed_path['query'], $query); + } + $this->drupalGet($this->httpsUrl($parsed_path['path']), ['query' => $query]); $this->assertResponse(200); } diff --git a/core/modules/user/src/Form/UserLoginForm.php b/core/modules/user/src/Form/UserLoginForm.php index 43b29e5..d9d33d6 100644 --- a/core/modules/user/src/Form/UserLoginForm.php +++ b/core/modules/user/src/Form/UserLoginForm.php @@ -129,11 +129,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) { $account = $this->userStorage->load($form_state->get('uid')); - // A destination was set, probably on an exception controller, + // A destination was set, probably on an exception controller. if (!$this->getRequest()->request->has('destination')) { $form_state->setRedirect( 'entity.user.canonical', - array('user' => $account->id()) + ['user' => $account->id()], + ['query' => ['state' => 'loggedin']] ); } else { diff --git a/core/modules/user/src/Plugin/Block/UserLoginBlock.php b/core/modules/user/src/Plugin/Block/UserLoginBlock.php index 34957c9..502ec07 100644 --- a/core/modules/user/src/Plugin/Block/UserLoginBlock.php +++ b/core/modules/user/src/Plugin/Block/UserLoginBlock.php @@ -2,6 +2,7 @@ namespace Drupal\user\Plugin\Block; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Access\AccessResult; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Routing\RedirectDestinationTrait; @@ -94,7 +95,17 @@ public function build() { unset($form['pass']['#attributes']['aria-describedby']); $form['name']['#size'] = 15; $form['pass']['#size'] = 15; - $form['#action'] = $this->url('', [], ['query' => $this->getDestinationArray(), 'external' => FALSE]); + + // Set the form action so that the user will be redirected to the current + // destination after logging in. Also append a query parameter to the + // destination in so we can check later if the login was successful; see + // user_init(). + $destination = $this->getDestinationArray(); + $options = UrlHelper::parse($destination['destination']); + $options['query']['state'] = 'loggedin'; + $destination['destination'] = $options['path'] . '?' . UrlHelper::buildQuery($options['query']); + $form['#action'] = $this->url('', [], ['query' => $destination, 'external' => FALSE]); + // Build action links. $items = array(); if (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) { diff --git a/core/modules/user/src/Tests/UserBlocksTest.php b/core/modules/user/src/Tests/UserBlocksTest.php index 9216a27..0eaa4e4 100644 --- a/core/modules/user/src/Tests/UserBlocksTest.php +++ b/core/modules/user/src/Tests/UserBlocksTest.php @@ -2,6 +2,7 @@ namespace Drupal\user\Tests; +use Drupal\Core\Url; use Drupal\simpletest\WebTestBase; /** @@ -73,7 +74,8 @@ function testUserLoginBlock() { $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'); + $url = Url::fromRoute('user.admin_permissions', [], ['absolute' => TRUE, 'query' => ['state' => 'loggedin']]); + $this->assertUrl($url, [], 'Still on the same page after login for access denied page'); // Now, log out and repeat with a non-403 page. $this->drupalLogout(); diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 90b813f..4801bc3 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -547,6 +547,19 @@ function user_login_finalize(UserInterface $account) { } /** + * Submit handler for the login form. + * + * Load current_user and perform standard login tasks. The user is then + * redirected to the My Account page. Setting the destination in the query + * string overrides the redirect. + */ +function user_login_form_submit($form, &$form_state) { + $account = User::load($form_state['uid']); + $form_state['redirect'] = ['user/' . $account->id(), ['query' => ['state' => 'loggedin']]]; + user_login_finalize($account); +} + +/** * Implements hook_user_login(). */ function user_user_login($account) { diff --git a/core/tests/Drupal/Tests/Core/Routing/RedirectDestinationTest.php b/core/tests/Drupal/Tests/Core/Routing/RedirectDestinationTest.php index fd99e99..2d733c3 100644 --- a/core/tests/Drupal/Tests/Core/Routing/RedirectDestinationTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RedirectDestinationTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\Core\Routing; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Routing\RedirectDestination; use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; @@ -51,7 +52,7 @@ protected function setupUrlGenerator() { ->willReturnCallback(function($route, $parameters, $options) { $query_string = ''; if (!empty($options['query'])) { - $query_string = '?' . $options['query']; + $query_string = '?' . UrlHelper::buildQuery($options['query']); } return '/current-path' . $query_string;