runff 1.0 commit
This commit is contained in:
362
lib/SimpleSAML/SessionHandlerPHP.php
Executable file
362
lib/SimpleSAML/SessionHandlerPHP.php
Executable file
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of SimpleSAMLphp. See the file COPYING in the root of the distribution for licence information.
|
||||
*
|
||||
* This file defines a session handler which uses the default php session handler for storage.
|
||||
*
|
||||
* @author Olav Morken, UNINETT AS. <andreas.solberg@uninett.no>
|
||||
* @package SimpleSAMLphp
|
||||
*/
|
||||
|
||||
namespace SimpleSAML;
|
||||
|
||||
use SimpleSAML\Error\CannotSetCookie;
|
||||
use SimpleSAML\Utils\HTTP;
|
||||
|
||||
class SessionHandlerPHP extends SessionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* This variable contains the session cookie name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cookie_name;
|
||||
|
||||
/**
|
||||
* An associative array containing the details of a session existing previously to creating or loading one with this
|
||||
* session handler. The keys of the array will be:
|
||||
*
|
||||
* - id: the ID of the session, as returned by session_id().
|
||||
* - name: the name of the session, as returned by session_name().
|
||||
* - cookie_params: the parameters of the session cookie, as returned by session_get_cookie_params().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $previous_session = array();
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the PHP session handling. This constructor is protected because it should only be called from
|
||||
* \SimpleSAML\SessionHandler::createSessionHandler(...).
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
// call the parent constructor in case it should become necessary in the future
|
||||
parent::__construct();
|
||||
|
||||
$config = \SimpleSAML_Configuration::getInstance();
|
||||
$this->cookie_name = $config->getString('session.phpsession.cookiename', null);
|
||||
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
if (session_name() === $this->cookie_name || $this->cookie_name === null) {
|
||||
Logger::warning(
|
||||
'There is already a PHP session with the same name as SimpleSAMLphp\'s session, or the '.
|
||||
"'session.phpsession.cookiename' configuration option is not set. Make sure to set ".
|
||||
"SimpleSAMLphp's cookie name with a value not used by any other applications."
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* We shouldn't have a session at this point, so it might be an application session. Save the details to
|
||||
* retrieve it later and commit.
|
||||
*/
|
||||
$this->previous_session['cookie_params'] = session_get_cookie_params();
|
||||
$this->previous_session['id'] = session_id();
|
||||
$this->previous_session['name'] = session_name();
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
if (!empty($this->cookie_name)) {
|
||||
session_name($this->cookie_name);
|
||||
} else {
|
||||
$this->cookie_name = session_name();
|
||||
}
|
||||
|
||||
$params = $this->getCookieParams();
|
||||
|
||||
if (!headers_sent()) {
|
||||
session_set_cookie_params(
|
||||
$params['lifetime'],
|
||||
$params['path'],
|
||||
$params['domain'],
|
||||
$params['secure'],
|
||||
$params['httponly']
|
||||
);
|
||||
}
|
||||
|
||||
$savepath = $config->getString('session.phpsession.savepath', null);
|
||||
if (!empty($savepath)) {
|
||||
session_save_path($savepath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method starts a session, making sure no warnings are generated due to headers being already sent.
|
||||
*/
|
||||
private function sessionStart()
|
||||
{
|
||||
$cacheLimiter = session_cache_limiter();
|
||||
if (headers_sent()) {
|
||||
/*
|
||||
* session_start() tries to send HTTP headers depending on the configuration, according to the
|
||||
* documentation:
|
||||
*
|
||||
* http://php.net/manual/en/function.session-start.php
|
||||
*
|
||||
* If headers have been already sent, it will then trigger an error since no more headers can be sent.
|
||||
* Being unable to send headers does not mean we cannot recover the session by calling session_start(),
|
||||
* so we still want to call it. In this case, though, we want to avoid session_start() to send any
|
||||
* headers at all so that no error is generated, so we clear the cache limiter temporarily (no headers
|
||||
* sent then) and restore it after successfully starting the session.
|
||||
*/
|
||||
session_cache_limiter('');
|
||||
}
|
||||
session_cache_limiter($cacheLimiter);
|
||||
@session_start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restore a previously-existing session.
|
||||
*
|
||||
* Use this method to restore a previous PHP session existing before SimpleSAMLphp initialized its own session.
|
||||
*
|
||||
* WARNING: do not use this method directly, unless you know what you are doing. Calling this method directly,
|
||||
* outside of SimpleSAML_Session, could cause SimpleSAMLphp's session to be lost or mess the application's one. The
|
||||
* session must always be saved properly before calling this method. If you don't understand what this is about,
|
||||
* don't use this method.
|
||||
*/
|
||||
public function restorePrevious()
|
||||
{
|
||||
if (empty($this->previous_session)) {
|
||||
return; // nothing to do here
|
||||
}
|
||||
|
||||
// close our own session
|
||||
session_write_close();
|
||||
|
||||
session_name($this->previous_session['name']);
|
||||
session_set_cookie_params(
|
||||
$this->previous_session['cookie_params']['lifetime'],
|
||||
$this->previous_session['cookie_params']['path'],
|
||||
$this->previous_session['cookie_params']['domain'],
|
||||
$this->previous_session['cookie_params']['secure'],
|
||||
$this->previous_session['cookie_params']['httponly']
|
||||
);
|
||||
session_id($this->previous_session['id']);
|
||||
$this->previous_session = array();
|
||||
$this->sessionStart();
|
||||
|
||||
/*
|
||||
* At this point, we have restored a previously-existing session, so we can't continue to use our session here.
|
||||
* Therefore, we need to load our session again in case we need it. We remove this handler from the parent
|
||||
* class so that the handler is initialized again if we ever need to do something with the session.
|
||||
*/
|
||||
parent::$sessionHandler = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new session id.
|
||||
*
|
||||
* @return string The new session id.
|
||||
*/
|
||||
public function newSessionId()
|
||||
{
|
||||
// generate new (secure) session id
|
||||
$sessionId = bin2hex(openssl_random_pseudo_bytes(16));
|
||||
\SimpleSAML_Session::createSession($sessionId);
|
||||
|
||||
return $sessionId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the session ID saved in the session cookie, if there's one.
|
||||
*
|
||||
* @return string|null The session id saved in the cookie or null if no session cookie was set.
|
||||
*
|
||||
* @throws \SimpleSAML_Error_Exception If the cookie is marked as secure but we are not using HTTPS.
|
||||
*/
|
||||
public function getCookieSessionId()
|
||||
{
|
||||
if (!self::hasSessionCookie()) {
|
||||
return null; // there's no session cookie, can't return ID
|
||||
}
|
||||
|
||||
// do not rely on session_id() as it can return the ID of a previous session. Get it from the cookie instead.
|
||||
session_id($_COOKIE[$this->cookie_name]);
|
||||
|
||||
$session_cookie_params = session_get_cookie_params();
|
||||
|
||||
if ($session_cookie_params['secure'] && !HTTP::isHTTPS()) {
|
||||
throw new \SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
|
||||
}
|
||||
|
||||
$this->sessionStart();
|
||||
return session_id();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the session cookie name.
|
||||
*
|
||||
* @return string The session cookie name.
|
||||
*/
|
||||
public function getSessionCookieName()
|
||||
{
|
||||
return $this->cookie_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save the current session to the PHP session array.
|
||||
*
|
||||
* @param \SimpleSAML_Session $session The session object we should save.
|
||||
*/
|
||||
public function saveSession(\SimpleSAML_Session $session)
|
||||
{
|
||||
$_SESSION['SimpleSAMLphp_SESSION'] = serialize($session);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the session from the PHP session array.
|
||||
*
|
||||
* @param string|null $sessionId The ID of the session we should load, or null to use the default.
|
||||
*
|
||||
* @return \SimpleSAML_Session|null The session object, or null if it doesn't exist.
|
||||
*
|
||||
* @throws \SimpleSAML_Error_Exception If it wasn't possible to disable session cookies or we are trying to load a
|
||||
* PHP session with a specific identifier and it doesn't match with the current session identifier.
|
||||
*/
|
||||
public function loadSession($sessionId = null)
|
||||
{
|
||||
assert(is_string($sessionId) || $sessionId === null);
|
||||
|
||||
if ($sessionId !== null) {
|
||||
if (session_id() === '') {
|
||||
// session not initiated with getCookieSessionId(), start session without setting cookie
|
||||
$ret = ini_set('session.use_cookies', '0');
|
||||
if ($ret === false) {
|
||||
throw new \SimpleSAML_Error_Exception('Disabling PHP option session.use_cookies failed.');
|
||||
}
|
||||
|
||||
session_id($sessionId);
|
||||
$this->sessionStart();
|
||||
} elseif ($sessionId !== session_id()) {
|
||||
throw new \SimpleSAML_Error_Exception('Cannot load PHP session with a specific ID.');
|
||||
}
|
||||
} elseif (session_id() === '') {
|
||||
self::getCookieSessionId();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['SimpleSAMLphp_SESSION'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$session = $_SESSION['SimpleSAMLphp_SESSION'];
|
||||
assert(is_string($session));
|
||||
|
||||
$session = unserialize($session);
|
||||
|
||||
return ($session !== false) ? $session : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the session cookie is set.
|
||||
*
|
||||
* This function will only return false if is is certain that the cookie isn't set.
|
||||
*
|
||||
* @return boolean True if it was set, false otherwise.
|
||||
*/
|
||||
public function hasSessionCookie()
|
||||
{
|
||||
return array_key_exists($this->cookie_name, $_COOKIE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the cookie parameters that should be used for session cookies.
|
||||
*
|
||||
* This function contains some adjustments from the default to provide backwards-compatibility.
|
||||
*
|
||||
* @return array The cookie parameters for our sessions.
|
||||
* @link http://www.php.net/manual/en/function.session-get-cookie-params.php
|
||||
*
|
||||
* @throws \SimpleSAML_Error_Exception If both 'session.phpsession.limitedpath' and 'session.cookie.path' options
|
||||
* are set at the same time in the configuration.
|
||||
*/
|
||||
public function getCookieParams()
|
||||
{
|
||||
$config = \SimpleSAML_Configuration::getInstance();
|
||||
|
||||
$ret = parent::getCookieParams();
|
||||
|
||||
if ($config->hasValue('session.phpsession.limitedpath') && $config->hasValue('session.cookie.path')) {
|
||||
throw new \SimpleSAML_Error_Exception(
|
||||
'You cannot set both the session.phpsession.limitedpath and session.cookie.path options.'
|
||||
);
|
||||
} elseif ($config->hasValue('session.phpsession.limitedpath')) {
|
||||
$ret['path'] = $config->getBoolean(
|
||||
'session.phpsession.limitedpath',
|
||||
false
|
||||
) ? $config->getBasePath() : '/';
|
||||
}
|
||||
|
||||
$ret['httponly'] = $config->getBoolean('session.phpsession.httponly', true);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a session cookie.
|
||||
*
|
||||
* @param string $sessionName The name of the session.
|
||||
* @param string|null $sessionID The session ID to use. Set to null to delete the cookie.
|
||||
* @param array|null $cookieParams Additional parameters to use for the session cookie.
|
||||
*
|
||||
* @throws \SimpleSAML\Error\CannotSetCookie If we can't set the cookie.
|
||||
*/
|
||||
public function setCookie($sessionName, $sessionID, array $cookieParams = null)
|
||||
{
|
||||
if ($cookieParams === null) {
|
||||
$cookieParams = session_get_cookie_params();
|
||||
}
|
||||
|
||||
if ($cookieParams['secure'] && !HTTP::isHTTPS()) {
|
||||
throw new CannotSetCookie(
|
||||
'Setting secure cookie on plain HTTP is not allowed.',
|
||||
CannotSetCookie::SECURE_COOKIE
|
||||
);
|
||||
}
|
||||
|
||||
if (headers_sent()) {
|
||||
throw new CannotSetCookie(
|
||||
'Headers already sent.',
|
||||
CannotSetCookie::HEADERS_SENT
|
||||
);
|
||||
}
|
||||
|
||||
session_set_cookie_params(
|
||||
$cookieParams['lifetime'],
|
||||
$cookieParams['path'],
|
||||
$cookieParams['domain'],
|
||||
$cookieParams['secure'],
|
||||
$cookieParams['httponly']
|
||||
);
|
||||
|
||||
if (session_id() !== '') {
|
||||
// session already started, close it
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
session_id($sessionID);
|
||||
$this->sessionStart();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user