--- /dev/null
+<?php
+namespace wcf\data\acp\session\virtual;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+use wcf\util\UserUtil;
+
+/**
+ * Virtual Sessions extend the original session system with a transparent layer.
+ * It's only purpose is to enforce session validation based on IP address and/or user agent.
+ *
+ * The legacy session system does not allow the same user being logged-in more than once
+ * and the same is true for WCF 2.1 unless we break most parts of the API.
+ * In order to solve this, we do allow multiple clients to share the exact same session
+ * among them, while the individual clients are tracked within wcf1_session_virtual.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.acp.session.virtual
+ * @category Community Framework
+ */
+class ACPSessionVirtual extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'acp_session_virtual';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'virtualSessionID';
+
+ /**
+ * Returns the active virtual session object or null.
+ *
+ * @param string $sessionID
+ * @return \wcf\data\session\virtual\SessionVirtual
+ */
+ public static function getExistingSession($sessionID) {
+ $sql = "SELECT *
+ FROM ".static::getDatabaseTableName()."
+ WHERE sessionID = ?
+ AND ipAddress = ?
+ AND userAgent = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $sessionID,
+ UserUtil::getIpAddress(),
+ UserUtil::getUserAgent()
+ ));
+
+ return $statement->fetchObject(static::class);
+ }
+
+ /**
+ * Returns the number of virtual sessions associated with the given session id.
+ *
+ * @param string $sessionID
+ * @return integer
+ */
+ public static function countVirtualSessions($sessionID) {
+ $sql = "SELECT COUNT(*) AS count
+ FROM ".static::getDatabaseTableName()."
+ WHERE sessionID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($sessionID));
+ $row = $statement->fetchArray();
+
+ return $row['count'];
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\acp\session\virtual;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\util\UserUtil;
+
+/**
+ * Executes virtual session-related actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.acp.session.virtual
+ * @category Community Framework
+ */
+class ACPSessionVirtualAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = ACPSessionVirtualEditor::class;
+
+ /**
+ * Attention: This method does not always return a new object, in case a matching virtual session
+ * already exists, the existing session will be returned rather than a new session being created.
+ *
+ * @see \wcf\data\AbstractDatabaseObjectAction::create()
+ */
+ public function create() {
+ // try to find an existing virtual session
+ $baseClass = call_user_func(array($this->className, 'getBaseClass'));
+ $virtualSession = call_user_func(array($baseClass, 'getExistingSession'), $this->parameters['data']['sessionID']);
+ if ($virtualSession !== null) {
+ return $virtualSession;
+ }
+
+ if (!isset($this->parameters['data']['lastActivityTime'])) $this->parameters['data']['lastActivityTime'] = TIME_NOW;
+ if (!isset($this->parameters['data']['ipAddress'])) $this->parameters['data']['ipAddress'] = UserUtil::getIpAddress();
+ if (!isset($this->parameters['data']['userAgent'])) $this->parameters['data']['userAgent'] = UserUtil::getUserAgent();
+
+ return parent::create();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\acp\session\virtual;
+use wcf\data\DatabaseObjectEditor;
+use wcf\system\WCF;
+
+/**
+ * Provides functions to edit virtual sessions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.acp.session.virtual
+ * @category Community Framework
+ */
+class ACPSessionVirtualEditor extends DatabaseObjectEditor {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = ACPSessionVirtual::class;
+
+ /**
+ * Updates last activity time of this virtual session.
+ */
+ public function updateLastActivityTime() {
+ $this->update(array(
+ 'lastActivityTime' => TIME_NOW
+ ));
+ }
+
+ /**
+ * Deletes the expired virtual sessions.
+ *
+ * @param integer $timestamp
+ */
+ public static function deleteExpiredSessions($timestamp) {
+ $sql = "DELETE FROM ".call_user_func(array(static::$baseClass, 'getDatabaseTableName'))."
+ WHERE lastActivityTime < ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($timestamp));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\acp\session\virtual;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of virtual sessions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.acp.session.virtual
+ * @category Community Framework
+ */
+class ACPSessionVirtualList extends DatabaseObjectList {
+ /**
+ * @see \wcf\data\DatabaseObjectList::$className
+ */
+ public $className = ACPSessionVirtual::class;
+}
<?php
namespace wcf\data\session\virtual;
-use wcf\data\DatabaseObject;
-use wcf\system\WCF;
-use wcf\util\UserUtil;
+use wcf\data\acp\session\virtual\ACPSessionVirtual;
/**
- * Virtual Sessions extend the original session system with a transparent layer.
- * It's only purpose is to enforce session validation based on IP address and/or user agent.
+ * Virtual sessions for the frontend.
*
- * The legacy session system does not allow the same user being logged-in more than once
- * and the same is true for WCF 2.1 unless we break most parts of the API.
- * In order to solve this, we do allow multiple clients to share the exact same session
- * among them, while the individual clients are tracked within wcf1_session_virtual.
- *
- * @author Alexander Ebert
+ * @see \wcf\data\acp\session\virtual\ACPSessionVirtual
+ * @author Tim Duesterhus
* @copyright 2001-2015 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package com.woltlab.wcf
* @property-read string $userAgent
* @property-read integer $lastActivityTime
*/
-class SessionVirtual extends DatabaseObject {
+class SessionVirtual extends ACPSessionVirtual {
/**
- * @see \wcf\data\DatabaseObject::$databaseTableName
+ * @inheritDoc
*/
protected static $databaseTableName = 'session_virtual';
/**
- * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+ * @inheritDoc
*/
protected static $databaseTableIndexName = 'virtualSessionID';
-
- /**
- * Returns the active virtual session object or null.
- *
- * @param string $sessionID
- * @return \wcf\data\session\virtual\SessionVirtual
- */
- public static function getExistingSession($sessionID) {
- $sql = "SELECT *
- FROM ".static::getDatabaseTableName()."
- WHERE sessionID = ?
- AND ipAddress = ?
- AND userAgent = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array(
- $sessionID,
- UserUtil::getIpAddress(),
- UserUtil::getUserAgent()
- ));
-
- return $statement->fetchObject(__CLASS__);
- }
-
- /**
- * Returns the number of virtual sessions associated with the given session id.
- *
- * @param string $sessionID
- * @return integer
- */
- public static function countVirtualSessions($sessionID) {
- $sql = "SELECT COUNT(*) AS count
- FROM ".static::getDatabaseTableName()."
- WHERE sessionID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array($sessionID));
- $row = $statement->fetchArray();
-
- return $row['count'];
- }
}
<?php
namespace wcf\data\session\virtual;
-use wcf\data\AbstractDatabaseObjectAction;
-use wcf\util\UserUtil;
+use wcf\data\acp\session\virtual\ACPSessionVirtualAction;
/**
- * Executes virtual session-related actions.
+ * Virtual sessions for the frontend.
*
- * @author Alexander Ebert
+ * @see \wcf\data\acp\session\virtual\ACPSessionVirtualAction
+ * @author Tim Duesterhus
* @copyright 2001-2015 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package com.woltlab.wcf
* @subpackage data.session.virtual
* @category Community Framework
*/
-class SessionVirtualAction extends AbstractDatabaseObjectAction {
+class SessionVirtualAction extends ACPSessionVirtualAction {
/**
- * @see \wcf\data\AbstractDatabaseObjectAction::$className
+ * @inheritDoc
*/
- protected $className = 'wcf\data\session\virtual\SessionVirtualEditor';
-
- /**
- * Attention: This method does not always return a new object, in case a matching virtual session
- * already exists, the existing session will be returned rather than a new session being created.
- *
- * @see \wcf\data\AbstractDatabaseObjectAction::create()
- */
- public function create() {
- // try to find an existing virtual session
- $baseClass = call_user_func(array($this->className, 'getBaseClass'));
- $virtualSession = call_user_func(array($baseClass, 'getExistingSession'), $this->parameters['data']['sessionID']);
- if ($virtualSession !== null) {
- return $virtualSession;
- }
-
- if (!isset($this->parameters['data']['lastActivityTime'])) $this->parameters['data']['lastActivityTime'] = TIME_NOW;
- if (!isset($this->parameters['data']['ipAddress'])) $this->parameters['data']['ipAddress'] = UserUtil::getIpAddress();
- if (!isset($this->parameters['data']['userAgent'])) $this->parameters['data']['userAgent'] = UserUtil::getUserAgent();
-
- return parent::create();
- }
+ protected $className = SessionVirtualEditor::class;
}
<?php
namespace wcf\data\session\virtual;
-use wcf\data\DatabaseObjectEditor;
-use wcf\system\WCF;
+use wcf\data\acp\session\virtual\ACPSessionVirtualEditor;
/**
- * Provides functions to edit virtual sessions.
+ * Virtual sessions for the frontend.
*
- * @author Alexander Ebert
+ * @see \wcf\data\acp\session\virtual\ACPSessionVirtualEditor
+ * @author Tim Duesterhus
* @copyright 2001-2015 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package com.woltlab.wcf
* @subpackage data.session.virtual
* @category Community Framework
*/
-class SessionVirtualEditor extends DatabaseObjectEditor {
+class SessionVirtualEditor extends ACPSessionVirtualEditor {
/**
- * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ * @inheritDoc
*/
- protected static $baseClass = 'wcf\data\session\virtual\SessionVirtual';
-
- /**
- * Updates last activity time of this virtual session.
- */
- public function updateLastActivityTime() {
- $this->update(array(
- 'lastActivityTime' => TIME_NOW
- ));
- }
-
- /**
- * Deletes the expired virtual sessions.
- *
- * @param integer $timestamp
- */
- public static function deleteExpiredSessions($timestamp) {
- $sql = "DELETE FROM ".call_user_func(array(static::$baseClass, 'getDatabaseTableName'))."
- WHERE lastActivityTime < ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array($timestamp));
- }
+ protected static $baseClass = SessionVirtual::class;
}
<?php
namespace wcf\data\session\virtual;
-use wcf\data\DatabaseObjectList;
+use wcf\data\acp\session\virtual\ACPSessionVirtualList;
/**
- * Represents a list of virtual sessions.
+ * Virtual sessions for the frontend.
*
- * @author Alexander Ebert
+ * @see \wcf\data\acp\session\virtual\ACPSessionVirtualList
+ * @author Tim Duesterhus
* @copyright 2001-2015 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package com.woltlab.wcf
* @subpackage data.session.virtual
* @category Community Framework
*/
-class SessionVirtualList extends DatabaseObjectList {
+class SessionVirtualList extends ACPSessionVirtualList {
/**
- * @see \wcf\data\DatabaseObjectList::$className
+ * @inheritDoc
*/
- public $className = 'wcf\data\session\virtual\SessionVirtual';
+ public $className = SessionVirtual::class;
}
<?php
namespace wcf\system\session;
+use wcf\data\acp\session\virtual\ACPSessionVirtual;
+use wcf\data\acp\session\virtual\ACPSessionVirtualAction;
+use wcf\data\acp\session\virtual\ACPSessionVirtualEditor;
use wcf\data\session\virtual\SessionVirtual;
use wcf\data\session\virtual\SessionVirtualAction;
use wcf\data\session\virtual\SessionVirtualEditor;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
+use wcf\system\WCFACP;
use wcf\util\HeaderUtil;
use wcf\util\PasswordUtil;
use wcf\util\StringUtil;
}
$this->user = new User($this->session->userID);
- if ($this->supportsVirtualSessions) $this->virtualSession = SessionVirtual::getExistingSession($sessionID);
+ if (class_exists(WCFACP::class, false)) {
+ $this->virtualSession = ACPSessionVirtual::getExistingSession($sessionID);
+ }
+ else {
+ $this->virtualSession = SessionVirtual::getExistingSession($sessionID);
+ }
if (!$this->validate()) {
$this->session = null;
return;
}
- if ($this->supportsVirtualSessions) $this->loadVirtualSession();
+ $this->loadVirtualSession();
}
/**
if ($this->virtualSession === null || $forceReload) {
$this->virtualSession = null;
if ($this->supportsVirtualSessions) {
- $virtualSessionAction = new SessionVirtualAction(array(), 'create', array('data' => array('sessionID' => $this->session->sessionID)));
+ if (class_exists(WCFACP::class, false)) {
+ $virtualSessionAction = new ACPSessionVirtualAction(array(), 'create', array('data' => array('sessionID' => $this->session->sessionID)));
+ }
+ else {
+ $virtualSessionAction = new SessionVirtualAction(array(), 'create', array('data' => array('sessionID' => $this->session->sessionID)));
+ }
try {
$returnValues = $virtualSessionAction->executeAction();
// MySQL error 23000 = unique key
// do not check against the message itself, some weird systems localize them
if ($e->getCode() == 23000) {
- $this->virtualSession = SessionVirtual::getExistingSession($this->session->sessionID);
+ if (class_exists(WCFACP::class, false)) {
+ $this->virtualSession = ACPSessionVirtual::getExistingSession($this->session->sessionID);
+ }
+ else {
+ $this->virtualSession = SessionVirtual::getExistingSession($this->session->sessionID);
+ }
}
}
}
*/
protected function validate() {
if (SESSION_VALIDATE_IP_ADDRESS) {
- if ($this->supportsVirtualSessions && ($this->virtualSession instanceof SessionVirtual)) {
+ if ($this->supportsVirtualSessions && ($this->virtualSession instanceof ACPSessionVirtual)) {
if ($this->virtualSession->ipAddress != UserUtil::getIpAddress()) {
return false;
}
}
if (SESSION_VALIDATE_USER_AGENT) {
- if ($this->supportsVirtualSessions && ($this->virtualSession instanceof SessionVirtual)) {
+ if ($this->supportsVirtualSessions && ($this->virtualSession instanceof ACPSessionVirtual)) {
if ($this->virtualSession->userAgent != UserUtil::getUserAgent()) {
return false;
}
case 0:
// delete virtual session
if ($this->virtualSession) {
- $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
+ if (class_exists(WCFACP::class, false)) {
+ $virtualSessionEditor = new ACPSessionVirtualEditor($this->virtualSession);
+ }
+ else {
+ $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
+ }
$virtualSessionEditor->delete();
}
+ if (class_exists(WCFACP::class, false)) {
+ $sessionCount = ACPSessionVirtual::countVirtualSessions($this->session->sessionID);
+ }
+ else {
+ $sessionCount = SessionVirtual::countVirtualSessions($this->session->sessionID);
+ }
+
// there are still other virtual sessions, create a new session
- if (SessionVirtual::countVirtualSessions($this->session->sessionID)) {
+ if ($sessionCount) {
// save session
$sessionData = array(
'sessionID' => StringUtil::getRandomID(),
$sessionEditor = new $this->sessionEditorClassName($this->session);
$sessionEditor->update($data);
- if ($this->virtualSession instanceof SessionVirtual) {
- $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
+ if ($this->virtualSession instanceof ACPSessionVirtual) {
+ if (class_exists(WCFACP::class, false)) {
+ $virtualSessionEditor = new ACPSessionVirtualEditor($this->virtualSession);
+ }
+ else {
+ $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
+ }
$virtualSessionEditor->updateLastActivityTime();
}
}
'lastActivityTime' => TIME_NOW
));
- if ($this->virtualSession instanceof SessionVirtual) {
- $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
+ if ($this->virtualSession instanceof ACPSessionVirtual) {
+ if (class_exists(WCFACP::class, false)) {
+ $virtualSessionEditor = new ACPSessionVirtualEditor($this->virtualSession);
+ }
+ else {
+ $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
+ }
$virtualSessionEditor->updateLastActivityTime();
}
}
self::resetSessions(array($this->user->userID));
// update last activity time
- if (!class_exists('\wcf\system\WCFACP', false)) {
+ if (!class_exists(WCFACP::class, false)) {
$editor = new UserEditor($this->user);
$editor->update(array('lastActivityTime' => TIME_NOW));
}
KEY sessionID (sessionID)
);
+DROP TABLE IF EXISTS wcf1_acp_session_virtual;
+CREATE TABLE wcf1_acp_session_virtual (
+ virtualSessionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ sessionID CHAR(40) NOT NULL,
+ ipAddress VARCHAR(39) NOT NULL DEFAULT '',
+ userAgent VARCHAR(255) NOT NULL DEFAULT '',
+ lastActivityTime INT(10) NOT NULL DEFAULT 0,
+ UNIQUE KEY (sessionID, ipAddress, userAgent)
+);
+
DROP TABLE IF EXISTS wcf1_acp_template;
CREATE TABLE wcf1_acp_template (
templateID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ALTER TABLE wcf1_acp_session_log ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+ALTER TABLE wcf1_acp_session_virtual ADD FOREIGN KEY (sessionID) REFERENCES wcf1_acp_session (sessionID) ON DELETE CASCADE ON UPDATE CASCADE;
+
ALTER TABLE wcf1_acp_template ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
ALTER TABLE wcf1_ad ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;