Index: typo3/classes/class.ajaxlogin.php =================================================================== --- typo3/classes/class.ajaxlogin.php (revision 10504) +++ typo3/classes/class.ajaxlogin.php (working copy) @@ -43,14 +43,14 @@ * @return void */ public function login(array $parameters, TYPO3AJAX $ajaxObj) { - if ($GLOBALS['BE_USER']->user['uid']) { - $formprotection = t3lib_formprotection_Factory::get(); - $token = $formprotection->generateToken('extDirect'); - - $json = array( - 'success' => TRUE, - 'token' => $token - ); + if ($this->isAuthorizedBackendSession()) { + $json = array('success' => TRUE); + $token = ''; + if ($this->hasLoginBeenProcessed()) { + $formprotection = t3lib_formprotection_Factory::get(); + $json['accessToken'] = $formprotection->generateToken('refreshTokens'); + $formprotection->persistTokens(); + } } else { $json = array('success' => FALSE); } @@ -59,6 +59,30 @@ } /** + * Checks if a user is logged in and the session is active. + * + * @return boolean + */ + protected function isAuthorizedBackendSession() { + return (isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof t3lib_beUserAuth && isset($GLOBALS['BE_USER']->user['uid'])); + } + + /** + * Check whether the user was not already authorized + * + * @return boolean + */ + protected function hasLoginBeenProcessed() { + $loginFormData = $GLOBALS['BE_USER']->getLoginFormData(); + + return ($loginFormData['status'] == 'login') + && isset($loginFormData['uname']) + && isset($loginFormData['uident']) + && isset($loginFormData['chalvalue']) + && ((string)$_COOKIE['be_typo_user'] !== (string)$GLOBALS['BE_USER']->id); + } + + /** * Logs out the current BE user * * @param array $parameters: Parameters (not used) @@ -138,6 +162,43 @@ $parent->addContent('challenge', $_SESSION['login_challenge']); $parent->setContentFormat('json'); } + + /** + * Generates new tokens for the ones found in the DOM. + * + * @param array $parameters: Parameters (not used) + * @param TYPO3AJAX $parent: The calling parent AJAX object + */ + public function refreshTokens(array $parameters, TYPO3AJAX $parent) { + $accessToken = (string)t3lib_div::_GP('accessToken'); + $formprotection = t3lib_formprotection_Factory::get(); + + if ($formprotection->validateToken($accessToken, 'refreshTokens')) { + $oldTokens = json_decode((string)t3lib_div::_GP('tokens')); + $regeneratedTokens = new stdClass(); + + foreach ($oldTokens as $oldToken) { + $newToken = $this->generateNewToken($oldToken); + $regeneratedTokens->$oldToken = $newToken; + } + } + $parent->addContent('newTokens', $regeneratedTokens); + $parent->setContentFormat('json'); + + $formprotection->persistTokens(); + } + + /** + * Generate new token. + * + * @param string $oldToken + * @return string regenerated Token + */ + protected function generateNewToken($oldToken) { + list ($tokenId, $formName) = explode('-', $oldToken); + return t3lib_formprotection_Factory::get()->generateToken($formName) . '-' . $formName; + } + } if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/classes/class.ajaxlogin.php'])) { Index: typo3/js/loginrefresh.js =================================================================== --- typo3/js/loginrefresh.js (revision 10504) +++ typo3/js/loginrefresh.js (working copy) @@ -298,7 +298,7 @@ // User is logged in Ext.getCmp("loginformWindow").hide(); TYPO3.loginRefresh.startTimer(); - TYPO3.ExtDirectToken = result.token; + TYPO3.loginRefresh.refreshTokens(result.accessToken); } else { // TODO: add failure to notification system instead of alert Ext.Msg.alert(TYPO3.LLL.core.refresh_login_failed, TYPO3.LLL.core.refresh_login_failed_message); @@ -329,7 +329,105 @@ } else { this.submitForm(); } - } + }, + + getOutdatedTokens: function() { + var tokens = []; + + var contentFrame = window.frames["content"].document.window; + var navigationFrame = window.frames["navigation"].document.window; + var searchTokenPlaces = [top, contentFrame]; + + if (navigationFrame !== undefined) { + searchTokenPlaces.push(navigationFrame); + } + + Ext.each(searchTokenPlaces, function(searchPlace) { + var links = searchPlace.Ext.query('a[href*=formToken]'); + Ext.each(links, function(linkTag) { + tokens.push(Ext.urlDecode(linkTag.href).formToken); + }); + + var formFields = searchPlace.Ext.query("form input[name=formToken]"); + Ext.each(formFields, function(inputField) { + tokens.push(inputField.value); + }); + + var linksOnclick = searchPlace.Ext.query('a[onclick*=formToken]'); + Ext.each(linksOnclick, function(linkTag) { + tokens.push(linkTag.attributes.onclick.value.match(/&formToken=([^&]*)&/).pop()); + }); + + if (searchPlace.TYPO3.ExtDirectToken !== undefined) { + tokens.push(searchPlace.TYPO3.ExtDirectToken); + } + + }); + + return tokens; + }, + + replaceOutdatedTokens: function(newTokens) { + var contentFrame = window.frames["content"].document.window; + var navigationFrame = window.frames["navigation"].document.window; + var searchTokenPlaces = [top, contentFrame]; + + if (navigationFrame !== undefined) { + searchTokenPlaces.push(navigationFrame); + } + + Ext.each(searchTokenPlaces, function(searchPlace) { + var links = searchPlace.Ext.query('a[href*=formToken]'); + Ext.each(links, function(linkTag) { + var url = Ext.urlDecode(linkTag.href); + url.formToken = newTokens[url.formToken]; + linkTag.href = unescape(Ext.urlEncode(url)); + }); + + var formFields = searchPlace.Ext.query("form input[name=formToken]"); + Ext.each(formFields, function(inputField) { + inputField.value = newTokens[inputField.value]; + }); + + var linksOnclick = searchPlace.Ext.query('a[onclick*=formToken]'); + Ext.each(linksOnclick, function(linkTag) { + var token = linkTag.attributes.onclick.value.match(/&formToken=([^&]*)&/).pop(); + linkTag.attributes.onclick.value = linkTag.attributes.onclick.value.replace(new RegExp(token), newTokens[token]); + }); + + if (searchPlace.TYPO3.ExtDirectToken !== undefined) { + searchPlace.TYPO3.ExtDirectToken = newTokens[searchPlace.TYPO3.ExtDirectToken]; + } + }); + }, + + refreshTokens: function(accessToken) { + Ext.Ajax.request({ + url: "ajax.php", + params: { + "ajaxID": "BackendLogin::refreshTokens", + "accessToken": accessToken, + "tokens": Ext.encode(this.getOutdatedTokens()) + }, + method: "POST", + scope: this, + success: function(response, opts) { + var result = Ext.util.JSON.decode(response.responseText); + TYPO3.loginRefresh.replaceOutdatedTokens(result.newTokens); + }, + failure: function(response, opts) { + TYPO3.Flashmessage.display( + TYPO3.Severity.error, + 'Refresh tokens', + 'Refreshing tokens after relogin faild. Please reload the backend.', + 30 + ); + } + + }); + + }, + }); Index: typo3/js/extjs/viewportConfiguration.js =================================================================== --- typo3/js/extjs/viewportConfiguration.js (revision 10504) +++ typo3/js/extjs/viewportConfiguration.js (working copy) @@ -89,7 +89,8 @@ id: 'typo3-navigationIframe', border: false, hidden: true, - xtype: 'iframePanel' + xtype: 'iframePanel', + name: 'navigation' } ] }, Index: typo3/index.php =================================================================== --- typo3/index.php (revision 10504) +++ typo3/index.php (working copy) @@ -398,7 +398,8 @@ t3lib_utility_Http::redirect($this->redirectToURL); } else { $formprotection = t3lib_formprotection_Factory::get(); - $token = $formprotection->generateToken('extDirect'); + $accessToken = $formprotection->generateToken('refreshTokens'); + $formprotection->persistTokens(); $TBE_TEMPLATE->JScode.=$TBE_TEMPLATE->wrapScriptTags(' if (parent.opener && (parent.opener.busy || parent.opener.TYPO3.loginRefresh)) { if (parent.opener.TYPO3.loginRefresh) { @@ -406,7 +407,7 @@ } else { parent.opener.busy.loginRefreshed(); } - parent.opener.TYPO3.ExtDirectToken = "' . $token . '"; + parent.opener.TYPO3.loginRefresh.refreshTokens("' . $accessToken . '"); parent.close(); } '); Index: t3lib/formprotection/class.t3lib_formprotection_backendformprotection.php =================================================================== --- t3lib/formprotection/class.t3lib_formprotection_backendformprotection.php (revision 10504) +++ t3lib/formprotection/class.t3lib_formprotection_backendformprotection.php (working copy) @@ -152,6 +152,20 @@ } /** + * Override the abstract class to be able to strip out + * the token id from the POST variable. + * + * @see t3lib/formprotection/t3lib_formprotection_Abstract::validateToken() + */ + public function validateToken( + $token, $formName, $action = '', $formInstanceName = '' + ) { + list($tokenId, $_) = explode('-', (string)$token); + + return parent::validateToken($tokenId, $formName, $action, $formInstanceName); + } + + /** * Creates or displayes an error message telling the user that the submitted * form token is invalid. * Index: t3lib/class.t3lib_befunc.php =================================================================== --- t3lib/class.t3lib_befunc.php (revision 10504) +++ t3lib/class.t3lib_befunc.php (working copy) @@ -3333,7 +3333,7 @@ */ public static function getUrlToken($formName = 'securityToken', $tokenName = 'formToken') { $formprotection = t3lib_formprotection_Factory::get(); - return '&' . $tokenName . '=' . $formprotection->generateToken($formName); + return '&' . $tokenName . '=' . $formprotection->generateToken($formName) . '-' . $formName; } /******************************************* Index: t3lib/class.t3lib_tceforms.php =================================================================== --- t3lib/class.t3lib_tceforms.php (revision 10504) +++ t3lib/class.t3lib_tceforms.php (working copy) @@ -4983,7 +4983,7 @@ */ public static function getHiddenTokenField($formName = 'securityToken', $tokenName = 'formToken') { $formprotection = t3lib_formprotection_Factory::get(); - return ''; + return ''; } /** Index: t3lib/class.t3lib_pagerenderer.php =================================================================== --- t3lib/class.t3lib_pagerenderer.php (revision 10504) +++ t3lib/class.t3lib_pagerenderer.php (working copy) @@ -986,7 +986,7 @@ // does this only with multiple arguments $this->addExtOnReadyCode(' (function() { - TYPO3.ExtDirectToken = "' . $token . '"; + TYPO3.ExtDirectToken = "' . $token . '-extDirect"; for (var api in Ext.app.ExtDirectAPI) { var provider = Ext.Direct.addProvider(Ext.app.ExtDirectAPI[api]); provider.on("beforecall", function(provider, transaction, meta) { Index: t3lib/config_default.php =================================================================== --- t3lib/config_default.php (revision 10504) +++ t3lib/config_default.php (working copy) @@ -510,6 +510,7 @@ 'BackendLogin::refreshLogin' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->refreshLogin', 'BackendLogin::isTimedOut' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->isTimedOut', 'BackendLogin::getChallenge' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->getChallenge', + 'BackendLogin::refreshTokens' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->refreshTokens', 'DonateWindow::disable' => 'typo3/classes/class.donatewindow.php:DonateWindow->disable', 'DonateWindow::postpone' => 'typo3/classes/class.donatewindow.php:DonateWindow->postpone', 'ExtDirect::getAPI' => 't3lib/extjs/class.t3lib_extjs_extdirectapi.php:t3lib_extjs_ExtDirectApi->getAPI',