From ccd177ada6823c27a6408427f19c238fd701c39e Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Wed, 6 Dec 2017 16:19:11 -0800 Subject: Store PHP sessions in the database --- app/Core/Base.php | 1 - app/Core/Http/OAuth2.php | 9 +-- app/Core/Queue/JobHandler.php | 3 +- app/Core/Security/AuthenticationManager.php | 2 +- app/Core/Security/Token.php | 12 ++-- app/Core/Session/FlashMessage.php | 19 +++--- app/Core/Session/SessionHandler.php | 70 ++++++++++++++++++++++ app/Core/Session/SessionManager.php | 6 +- app/Core/Session/SessionStorage.php | 92 ----------------------------- app/Core/User/UserSession.php | 88 +++++++++++++++++++++------ 10 files changed, 168 insertions(+), 134 deletions(-) create mode 100644 app/Core/Session/SessionHandler.php delete mode 100644 app/Core/Session/SessionStorage.php (limited to 'app/Core') diff --git a/app/Core/Base.php b/app/Core/Base.php index a36828c4..709327a7 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -48,7 +48,6 @@ use Pimple\Container; * @property \Kanboard\Core\Security\Token $token * @property \Kanboard\Core\Session\FlashMessage $flash * @property \Kanboard\Core\Session\SessionManager $sessionManager - * @property \Kanboard\Core\Session\SessionStorage $sessionStorage * @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager * @property \Kanboard\Core\User\GroupSync $groupSync * @property \Kanboard\Core\User\UserProfile $userProfile diff --git a/app/Core/Http/OAuth2.php b/app/Core/Http/OAuth2.php index 211ca5b4..f47927e1 100644 --- a/app/Core/Http/OAuth2.php +++ b/app/Core/Http/OAuth2.php @@ -53,11 +53,11 @@ class OAuth2 extends Base */ public function getState() { - if (! isset($this->sessionStorage->oauthState) || empty($this->sessionStorage->oauthState)) { - $this->sessionStorage->oauthState = $this->token->getToken(); + if (! session_exists('oauthState')) { + session_set('oauthState', $this->token->getToken()); } - return $this->sessionStorage->oauthState; + return session_get('oauthState'); } /** @@ -140,11 +140,12 @@ class OAuth2 extends Base * @access public * @param string $token * @param string $type - * @return string + * @return $this */ public function setAccessToken($token, $type = 'bearer') { $this->accessToken = $token; $this->tokenType = $type; + return $this; } } diff --git a/app/Core/Queue/JobHandler.php b/app/Core/Queue/JobHandler.php index 11c1fb69..d7e7d099 100644 --- a/app/Core/Queue/JobHandler.php +++ b/app/Core/Queue/JobHandler.php @@ -67,8 +67,7 @@ class JobHandler extends Base */ protected function prepareJobSession($user_id) { - $session = array(); - $this->sessionStorage->setStorage($session); + session_flush(); if ($user_id > 0) { $user = $this->userModel->getById($user_id); diff --git a/app/Core/Security/AuthenticationManager.php b/app/Core/Security/AuthenticationManager.php index b1ba76cf..e7a3c8d4 100644 --- a/app/Core/Security/AuthenticationManager.php +++ b/app/Core/Security/AuthenticationManager.php @@ -72,7 +72,7 @@ class AuthenticationManager extends Base foreach ($this->filterProviders('SessionCheckProviderInterface') as $provider) { if (! $provider->isValidSession()) { $this->logger->debug('Invalidate session for '.$this->userSession->getUsername()); - $this->sessionStorage->flush(); + session_flush(); $this->preAuthentication(); return false; } diff --git a/app/Core/Security/Token.php b/app/Core/Security/Token.php index cbd784a8..9b0c5769 100644 --- a/app/Core/Security/Token.php +++ b/app/Core/Security/Token.php @@ -32,12 +32,12 @@ class Token extends Base */ public function getCSRFToken() { - if (! isset($this->sessionStorage->csrf)) { - $this->sessionStorage->csrf = array(); + if (! session_exists('csrf')) { + session_set('csrf', []); } $nonce = self::getToken(); - $this->sessionStorage->csrf[$nonce] = true; + session_merge('csrf', [$nonce => true]); return $nonce; } @@ -51,8 +51,10 @@ class Token extends Base */ public function validateCSRFToken($token) { - if (isset($this->sessionStorage->csrf[$token])) { - unset($this->sessionStorage->csrf[$token]); + $tokens = session_get('csrf'); + if (isset($tokens[$token])) { + unset($tokens[$token]); + session_set('csrf', $tokens); return true; } diff --git a/app/Core/Session/FlashMessage.php b/app/Core/Session/FlashMessage.php index e02d056d..037717c2 100644 --- a/app/Core/Session/FlashMessage.php +++ b/app/Core/Session/FlashMessage.php @@ -7,7 +7,7 @@ use Kanboard\Core\Base; /** * Session Flash Message * - * @package session + * @package Kanboard\Core\Session * @author Frederic Guillot */ class FlashMessage extends Base @@ -43,11 +43,11 @@ class FlashMessage extends Base */ public function setMessage($key, $message) { - if (! isset($this->sessionStorage->flash)) { - $this->sessionStorage->flash = array(); + if (! session_exists('flash')) { + session_set('flash', []); } - $this->sessionStorage->flash[$key] = $message; + session_merge('flash', [$key => $message]); } /** @@ -61,9 +61,14 @@ class FlashMessage extends Base { $message = ''; - if (isset($this->sessionStorage->flash[$key])) { - $message = $this->sessionStorage->flash[$key]; - unset($this->sessionStorage->flash[$key]); + if (session_exists('flash')) { + $messages = session_get('flash'); + + if (isset($messages[$key])) { + $message = $messages[$key]; + unset($messages[$key]); + session_set('flash', $messages); + } } return $message; diff --git a/app/Core/Session/SessionHandler.php b/app/Core/Session/SessionHandler.php new file mode 100644 index 00000000..135e0ab0 --- /dev/null +++ b/app/Core/Session/SessionHandler.php @@ -0,0 +1,70 @@ +db = $db; + } + + public function close() + { + return true; + } + + public function destroy($sessionID) + { + return $this->db->table(self::TABLE)->eq('id', $sessionID)->remove(); + } + + public function gc($maxlifetime) + { + return $this->db->table(self::TABLE)->lt('expire_at', time())->remove(); + } + + public function open($savePath, $name) + { + return true; + } + + public function read($sessionID) + { + $result = $this->db->table(self::TABLE)->eq('id', $sessionID)->findOneColumn('data'); + return $result ?: ''; + } + + public function write($sessionID, $data) + { + $lifetime = time() + (ini_get('session.gc_maxlifetime') ?: 1440); + + if ($this->db->table(self::TABLE)->eq('id', $sessionID)->exists()) { + return $this->db->table(self::TABLE)->eq('id', $sessionID)->update(array( + 'expire_at' => $lifetime, + 'data' => $data, + )); + } + + return $this->db->table(self::TABLE)->insert(array( + 'id' => $sessionID, + 'expire_at' => $lifetime, + 'data' => $data, + )); + } +} diff --git a/app/Core/Session/SessionManager.php b/app/Core/Session/SessionManager.php index 4f9f2c0a..e3d5cf15 100644 --- a/app/Core/Session/SessionManager.php +++ b/app/Core/Session/SessionManager.php @@ -7,7 +7,7 @@ use Kanboard\Core\Base; /** * Session Manager * - * @package session + * @package Kanboard\Core\Session * @author Frederic Guillot */ class SessionManager extends Base @@ -38,6 +38,8 @@ class SessionManager extends Base */ public function open() { + session_set_save_handler(new SessionHandler($this->db), true); + $this->configure(); if (ini_get('session.auto_start') == 1) { @@ -46,8 +48,6 @@ class SessionManager extends Base session_name('KB_SID'); session_start(); - - $this->sessionStorage->setStorage($_SESSION); } /** diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php deleted file mode 100644 index bb6771f1..00000000 --- a/app/Core/Session/SessionStorage.php +++ /dev/null @@ -1,92 +0,0 @@ -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; - } - - /** - * Flush session data - * - * @access public - */ - public function flush() - { - $session = get_object_vars($this); - unset($session['storage']); - - foreach (array_keys($session) as $property) { - unset($this->$property); - } - } - - /** - * Copy class properties to external storage - * - * @access public - */ - public function __destruct() - { - $this->storage = $this->getAll(); - } -} diff --git a/app/Core/User/UserSession.php b/app/Core/User/UserSession.php index f3f7359a..0206be80 100644 --- a/app/Core/User/UserSession.php +++ b/app/Core/User/UserSession.php @@ -44,8 +44,8 @@ class UserSession extends Base $user['is_ldap_user'] = isset($user['is_ldap_user']) ? (bool) $user['is_ldap_user'] : false; $user['twofactor_activated'] = isset($user['twofactor_activated']) ? (bool) $user['twofactor_activated'] : false; - $this->sessionStorage->user = $user; - $this->sessionStorage->postAuthenticationValidated = false; + session_set('user', $user); + session_set('postAuthenticationValidated', false); } /** @@ -56,7 +56,7 @@ class UserSession extends Base */ public function getAll() { - return $this->sessionStorage->user; + return session_get('user'); } /** @@ -67,7 +67,11 @@ class UserSession extends Base */ public function getRole() { - return $this->sessionStorage->user['role']; + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['role']; } /** @@ -78,7 +82,7 @@ class UserSession extends Base */ public function isPostAuthenticationValidated() { - return isset($this->sessionStorage->postAuthenticationValidated) && $this->sessionStorage->postAuthenticationValidated === true; + return session_is_true('postAuthenticationValidated'); } /** @@ -88,7 +92,7 @@ class UserSession extends Base */ public function validatePostAuthentication() { - $this->sessionStorage->postAuthenticationValidated = true; + session_set('postAuthenticationValidated', true); } /** @@ -99,7 +103,11 @@ class UserSession extends Base */ public function hasPostAuthentication() { - return isset($this->sessionStorage->user['twofactor_activated']) && $this->sessionStorage->user['twofactor_activated'] === true; + if (! $this->isLogged()) { + return false; + } + + return session_get('user')['twofactor_activated'] === true; } /** @@ -109,7 +117,7 @@ class UserSession extends Base */ public function disablePostAuthentication() { - $this->sessionStorage->user['twofactor_activated'] = false; + session_merge('user', ['twofactor_activated' => false]); } /** @@ -120,7 +128,7 @@ class UserSession extends Base */ public function isAdmin() { - return isset($this->sessionStorage->user['role']) && $this->sessionStorage->user['role'] === Role::APP_ADMIN; + return $this->getRole() === Role::APP_ADMIN; } /** @@ -131,7 +139,11 @@ class UserSession extends Base */ public function getId() { - return isset($this->sessionStorage->user['id']) ? (int) $this->sessionStorage->user['id'] : 0; + if (! $this->isLogged()) { + return 0; + } + + return session_get('user')['id']; } /** @@ -142,7 +154,41 @@ class UserSession extends Base */ public function getUsername() { - return isset($this->sessionStorage->user['username']) ? $this->sessionStorage->user['username'] : ''; + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['username']; + } + + /** + * Get user language + * + * @access public + * @return string + */ + public function getLanguage() + { + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['language']; + } + + /** + * Get user timezone + * + * @access public + * @return string + */ + public function getTimezone() + { + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['timezone']; } /** @@ -153,7 +199,7 @@ class UserSession extends Base */ public function hasSubtaskListActivated() { - return isset($this->sessionStorage->subtaskListToggle) && ! empty($this->sessionStorage->subtaskListToggle); + return session_is_true('subtaskListToggle'); } /** @@ -164,30 +210,34 @@ class UserSession extends Base */ public function isLogged() { - return isset($this->sessionStorage->user) && ! empty($this->sessionStorage->user); + return session_exists('user') && session_get('user') !== []; } /** * Get project filters from the session * * @access public - * @param integer $project_id + * @param integer $projectID * @return string */ - public function getFilters($project_id) + public function getFilters($projectID) { - return ! empty($this->sessionStorage->filters[$project_id]) ? $this->sessionStorage->filters[$project_id] : 'status:open'; + if (! session_exists('filters:'.$projectID)) { + return 'status:open'; + } + + return session_get('filters:'.$projectID); } /** * Save project filters in the session * * @access public - * @param integer $project_id + * @param integer $projectID * @param string $filters */ - public function setFilters($project_id, $filters) + public function setFilters($projectID, $filters) { - $this->sessionStorage->filters[$project_id] = $filters; + session_set('filters:'.$projectID, $filters); } } -- cgit v1.2.3