diff --git a/core/core.services.yml b/core/core.services.yml index 72f2c5a..8db42eb 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1209,8 +1209,6 @@ services: arguments: ['@private_key', '@cache.default'] current_user: class: Drupal\Core\Session\AccountProxy - tags: - - { name: needs_destruction } session_configuration: class: Drupal\Core\Session\SessionConfiguration arguments: ['%session.storage.options%'] diff --git a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php index 187b3a9..9875a85 100644 --- a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php +++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php @@ -10,9 +10,9 @@ use Drupal\Component\Utility\Crypt; use Drupal\Core\Authentication\AuthenticationProviderInterface; use Drupal\Core\Database\Connection; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AnonymousUserSession; use Drupal\Core\Session\UserSession; -use Drupal\Core\Site\Settings; use Drupal\Core\Session\SessionConfigurationInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -62,60 +62,45 @@ public function applies(Request $request) { public function authenticate(Request $request) { $session = $request->getSession(); if ($session->start()) { - $values = getSessionRecord($session); - - // We found the client's session record and they are an authenticated, - // active user. - if ($values && $values['uid'] > 0 && $values['status'] == 1) { - // Add roles element to $user. - $rids = getUserRoles($values['uid']); - $values['roles'] = array_merge(array(DRUPAL_AUTHENTICATED_RID), $rids); - $user = new UserSession($values); - } - elseif ($values) { - // The user is anonymous or blocked. Only preserve two fields from the - // {sessions} table. - $user = new UserSession(array( - 'session' => $values['session'], - 'access' => $values['access'], - )); - } - else { - // The session has expired. - $user = new AnonymousUserSession(); - } - return $user; + return $this->getUserFromSession($session); } return NULL; } /** - * Returns the user's session record. + * Returns the user entity for the session. * - * @param Symfony\Component\HttpFoundation\Session\SessionInterface $session + * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session * The session. * - * @return array|NULL - * The user's session record + * @return \Drupal\Core\Session\AccountInterface + * The current user object. */ - protected function getSessionRecord(SessionInterface $session) { - return $this->connection->query("SELECT u.*, s.* FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.default_langcode = 1 AND s.sid = :sid", array( + protected function getUserFromSession(SessionInterface $session) { + $values = $this->connection->query("SELECT u.*, s.* FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.default_langcode = 1 AND s.sid = :sid", [ ':sid' => Crypt::hashBase64($session->getId()), - ))->fetchAssoc(); - } + ])->fetchAssoc(); - /** - * Returns the user's roles. - * - * @param string $uid - * The user's id. - * - * @return array|NULL - * The user's roles - */ - protected function getUserRoles($uid) { - return $this->connection->query("SELECT ur.roles_target_id as rid FROM {user__roles} ur WHERE ur.entity_id = :uid", array( - ':uid' => $uid, - ))->fetchCol(); + if ($values && $values['uid'] > 0 && $values['status'] == 1) { + // We found the client's session record and they are an authenticated, + // active user. + $rids = $this->connection->query("SELECT ur.roles_target_id as rid FROM {user__roles} ur WHERE ur.entity_id = :uid", [ + ':uid' => $values['uid'], + ])->fetchCol(); + // Add user's roles. + $values['roles'] = array_merge([AccountInterface::AUTHENTICATED_ROLE], $rids); + return new UserSession($values); + } + elseif ($values) { + // The user is anonymous or blocked. Only preserve two fields from the + // {sessions} table. + return new UserSession([ + 'session' => $values['session'], + 'access' => $values['access'], + ]); + } + // The session has expired. + return new AnonymousUserSession(); } + } diff --git a/core/lib/Drupal/Core/Session/AccountProxy.php b/core/lib/Drupal/Core/Session/AccountProxy.php index bc36c46..eae71e7 100644 --- a/core/lib/Drupal/Core/Session/AccountProxy.php +++ b/core/lib/Drupal/Core/Session/AccountProxy.php @@ -7,9 +7,6 @@ namespace Drupal\Core\Session; -use Drupal\Core\DestructableInterface; -use Drupal\Core\Site\Settings; - /** * A proxied implementation of AccountInterface. * @@ -21,7 +18,7 @@ * allows legacy code to change the current user where the user cannot be * directly injected into dependent code. */ -class AccountProxy implements AccountProxyInterface, DestructableInterface { +class AccountProxy implements AccountProxyInterface { /** * The instantiated account. @@ -186,18 +183,6 @@ public function setInitialAccountId($account_id) { } /** - * {@inheritdoc} - */ - public function destruct() { - // Update last access time (no more than once per 180 seconds). - if ($this->account->isAuthenticated() && REQUEST_TIME - $this->account->getLastAccessedTime() > Settings::get('session_write_interval', 180)) { - /** @var \Drupal\user\UserStorageInterface $storage */ - $storage = \Drupal::entityManager()->getStorage('user'); - $storage->updateLastAccessTimestamp($this->account, REQUEST_TIME); - } - } - - /** * Load a user entity. * * The entity manager requires additional initialization code and cache diff --git a/core/modules/user/src/EventSubscriber/UserRequestSubscriber.php b/core/modules/user/src/EventSubscriber/UserRequestSubscriber.php new file mode 100644 index 0000000..87b63f4 --- /dev/null +++ b/core/modules/user/src/EventSubscriber/UserRequestSubscriber.php @@ -0,0 +1,72 @@ +account = $account; + $this->entityManager = $entity_manager; + } + + /** + * Updates the current user's last access time. + * + * @param \Symfony\Component\HttpKernel\Event\FinishRequestEvent $event + * The event to process. + */ + public function onKernelRequestFinish(FinishRequestEvent $event) { + if ($this->account->isAuthenticated() && REQUEST_TIME - $this->account->getLastAccessedTime() > Settings::get('session_write_interval', 180)) { + // Do that no more than once per 180 seconds. + /** @var \Drupal\user\UserStorageInterface $storage */ + $storage = $this->entityManager->getStorage('user'); + $storage->updateLastAccessTimestamp($this->account, REQUEST_TIME); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[KernelEvents::FINISH_REQUEST][] = ['onKernelRequestFinish', 0]; + return $events; + } + +} diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml index 792eec0..d725fa3 100644 --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -35,6 +35,11 @@ services: arguments: ['@current_user', '@url_generator'] tags: - { name: event_subscriber } + user_last_access_subscriber: + class: Drupal\user\EventSubscriber\UserRequestSubscriber + arguments: ['@current_user', '@entity.manager'] + tags: + - { name: event_subscriber } theme.negotiator.admin_theme: class: Drupal\user\Theme\AdminNegotiator arguments: ['@current_user', '@config.factory', '@entity.manager', '@router.admin_context']