Store PHP sessions in the database
This commit is contained in:
committed by
Frédéric Guillot
parent
421531bd4f
commit
ccd177ada6
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
70
app/Core/Session/SessionHandler.php
Normal file
70
app/Core/Session/SessionHandler.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Session;
|
||||
|
||||
use PicoDb\Database;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class SessionHandler
|
||||
*
|
||||
* @package Kanboard\Core\Session
|
||||
*/
|
||||
class SessionHandler implements SessionHandlerInterface
|
||||
{
|
||||
const TABLE = 'sessions';
|
||||
|
||||
/**
|
||||
* @var Database
|
||||
*/
|
||||
private $db;
|
||||
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->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,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Session;
|
||||
|
||||
/**
|
||||
* Session Storage
|
||||
*
|
||||
* @package session
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property array $user
|
||||
* @property array $flash
|
||||
* @property array $csrf
|
||||
* @property array $postAuthenticationValidated
|
||||
* @property array $filters
|
||||
* @property string $redirectAfterLogin
|
||||
* @property string $captcha
|
||||
* @property string $commentSorting
|
||||
* @property bool $hasSubtaskInProgress
|
||||
* @property bool $hasRememberMe
|
||||
* @property bool $subtaskListToggle
|
||||
* @property string $scope
|
||||
* @property bool $twoFactorBeforeCodeCalled
|
||||
* @property string $twoFactorSecret
|
||||
* @property string $oauthState
|
||||
* @property int $smsTwoFactorSecret
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user