Add update instructions for session refactoring
authorTim Düsterhus <duesterhus@woltlab.com>
Thu, 22 Oct 2020 12:47:56 +0000 (14:47 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 26 Nov 2020 10:43:12 +0000 (11:43 +0100)
com.woltlab.wcf/package.xml
wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_1_cookies.php [new file with mode: 0644]
wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_2_user_session.php [new file with mode: 0644]
wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_3_drop_virtual.php [new file with mode: 0644]
wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_4_clean_acp_session.php [new file with mode: 0644]
wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_updateStyle.php [new file with mode: 0644]

index bf5cfa36617e3ae66d1cfe09d19c846354bf4fb5..3dea4502d45ade464eeccd266637c7cb55f5527c 100644 (file)
        </instructions>
        
        <instructions type="update" fromversion="5.3.*">
+               <!--
+tar cvf com.woltlab.wcf/files_pre.tar -C wcfsetup/install/files/ \
+       acp/update_com.woltlab.wcf_5.4_checkOwnerGroup.php \
+       acp/update_com.woltlab.wcf_5.4_session_1_cookies.php \
+       acp/update_com.woltlab.wcf_5.4_session_2_user_session.php \
+       lib/util/HeaderUtil.class.php \
+       lib/system/package/plugin/AbstractPackageInstallationPlugin.class.php \
+       lib/system/package/plugin/ScriptPackageInstallationPlugin.class.php \
+       lib/system/package/plugin/FilePackageInstallationPlugin.class.php
+               -->
+               <instruction type="file" run="standalone">files_pre.tar</instruction>
+               
                <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_checkOwnerGroup.php</instruction>
                
-               <instruction type="file" />
+               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_session_1_cookies.php</instruction>
+               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_session_2_user_session.php</instruction>
+               
+               <instruction type="file" skipStyleUpdate="true" />
                <instruction type="acpTemplate" />
                <instruction type="template" />
                
-               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_removeFiles.php</instruction>
+               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_session_3_drop_virtual.php</instruction>
+               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_session_4_clean_acp_session.php</instruction>
                <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_db.php</instruction>
                
+               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_removeFiles.php</instruction>
+               
+               <instruction type="script" run="standalone">acp/update_com.woltlab.wcf_5.4_updateStyle.php</instruction>
+               
                <instruction type="objectTypeDefinition" />
                <instruction type="option" />
        </instructions>
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_1_cookies.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_1_cookies.php
new file mode 100644 (file)
index 0000000..7bd8faf
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Sets the new session cookies.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core
+ */
+
+use wcf\system\application\ApplicationHandler;
+use wcf\system\form\container\GroupFormElementContainer;
+use wcf\system\form\element\LabelFormElement;
+use wcf\system\form\FormDocument;
+use wcf\system\request\RouteHandler;
+use wcf\system\WCF;
+use wcf\util\CryptoUtil;
+use wcf\util\HeaderUtil;
+
+// 1) Check whether the cookies are already in place.
+$sessionIdOkay = false;
+if (!empty($_COOKIE[COOKIE_PREFIX."acp_session"])) {
+       $cookieValue = CryptoUtil::getValueFromSignedString($_COOKIE[COOKIE_PREFIX."acp_session"]);
+       if ($cookieValue) {
+               $sessionID = \bin2hex($cookieValue);
+               if ($sessionID === WCF::getSession()->sessionID) {
+                       $sessionIdOkay = true;
+               }
+       }
+}
+
+$xsrfTokenOkay = false;
+if (!empty($_COOKIE['XSRF-TOKEN'])) {
+       if (CryptoUtil::validateSignedString($_COOKIE['XSRF-TOKEN'])) {
+               $xsrfTokenOkay = true;
+       }
+}
+
+if ($sessionIdOkay && $xsrfTokenOkay) {
+       // The process may continue;
+       return;
+}
+
+// 2) Set new session cookie.
+HeaderUtil::setCookie(
+       "acp_session",
+       CryptoUtil::createSignedString(
+               \hex2bin(WCF::getSession()->sessionID)
+       )
+);
+
+// 3) Set new XSRF-TOKEN cookie.
+$sameSite = $cookieDomain = '';
+if (ApplicationHandler::getInstance()->isMultiDomainSetup()) {
+       // We need to specify the cookieDomain in a multi domain set-up, because
+       // otherwise no cookies are sent to subdomains.
+       $cookieDomain = HeaderUtil::getCookieDomain();
+       $cookieDomain = ($cookieDomain !== null ? '; domain='.$cookieDomain : '');
+}
+else {
+       // SameSite=strict is not supported in a multi domain set-up, because
+       // it breaks cross-application requests.
+       $sameSite = '; SameSite=strict';
+}
+
+do {
+       $bytes = \bin2hex(\random_bytes(16));
+}
+while (strpos(base64_encode($bytes), '+') !== false);
+$xsrfToken = CryptoUtil::createSignedString($bytes);
+WCF::getSession()->register('__SECURITY_TOKEN', $xsrfToken);
+\header('set-cookie: XSRF-TOKEN='.\rawurlencode($xsrfToken).'; path=/'.$cookieDomain.(RouteHandler::secureConnection() ? '; secure' : '').$sameSite, false);
+
+// 4) Adjust the SECURITY_TOKEN.
+$container = new GroupFormElementContainer();
+$container->setLabel('Set Cookies'); // TODO
+$container->setDescription('Sets cookies');
+
+$label = new LabelFormElement($container);
+$label->setLabel('Set Cookies');
+$label->setDescription(
+<<<EOT
+<script>(function() {
+var oldToken = SECURITY_TOKEN;
+SECURITY_TOKEN = encodeURIComponent("$xsrfToken");
+var oldExecute = WCF.ACP.Package.Installation.prototype._executeStep;
+WCF.ACP.Package.Installation.prototype._executeStep = function (step, node, additionalData) {
+       var request = this._proxy._ajaxRequest;
+       request.setOption('url', request.getOption('url').replace(oldToken, SECURITY_TOKEN));
+
+       return oldExecute.call(this, step, node, additionalData);
+}
+})();
+</script>
+EOT
+);
+
+$container->appendChild($label);
+
+$document = new FormDocument("cookies_set");
+$document->appendContainer($container);
+
+return $document;
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_2_user_session.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_2_user_session.php
new file mode 100644 (file)
index 0000000..2c21d25
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+use wcf\system\database\table\column\CharDatabaseTableColumn;
+use wcf\system\database\table\column\IntDatabaseTableColumn;
+use wcf\system\database\table\column\MediumblobDatabaseTableColumn;
+use wcf\system\database\table\column\NotNullInt10DatabaseTableColumn;
+use wcf\system\database\table\column\NotNullVarchar255DatabaseTableColumn;
+use wcf\system\database\table\column\VarcharDatabaseTableColumn;
+use wcf\system\database\table\DatabaseTable;
+use wcf\system\database\table\DatabaseTableChangeProcessor;
+use wcf\system\database\table\index\DatabaseTableForeignKey;
+use wcf\system\database\table\index\DatabaseTableIndex;
+use wcf\system\WCF;
+
+/**
+ * Creates the user_session table.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core
+ */
+
+$tables = [
+       DatabaseTable::create('wcf1_user_session')
+               ->columns([
+                       CharDatabaseTableColumn::create('sessionID')
+                               ->length(40)
+                               ->notNull(),
+                       IntDatabaseTableColumn::create('userID')
+                               ->length(10),
+                       NotNullVarchar255DatabaseTableColumn::create('userAgent')
+                               ->defaultValue(''),
+                       VarcharDatabaseTableColumn::create('ipAddress')
+                               ->length(39)
+                               ->defaultValue(''),
+                       NotNullInt10DatabaseTableColumn::create('lastActivityTime')
+                               ->defaultValue(0),
+                       MediumblobDatabaseTableColumn::create('sessionVariables'),
+               ])
+               ->indices([
+                       DatabaseTableIndex::create()
+                               ->type(DatabaseTableIndex::PRIMARY_TYPE)
+                               ->columns(['sessionID']),
+                       DatabaseTableIndex::create()
+                               ->columns(['userID']),
+                       DatabaseTableIndex::create()
+                               ->columns(['lastActivityTime']),
+               ])
+               ->foreignKeys([
+                       DatabaseTableForeignKey::create()
+                               ->columns(['userID'])
+                               ->referencedTable('wcf1_user')
+                               ->referencedColumns(['userID'])
+                               ->onDelete('CASCADE')
+               ]),
+];
+
+(new DatabaseTableChangeProcessor(
+/** @var ScriptPackageInstallationPlugin $this */
+       $this->installation->getPackage(),
+       $tables,
+       WCF::getDB()->getEditor())
+)->process();
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_3_drop_virtual.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_3_drop_virtual.php
new file mode 100644 (file)
index 0000000..6c6a678
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+use wcf\system\database\table\DatabaseTableChangeProcessor;
+use wcf\system\database\table\PartialDatabaseTable;
+use wcf\system\WCF;
+
+/**
+ * Removes the *_session_virtual tables.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core
+ */
+
+$tables = [
+       PartialDatabaseTable::create('wcf1_session_virtual')
+               ->drop(),
+       PartialDatabaseTable::create('wcf1_acp_session_virtual')
+               ->drop(),
+];
+
+(new DatabaseTableChangeProcessor(
+/** @var ScriptPackageInstallationPlugin $this */
+       $this->installation->getPackage(),
+       $tables,
+       WCF::getDB()->getEditor())
+)->process();
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_4_clean_acp_session.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_session_4_clean_acp_session.php
new file mode 100644 (file)
index 0000000..99ebb55
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+use wcf\system\database\table\column\MediumblobDatabaseTableColumn;
+use wcf\system\database\table\column\NotNullVarchar255DatabaseTableColumn;
+use wcf\system\database\table\column\VarcharDatabaseTableColumn;
+use wcf\system\database\table\DatabaseTableChangeProcessor;
+use wcf\system\database\table\index\DatabaseTableIndex;
+use wcf\system\database\table\PartialDatabaseTable;
+use wcf\system\WCF;
+
+/**
+ * Adjusts wcf1_acp_session and wcf1_acp_session_access_log.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core
+ */
+
+$tables = [
+       PartialDatabaseTable::create('wcf1_acp_session')
+               ->columns([
+                       VarcharDatabaseTableColumn::create('requestURI')->drop(),
+                       VarcharDatabaseTableColumn::create('requestMethod')->drop(),
+                       MediumblobDatabaseTableColumn::create('sessionVariables'),
+               ])
+               ->indices([
+                       DatabaseTableIndex::create()
+                               ->columns(['userID']),
+                       DatabaseTableIndex::create()
+                               ->columns(['lastActivityTime']),
+               ]),
+       PartialDatabaseTable::create('wcf1_acp_session_access_log')
+               ->columns([
+                       NotNullVarchar255DatabaseTableColumn::create('requestMethod')
+                               ->defaultValue(''),
+               ])
+];
+
+(new DatabaseTableChangeProcessor(
+/** @var ScriptPackageInstallationPlugin $this */
+       $this->installation->getPackage(),
+       $tables,
+       WCF::getDB()->getEditor())
+)->process();
diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_updateStyle.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_updateStyle.php
new file mode 100644 (file)
index 0000000..64c3700
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+use wcf\util\StyleUtil;
+
+/**
+ * Runs the skipped style update.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core
+ */
+
+StyleUtil::updateStyleFile();