From: Alexander Ebert Date: Sun, 25 May 2014 20:35:49 +0000 (+0200) Subject: Prevents rare race condition during user login X-Git-Tag: 2.1.0_Alpha_1~711^2~10 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=46dd7463a7dac713dfe7a217b5edf97d25c0d376;p=GitHub%2FWoltLab%2FWCF.git Prevents rare race condition during user login In some cases it might happen, that two logins for the same users occur almost synchronously (e.g. starting browsers with two or more tabs pointing to the same URL). In that case it is possible that two session would have been created since the creation of the session takes place after the check from the second request passed. --- diff --git a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php index 4f1151a1d6..176080ac63 100644 --- a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php +++ b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php @@ -18,6 +18,7 @@ use wcf\util\HeaderUtil; use wcf\util\PasswordUtil; use wcf\util\StringUtil; use wcf\util\UserUtil; +use wcf\system\database\DatabaseException; /** * Handles sessions. @@ -561,7 +562,7 @@ class SessionHandler extends SingletonFactory { WHERE sessionID <> ? AND userID = ?"; $statement = WCF::getDB()->prepareStatement($sql); - $statement->execute(array($this->sessionID, $user->userID)); + //$statement->execute(array($this->sessionID, $user->userID)); // reset session variables $this->variables = array(); @@ -574,9 +575,33 @@ class SessionHandler extends SingletonFactory { if (!$hideSession) { // update session $sessionEditor = new $this->sessionEditorClassName($this->session); - $sessionEditor->update(array( - 'userID' => $this->user->userID - )); + + try { + $sessionEditor->update(array( + 'userID' => $this->user->userID + )); + } + catch (DatabaseException $e) { + // MySQL error 23000 = unique key + // do not check against the message itself, some weird systems localize them + if ($e->getCode() == 23000) { + // 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)); + + // update session + $sessionEditor->update(array( + 'userID' => $user->userID + )); + } + else { + // not our business + throw $e; + } + } } // reset caches @@ -646,9 +671,30 @@ class SessionHandler extends SingletonFactory { if ($session === null) { // update session $sessionEditor = new $this->sessionEditorClassName($this->session); - $sessionEditor->update(array( - 'userID' => $user->userID - )); + + try { + $sessionEditor->update(array( + 'userID' => $user->userID + )); + } + catch (DatabaseException $e) { + // MySQL error 23000 = unique key + // do not check against the message itself, some weird systems localize them + if ($e->getCode() == 23000) { + // find existing session for this user + $session = call_user_func(array($this->sessionClassName, 'getSessionByUserID'), $user->userID); + + // update session + $sessionEditor = new $this->sessionEditorClassName($this->session); + $sessionEditor->update(array( + 'userID' => $user->userID + )); + } + else { + // not our business + throw $e; + } + } } else { // delete guest session diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 9227c39d03..654ef966fe 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -835,7 +835,8 @@ CREATE TABLE wcf1_session ( objectID INT(10) NOT NULL DEFAULT 0, sessionVariables MEDIUMTEXT, spiderID INT(10), - KEY packageID (lastActivityTime, spiderID) + KEY packageID (lastActivityTime, spiderID), + UNIQUE KEY userID (userID) ); DROP TABLE IF EXISTS wcf1_session_virtual;