From ac094463c6d04389f41a628662900feec34418cf Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Sun, 20 Apr 2014 16:12:32 +0200 Subject: [PATCH] Improved session handling, added changeUserVirtual() Instead of altering changeUser(), I've added changeUserVirtual() (and reverted the original method), because all these numerous different cases would have caused the method to be a nightmare. --- .../system/session/SessionHandler.class.php | 155 ++++++++++++------ 1 file changed, 107 insertions(+), 48 deletions(-) diff --git a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php index 670b2ad96e..4f1151a1d6 100644 --- a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php +++ b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php @@ -14,6 +14,7 @@ use wcf\system\user\authentication\UserAuthenticationFactory; use wcf\system\user\storage\UserStorageHandler; use wcf\system\SingletonFactory; use wcf\system\WCF; +use wcf\util\HeaderUtil; use wcf\util\PasswordUtil; use wcf\util\StringUtil; use wcf\util\UserUtil; @@ -545,61 +546,131 @@ class SessionHandler extends SingletonFactory { * * @param \wcf\data\userUser $user * @param boolean $hideSession if true, database won't be updated + * @return boolean */ public function changeUser(User $user, $hideSession = false) { + if ($this->supportsVirtualSessions) { + return $this->changeUserVirtual($user); + } + $sessionTable = call_user_func(array($this->sessionClassName, 'getDatabaseTableName')); - $isNewSession = true; - if ($user->userID) { - if ($this->supportsVirtualSessions) { - // find existing session + if ($user->userID && !$hideSession) { + // user is not a guest, delete all other sessions of this user + $sql = "DELETE FROM ".$sessionTable." + WHERE sessionID <> ? + AND userID = ?"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute(array($this->sessionID, $user->userID)); + + // reset session variables + $this->variables = array(); + $this->variablesChanged = true; + } + + // update user reference + $this->user = $user; + + if (!$hideSession) { + // update session + $sessionEditor = new $this->sessionEditorClassName($this->session); + $sessionEditor->update(array( + 'userID' => $this->user->userID + )); + } + + // reset caches + $this->groupData = null; + $this->languageIDs = null; + $this->languageID = $this->user->languageID; + $this->styleID = $this->user->styleID; + + return true; + } + + /** + * Changes the user stored in the session, this method is different from changeUser() because it + * attempts to re-use sessions unless there are other virtual sessions for the same user (userID != 0). + * In reverse, logging out attempts to re-use the current session or spawns a new session depending + * on other virtual sessions. + * + * @param \wcf\data\user\User $user + */ + protected function changeUserVirtual(User $user) { + $sessionTable = call_user_func(array($this->sessionClassName, 'getDatabaseTableName')); + + switch ($user->userID) { + // + // user -> guest (logout) + // + case 0: + // delete virtual session + $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession); + $virtualSessionEditor->delete(); + + // there are still other virtual sessions, create a new session + if (SessionVirtual::countVirtualSessions($this->session->sessionID)) { + // save session + $sessionData = array( + 'sessionID' => StringUtil::getRandomID(), + 'userID' => $user->userID, + 'ipAddress' => UserUtil::getIpAddress(), + 'userAgent' => UserUtil::getUserAgent(), + 'lastActivityTime' => TIME_NOW, + 'requestURI' => UserUtil::getRequestURI(), + 'requestMethod' => (!empty($_SERVER['REQUEST_METHOD']) ? substr($_SERVER['REQUEST_METHOD'], 0, 7) : '') + ); + + $this->session = call_user_func(array($this->sessionEditorClassName, 'create'), $sessionData); + + HeaderUtil::setCookie('cookieHash', $this->session->sessionID); + } + else { + // this was the last virtual session, re-use current session + // update session + $sessionEditor = new $this->sessionEditorClassName($this->session); + $sessionEditor->update(array( + 'userID' => $user->userID + )); + } + break; + + // + // guest -> user (login) + // + default: + // find existing session for this user $session = call_user_func(array($this->sessionClassName, 'getSessionByUserID'), $user->userID); - if ($session !== null) { + // no session exists, re-use current session + if ($session === null) { + // update session + $sessionEditor = new $this->sessionEditorClassName($this->session); + $sessionEditor->update(array( + 'userID' => $user->userID + )); + } + else { // delete guest session $sessionEditor = new $this->sessionEditorClassName($this->session); $sessionEditor->delete(); // inherit existing session $this->session = $session; - $this->user = $user; - $this->loadVirtualSession(true); - - $isNewSession = false; } - } - else if (!$hideSession) { - // user is not a guest, delete all other sessions of this user - $sql = "DELETE FROM ".$sessionTable." - WHERE sessionID <> ? - AND userID = ?"; - $statement = WCF::getDB()->prepareStatement($sql); - $statement->execute(array($this->sessionID, $user->userID)); - - // reset session variables - $this->variables = array(); - $this->variablesChanged = true; - } + break; } - if ($isNewSession) { - // update user reference - $this->user = $user; - - if (!$hideSession) { - // update session - $sessionEditor = new $this->sessionEditorClassName($this->session); - $sessionEditor->update(array( - 'userID' => $this->user->userID - )); - } - } + $this->user = $user; + $this->loadVirtualSession(true); // reset caches $this->groupData = null; $this->languageIDs = null; $this->languageID = $this->user->languageID; $this->styleID = $this->user->styleID; + + return false; } /** @@ -671,22 +742,10 @@ class SessionHandler extends SingletonFactory { } // set user to guest - $this->changeUser(new User(null), true); + $deleteSession = $this->changeUser(new User(null), true); // remove session - $deleteSession = true; - if ($this->supportsVirtualSessions && ($this->virtualSession instanceof SessionVirtual)) { - // delete the virtual session - $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession); - $virtualSessionEditor->delete(); - - if (SessionVirtual::countVirtualSessions($this->session->sessionID)) { - // there are still remaining virtual sessions, do not delete master session - $deleteSession = false; - } - } - - if ($deleteSession) { + if ($deleteSession !== false) { $sessionEditor = new $this->sessionEditorClassName($this->session); $sessionEditor->delete(); } -- 2.20.1