Commit 37af5099 authored by catch's avatar catch

Issue #2238087 by znerol, YesCT: Rebase SessionManager onto Symfony NativeSessionStorage.

parent fd0dcf94
......@@ -767,9 +767,12 @@ services:
arguments: ['@authentication', '@request']
session_manager:
class: Drupal\Core\Session\SessionManager
arguments: ['@request_stack', '@database']
arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@settings']
tags:
- { name: persist }
session_manager.metadata_bag:
class: Drupal\Core\Session\MetadataBag
arguments: ['@settings']
asset.css.collection_renderer:
class: Drupal\Core\Asset\CssCollectionRenderer
arguments: [ '@state' ]
......
<?php
/**
* @file
* Contains \Drupal\Core\Session\MetadataBag.
*/
namespace Drupal\Core\Session;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag as SymfonyMetadataBag;
/**
* Provides a container for application specific session metadata.
*/
class MetadataBag extends SymfonyMetadataBag {
/**
* Constructs a new metadata bag instance.
*
* @param \Drupal\Core\Site\Settings $settings
* The settings instance.
*/
public function __construct(Settings $settings) {
$update_threshold = $settings->get('session_write_interval', 180);
parent::__construct('_sf2_meta', $update_threshold);
}
}
......@@ -12,11 +12,12 @@
use Drupal\Core\Site\Settings;
use Drupal\Core\Utility\Error;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
/**
* Default session handler.
*/
class SessionHandler implements \SessionHandlerInterface {
class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
/**
* The session manager.
......@@ -39,13 +40,6 @@ class SessionHandler implements \SessionHandlerInterface {
*/
protected $connection;
/**
* An array containing the sid and data from last read.
*
* @var array
*/
protected $lastRead;
/**
* Constructs a new SessionHandler instance.
*
......@@ -77,9 +71,9 @@ public function read($sid) {
// Handle the case of first time visitors and clients that don't store
// cookies (eg. web crawlers).
$insecure_session_name = substr(session_name(), 1);
$insecure_session_name = $this->sessionManager->getInsecureName();
$cookies = $this->requestStack->getCurrentRequest()->cookies;
if (!$cookies->has(session_name()) && !$cookies->has($insecure_session_name)) {
if (!$cookies->has($this->getName()) && !$cookies->has($insecure_session_name)) {
$user = new UserSession();
return '';
}
......@@ -136,11 +130,6 @@ public function read($sid) {
$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;
}
......@@ -158,48 +147,40 @@ public function write($sid, $value) {
// 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));
}
// 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 ($this->sessionManager->isMixedMode()) {
$insecure_session_name = $this->sessionManager->getInsecureName();
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();
}
elseif ($this->sessionManager->isMixedMode()) {
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')
......@@ -252,12 +233,12 @@ public function destroy($sid) {
$user = new AnonymousUserSession();
// Unset the session cookies.
$this->deleteCookie(session_name());
$this->deleteCookie($this->getName());
if ($is_https) {
$this->deleteCookie(substr(session_name(), 1), FALSE);
$this->deleteCookie($this->sessionManager->getInsecureName(), FALSE);
}
elseif (Settings::get('mixed_mode_sessions', FALSE)) {
$this->deleteCookie('S' . session_name(), TRUE);
elseif ($this->sessionManager->isMixedMode()) {
$this->deleteCookie('S' . $this->getName(), TRUE);
}
return TRUE;
}
......
......@@ -7,10 +7,12 @@
namespace Drupal\Core\Session;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
/**
* Defines the session manager interface.
*/
interface SessionManagerInterface {
interface SessionManagerInterface extends SessionStorageInterface {
/**
* Initializes the session handler, starting a session if needed.
......@@ -19,28 +21,6 @@ interface SessionManagerInterface {
*/
public function initialize();
/**
* Starts a session forcefully, preserving already set session data.
*/
public function start();
/**
* Commits the current session, if necessary.
*
* If an anonymous user already have an empty session, destroy it.
*/
public function save();
/**
* Returns whether a session has been started.
*/
public function isStarted();
/**
* Called when an anonymous user becomes authenticated or vice-versa.
*/
public function regenerate();
/**
* Ends a specific user's session(s).
*
......@@ -77,4 +57,28 @@ public function disable();
*/
public function enable();
/**
* Returns whether mixed mode SSL sessions are enabled in the session manager.
*
* @return bool
* Value of the mixed mode SSL sessions flag.
*/
public function isMixedMode();
/**
* Enables or disables mixed mode SSL sessions in the session manager.
*
* @param bool $mixed_mode
* New value for the mixed mode SSL sessions flag.
*/
public function setMixedMode($mixed_mode);
/**
* Returns the name of the insecure session when operating in mixed mode SSL.
*
* @return string
* The name of the insecure session.
*/
public function getInsecureName();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment