sessionManager = $session_manager; $this->requestStack = $request_stack; $this->connection = $connection; } /** * {@inheritdoc} */ public function open($save_path, $name) { return TRUE; } /** * {@inheritdoc} */ public function read($sid) { global $user; // Handle the case of first time visitors and clients that don't store // cookies (eg. web crawlers). $insecure_session_name = substr(session_name(), 1); $cookies = $this->requestStack->getCurrentRequest()->cookies; if (!$cookies->has(session_name()) && !$cookies->has($insecure_session_name)) { $user = new UserSession(); return ''; } // Otherwise, if the session is still active, we have a record of the // client's session in the database. If it's HTTPS then we are either have a // HTTPS session or we are about to log in so we check the sessions table // for an anonymous session with the non-HTTPS-only cookie. The session ID // that is in the user's cookie is hashed before being stored in the // database as a security measure. Thus, we have to hash it to match the // database. if ($this->requestStack->getCurrentRequest()->isSecure()) { // Try to load a session using the HTTPS-only secure session id. $values = $this->connection->query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.ssid = :ssid", array( ':ssid' => Crypt::hashBase64($sid), ))->fetchAssoc(); if (!$values) { // Fallback and try to load the anonymous non-HTTPS session. Use the // non-HTTPS session id as the key. if ($cookies->has($insecure_session_name)) { $values = $this->connection->query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid AND s.uid = 0", array( ':sid' => Crypt::hashBase64($cookies->get($insecure_session_name)), ))->fetchAssoc(); } } } else { // Try to load a session using the non-HTTPS session id. $values = $this->connection->query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array( ':sid' => Crypt::hashBase64($sid), ))->fetchAssoc(); } // 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 = $this->connection->query("SELECT ur.rid FROM {users_roles} ur WHERE ur.uid = :uid", array( ':uid' => $values['uid'], ))->fetchCol(); $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 UserSession(); } // Store the session that was read for comparison in self::write(). $this->lastRead = array( 'sid' => $sid, 'value' => $user->session, ); return $user->session; } /** * {@inheritdoc} */ public function write($sid, $value) { global $user; // The exception handler is not active at this point, so we need to do it // manually. try { if (!$this->sessionManager->isEnabled()) { // We don't have anything to do if we are not allowed to save the // session. return TRUE; } // Check whether $_SESSION has been changed in this request. $is_changed = empty($this->lastRead) || $this->lastRead['sid'] != $sid || $this->lastRead['value'] !== $value; // For performance reasons, do not update the sessions table, unless // $_SESSION has changed or more than 180 has passed since the last // update. $needs_update = !$user->getLastAccessedTime() || REQUEST_TIME - $user->getLastAccessedTime() > Settings::get('session_write_interval', 180); if ($is_changed || $needs_update) { // Either ssid or sid or both will be added from $key below. $fields = array( 'uid' => $user->id(), 'hostname' => $this->requestStack->getCurrentRequest()->getClientIP(), 'session' => $value, 'timestamp' => REQUEST_TIME, ); // Use the session ID as 'sid' and an empty string as 'ssid' by default. // read() does not allow empty strings so that's a safe default. $key = array('sid' => Crypt::hashBase64($sid), 'ssid' => ''); // On HTTPS connections, use the session ID as both 'sid' and 'ssid'. if ($this->requestStack->getCurrentRequest()->isSecure()) { $key['ssid'] = Crypt::hashBase64($sid); $cookies = $this->requestStack->getCurrentRequest()->cookies; // The "secure pages" setting allows a site to simultaneously use both // secure and insecure session cookies. If enabled and both cookies // are presented then use both keys. The session ID from the cookie is // hashed before being stored in the database as a security measure. if (Settings::get('mixed_mode_sessions', FALSE)) { $insecure_session_name = substr(session_name(), 1); if ($cookies->has($insecure_session_name)) { $key['sid'] = Crypt::hashBase64($cookies->get($insecure_session_name)); } } } elseif (Settings::get('mixed_mode_sessions', FALSE)) { unset($key['ssid']); } $this->connection->merge('sessions') ->keys($key) ->fields($fields) ->execute(); } // Likewise, do not update access time more than once per 180 seconds. if ($user->isAuthenticated() && REQUEST_TIME - $user->getLastAccessedTime() > Settings::get('session_write_interval', 180)) { $this->connection->update('users') ->fields(array( 'access' => REQUEST_TIME, )) ->condition('uid', $user->id()) ->execute(); } return TRUE; } catch (\Exception $exception) { require_once DRUPAL_ROOT . '/core/includes/errors.inc'; // If we are displaying errors, then do so with no possibility of a // further uncaught exception being thrown. if (error_displayable()) { print '
' . Error::renderExceptionSafe($exception) . '