Index: t3lib/class.t3lib_querygenerator.php
===================================================================
--- t3lib/class.t3lib_querygenerator.php (revision 4405)
+++ t3lib/class.t3lib_querygenerator.php (working copy)
@@ -1511,6 +1511,7 @@
';
return $out;
Index: t3lib/class.t3lib_userauth.php
===================================================================
--- t3lib/class.t3lib_userauth.php (revision 4405)
+++ t3lib/class.t3lib_userauth.php (working copy)
@@ -249,8 +249,17 @@
// Make certain that NO user is set initially. ->check_authentication may have set a session-record which will provide us with a user record in the next section:
unset($this->user);
+ // determine whether we need to skip session update.
+ // This is used mainly for checking session timeout without
+ // refreshing the session itself while checking.
+ if(t3lib_div::_GP('skipSessionUpdate')) {
+ $skipSessionUpdate = true;
+ } else {
+ $skipSessionUpdate = false;
+ }
+
// re-read user session
- $this->user = $this->fetchUserSession();
+ $this->user = $this->fetchUserSession($skipSessionUpdate);
if ($this->writeDevLog && is_array($this->user)) t3lib_div::devLog('User session finally read: '.t3lib_div::arrayToLogString($this->user, array($this->userid_column,$this->username_column)), 't3lib_userAuth', -1);
if ($this->writeDevLog && !is_array($this->user)) t3lib_div::devLog('No user session found.', 't3lib_userAuth', 2);
@@ -413,8 +422,17 @@
// the following code makes auto-login possible (if configured). No submitted data needed
+ // determine whether we need to skip session update.
+ // This is used mainly for checking session timeout without
+ // refreshing the session itself while checking.
+ if(t3lib_div::_GP('skipSessionUpdate')) {
+ $skipSessionUpdate = true;
+ } else {
+ $skipSessionUpdate = false;
+ }
+
// re-read user session
- $authInfo['userSession'] = $this->fetchUserSession();
+ $authInfo['userSession'] = $this->fetchUserSession($skipSessionUpdate);
$haveSession = is_array($authInfo['userSession']) ? TRUE : FALSE;
if ($this->writeDevLog) {
@@ -527,7 +545,6 @@
// reset failure flag
$this->loginFailure = FALSE;
-
// Insert session record if needed:
if (!($haveSession && (
$tempuser['ses_id']==$this->id || // check if the tempuser has the current session id
@@ -653,7 +670,7 @@
*
* @return array user session data
*/
- function fetchUserSession() {
+ function fetchUserSession($skipSessionUpdate = false) {
$user = '';
@@ -682,6 +699,7 @@
// If timeout > 0 (true) and currenttime has not exceeded the latest sessions-time plus the timeout in seconds then accept user
// Option later on: We could check that last update was at least x seconds ago in order not to update twice in a row if one script redirects to another...
if ($timeout>0 && ($GLOBALS['EXEC_TIME'] < ($user['ses_tstamp']+$timeout))) {
+ if(!$skipSessionUpdate) {
$GLOBALS['TYPO3_DB']->exec_UPDATEquery(
$this->session_table,
'ses_id='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, $this->session_table).'
@@ -689,6 +707,8 @@
array('ses_tstamp' => $GLOBALS['EXEC_TIME'])
);
$user['ses_tstamp'] = $GLOBALS['EXEC_TIME']; // Make sure that the timestamp is also updated in the array
+ }
+
} else {
$this->logoff(); // delete any user set...
}
Index: t3lib/config_default.php
===================================================================
--- t3lib/config_default.php (revision 4405)
+++ t3lib/config_default.php (working copy)
@@ -243,7 +243,11 @@
'ShortcutMenu::create' => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->createAjaxShortcut',
'ModuleMenu::saveMenuState' => 'typo3/classes/class.modulemenu.php:ModuleMenu->saveMenuState',
'ModuleMenu::render' => 'typo3/classes/class.modulemenu.php:ModuleMenu->renderAjax',
- 'SC_mod_web_perm_ajax::dispatch' => 'typo3/mod/web/perm/class.sc_mod_web_perm_ajax.php:SC_mod_web_perm_ajax->dispatch'
+ 'SC_mod_web_perm_ajax::dispatch' => 'typo3/mod/web/perm/class.sc_mod_web_perm_ajax.php:SC_mod_web_perm_ajax->dispatch',
+ 'BackendLogin::login' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->login',
+ 'BackendLogin::logout' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->logout',
+ 'BackendLogin::refreshLogin' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->refreshLogin',
+ 'BackendLogin::isTimedOut' => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->isTimedOut',
),
'XCLASS' => Array(), // See 'Inside TYPO3' document for more information.
),
Index: typo3/ajax.php
===================================================================
--- typo3/ajax.php (revision 4405)
+++ typo3/ajax.php (working copy)
@@ -32,11 +32,29 @@
$TYPO3_AJAX = true;
+// include t3lib_div at this time to get the GET/POST methods it provides
+require_once('../t3lib/class.t3lib_div.php');
+
+// first get the ajaxID
+$ajaxID = (string) t3lib_div::_GP('ajaxID');
+
+// this is a list of requests that don't necessarily need a valid BE user
+$noUserAjaxIDs = array(
+ 'BackendLogin::login',
+ 'BackendLogin::logout',
+ 'BackendLogin::refreshLogin',
+ 'BackendLogin::isTimedOut'
+);
+
+// if we're trying to do an ajax login, don't require a user.
+if(in_array($ajaxID, $noUserAjaxIDs)) {
+ define('TYPO3_PROCEED_IF_NO_USER', 1);
+}
+
require('init.php');
require('classes/class.typo3ajax.php');
// finding the script path from the variable
-$ajaxID = (string) t3lib_div::_GP('ajaxID');
$ajaxScript = $TYPO3_CONF_VARS['BE']['AJAX'][$ajaxID];
Index: typo3/backend.php
===================================================================
--- typo3/backend.php (revision 4405)
+++ typo3/backend.php (working copy)
@@ -82,6 +82,7 @@
* @return void
*/
public function __construct() {
+
// Initializes the backend modules structure for use later.
$this->moduleLoader = t3lib_div::makeInstance('t3lib_loadModules');
$this->moduleLoader->load($GLOBALS['TBE_MODULES']);
@@ -93,6 +94,8 @@
$this->jsFiles = array(
'contrib/prototype/prototype.js',
'contrib/scriptaculous/scriptaculous.js?load=builder,effects,controls,dragdrop',
+ 'contrib/extJS/ext-prototype-adapter.js',
+ 'contrib/extJS/ext-all.js',
'md5.js',
'js/backend.js',
'js/common.js',
@@ -108,7 +111,9 @@
$this->cssFiles = array(
'backend-scaffolding' => 'css/backend-scaffolding.css',
'backend-style' => 'css/backend-style.css',
- 'modulemenu' => 'css/modulemenu.css'
+ 'modulemenu' => 'css/modulemenu.css',
+ 'extJS' => 'contrib/extJS/resources/css/ext-all.css',
+ 'extJS-gray' => 'contrib/extJS/resources/css/xtheme-gray.css'
);
$this->toolbarItems = array();
@@ -330,6 +335,24 @@
$menuFrameName = 'topmenuFrame';
}
+ // create challenge for the (re)login form and save it in the session.
+ $challenge = md5(uniqid('').getmypid());
+ session_start();
+ $_SESSION['login_challenge'] = $challenge;
+
+
+ // $loginLabels = explode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['loginLabels']);
+
+ // create countdown markup for the timeout countdown.
+ $countdown = addslashes(sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_countdown'), '30'));
+
+ // determine security level from conf vars and default to super challenged
+ if ($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) {
+ $this->loginSecurityLevel = $GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'];
+ } else {
+ $this->loginSecurityLevel = 'superchallenged';
+ }
+
$this->js .= '
/**
* Function similar to PHPs rawurlencode();
@@ -371,6 +394,7 @@
this.username = "'.$GLOBALS['BE_USER']->user['username'].'";
this.uniqueID = "'.t3lib_div::shortMD5(uniqid('')).'";
this.navFrameWidth = 0;
+ this.securityLevel = "'.$this->loginSecurityLevel.'";
}
var TS = new typoSetup();
@@ -379,43 +403,228 @@
*/
function busy() { //
this.loginRefreshed = busy_loginRefreshed;
- this.checkLoginTimeout = busy_checkLoginTimeout;
this.openRefreshWindow = busy_OpenRefreshWindow;
this.busyloadTime=0;
this.openRefreshW=0;
this.reloginCancelled=0;
+ this.earlyRelogin=0;
+
+ // starts the timer and resets the earlyRelogin variable so that
+ // the countdown works properly.
+ this.startTimer = function() {
+ this.earlyRelogin = 0;
+ this.timer.start();
+ }
+
+ this.stopTimer = function() {
+ this.timer.stop();
+ }
+
+ // simple timer that polls the server to determine imminent timeout.
+ this.timer = new Ajax.PeriodicalUpdater("","ajax.php", {
+ method: "get",
+ frequency: 10,
+ parameters: "ajaxID=BackendLogin::isTimedOut&skipSessionUpdate=1",
+ onSuccess: function(e) {
+ var login = e.responseJSON.login.evalJSON();
+ if(login.timed_out) {
+ busy.openRefreshWindow();
+ }
+ }
+ });
+
+ // this function runs the countdown and opens the login window
+ // as soon as the countdown expires.
+ this.countDown = function(progressControl, progressTextFormat, secondsRemaining, totalSeconds) {
+
+ if(busy.earlyRelogin == 0) {
+ progressControl.updateText(String.format(progressTextFormat, secondsRemaining));
+ progressControl.updateProgress(secondsRemaining/(1.0*totalSeconds));
+ if(secondsRemaining > 0) {
+ setTimeout(function () {
+ busy.countDown(progressControl, progressTextFormat, secondsRemaining - 1, totalSeconds);
+ }, 1000);
+ } else {
+ busy.openRefreshW = 1;
+ busy.openLogin();
+ }
+ }
+ };
+
+ // Closes the countdown window and opens a new one with a login form.
+ this.openLogin = function() {
+ var login;
+ doChallengeResponse = function(superchallenged) { //
+ password = $$("#loginform form")[0].p_field.value;
+
+ if (password) {
+ if (superchallenged) {
+ password = MD5(password); // this makes it superchallenged!!
+ }
+ // alert($F("login_username"));
+ str = $("login_username").value+":"+password+":"+$("challenge").value;
+ $("userident").value = MD5(str);
+ $("password").value = "";
+
+ return true;
+ }
+ }
+
+ submitForm = function() {
+ if(TS.securityLevel == "superchallenged") {
+ doChallengeResponse(1);
+ } else if (TS.securityLevel == "challenged") {
+ doChallengeResponse(0);
+ } else {
+ $("userident").value = $$("#loginform form")[0].p_field.value;
+ $("password").value= "";
+ }
+
+ login.getForm().submit({
+ method: "post",
+ waitTitle: "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_logging_in').'",
+ waitMsg: " ",
+ params: "ajaxID=BackendLogin::login&login_status=login",
+ success: function() {
+ win.close();
+ setTimeout("busy.startTimer()", 2000);
+
+ },
+
+ failure: function() {
+ // TODO: add failure to notification system instead of alert
+ // Ext.tip.msg("Login failed", "Username or Password incorrect!");
+ Ext.Msg.alert("'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_failed').'", "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_failed_message').'");
+ }
+ });
+ }
+
+ new Ajax.Request("ajax.php", {
+ method: "get",
+ frequency: 10,
+ parameters: "ajaxID=BackendLogin::logout",
+ });
+
+ Ext.onReady(function(){
+ login = new Ext.FormPanel({
+ url: "ajax.php",
+ id: "loginform",
+ //frame:true,
+ title: "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_title').'",
+ defaultType: "textfield",
+ width: "100%",
+ bodyStyle: "padding: 5px 5px 3px 5px; border-width: 0; margin-bottom: 7px;",
+
+ items: [{
+ xtype: "panel",
+ bodyStyle: "margin-bottom: 7px; border: none;",
+ html: "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.login_expired').'",
+ },{
+ fieldLabel: "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_username').'",
+ name: "username",
+ id: "login_username",
+ allowBlank: false,
+ width: 250
+ },{
+ fieldLabel: "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_password').'",
+ name: "p_field",
+ width: 250,
+ id: "password",
+ inputType: "password"
+ },{
+ xtype: "hidden",
+ name: "userident",
+ id: "userident",
+ value: ""
+ }, {
+ xtype: "hidden",
+ name: "challenge",
+ id: "challenge",
+ value: "'.$challenge.'"
+ }
+ ],
+ keys:({
+ key: Ext.EventObject.ENTER,
+ fn: submitForm,
+ scope: this
+ }),
+ buttons: [{
+ text: "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_login_button').'",
+ formBind: true,
+ handler: submitForm
+ }]
+ });
+ win.close();
+ win = new Ext.Window({
+// layout:"fit",
+ width: 450,
+ autoHeight: true,
+ closable: false,
+ resizable: false,
+ plain: true,
+ border: false,
+ modal: true,
+ draggable: false,
+ items: [login]
+ });
+ win.show();
+ });
+ }
}
+
function busy_loginRefreshed() { //
- var date = new Date();
- this.busyloadTime = Math.floor(date.getTime()/1000);
this.openRefreshW=0;
+ this.earlyRelogin=0;
}
- function busy_checkLoginTimeout() { //
- var date = new Date();
- var theTime = Math.floor(date.getTime()/1000);
- if (theTime > this.busyloadTime+'.intval($GLOBALS['BE_USER']->auth_timeout_field).'-30) {
- return true;
- }
- }
- function busy_OpenRefreshWindow() { //
- vHWin=window.open("login_frameset.php","relogin_"+TS.uniqueID,"height=350,width=700,status=0,menubar=0,location=1");
- vHWin.focus();
- this.openRefreshW=1;
- }
- function busy_checkLoginTimeout_timer() { //
- if (busy.checkLoginTimeout() && !busy.reloginCancelled && !busy.openRefreshW) {
- if (confirm('.$GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login')).')) {
- busy.openRefreshWindow();
- } else {
- busy.reloginCancelled = 1;
- }
- }
- window.setTimeout("busy_checkLoginTimeout_timer();",2*1000); // Each 2nd second is enough for checking. The popup will be triggered 10 seconds before the login expires (see above, busy_checkLoginTimeout())
- // Detecting the frameset module navigation frame widths (do this AFTER setting new timeout so that any errors in the code below does not prevent another time to be set!)
- if (top && top.content && top.content.nav_frame && top.content.nav_frame.document && top.content.nav_frame.document.body) {
- TS.navFrameWidth = (top.content.nav_frame.document.documentElement && top.content.nav_frame.document.documentElement.clientWidth) ? top.content.nav_frame.document.documentElement.clientWidth : top.content.nav_frame.document.body.clientWidth;
- }
+ function busy_OpenRefreshWindow() {
+ this.openRefreshW = 1;
+
+ busy.stopTimer();
+
+ var seconds = 30;
+ var progressTextFormat = "' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_countdown') . '";
+ var progressText = String.format(progressTextFormat, seconds);
+ var progressControl = new Ext.ProgressBar({
+ autoWidth: true,
+ autoHeight: true,
+ value: 1,
+ text: progressText
+ });
+
+ win = new Ext.Window({
+ closable: false,
+ resizable: false,
+ draggable: false,
+ modal: true,
+ items: [{
+ xtype: "panel",
+ bodyStyle: "padding: 5px 5px 3px 5px; border-width: 0; margin-bottom: 7px;",
+ bodyBorder: false,
+ autoHeight: true,
+ autoWidth: true,
+ html: "' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.login_about_to_expire') . '"
+ },
+ progressControl
+ ],
+ title: "' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.login_about_to_expire_title') . '",
+ width: 450,
+
+ buttons: [{
+ text : "'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.refresh_login_refresh_button').'",
+ handler : function() {
+ new Ajax.Request("ajax.php", {
+ method: "get",
+ parameters: "ajaxID=BackendLogin::refreshLogin",
+ });
+ win.close();
+ busy.earlyRelogin = 1;
+ setTimeout("busy.startTimer()", 2000);
+ }
+ }]
+ });
+ win.show();
+ busy.countDown(progressControl, progressTextFormat, seconds, seconds);
}
/**
@@ -487,7 +696,6 @@
*/
var busy = new busy();
busy.loginRefreshed();
- busy_checkLoginTimeout_timer();
/**
* Function used to switch modules
Index: typo3/classes/class.ajaxlogin.php
===================================================================
--- typo3/classes/class.ajaxlogin.php (revision 0)
+++ typo3/classes/class.ajaxlogin.php (revision 0)
@@ -0,0 +1,120 @@
+
+ */
+class AjaxLogin
+{
+
+ /**
+ * Handles the actual login process, more specifically it defines the response.
+ * The login details were sent in as part of the ajax request and automatically logged in
+ * the user inside the init.php part of the ajax call. If that was successful, we have
+ * a BE user and reset the timer and hide the login window.
+ * If it was unsuccessful, we display that and show the login box again.
+ *
+ * @param string $params Always empty.
+ * @param string $ajaxObj The Ajax object used to return content and set content types
+ * @return void
+ */
+ public function login($params = array(), TYPO3AJAX &$ajaxObj = null) {
+ if ($GLOBALS['BE_USER']->user['uid']) {
+ $json =
+ '{success: true}';
+ } else {
+ $json =
+ '{success: false}';
+ }
+ $ajaxObj->addContent('login', $json);
+ }
+
+ /**
+ * Logs out the current BE user
+ *
+ * @param string $params Always empty.
+ * @param string $TYPO3AJAX The Ajax object used to return content and set content types
+ * @return void
+ */
+
+ public function logout($params = array(), TYPO3AJAX &$ajaxObj = null) {
+ $GLOBALS['BE_USER']->logoff();
+ if($GLOBALS['BE_USER']->user['uid']) {
+ $ajaxObj->addContent('logout', '{sucess: false}');
+ } else {
+ $ajaxObj->addContent('logout', '{sucess: true}');
+ }
+ }
+
+ /**
+ * Refreshes the login without needing login information. We just refresh the session.
+ *
+ *
+ * @param string $params Always empty.
+ * @param string $ajaxObj The Ajax object used to return content and set content types
+ * @return void
+ */
+ public function refreshLogin($params = array(), TYPO3AJAX &$ajaxObj = null) {
+ $GLOBALS['BE_USER']->checkAuthentication();
+ $ajaxObj->addContent('refresh', '{sucess: true}');
+ }
+
+
+ /**
+ * Checks if the user session is expired yet
+ *
+ * @param string $params Always empty.
+ * @param string $TYPO3AJAX The Ajax object used to return content and set content types
+ * @return void
+ */
+
+ function isTimedOut($params = array(), TYPO3AJAX &$ajaxObj = null) {
+ if(is_object($GLOBALS['BE_USER'])) {
+ $GLOBALS['BE_USER']->fetchUserSession(true);
+ $ses_tstamp = $GLOBALS['BE_USER']->user['ses_tstamp'];
+ $timeout = $GLOBALS['BE_USER']->auth_timeout_field;
+
+ // if 60 seconds from now is later than the session timeout, we need to show the refresh dialog.
+ // 60 is somewhat arbitrary to allow for a little room during the countdown and load times, etc.
+ if($GLOBALS['EXEC_TIME'] >= $ses_tstamp+$timeout-60) {
+ $ajaxObj->addContent('login', '{timed_out: true}');
+ $ajaxObj->setContentFormat('json');
+ } else {
+ $ajaxObj->addContent('login', '{timed_out: false}');
+ $ajaxObj->setContentFormat('json');
+ }
+ } else {
+ $ajaxObj->addContent('login', '{success: false, error: "No BE_USER object"}');
+ }
+ }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.ajaxlogin.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.ajaxlogin.php']);
+}
+?>
\ No newline at end of file
Index: typo3/jsfunc.tbe_editor.js
===================================================================
--- typo3/jsfunc.tbe_editor.js (revision 4405)
+++ typo3/jsfunc.tbe_editor.js (working copy)
@@ -397,10 +397,12 @@
*/
checkSubmit: function(sendAlert) {
var funcIndex, funcMax, funcRes;
- if (TBE_EDITOR.checkLoginTimeout() && confirm(TBE_EDITOR.labels.refresh_login)) {
- vHWin=window.open(TBE_EDITOR.backPath+'login_frameset.php?','relogin','height=300,width=400,status=0,menubar=0');
- vHWin.focus();
- return false;
+ if(TBE_EDITOR.backend_interface == "backend_old") {
+ if (TBE_EDITOR.checkLoginTimeout() && confirm(TBE_EDITOR.labels.refresh_login)) {
+ vHWin=window.open(TBE_EDITOR.backPath+'login_frameset.php?','relogin','height=300,width=400,status=0,menubar=0');
+ vHWin.focus();
+ return false;
+ }
}
var OK=1;
Index: typo3/sysext/lang/locallang_core.xml
===================================================================
--- typo3/sysext/lang/locallang_core.xml (revision 4405)
+++ typo3/sysext/lang/locallang_core.xml (working copy)
@@ -95,8 +95,8 @@
-
-
+
+
@@ -146,6 +146,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+