Using cookies for ACP sessions
authorAlexander Ebert <ebert@woltlab.com>
Wed, 6 Apr 2016 14:48:10 +0000 (16:48 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 6 Apr 2016 14:55:11 +0000 (16:55 +0200)
wcfsetup/install/files/acp/templates/login.tpl
wcfsetup/install/files/lib/acp/form/LoginForm.class.php
wcfsetup/install/files/lib/form/LoginForm.class.php
wcfsetup/install/files/lib/system/WCFACP.class.php
wcfsetup/install/files/lib/system/session/ACPSessionFactory.class.php
wcfsetup/install/files/lib/system/session/SessionFactory.class.php
wcfsetup/install/files/lib/system/session/SessionHandler.class.php

index 0c20f605bf6916246ea8b6ea5fba35940b2bfd3f..229deb3759f359f185a96abac981ee92596a78a8 100644 (file)
@@ -2,7 +2,11 @@
 
 <div id="login" style="display: none">
        <form method="post" action="{link controller='Login'}{/link}">
-               {include file='formError'}
+               {if !$errorField|empty && $errorField == 'cookie'}
+                       <p class="error">{lang}wcf.user.login.error.cookieRequired{/lang}</p>
+               {else}
+                       {include file='formError'}
+               {/if}
                
                <dl{if $errorField == 'username'} class="formError"{/if}>
                        <dt><label for="username">{lang}wcf.user.username{/lang}</label></dt>
index bae20a48f128579a1d564717ad81a36809b8e4ba..890e194052e6dab9c39332b983c920b34703da3a 100755 (executable)
@@ -175,6 +175,10 @@ class LoginForm extends AbstractCaptchaForm {
        public function validate() {
                parent::validate();
                
+               if (!WCF::getSession()->hasValidCookie()) {
+                       throw new UserInputException('cookie');
+               }
+               
                // error handling
                if (empty($this->username)) {
                        throw new UserInputException('username');
index 320ae1e8fc41efa9b60f712ef295829a8bd10ecf..685f2ab4ff34ca6203b94a3067703ea38fc94c84 100644 (file)
@@ -54,17 +54,6 @@ class LoginForm extends \wcf\acp\form\LoginForm {
                if (isset($_POST['useCookies'])) $this->useCookies = intval($_POST['useCookies']);
        }
        
-       /**
-        * @see \wcf\form\IForm::validate()
-        */
-       public function validate() {
-               if (!WCF::getSession()->hasValidCookie()) {
-                       throw new UserInputException('cookie');
-               }
-               
-               parent::validate();
-       }
-       
        /**
         * @see \wcf\form\IForm::save()
         */
index cd0a87fba31ff8b91d02ed7638a7724fe43abf3c..7436fb4386f5e499300b93b480894776045e8f41 100644 (file)
@@ -182,10 +182,13 @@ class WCFACP extends WCF {
         * @see \wcf\system\WCF::initSession()
         */
        protected function initSession() {
+               self::$sessionObj = SessionHandler::getInstance();
+               self::$sessionObj->setCookieSuffix('_acp');
+               
                $factory = new ACPSessionFactory();
                $factory->load();
                
-               self::$sessionObj = SessionHandler::getInstance();
+               self::$sessionObj->setHasValidCookie($factory->hasValidCookie());
        }
        
        /**
index 1af4a4e8172001e38fbbd3ba543d254fe7b1d990..7f5b92fa3100cfcb141509e61569c66352bd03c5 100644 (file)
@@ -1,23 +1,31 @@
 <?php
 namespace wcf\system\session;
+use wcf\data\acp\session\ACPSessionEditor;
 use wcf\system\event\EventHandler;
+use wcf\util\HeaderUtil;
 
 /**
  * Handles the ACP session of the active user.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage system.session
  * @category   Community Framework
  */
 class ACPSessionFactory {
+       /**
+        * suffix used to tell ACP and frontend cookies apart
+        * @var string
+        */
+       protected $cookieSuffix = '_acp';
+       
        /**
         * session editor class name
         * @var string
         */
-       protected $sessionEditor = 'wcf\data\acp\session\ACPSessionEditor';
+       protected $sessionEditor = ACPSessionEditor::class;
        
        /**
         * Loads the object of the active session.
@@ -47,6 +55,12 @@ class ACPSessionFactory {
         * @since       2.2
         */
        public function hasValidCookie() {
+               if (isset($_COOKIE[COOKIE_PREFIX.'cookieHash'.$this->cookieSuffix])) {
+                       if ($_COOKIE[COOKIE_PREFIX.'cookieHash'.$this->cookieSuffix] == SessionHandler::getInstance()->sessionID) {
+                               return true;
+                       }
+               }
+               
                return false;
        }
        
@@ -54,25 +68,24 @@ class ACPSessionFactory {
         * Initializes the session system.
         */
        protected function init() {
+               if (!$this->hasValidCookie()) {
+                       // cookie support will be enabled upon next request
+                       HeaderUtil::setCookie('cookieHash'.$this->cookieSuffix, SessionHandler::getInstance()->sessionID);
+               }
+               
                SessionHandler::getInstance()->initSession();
        }
        
        /**
-        * Returns the session id from request (GET/POST). Returns an empty string,
-        * if no session id was given.
+        * Returns the session id from cookie. Returns an empty string,
+        * if no session cookie was provided.
         * 
         * @return      string
         */
        protected function readSessionID() {
-               if (isset($_GET['s'])) {
-                       if (is_string($_GET['s'])) {
-                               return $_GET['s'];
-                       }
-               }
-               else if (isset($_POST['s'])) {
-                       if (is_string($_POST['s'])) {
-                               return $_POST['s'];
-                       }
+               // get sessionID from cookie
+               if (isset($_COOKIE[COOKIE_PREFIX.'cookieHash'.$this->cookieSuffix])) {
+                       return $_COOKIE[COOKIE_PREFIX . 'cookieHash'.$this->cookieSuffix];
                }
                
                return '';
index 7348e323d5daface577f6b2a9d79931c5d7c8e39..40939d2e983fb8f4ba99c3d50cf74fc2500567f7 100644 (file)
@@ -1,12 +1,12 @@
 <?php
 namespace wcf\system\session;
-use wcf\util\HeaderUtil;
+use wcf\data\session\SessionEditor;
 
 /**
  * Handles the session of the active user.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage system.session
@@ -14,49 +14,12 @@ use wcf\util\HeaderUtil;
  */
 class SessionFactory extends ACPSessionFactory {
        /**
-        * @see \wcf\system\session\ACPSessionFactory::$sessionEditor
+        * @inheritDoc
         */
-       protected $sessionEditor = 'wcf\data\session\SessionEditor';
+       protected $cookieSuffix = '';
        
        /**
-        * @see \wcf\system\session\ACPSessionFactory::hasValidCookie()
-        * @since       2.2
+        * @inheritDoc
         */
-       public function hasValidCookie() {
-               if (isset($_COOKIE[COOKIE_PREFIX.'cookieHash'])) {
-                       if ($_COOKIE[COOKIE_PREFIX.'cookieHash'] == SessionHandler::getInstance()->sessionID) {
-                               return true;
-                       }
-               }
-               
-               return false;
-       }
-       
-       /**
-        * @see \wcf\system\session\ACPSessionFactory::readSessionID()
-        */
-       protected function readSessionID() {
-               // get sessionID from cookie
-               if (isset($_COOKIE[COOKIE_PREFIX.'cookieHash'])) {
-                       return $_COOKIE[COOKIE_PREFIX . 'cookieHash'];
-               }
-               
-               return '';
-       }
-       
-       /**
-        * @see \wcf\system\session\ACPSessionFactory::init()
-        */
-       protected function init() {
-               if (!$this->hasValidCookie()) {
-                       // cookie support will be enabled upon next request
-                       HeaderUtil::setCookie('cookieHash', SessionHandler::getInstance()->sessionID);
-               }
-               
-               // enable cookie support
-               
-               SessionHandler::getInstance()->enableCookies();
-               
-               parent::init();
-       }
+       protected $sessionEditor = SessionEditor::class;
 }
index 6352dda2d1a64bac4f24cab07443975d62646256..7dfc1c57aecebf2d3cc29309fc3ae65760b6052b 100644 (file)
@@ -37,6 +37,12 @@ use wcf\util\UserUtil;
  * @category   Community Framework
  */
 class SessionHandler extends SingletonFactory {
+       /**
+        * suffix used to tell ACP and frontend cookies apart
+        * @var string
+        */
+       protected $cookieSuffix = '';
+       
        /**
         * prevents update on shutdown
         * @var boolean
@@ -103,12 +109,6 @@ class SessionHandler extends SingletonFactory {
         */
        protected $styleID = null;
        
-       /**
-        * enable cookie support
-        * @var boolean
-        */
-       protected $useCookies = false;
-       
        /**
         * user object
         * @var \wcf\data\user\User
@@ -166,6 +166,15 @@ class SessionHandler extends SingletonFactory {
                $this->usersOnlyPermissions = UserGroupOptionCacheBuilder::getInstance()->getData(array(), 'usersOnlyOptions');
        }
        
+       /**
+        * Suffix used to tell ACP and frontend cookies apart
+        * 
+        * @param       string  $cookieSuffix   cookie suffix
+        */
+       public function setCookieSuffix($cookieSuffix) {
+               $this->cookieSuffix = $cookieSuffix;
+       }
+       
        /**
         * Sets a boolean value to determine if the client provided a valid session cookie.
         * 
@@ -252,25 +261,7 @@ class SessionHandler extends SingletonFactory {
                // fetch new session data from database
                $this->session = new $this->sessionClassName($newSessionID);
                
-               if ($this->useCookies) {
-                       // we know that the user accepts cookies, simply send new session id
-                       HeaderUtil::setCookie('cookieHash', $newSessionID);
-               }
-               else if ($_SERVER['REQUEST_METHOD'] === 'GET') {
-                       // user maybe does not accept cookies, replace session id in url
-                       // otherwise reloading the page will generate a new session
-                       
-                       $this->update();
-                       HeaderUtil::redirect(str_replace('s='.$oldSessionID, 's='.$newSessionID, UserUtil::getRequestURI()));
-                       exit;
-               }
-       }
-       
-       /**
-        * Enables cookie support.
-        */
-       public function enableCookies() {
-               $this->useCookies = true;
+               HeaderUtil::setCookie('cookieHash'.$this->cookieSuffix, $newSessionID);
        }
        
        /**
@@ -298,20 +289,12 @@ class SessionHandler extends SingletonFactory {
         * Defines global wcf constants related to session.
         */
        protected function defineConstants() {
-               if ($this->useCookies || $this->session->spiderID) {
-                       if (!defined('SID_ARG_1ST')) define('SID_ARG_1ST', '');
-                       if (!defined('SID_ARG_2ND')) define('SID_ARG_2ND', '');
-                       if (!defined('SID_ARG_2ND_NOT_ENCODED')) define('SID_ARG_2ND_NOT_ENCODED', '');
-                       if (!defined('SID')) define('SID', '');
-                       if (!defined('SID_INPUT_TAG')) define('SID_INPUT_TAG', '');
-               }
-               else {
-                       if (!defined('SID_ARG_1ST')) define('SID_ARG_1ST', '?s='.$this->session->sessionID);
-                       if (!defined('SID_ARG_2ND')) define('SID_ARG_2ND', '&amp;s='.$this->session->sessionID);
-                       if (!defined('SID_ARG_2ND_NOT_ENCODED')) define('SID_ARG_2ND_NOT_ENCODED', '&s='.$this->session->sessionID);
-                       if (!defined('SID')) define('SID', $this->session->sessionID);
-                       if (!defined('SID_INPUT_TAG')) define('SID_INPUT_TAG', '<input type="hidden" name="s" value="'.$this->session->sessionID.'" />');
-               }
+               /* the SID*-constants below are deprecated since 2.2 */
+               if (!defined('SID_ARG_1ST')) define('SID_ARG_1ST', '');
+               if (!defined('SID_ARG_2ND')) define('SID_ARG_2ND', '');
+               if (!defined('SID_ARG_2ND_NOT_ENCODED')) define('SID_ARG_2ND_NOT_ENCODED', '');
+               if (!defined('SID')) define('SID', '');
+               if (!defined('SID_INPUT_TAG')) define('SID_INPUT_TAG', '');
                
                // security token
                if (!defined('SECURITY_TOKEN')) define('SECURITY_TOKEN', $this->getSecurityToken());
@@ -777,7 +760,7 @@ class SessionHandler extends SingletonFactory {
                                        
                                        $this->session = call_user_func(array($this->sessionEditorClassName, 'create'), $sessionData);
                                        
-                                       HeaderUtil::setCookie('cookieHash', $this->session->sessionID);
+                                       HeaderUtil::setCookie('cookieHash'.$this->cookieSuffix, $this->session->sessionID);
                                }
                                else {
                                        // this was the last virtual session, re-use current session
@@ -844,7 +827,7 @@ class SessionHandler extends SingletonFactory {
                                                $this->register('__SECURITY_TOKEN', $variables['__SECURITY_TOKEN']);
                                        }
                                        
-                                       HeaderUtil::setCookie('cookieHash', $this->session->sessionID);
+                                       HeaderUtil::setCookie('cookieHash'.$this->cookieSuffix, $this->session->sessionID);
                                }
                        break;
                }