diff options
author | Frederic Guillot <fred@kanboard.net> | 2015-11-15 12:50:33 -0500 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2015-11-15 12:50:33 -0500 |
commit | a675271ad71b7713d1b33bdba3c51b2b04813229 (patch) | |
tree | e54d8a95e16ca521193b9fd5a5eb071aa2910823 /app/Core | |
parent | 2fc402f6733573627ad25394d109b9f848ef04f6 (diff) |
Rewrite of session management
Diffstat (limited to 'app/Core')
-rw-r--r-- | app/Core/Base.php | 4 | ||||
-rw-r--r-- | app/Core/Mail/Client.php | 2 | ||||
-rw-r--r-- | app/Core/Security/Token.php | 10 | ||||
-rw-r--r-- | app/Core/Session.php | 144 | ||||
-rw-r--r-- | app/Core/Session/FlashMessage.php | 71 | ||||
-rw-r--r-- | app/Core/Session/SessionManager.php | 102 | ||||
-rw-r--r-- | app/Core/Session/SessionStorage.php | 71 |
7 files changed, 253 insertions, 151 deletions
diff --git a/app/Core/Base.php b/app/Core/Base.php index 11f4e31b..d3171024 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -10,6 +10,9 @@ use Pimple\Container; * @package core * @author Frederic Guillot * + * @property \Kanboard\Core\Session\SessionManager $sessionManager + * @property \Kanboard\Core\Session\SessionStorage $sessionStorage + * @property \Kanboard\Core\Session\FlashMessage $flash * @property \Kanboard\Core\Helper $helper * @property \Kanboard\Core\Mail\Client $emailClient * @property \Kanboard\Core\Paginator $paginator @@ -17,7 +20,6 @@ use Pimple\Container; * @property \Kanboard\Core\Http\Request $request * @property \Kanboard\Core\Http\Router $router * @property \Kanboard\Core\Http\Response $response - * @property \Kanboard\Core\Session $session * @property \Kanboard\Core\Template $template * @property \Kanboard\Core\OAuth2 $oauth * @property \Kanboard\Core\Lexer $lexer diff --git a/app/Core/Mail/Client.php b/app/Core/Mail/Client.php index 52caef73..7b4268bd 100644 --- a/app/Core/Mail/Client.php +++ b/app/Core/Mail/Client.php @@ -51,7 +51,7 @@ class Client extends Base $author = 'Kanboard'; if ($this->userSession->isLogged()) { - $author = e('%s via Kanboard', $this->user->getFullname($this->session['user'])); + $author = e('%s via Kanboard', $this->helper->user->getFullname()); } $this->getTransport(MAIL_TRANSPORT)->sendEmail($email, $name, $subject, $html, $author); diff --git a/app/Core/Security/Token.php b/app/Core/Security/Token.php index 7aca08af..2bb66ef2 100644 --- a/app/Core/Security/Token.php +++ b/app/Core/Security/Token.php @@ -38,12 +38,12 @@ class Token extends Base */ public function getCSRFToken() { - if (! isset($_SESSION['csrf_tokens'])) { - $_SESSION['csrf_tokens'] = array(); + if (! isset($this->sessionStorage->csrf)) { + $this->sessionStorage->csrf = array(); } $nonce = self::getToken(); - $_SESSION['csrf_tokens'][$nonce] = true; + $this->sessionStorage->csrf[$nonce] = true; return $nonce; } @@ -57,8 +57,8 @@ class Token extends Base */ public function validateCSRFToken($token) { - if (isset($_SESSION['csrf_tokens'][$token])) { - unset($_SESSION['csrf_tokens'][$token]); + if (isset($this->sessionStorage->csrf[$token])) { + unset($this->sessionStorage->csrf[$token]); return true; } diff --git a/app/Core/Session.php b/app/Core/Session.php deleted file mode 100644 index dd1e760e..00000000 --- a/app/Core/Session.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php - -namespace Kanboard\Core; - -use ArrayAccess; -use Kanboard\Core\Http\Request; - -/** - * Session class - * - * @package core - * @author Frederic Guillot - */ -class Session implements ArrayAccess -{ - /** - * Return true if the session is open - * - * @static - * @access public - * @return boolean - */ - public static function isOpen() - { - return session_id() !== ''; - } - - /** - * Open a session - * - * @access public - * @param string $base_path Cookie path - */ - public function open($base_path = '/') - { - // HttpOnly and secure flags for session cookie - session_set_cookie_params( - SESSION_DURATION, - $base_path ?: '/', - null, - Request::isHTTPS(), - true - ); - - // Avoid session id in the URL - ini_set('session.use_only_cookies', '1'); - - // Enable strict mode - if (version_compare(PHP_VERSION, '7.0.0') < 0) { - ini_set('session.use_strict_mode', '1'); - } - - // Ensure session ID integrity - ini_set('session.entropy_file', '/dev/urandom'); - ini_set('session.entropy_length', '32'); - ini_set('session.hash_bits_per_character', 6); - - // If the session was autostarted with session.auto_start = 1 in php.ini destroy it - if (isset($_SESSION)) { - session_destroy(); - } - - // Custom session name - session_name('__S'); - - // Start the session - session_start(); - - // Regenerate the session id to avoid session fixation issue - if (empty($_SESSION['__validated'])) { - session_regenerate_id(true); - $_SESSION['__validated'] = 1; - } - } - - /** - * Destroy the session - * - * @access public - */ - public function close() - { - // Flush all sessions variables - $_SESSION = array(); - - // Destroy the session cookie - $params = session_get_cookie_params(); - - setcookie( - session_name(), - '', - time() - 42000, - $params['path'], - $params['domain'], - $params['secure'], - $params['httponly'] - ); - - // Destroy session data - session_destroy(); - } - - /** - * Register a flash message (success notification) - * - * @access public - * @param string $message Message - */ - public function flash($message) - { - $_SESSION['flash_message'] = $message; - } - - /** - * Register a flash error message (error notification) - * - * @access public - * @param string $message Message - */ - public function flashError($message) - { - $_SESSION['flash_error_message'] = $message; - } - - public function offsetSet($offset, $value) - { - $_SESSION[$offset] = $value; - } - - public function offsetExists($offset) - { - return isset($_SESSION[$offset]); - } - - public function offsetUnset($offset) - { - unset($_SESSION[$offset]); - } - - public function offsetGet($offset) - { - return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; - } -} diff --git a/app/Core/Session/FlashMessage.php b/app/Core/Session/FlashMessage.php new file mode 100644 index 00000000..e02d056d --- /dev/null +++ b/app/Core/Session/FlashMessage.php @@ -0,0 +1,71 @@ +<?php + +namespace Kanboard\Core\Session; + +use Kanboard\Core\Base; + +/** + * Session Flash Message + * + * @package session + * @author Frederic Guillot + */ +class FlashMessage extends Base +{ + /** + * Add success message + * + * @access public + * @param string $message + */ + public function success($message) + { + $this->setMessage('success', $message); + } + + /** + * Add failure message + * + * @access public + * @param string $message + */ + public function failure($message) + { + $this->setMessage('failure', $message); + } + + /** + * Add new flash message + * + * @access public + * @param string $key + * @param string $message + */ + public function setMessage($key, $message) + { + if (! isset($this->sessionStorage->flash)) { + $this->sessionStorage->flash = array(); + } + + $this->sessionStorage->flash[$key] = $message; + } + + /** + * Get flash message + * + * @access public + * @param string $key + * @return string + */ + public function getMessage($key) + { + $message = ''; + + if (isset($this->sessionStorage->flash[$key])) { + $message = $this->sessionStorage->flash[$key]; + unset($this->sessionStorage->flash[$key]); + } + + return $message; + } +} diff --git a/app/Core/Session/SessionManager.php b/app/Core/Session/SessionManager.php new file mode 100644 index 00000000..6153efeb --- /dev/null +++ b/app/Core/Session/SessionManager.php @@ -0,0 +1,102 @@ +<?php + +namespace Kanboard\Core\Session; + +use Kanboard\Core\Base; +use Kanboard\Core\Http\Request; + +/** + * Session Manager + * + * @package session + * @author Frederic Guillot + */ +class SessionManager extends Base +{ + /** + * Return true if the session is open + * + * @static + * @access public + * @return boolean + */ + public static function isOpen() + { + return session_id() !== ''; + } + + /** + * Create a new session + * + * @access public + */ + public function open() + { + $this->configure(); + + if (ini_get('session.auto_start') == 1) { + session_destroy(); + } + + session_name('KB_SID'); + session_start(); + + $this->container['sessionStorage']->setStorage($_SESSION); + } + + /** + * Destroy the session + * + * @access public + */ + public function close() + { + // Destroy the session cookie + $params = session_get_cookie_params(); + + setcookie( + session_name(), + '', + time() - 42000, + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] + ); + + session_unset(); + session_destroy(); + } + + /** + * Define session settings + * + * @access private + */ + private function configure() + { + // Session cookie: HttpOnly and secure flags + session_set_cookie_params( + SESSION_DURATION, + $this->helper->url->dir() ?: '/', + null, + Request::isHTTPS(), + true + ); + + // Avoid session id in the URL + ini_set('session.use_only_cookies', '1'); + ini_set('session.use_trans_sid', '0'); + + // Enable strict mode + ini_set('session.use_strict_mode', '1'); + + // Better session hash + ini_set('session.hash_function', 'sha512'); + ini_set('session.hash_bits_per_character', 6); + + // Set an additional entropy + ini_set('session.entropy_file', '/dev/urandom'); + ini_set('session.entropy_length', '256'); + } +} diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php new file mode 100644 index 00000000..54d803f7 --- /dev/null +++ b/app/Core/Session/SessionStorage.php @@ -0,0 +1,71 @@ +<?php + +namespace Kanboard\Core\Session; + +/** + * Session Storage + * + * @package session + * @author Frederic Guillot + * + * @property array $config + * @property array $user + * @property array $flash + * @property array $csrf + * @property array $postAuth + * @property string $redirectAfterLogin + * @property string $captcha + * @property string $commentSorting + * @property bool $hasSubtaskInProgress + * @property bool $boardCollapsed + */ +class SessionStorage +{ + /** + * Pointer to external storage + * + * @access private + * @var array + */ + private $storage = array(); + + /** + * Set external storage + * + * @access public + * @param array $storage External session storage (example: $_SESSION) + */ + public function setStorage(array &$storage) + { + $this->storage =& $storage; + + // Load dynamically existing session variables into object properties + foreach ($storage as $key => $value) { + $this->$key = $value; + } + } + + /** + * Get all session variables + * + * @access public + * @return array + */ + public function getAll() + { + $session = get_object_vars($this); + unset($session['storage']); + + return $session; + } + + /** + * Copy class properties to external storage + * + * @access public + */ + public function __destruct() + { + $this->storage = $this->getAll(); + } +} |