Fix bug that caused ajax-request to override values written to the session

Authentication/Session.php and its Subclasses do not have a open/closed -state
anymore. Read will refresh the session, write will always write the changes,
and opening/closing will be handled internally.

refs #5101
This commit is contained in:
Matthias Jentsch 2013-11-20 12:01:40 +01:00
parent 9886df95a4
commit 941ce6d68e
9 changed files with 80 additions and 231 deletions

View File

@ -48,15 +48,6 @@ class AuthenticationController extends ActionController
*/ */
protected $requiresAuthentication = false; protected $requiresAuthentication = false;
/**
* This controller modifies the session
*
* @var bool
*
* @see \Icinga\Web\Controller\ActionController::$modifiesSession
*/
protected $modifiesSession = true;
/** /**
* Log into the application * Log into the application
*/ */
@ -69,9 +60,7 @@ class AuthenticationController extends ActionController
$this->view->form->setRequest($this->_request); $this->view->form->setRequest($this->_request);
$this->view->title = "Icinga Web Login"; $this->view->title = "Icinga Web Login";
try { try {
$auth = AuthManager::getInstance(null, array( $auth = AuthManager::getInstance();
'writeSession' => $this->modifiesSession
));
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {
$this->redirectNow('index?_render=body'); $this->redirectNow('index?_render=body');
@ -104,9 +93,7 @@ class AuthenticationController extends ActionController
public function logoutAction() public function logoutAction()
{ {
$this->_helper->layout->setLayout('inline'); $this->_helper->layout->setLayout('inline');
$auth = AuthManager::getInstance(null, array( $auth = AuthManager::getInstance();
'writeSession' => $this->modifiesSession
));
$auth->removeAuthorization(); $auth->removeAuthorization();
$this->redirectToLogin(); $this->redirectToLogin();
} }

View File

@ -40,15 +40,6 @@ use \Icinga\Form\Preference\GeneralForm;
class PreferenceController extends BasePreferenceController class PreferenceController extends BasePreferenceController
{ {
/**
* This controller modifies the session
*
* @var bool
*
* @see \Icinga\Web\Controller\ActionController::$modifiesSession
*/
protected $modifiesSession = true;
/** /**
* Create tabs for this preference controller * Create tabs for this preference controller
* *

View File

@ -232,12 +232,7 @@ class Web extends ApplicationBootstrap
*/ */
private function setupUser() private function setupUser()
{ {
$authenticationManager = AuthenticationManager::getInstance( $authenticationManager = AuthenticationManager::getInstance();
null,
array(
'writeSession' => true
)
);
if ($authenticationManager->isAuthenticated() === true) { if ($authenticationManager->isAuthenticated() === true) {
$user = $authenticationManager->getUser(); $user = $authenticationManager->getUser();

View File

@ -121,16 +121,10 @@ class Manager
} }
if (!isset($options['sessionClass'])) { if (!isset($options['sessionClass'])) {
$this->session = new PhpSession($config->session); $this->session = new PhpSession();
} else { } else {
$this->session = $options['sessionClass']; $this->session = $options['sessionClass'];
} }
if (isset($options['writeSession']) && $options['writeSession'] === true) {
$this->session->read(true);
} else {
$this->session->read();
}
} }
/** /**
@ -353,6 +347,7 @@ class Manager
return false; return false;
} }
$this->user->sendMessage("Hallo ich bin nachricht.");
if ($persist == true) { if ($persist == true) {
$this->persistCurrentUser(); $this->persistCurrentUser();
$this->session->write(); $this->session->write();

View File

@ -51,21 +51,7 @@ class PhpSession extends Session
* *
* @var string * @var string
*/ */
const SESSION_NAME = 'Icinga2Web'; private $sessionName = 'Icingaweb2';
/**
* Flag if session is open
*
* @var bool
*/
private $isOpen = false;
/**
* Flag if session is flushed
*
* @var bool
*/
private $isFlushed = false;
/** /**
* Configuration for cookie options * Configuration for cookie options
@ -96,8 +82,12 @@ class PhpSession extends Session
} else { } else {
$options = PhpSession::$defaultCookieOptions; $options = PhpSession::$defaultCookieOptions;
} }
if (array_key_exists('test_session_name', $options)) {
$this->sessionName = $options['test_session_name'];
unset($options['test_session_name']);
}
foreach ($options as $sessionVar => $value) { foreach ($options as $sessionVar => $value) {
if (ini_set("session.".$sessionVar, $value) === false) { if (ini_set("session." . $sessionVar, $value) === false) {
Logger::warn( Logger::warn(
'Could not set php.ini setting %s = %s. This might affect your sessions behaviour.', 'Could not set php.ini setting %s = %s. This might affect your sessions behaviour.',
$sessionVar, $sessionVar,
@ -108,142 +98,51 @@ class PhpSession extends Session
if (!is_writable(session_save_path())) { if (!is_writable(session_save_path())) {
throw new ConfigurationError('Can\'t save session'); throw new ConfigurationError('Can\'t save session');
} }
$this->read();
} }
/** /**
* Return true when the session has not yet been closed * Open a PHP session
*
* @return bool
*/ */
private function sessionCanBeChanged() private function open()
{ {
if ($this->isFlushed) { session_name($this->sessionName);
Logger::error('Tried to work on a closed session, session changes will be ignored');
return false;
}
return true;
}
/**
* Return true when the session has not yet been opened
*
* @return bool
*/
private function sessionCanBeOpened()
{
if ($this->isOpen) {
Logger::warn('Tried to open a session more than once');
return false;
}
return $this->sessionCanBeChanged();
}
/**
* Open a PHP session when possible
*
* @return bool True on success
*/
public function open()
{
if (!$this->sessionCanBeOpened()) {
return false;
}
session_name(self::SESSION_NAME);
session_start(); session_start();
$this->isOpen = true; }
/**
* Read all values written to the underling session and make them accessible.
*/
public function read()
{
$this->open();
$this->setAll($_SESSION); $this->setAll($_SESSION);
return true; session_write_close();
}
/**
* Ensure that the session is open modifiable
*
* @return bool True on success
*/
private function ensureOpen()
{
// try to open first
if (!$this->isOpen) {
if (!$this->open()) {
return false;
}
}
return true;
}
/**
* Read all values written to the underling session and
* makes them accessible. if keepOpen is not set, the session
* is immediately closed again
*
* @param bool $keepOpen Set to true when modifying the session
*
* @return bool True on success
*/
public function read($keepOpen = false)
{
if (!$this->ensureOpen()) {
return false;
}
if ($keepOpen) {
return true;
}
$this->close();
return true;
} }
/** /**
* Write all values of this session object to the underlying session implementation * Write all values of this session object to the underlying session implementation
*
* If keepOpen is not set, the session is closed
*
* @param bool $keepOpen Set to true when modifying the session further
*
* @return bool True on success
*/ */
public function write($keepOpen = false) public function write()
{ {
if (!$this->ensureOpen()) { $this->open();
return false;
}
foreach ($this->getAll() as $key => $value) { foreach ($this->getAll() as $key => $value) {
$_SESSION[$key] = $value; $_SESSION[$key] = $value;
} }
if ($keepOpen) {
return null;
}
$this->close();
return null;
}
/**
* Close and writes the session
*
* Only call this if you want the session to be closed without any changes.
*
* @see PHPSession::write
*/
public function close()
{
if (!$this->isFlushed) {
session_write_close(); session_write_close();
} }
$this->isFlushed = true;
}
/** /**
* Delete the current session, causing all session information to be lost * Delete the current session, causing all session information to be lost
*/ */
public function purge() public function purge()
{ {
if ($this->ensureOpen()) { $this->open();
$_SESSION = array(); $_SESSION = array();
$this->setAll(array(), true);
session_destroy(); session_destroy();
$this->clearCookies(); $this->clearCookies();
$this->close(); session_write_close();
}
} }
/** /**

View File

@ -41,29 +41,15 @@ abstract class Session
*/ */
private $sessionValues = array(); private $sessionValues = array();
/**
* Open a session or creates a new one if not exists
*/
abstract public function open();
/** /**
* Read all values from the underlying session implementation * Read all values from the underlying session implementation
*
* @param bool $keepOpen True to keep the session open
*/ */
abstract public function read($keepOpen = false); abstract public function read();
/** /**
* Persists changes to the underlying session implementation * Persists changes to the underlying session implementation
*
* @param bool $keepOpen True to keep the session open
*/ */
abstract public function write($keepOpen = false); abstract public function write();
/**
* Close session
*/
abstract public function close();
/** /**
* Purge session * Purge session

View File

@ -31,7 +31,8 @@ namespace Icinga;
use \DateTimeZone; use \DateTimeZone;
use \InvalidArgumentException; use \InvalidArgumentException;
use Icinga\User\Preferences; use \Icinga\User\Preferences;
use \Icinga\Authentication\PhpSession;
/** /**
* This class represents an authorized user * This class represents an authorized user
@ -105,6 +106,13 @@ class User
*/ */
private $preferences; private $preferences;
/**
* Queued notifications for this user.
*
* @var array()
*/
private $messages;
/** /**
* Creates a user object given the provided information * Creates a user object given the provided information
* *
@ -334,4 +342,19 @@ class User
} }
return new DateTimeZone($tz); return new DateTimeZone($tz);
} }
/**
* Send a message to this user that can be accessed in other requests.
*
* @param $msg
*/
public function sendMessage($msg)
{
$this->messages[] = $msg;
}
public function getMessages()
{
return $this->messages;
}
} }

View File

@ -58,17 +58,6 @@ class ActionController extends Zend_Controller_Action
*/ */
protected $requiresAuthentication = true; protected $requiresAuthentication = true;
/**
* Set true when this controller modifies the session
*
* otherwise the session will be written back to disk and closed before the controller
* action is executed, leading to every modification in the session to be lost after
* the response is submitted
*
* @var bool
*/
protected $modifiesSession = false;
/** /**
* The constructor starts benchmarking, loads the configuration and sets * The constructor starts benchmarking, loads the configuration and sets
* other useful controller properties * other useful controller properties
@ -114,10 +103,7 @@ class ActionController extends Zend_Controller_Action
return false; return false;
} }
return !AuthManager::getInstance( return !AuthManager::getInstance()->isAuthenticated();
null,
array('writeSession' => $this->modifiesSession)
)->isAuthenticated();
} }
/** /**

View File

@ -49,23 +49,22 @@ class PhpSessionTest extends BaseTestCase
{ {
private function getSession() private function getSession()
{ {
if (!is_writable('/tmp')) { if (!is_writable('/tmp')) {
$this->markTestSkipped('Could not write to session directory'); $this->markTestSkipped('Could not write to session directory');
} }
return new PhpSession( return new PhpSession(
array( array(
'use_cookies' => false, 'use_cookies' => false,
'save_path' => '/tmp' 'save_path' => '/tmp',
'test_session_name' => 'IcingawebUnittest'
) )
); );
} }
/** /**
* Test the creation of a PhpSession object * Test the creation of a PhpSession object
* *
* @runInSeparateProcess * @runInSeparateProcess
**/ */
public function testSessionCreation() public function testSessionCreation()
{ {
$this->getSession(); $this->getSession();
@ -76,40 +75,28 @@ class PhpSessionTest extends BaseTestCase
* *
* @runInSeparateProcess * @runInSeparateProcess
*/ */
public function testOpenSession() public function testSessionReadWrite()
{ {
$this->assertEquals(session_id(), '', 'Asserting test precondition: session not being setup yet ');
$session = $this->getSession(); $session = $this->getSession();
$session->open(); $session->purge();
$this->assertNotEquals(session_id(), '', 'Asserting a Session ID being available after PhpSession::open()'); $this->assertEquals(null, $session->get('key'));
$session->set('key', 'value');
$session->write();
$session->read();
$this->assertEquals('value', $session->get('key'));
} }
/** /**
* Test a session being closed by PhpSession::close() * Test a session being closed by PhpSession::close()
* *
* @runInSeparateProcess * @runInSeparateProcess
**/
public function testCloseSession()
{
$this->assertEquals(session_id(), '', 'Asserting test precondition: session not being setup yet ');
$session = $this->getSession();
$session->open();
$this->assertNotEquals(session_id(), '', 'Asserting a Session ID being available after PhpSession::open()');
$session->close();
}
/**
* Test if a session is correctly purged when calling PhpSession::purge()
*
* @runInSeparateProcess
*/ */
public function testPurgeSession() public function testPurgeSession()
{ {
$this->assertEquals(session_id(), '', 'Asserting test precondition: session not being setup yet ');
$session = $this->getSession(); $session = $this->getSession();
$session->open(); $session->set('key2', 'value2');
$this->assertNotEquals(session_id(), '', 'Asserting a Session ID being available after PhpSession::open()');
$session->purge(); $session->purge();
$this->assertEquals(session_id(), '', 'Asserting no Session ID being available after PhpSession::purge()'); $session->read();
$this->assertEquals(null, $session->get('key2'));
} }
} }