Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
D
drupal
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Custom Issue Tracker
Custom Issue Tracker
Labels
Merge Requests
225
Merge Requests
225
Requirements
Requirements
List
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Analytics
Analytics
Code Review
Insights
Issue
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
project
drupal
Commits
37af5099
Commit
37af5099
authored
May 09, 2014
by
catch
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue
#2238087
by znerol, YesCT: Rebase SessionManager onto Symfony NativeSessionStorage.
parent
fd0dcf94
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
228 additions
and
125 deletions
+228
-125
core/core.services.yml
core/core.services.yml
+4
-1
core/lib/Drupal/Core/Session/MetadataBag.php
core/lib/Drupal/Core/Session/MetadataBag.php
+29
-0
core/lib/Drupal/Core/Session/SessionHandler.php
core/lib/Drupal/Core/Session/SessionHandler.php
+38
-57
core/lib/Drupal/Core/Session/SessionManager.php
core/lib/Drupal/Core/Session/SessionManager.php
+130
-44
core/lib/Drupal/Core/Session/SessionManagerInterface.php
core/lib/Drupal/Core/Session/SessionManagerInterface.php
+27
-23
No files found.
core/core.services.yml
View file @
37af5099
...
@@ -767,9 +767,12 @@ services:
...
@@ -767,9 +767,12 @@ services:
arguments
:
[
'
@authentication'
,
'
@request'
]
arguments
:
[
'
@authentication'
,
'
@request'
]
session_manager
:
session_manager
:
class
:
Drupal\Core\Session\SessionManager
class
:
Drupal\Core\Session\SessionManager
arguments
:
[
'
@request_stack'
,
'
@database'
]
arguments
:
[
'
@request_stack'
,
'
@database'
,
'
@session_manager.metadata_bag'
,
'
@settings'
]
tags
:
tags
:
-
{
name
:
persist
}
-
{
name
:
persist
}
session_manager.metadata_bag
:
class
:
Drupal\Core\Session\MetadataBag
arguments
:
[
'
@settings'
]
asset.css.collection_renderer
:
asset.css.collection_renderer
:
class
:
Drupal\Core\Asset\CssCollectionRenderer
class
:
Drupal\Core\Asset\CssCollectionRenderer
arguments
:
[
'
@state'
]
arguments
:
[
'
@state'
]
...
...
core/lib/Drupal/Core/Session/MetadataBag.php
0 → 100644
View file @
37af5099
<?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
);
}
}
core/lib/Drupal/Core/Session/SessionHandler.php
View file @
37af5099
...
@@ -12,11 +12,12 @@
...
@@ -12,11 +12,12 @@
use
Drupal\Core\Site\Settings
;
use
Drupal\Core\Site\Settings
;
use
Drupal\Core\Utility\Error
;
use
Drupal\Core\Utility\Error
;
use
Symfony\Component\HttpFoundation\RequestStack
;
use
Symfony\Component\HttpFoundation\RequestStack
;
use
Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy
;
/**
/**
* Default session handler.
* Default session handler.
*/
*/
class
SessionHandler
implements
\
SessionHandlerInterface
{
class
SessionHandler
extends
AbstractProxy
implements
\
SessionHandlerInterface
{
/**
/**
* The session manager.
* The session manager.
...
@@ -39,13 +40,6 @@ class SessionHandler implements \SessionHandlerInterface {
...
@@ -39,13 +40,6 @@ class SessionHandler implements \SessionHandlerInterface {
*/
*/
protected
$connection
;
protected
$connection
;
/**
* An array containing the sid and data from last read.
*
* @var array
*/
protected
$lastRead
;
/**
/**
* Constructs a new SessionHandler instance.
* Constructs a new SessionHandler instance.
*
*
...
@@ -77,9 +71,9 @@ public function read($sid) {
...
@@ -77,9 +71,9 @@ public function read($sid) {
// Handle the case of first time visitors and clients that don't store
// Handle the case of first time visitors and clients that don't store
// cookies (eg. web crawlers).
// cookies (eg. web crawlers).
$insecure_session_name
=
substr
(
session_name
(),
1
);
$insecure_session_name
=
$this
->
sessionManager
->
getInsecureName
(
);
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
if
(
!
$cookies
->
has
(
session_n
ame
())
&&
!
$cookies
->
has
(
$insecure_session_name
))
{
if
(
!
$cookies
->
has
(
$this
->
getN
ame
())
&&
!
$cookies
->
has
(
$insecure_session_name
))
{
$user
=
new
UserSession
();
$user
=
new
UserSession
();
return
''
;
return
''
;
}
}
...
@@ -136,11 +130,6 @@ public function read($sid) {
...
@@ -136,11 +130,6 @@ public function read($sid) {
$user
=
new
UserSession
();
$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
;
return
$user
->
session
;
}
}
...
@@ -158,48 +147,40 @@ public function write($sid, $value) {
...
@@ -158,48 +147,40 @@ public function write($sid, $value) {
// session.
// session.
return
TRUE
;
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.
// Either ssid or sid or both will be added from $key below.
$fields
=
array
(
$fields
=
array
(
'uid'
=>
$user
->
id
(),
'uid'
=>
$user
->
id
(),
'hostname'
=>
$this
->
requestStack
->
getCurrentRequest
()
->
getClientIP
(),
'hostname'
=>
$this
->
requestStack
->
getCurrentRequest
()
->
getClientIP
(),
'session'
=>
$value
,
'session'
=>
$value
,
'timestamp'
=>
REQUEST_TIME
,
'timestamp'
=>
REQUEST_TIME
,
);
);
// Use the session ID as 'sid' and an empty string as 'ssid' by default.
// 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.
// read() does not allow empty strings so that's a safe default.
$key
=
array
(
'sid'
=>
Crypt
::
hashBase64
(
$sid
),
'ssid'
=>
''
);
$key
=
array
(
'sid'
=>
Crypt
::
hashBase64
(
$sid
),
'ssid'
=>
''
);
// On HTTPS connections, use the session ID as both 'sid' and 'ssid'.
// On HTTPS connections, use the session ID as both 'sid' and 'ssid'.
if
(
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
())
{
if
(
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
())
{
$key
[
'ssid'
]
=
Crypt
::
hashBase64
(
$sid
);
$key
[
'ssid'
]
=
Crypt
::
hashBase64
(
$sid
);
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
// The "secure pages" setting allows a site to simultaneously use both
// The "secure pages" setting allows a site to simultaneously use both
// secure and insecure session cookies. If enabled and both cookies
// secure and insecure session cookies. If enabled and both cookies
// are presented then use both keys. The session ID from the cookie is
// are presented then use both keys. The session ID from the cookie is
// hashed before being stored in the database as a security measure.
// hashed before being stored in the database as a security measure.
if
(
$this
->
sessionManager
->
isMixedMode
())
{
if
(
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
))
{
$insecure_session_name
=
$this
->
sessionManager
->
getInsecureName
();
$insecure_session_name
=
substr
(
session_name
(),
1
);
if
(
$cookies
->
has
(
$insecure_session_name
))
{
if
(
$cookies
->
has
(
$insecure_session_name
))
{
$key
[
'sid'
]
=
Crypt
::
hashBase64
(
$cookies
->
get
(
$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.
// 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
))
{
if
(
$user
->
isAuthenticated
()
&&
REQUEST_TIME
-
$user
->
getLastAccessedTime
()
>
Settings
::
get
(
'session_write_interval'
,
180
))
{
$this
->
connection
->
update
(
'users'
)
$this
->
connection
->
update
(
'users'
)
...
@@ -252,12 +233,12 @@ public function destroy($sid) {
...
@@ -252,12 +233,12 @@ public function destroy($sid) {
$user
=
new
AnonymousUserSession
();
$user
=
new
AnonymousUserSession
();
// Unset the session cookies.
// Unset the session cookies.
$this
->
deleteCookie
(
session_n
ame
());
$this
->
deleteCookie
(
$this
->
getN
ame
());
if
(
$is_https
)
{
if
(
$is_https
)
{
$this
->
deleteCookie
(
substr
(
session_name
(),
1
),
FALSE
);
$this
->
deleteCookie
(
$this
->
sessionManager
->
getInsecureName
(
),
FALSE
);
}
}
elseif
(
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
))
{
elseif
(
$this
->
sessionManager
->
isMixedMode
(
))
{
$this
->
deleteCookie
(
'S'
.
session_n
ame
(),
TRUE
);
$this
->
deleteCookie
(
'S'
.
$this
->
getN
ame
(),
TRUE
);
}
}
return
TRUE
;
return
TRUE
;
}
}
...
...
core/lib/Drupal/Core/Session/SessionManager.php
View file @
37af5099
...
@@ -13,11 +13,36 @@
...
@@ -13,11 +13,36 @@
use
Drupal\Core\Session\SessionHandler
;
use
Drupal\Core\Session\SessionHandler
;
use
Drupal\Core\Site\Settings
;
use
Drupal\Core\Site\Settings
;
use
Symfony\Component\HttpFoundation\RequestStack
;
use
Symfony\Component\HttpFoundation\RequestStack
;
use
Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler
;
use
Symfony\Component\HttpFoundation\Session\Storage\MetadataBag
as
SymfonyMetadataBag
;
use
Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage
;
/**
/**
* Manages user sessions.
* Manages user sessions.
*
* This class implements the custom session management code inherited from
* Drupal 7 on top of the corresponding Symfony component. Regrettably the name
* NativeSessionStorage is not quite accurate. In fact the responsibility for
* storing and retrieving session data has been extracted from it in Symfony 2.1
* but the class name was not changed.
*
* @todo
* In fact the NativeSessionStorage class already implements all of the
* functionality required by a typical Symfony application. Normally it is not
* necessary to subclass it at all. In order to reach the point where Drupal
* can use the Symfony session management unmodified, the code implemented
* here needs to be extracted either into a dedicated session handler proxy
* (e.g. mixed mode SSL, sid-hashing) or relocated to the authentication
* subsystem.
*/
*/
class
SessionManager
implements
SessionManagerInterface
{
class
SessionManager
extends
NativeSessionStorage
implements
SessionManagerInterface
{
/**
* Whether or not the session manager is operating in mixed mode SSL.
*
* @var bool
*/
protected
$mixedMode
;
/**
/**
* The request stack.
* The request stack.
...
@@ -54,14 +79,30 @@ class SessionManager implements SessionManagerInterface {
...
@@ -54,14 +79,30 @@ class SessionManager implements SessionManagerInterface {
/**
/**
* Constructs a new session manager instance.
* Constructs a new session manager instance.
*
*
* @param \Symfony\Component\HttpFoundation\RequestStack $request
* @param \Symfony\Component\HttpFoundation\RequestStack $request
_stack
* The request stack.
* The request stack.
* @param \Drupal\Core\Database\Connection $connection
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
* The database connection.
* @param \Symfony\Component\HttpFoundation\Session\Storage\MetadataBag $metadata_bag
* The session metadata bag.
* @param \Drupal\Core\Site\Settings $settings
* The settings instance.
*/
*/
public
function
__construct
(
RequestStack
$request_stack
,
Connection
$connection
)
{
public
function
__construct
(
RequestStack
$request_stack
,
Connection
$connection
,
SymfonyMetadataBag
$metadata_bag
,
Settings
$settings
)
{
parent
::
__construct
();
$this
->
requestStack
=
$request_stack
;
$this
->
requestStack
=
$request_stack
;
$this
->
connection
=
$connection
;
$this
->
connection
=
$connection
;
$this
->
setMetadataBag
(
$metadata_bag
);
$this
->
setMixedMode
(
$settings
->
get
(
'mixed_mode_sessions'
,
FALSE
));
// @todo When not using the Symfony Session object, the list of bags in the
// NativeSessionStorage will remain uninitialized. This will lead to
// errors in NativeSessionHandler::loadSession. Remove this after
// https://drupal.org/node/2229145, when we will be using the Symfony
// session object (which registers an attribute bag with the
// manager upon instantiation).
$this
->
bags
=
array
();
}
}
/**
/**
...
@@ -72,12 +113,14 @@ public function initialize() {
...
@@ -72,12 +113,14 @@ public function initialize() {
// Register the default session handler.
// Register the default session handler.
// @todo Extract session storage from session handler into a service.
// @todo Extract session storage from session handler into a service.
$handler
=
new
SessionHandler
(
$this
,
$this
->
requestStack
,
$this
->
connection
);
$save_handler
=
new
SessionHandler
(
$this
,
$this
->
requestStack
,
$this
->
connection
);
session_set_save_handler
(
$handler
,
TRUE
);
$write_check_handler
=
new
WriteCheckSessionHandler
(
$save_handler
);
$this
->
setSaveHandler
(
$write_check_handler
);
$is_https
=
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
();
$is_https
=
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
();
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
if
((
$cookies
->
has
(
session_name
())
&&
(
$session_name
=
$cookies
->
get
(
session_name
())))
||
(
$is_https
&&
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
)
&&
(
$cookies
->
has
(
substr
(
session_name
(),
1
)))
&&
(
$session_name
=
$cookies
->
get
(
substr
(
session_name
(),
1
)))))
{
$insecure_session_name
=
$this
->
getInsecureName
();
if
((
$cookies
->
has
(
$this
->
getName
())
&&
(
$session_name
=
$cookies
->
get
(
$this
->
getName
())))
||
(
$is_https
&&
$this
->
isMixedMode
()
&&
(
$cookies
->
has
(
$insecure_session_name
)
&&
(
$session_name
=
$cookies
->
get
(
$insecure_session_name
)))))
{
// If a session cookie exists, initialize the session. Otherwise the
// If a session cookie exists, initialize the session. Otherwise the
// session is only started on demand in save(), making
// session is only started on demand in save(), making
// anonymous users not use a session cookie unless something is stored in
// anonymous users not use a session cookie unless something is stored in
...
@@ -94,12 +137,8 @@ public function initialize() {
...
@@ -94,12 +137,8 @@ public function initialize() {
// session ID in advance.
// session ID in advance.
$this
->
lazySession
=
TRUE
;
$this
->
lazySession
=
TRUE
;
$user
=
new
AnonymousUserSession
();
$user
=
new
AnonymousUserSession
();
// Less random sessions (which are much faster to generate) are used for
$this
->
setId
(
Crypt
::
randomBytesBase64
());
// anonymous users than are generated in regenerate() when
if
(
$is_https
&&
$this
->
isMixedMode
())
{
// a user becomes authenticated.
session_id
(
Crypt
::
randomBytesBase64
());
if
(
$is_https
&&
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
))
{
$insecure_session_name
=
substr
(
session_name
(),
1
);
$session_id
=
Crypt
::
randomBytesBase64
();
$session_id
=
Crypt
::
randomBytesBase64
();
$cookies
->
set
(
$insecure_session_name
,
$session_id
);
$cookies
->
set
(
$insecure_session_name
,
$session_id
);
}
}
...
@@ -113,18 +152,20 @@ public function initialize() {
...
@@ -113,18 +152,20 @@ public function initialize() {
* {@inheritdoc}
* {@inheritdoc}
*/
*/
public
function
start
()
{
public
function
start
()
{
if
(
$this
->
isCli
()
||
$this
->
isStarted
())
{
if
(
!
$this
->
isEnabled
()
||
$this
->
isCli
())
{
return
;
return
;
}
}
// Save current session data before starting it, as PHP will destroy it.
// Save current session data before starting it, as PHP will destroy it.
$session_data
=
isset
(
$_SESSION
)
?
$_SESSION
:
NULL
;
$session_data
=
isset
(
$_SESSION
)
?
$_SESSION
:
NULL
;
session_
start
();
$result
=
parent
::
start
();
// Restore session data.
// Restore session data.
if
(
!
empty
(
$session_data
))
{
if
(
!
empty
(
$session_data
))
{
$_SESSION
+=
$session_data
;
$_SESSION
+=
$session_data
;
}
}
return
$result
;
}
}
/**
/**
...
@@ -141,7 +182,7 @@ public function save() {
...
@@ -141,7 +182,7 @@ public function save() {
if
(
$user
->
isAnonymous
()
&&
$this
->
isSessionObsolete
())
{
if
(
$user
->
isAnonymous
()
&&
$this
->
isSessionObsolete
())
{
// There is no session data to store, destroy the session if it was
// There is no session data to store, destroy the session if it was
// previously started.
// previously started.
if
(
$this
->
isStarted
())
{
if
(
$this
->
getSaveHandler
()
->
isActive
())
{
session_destroy
();
session_destroy
();
}
}
}
}
...
@@ -150,8 +191,8 @@ public function save() {
...
@@ -150,8 +191,8 @@ public function save() {
// started.
// started.
if
(
!
$this
->
isStarted
())
{
if
(
!
$this
->
isStarted
())
{
$this
->
start
();
$this
->
start
();
if
(
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
()
&&
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
))
{
if
(
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
()
&&
$this
->
isMixedMode
(
))
{
$insecure_session_name
=
substr
(
session_name
(),
1
);
$insecure_session_name
=
$this
->
getInsecureName
(
);
$params
=
session_get_cookie_params
();
$params
=
session_get_cookie_params
();
$expire
=
$params
[
'lifetime'
]
?
REQUEST_TIME
+
$params
[
'lifetime'
]
:
0
;
$expire
=
$params
[
'lifetime'
]
?
REQUEST_TIME
+
$params
[
'lifetime'
]
:
0
;
$cookie_params
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
$cookie_params
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
...
@@ -159,21 +200,14 @@ public function save() {
...
@@ -159,21 +200,14 @@ public function save() {
}
}
}
}
// Write the session data.
// Write the session data.
session_write_clos
e
();
parent
::
sav
e
();
}
}
}
}
/**
/**
* {@inheritdoc}
* {@inheritdoc}
*/
*/
public
function
isStarted
()
{
public
function
regenerate
(
$destroy
=
FALSE
,
$lifetime
=
NULL
)
{
return
session_status
()
===
\
PHP_SESSION_ACTIVE
;
}
/**
* {@inheritdoc}
*/
public
function
regenerate
()
{
global
$user
;
global
$user
;
// Nothing to do if we are not allowed to change the session.
// Nothing to do if we are not allowed to change the session.
...
@@ -181,11 +215,17 @@ public function regenerate() {
...
@@ -181,11 +215,17 @@ public function regenerate() {
return
;
return
;
}
}
// We do not support the optional $destroy and $lifetime parameters as long
// as #2238561 remains open.
if
(
$destroy
||
isset
(
$lifetime
))
{
throw
new
\
InvalidArgumentException
(
'The optional parameters $destroy and $lifetime of SessionManager::regenerate() are not supported currently'
);
}
$is_https
=
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
();
$is_https
=
$this
->
requestStack
->
getCurrentRequest
()
->
isSecure
();
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
$cookies
=
$this
->
requestStack
->
getCurrentRequest
()
->
cookies
;
if
(
$is_https
&&
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
))
{
if
(
$is_https
&&
$this
->
isMixedMode
(
))
{
$insecure_session_name
=
substr
(
session_name
(),
1
);
$insecure_session_name
=
$this
->
getInsecureName
(
);
if
(
!
isset
(
$this
->
lazySession
)
&&
$cookies
->
has
(
$insecure_session_name
))
{
if
(
!
isset
(
$this
->
lazySession
)
&&
$cookies
->
has
(
$insecure_session_name
))
{
$old_insecure_session_id
=
$cookies
->
get
(
$insecure_session_name
);
$old_insecure_session_id
=
$cookies
->
get
(
$insecure_session_name
);
}
}
...
@@ -200,14 +240,13 @@ public function regenerate() {
...
@@ -200,14 +240,13 @@ public function regenerate() {
}
}
if
(
$this
->
isStarted
())
{
if
(
$this
->
isStarted
())
{
$old_session_id
=
session_i
d
();
$old_session_id
=
$this
->
getI
d
();
}
}
session_id
(
Crypt
::
randomBytesBase64
());
session_id
(
Crypt
::
randomBytesBase64
());
// @todo As soon as https://drupal.org/node/2238087 lands, the token seed
// @todo The token seed can be moved onto \Drupal\Core\Session\MetadataBag.
// can be moved onto Drupal\Core\Session\MetadataBag. The session manager
// The session manager then needs to notify the metadata bag when the
// then needs to notify the metadata bag when the token should be
// token should be regenerated. https://drupal.org/node/2256257
// regenerated.
if
(
!
empty
(
$_SESSION
))
{
if
(
!
empty
(
$_SESSION
))
{
unset
(
$_SESSION
[
'csrf_token_seed'
]);
unset
(
$_SESSION
[
'csrf_token_seed'
]);
}
}
...
@@ -215,13 +254,13 @@ public function regenerate() {
...
@@ -215,13 +254,13 @@ public function regenerate() {
if
(
isset
(
$old_session_id
))
{
if
(
isset
(
$old_session_id
))
{
$params
=
session_get_cookie_params
();
$params
=
session_get_cookie_params
();
$expire
=
$params
[
'lifetime'
]
?
REQUEST_TIME
+
$params
[
'lifetime'
]
:
0
;
$expire
=
$params
[
'lifetime'
]
?
REQUEST_TIME
+
$params
[
'lifetime'
]
:
0
;
setcookie
(
session_name
(),
session_i
d
(),
$expire
,
$params
[
'path'
],
$params
[
'domain'
],
$params
[
'secure'
],
$params
[
'httponly'
]);
setcookie
(
$this
->
getName
(),
$this
->
getI
d
(),
$expire
,
$params
[
'path'
],
$params
[
'domain'
],
$params
[
'secure'
],
$params
[
'httponly'
]);
$fields
=
array
(
'sid'
=>
Crypt
::
hashBase64
(
session_i
d
()));
$fields
=
array
(
'sid'
=>
Crypt
::
hashBase64
(
$this
->
getI
d
()));
if
(
$is_https
)
{
if
(
$is_https
)
{
$fields
[
'ssid'
]
=
Crypt
::
hashBase64
(
session_i
d
());
$fields
[
'ssid'
]
=
Crypt
::
hashBase64
(
$this
->
getI
d
());
// If the "secure pages" setting is enabled, use the newly-created
// If the "secure pages" setting is enabled, use the newly-created
// insecure session identifier as the regenerated sid.
// insecure session identifier as the regenerated sid.
if
(
Settings
::
get
(
'mixed_mode_sessions'
,
FALSE
))
{
if
(
$this
->
isMixedMode
(
))
{
$fields
[
'sid'
]
=
Crypt
::
hashBase64
(
$session_id
);
$fields
[
'sid'
]
=
Crypt
::
hashBase64
(
$session_id
);
}
}
}
}
...
@@ -235,7 +274,7 @@ public function regenerate() {
...
@@ -235,7 +274,7 @@ public function regenerate() {
// the secure site but a session was active on the insecure site, update
// the secure site but a session was active on the insecure site, update
// the insecure session with the new session identifiers.
// the insecure session with the new session identifiers.
$this
->
connection
->
update
(
'sessions'
)
$this
->
connection
->
update
(
'sessions'
)
->
fields
(
array
(
'sid'
=>
Crypt
::
hashBase64
(
$session_id
),
'ssid'
=>
Crypt
::
hashBase64
(
session_i
d
())))
->
fields
(
array
(
'sid'
=>
Crypt
::
hashBase64
(
$session_id
),
'ssid'
=>
Crypt
::
hashBase64
(
$this
->
getI
d
())))
->
condition
(
'sid'
,
Crypt
::
hashBase64
(
$old_insecure_session_id
))
->
condition
(
'sid'
,
Crypt
::
hashBase64
(
$old_insecure_session_id
))
->
execute
();
->
execute
();
}
}
...
@@ -286,6 +325,27 @@ public function enable() {
...
@@ -286,6 +325,27 @@ public function enable() {
return
$this
;
return
$this
;
}
}
/**
* {@inheritdoc}
*/
public
function
isMixedMode
()
{
return
$this
->
mixedMode
;
}
/**
* {@inheritdoc}
*/
public
function
setMixedMode
(
$mixed_mode
)
{
$this
->
mixedMode
=
(
bool
)
$mixed_mode
;
}
/**
* {@inheritdoc}
*/
public
function
getInsecureName
()
{
return
substr
(
$this
->
getName
(),
1
);
}
/**
/**
* Returns whether the current PHP process runs on CLI.
* Returns whether the current PHP process runs on CLI.
*
*
...
@@ -305,11 +365,29 @@ protected function isCli() {
...
@@ -305,11 +365,29 @@ protected function isCli() {
* destroyed.
* destroyed.
*/
*/
protected
function
isSessionObsolete
()
{
protected
function
isSessionObsolete
()
{
// Return early when $_SESSION is empty or not initialized.
$used_session_keys
=
array_filter
(
$this
->
getSessionDataMask
());
return
empty
(
$used_session_keys
);
}
/**
* Returns a map specifying which session key is containing user data.
*
* @return array
* An array where keys correspond to the session keys and the values are
* booleans specifying whether the corresponding session key contains any
* user data.
*/
protected
function
getSessionDataMask
()
{
if
(
empty
(
$_SESSION
))
{
if
(
empty
(
$_SESSION
))
{
return
TRUE
;
return
array
()
;
}
}
// Start out with a completely filled mask.
$mask
=
array_fill_keys
(
array_keys
(
$_SESSION
),
TRUE
);
// Ignore the metadata bag, it does not contain any user data.
$mask
[
$this
->
metadataBag
->
getStorageKey
()]
=
FALSE
;
// Ignore the CSRF token seed.
// Ignore the CSRF token seed.
//
//
// @todo Anonymous users should not get a CSRF token at any time, or if they
// @todo Anonymous users should not get a CSRF token at any time, or if they
...
@@ -317,10 +395,18 @@ protected function isSessionObsolete() {
...
@@ -317,10 +395,18 @@ protected function isSessionObsolete() {
// session once obsolete. Since that is not guaranteed to be the case,
// session once obsolete. Since that is not guaranteed to be the case,
// this check force-ignores the CSRF token, so as to avoid performance
// this check force-ignores the CSRF token, so as to avoid performance
// regressions.
// regressions.
// As soon as https://drupal.org/node/2238087 lands, the token seed can be
// The token seed can be moved onto \Drupal\Core\Session\MetadataBag. This
// moved onto \Drupal\Core\Session\MetadataBag. This will result in the
// will result in the CSRF token being ignored automatically.
// CSRF token to be ignored automatically.