Merge pull request #14313 from nulll-pointer/enh_1

Fixes #13654 
Login modal after session expiration
This commit is contained in:
Isaac Bennetch 2018-08-15 09:40:54 -04:00 committed by GitHub
commit 4576538be6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 292 additions and 37 deletions

View File

@ -336,7 +336,115 @@ var AJAX = {
if (typeof onsubmit !== 'function' || onsubmit.apply(this, [event])) {
AJAX.active = true;
AJAX.$msgbox = PMA_ajaxShowMessage();
$.post(url, params, AJAX.responseHandler);
if($(this).attr('id') === 'login_form') {
$.post(url, params, AJAX.loginResponseHandler);
} else {
$.post(url, params, AJAX.responseHandler);
}
}
}
},
/**
* Response handler to handle login request from login modal after session expiration
*
* To refer to self use 'AJAX', instead of 'this' as this function
* is called in the jQuery context.
*
* @param object data Event data
*
* @return void
*/
loginResponseHandler: function (data) {
if (typeof data === 'undefined' || data === null) {
return;
}
PMA_ajaxRemoveMessage(AJAX.$msgbox);
PMA_commonParams.set("token", data.new_token);
AJAX.scriptHandler.load([]);
if (data._displayMessage) {
$('#page_content').prepend(data._displayMessage);
PMA_highlightSQL($('#page_content'));
}
$('#pma_errors').remove();
var msg = '';
if (data._errSubmitMsg) {
msg = data._errSubmitMsg;
}
if (data._errors) {
$('<div/>', { id : 'pma_errors', class : 'clearfloat' })
.insertAfter('#selflink')
.append(data._errors);
// bind for php error reporting forms (bottom)
$('#pma_ignore_errors_bottom').on('click', function (e) {
e.preventDefault();
PMA_ignorePhpErrors();
});
$('#pma_ignore_all_errors_bottom').on('click', function (e) {
e.preventDefault();
PMA_ignorePhpErrors(false);
});
// In case of 'sendErrorReport'='always'
// submit the hidden error reporting form.
if (data._sendErrorAlways === '1' &&
data._stopErrorReportLoop !== '1'
) {
$('#pma_report_errors_form').submit();
PMA_ajaxShowMessage(PMA_messages.phpErrorsBeingSubmitted, false);
$('html, body').animate({ scrollTop:$(document).height() }, 'slow');
} else if (data._promptPhpErrors) {
// otherwise just prompt user if it is set so.
msg = msg + PMA_messages.phpErrorsFound;
// scroll to bottom where all the errors are displayed.
$('html, body').animate({ scrollTop:$(document).height() }, 'slow');
}
}
PMA_ajaxShowMessage(msg, false);
// bind for php error reporting forms (popup)
$('#pma_ignore_errors_popup').on('click', function () {
PMA_ignorePhpErrors();
});
$('#pma_ignore_all_errors_popup').on('click', function () {
PMA_ignorePhpErrors(false);
});
if (typeof data.success !== 'undefined' && data.success) {
// reload page if user trying to login has changed
if(PMA_commonParams.get('user') !== data._params['user']) {
window.location = "index.php";
PMA_ajaxShowMessage(PMA_messages.strLoading, false);
AJAX.active = false;
AJAX.xhr = null;
return;
}
// remove the login modal if the login is successful otherwise show error.
if(typeof data.logged_in !== 'undefined' && data.logged_in === 1) {
if($("#modalOverlay").length) {
$("#modalOverlay").remove();
}
$("fieldset.disabled_for_expiration").removeAttr("disabled").removeClass("disabled_for_expiration");
AJAX.fireTeardown("functions.js");
AJAX.fireOnload("functions.js");
}
if(typeof data.new_token !== 'undefined') {
$("input[name=token]").val(data.new_token);
}
} else if(typeof data.logged_in !== 'undefined' && data.logged_in === 0) {
$("#modalOverlay").replaceWith(data.error);
} else {
PMA_ajaxShowMessage(data.error, false);
AJAX.active = false;
AJAX.xhr = null;
PMA_handleRedirectAndReload(data);
if (data.fieldWithError) {
$(':input.error').removeClass('error');
$('#' + data.fieldWithError).addClass('error');
}
}
},

View File

@ -949,7 +949,8 @@ AJAX.registerOnload('functions.js', function () {
'server' : PMA_commonParams.get('server'),
'db' : PMA_commonParams.get('db'),
'guid': guid,
'access_time':_idleSecondsCounter
'access_time': _idleSecondsCounter,
'check_timeout': 1
};
$.ajax({
type: 'POST',
@ -978,7 +979,19 @@ AJAX.registerOnload('functions.js', function () {
if (isStorageSupported('sessionStorage')) {
window.sessionStorage.clear();
}
window.location.reload(true);
// append the login form on the page, disable all the forms which were not disabled already, close all the open jqueryui modal boxes
if (!$("#modalOverlay").length) {
$("fieldset").not(':disabled').attr("disabled", "disabled").addClass("disabled_for_expiration");
$('body').append(data.error);
$(".ui-dialog").each(function(i) {
$("#" + $(this).attr("aria-describedby")).dialog("close");
});
$("#input_username").focus();
} else {
PMA_commonParams.set("token", data.new_token);
$("input[name=token]").val(data.new_token);
}
_idleSecondsCounter = 0;
}
}
});
@ -4321,6 +4334,9 @@ function PMA_slidingMessage (msg, $obj) {
*/
AJAX.registerOnload('functions.js', function () {
var $elm = $('#sqlquery');
if ($elm.siblings().filter(".CodeMirror").length > 0) {
return;
}
if ($elm.length > 0) {
if (typeof CodeMirror !== 'undefined') {
codemirror_editor = PMA_getSQLEditor($elm);

View File

@ -38,6 +38,10 @@ $(document).ready(function () {
}
});
$(document).on('keydown', function (e) {
//disable the shortcuts when session has timed out.
if ($("#modalOverlay").length > 0) {
return;
}
if (e.ctrlKey && e.altKey && e.keyCode === keyC) {
PMA_console.toggle();
}

View File

@ -177,7 +177,7 @@ AJAX.registerTeardown('sql.js', function () {
$(document).off('mouseenter', 'th.column_heading.pointer');
$(document).off('mouseleave', 'th.column_heading.pointer');
$(document).off('click', 'th.column_heading.marker');
$(window).off('scroll');
$(document).off('scroll', window);
$(document).off('keyup', '.filter_rows');
$(document).off('click', '#printView');
if (codemirror_editor) {
@ -405,7 +405,7 @@ AJAX.registerOnload('sql.js', function () {
var $stick_columns = initStickyColumns($table_results);
rearrangeStickyColumns($stick_columns, $table_results);
// adjust sticky columns on scroll
$(window).on('scroll', function () {
$(document).on('scroll', window, function () {
handleStickyColumns($stick_columns, $table_results);
});
});

View File

@ -86,7 +86,10 @@ class AuthenticationCookie extends AuthenticationPlugin
global $conn_error;
$response = Response::getInstance();
if ($response->loginPage()) {
// When sending login modal after session has expired, send the new token explicitly with the response to update the token in all the forms having a hidden token.
$session_expired = isset($_REQUEST['check_timeout']) || isset($_REQUEST['session_timedout']);
if (!$session_expired && $response->loginPage()) {
if (defined('TESTSUITE')) {
return true;
} else {
@ -94,6 +97,23 @@ class AuthenticationCookie extends AuthenticationPlugin
}
}
// When sending login modal after session has expired, send the new token explicitly with the response to update the token in all the forms having a hidden token.
if($session_expired) {
$response->setRequestStatus(false);
$response->addJSON(
'new_token',
$_SESSION[' PMA_token ']
);
}
// logged_in response parameter is used to check if the login, using the modal was successful after session expiration
if(isset($_REQUEST['session_timedout'])) {
$response->addJSON(
'logged_in',
0
);
}
// No recall if blowfish secret is not configured as it would produce
// garbage
if ($GLOBALS['cfg']['LoginCookieRecall']
@ -109,7 +129,14 @@ class AuthenticationCookie extends AuthenticationPlugin
$autocomplete = ' autocomplete="off"';
}
echo $this->template->render('login/header', ['theme' => $GLOBALS['PMA_Theme']]);
// wrap the login form in a div which overlays the whole page.
if($session_expired) {
echo $this->template->render('login/header', ['theme' => $GLOBALS['PMA_Theme'],
'add_class' => ' modal_form', 'session_expired' => 1]);
} else {
echo $this->template->render('login/header', ['theme' => $GLOBALS['PMA_Theme'],
'add_class' => '', 'session_expired' => 0]);
}
if ($GLOBALS['cfg']['DBG']['demo']) {
echo '<fieldset>';
@ -148,10 +175,15 @@ class AuthenticationCookie extends AuthenticationPlugin
<br />
<!-- Login form -->
<form method="post" id="login_form" action="index.php" name="login_form"' , $autocomplete ,
' class="disableAjax login hide js-show">
' class="' . ($session_expired ? "" : "disableAjax hide ") . 'login js-show">
<fieldset>
<legend>';
echo '<input type="hidden" name="set_session" value="', htmlspecialchars(session_id()), '" />';
// Add a hidden element session_timedout which is used to check if the user requested login after session expiration
if($session_expired) {
echo '<input type="hidden" name="session_timedout" value="1" />';
}
echo __('Log in');
echo Util::showDocu('index');
echo '</legend>';
@ -236,8 +268,16 @@ class AuthenticationCookie extends AuthenticationPlugin
$GLOBALS['error_handler']->dispErrors();
echo '</div>';
}
echo $this->template->render('login/footer');
// close the wrapping div tag, if the request is after session timeout
if($session_expired) {
echo $this->template->render('login/footer', ['session_expired' => 1]);
} else {
echo $this->template->render('login/footer', ['session_expired' => 0]);
}
echo Config::renderFooter();
if (! defined('TESTSUITE')) {
exit;
} else {
@ -472,6 +512,7 @@ class AuthenticationCookie extends AuthenticationPlugin
{
// Name and password cookies need to be refreshed each time
// Duration = one month for username
$this->storeUsernameCookie($this->user);
// Duration = as configured
@ -480,27 +521,54 @@ class AuthenticationCookie extends AuthenticationPlugin
if (! isset($_POST['change_pw'])) {
$this->storePasswordCookie($this->password);
}
// URL where to go:
$redirect_url = './index.php';
// any parameters to pass?
$url_params = array();
if (strlen($GLOBALS['db']) > 0) {
$url_params['db'] = $GLOBALS['db'];
}
if (strlen($GLOBALS['table']) > 0) {
$url_params['table'] = $GLOBALS['table'];
}
// any target to pass?
if (! empty($GLOBALS['target'])
&& $GLOBALS['target'] != 'index.php'
) {
$url_params['target'] = $GLOBALS['target'];
}
// user logged in successfully after session expiration
if(isset($_REQUEST['session_timedout'])) {
$response = Response::getInstance();
$response->addJSON(
'logged_in',
1
);
$response->addJSON(
'success',
1
);
$response->addJSON(
'new_token',
$_SESSION[' PMA_token ']
);
if($user_changed) {
$response->addJSON(
'user_changed',
1
);
}
if (! defined('TESTSUITE')) {
exit;
} else {
return false;
}
}
// Set server cookies if required (once per session) and, in this case,
// force reload to ensure the client accepts cookies
if (! $GLOBALS['from_cookie']) {
// URL where to go:
$redirect_url = './index.php';
// any parameters to pass?
$url_params = [];
if (strlen($GLOBALS['db']) > 0) {
$url_params['db'] = $GLOBALS['db'];
}
if (strlen($GLOBALS['table']) > 0) {
$url_params['table'] = $GLOBALS['table'];
}
// any target to pass?
if (! empty($GLOBALS['target'])
&& $GLOBALS['target'] != 'index.php'
) {
$url_params['target'] = $GLOBALS['target'];
}
/**
* Clear user cache.

View File

@ -4285,17 +4285,26 @@ class Util
public static function getStartAndNumberOfRowsPanel($sql_query)
{
$template = new Template();
$pos = isset($_REQUEST['pos'])
? $_REQUEST['pos']
: $_SESSION['tmpval']['pos'];
if (isset($_REQUEST['session_max_rows'])) {
$rows = $_REQUEST['session_max_rows'];
} else if (isset($_SESSION['tmpval']['max_rows'])
&& $_SESSION['tmpval']['max_rows'] != 'all'
) {
$rows = $_SESSION['tmpval']['max_rows'];
} else {
if ($_SESSION['tmpval']['max_rows'] != 'all') {
$rows = $_SESSION['tmpval']['max_rows'];
} else {
$rows = $GLOBALS['cfg']['MaxRows'];
}
$rows = $GLOBALS['cfg']['MaxRows'];
$_SESSION['tmpval']['max_rows'] = $rows;
}
if(isset($_REQUEST['pos'])) {
$pos = $_REQUEST['pos'];
} else if(isset($_SESSION['tmpval']['pos'])) {
$pos = $_SESSION['tmpval']['pos'];
} else {
$number_of_line = intval($_REQUEST['unlim_num_rows']);
$pos = ((ceil($number_of_line / $rows) - 1) * $rows);
$_SESSION['tmpval']['pos'] = $pos;
}
return $template->render('start_and_number_of_rows_panel', [

View File

@ -1 +1,4 @@
</div>
{% if check_timeout == true %}
</div>
{% endif %}

View File

@ -1,4 +1,7 @@
<div class="container">
{% if session_expired == true %}
<div id="modalOverlay">
{% endif %}
<div class="container{{ add_class }}">
<a href="{{ 'https://www.phpmyadmin.net/'|link }}" target="_blank" rel="noopener noreferrer" class="logo">
<img src="{{ theme.getImgPath('logo_right.png', 'pma_logo.png') }}" id="imLogo" name="imLogo" alt="phpMyAdmin" border="0" />
</a>

View File

@ -221,7 +221,7 @@ class AuthenticationCookieTest extends PmaTestCase
$this->assertContains(
'<form method="post" id="login_form" action="index.php" name="login_form" ' .
'class="disableAjax login hide js-show">',
'class="(disableAjax hide login js-show|login js-show)">',
$result
);
@ -324,7 +324,7 @@ class AuthenticationCookieTest extends PmaTestCase
$this->assertContains(
'<form method="post" id="login_form" action="index.php" name="login_form" ' .
'autocomplete="off" class="disableAjax login hide js-show">',
'class="(disableAjax hide login js-show|login js-show)">',
$result
);

View File

@ -629,6 +629,28 @@ body#loginform div.container {
margin: 0 auto;
}
div.container.modal_form {
margin: 0 auto;
width: 30em;
text-align: center;
background: #fff;
z-index: 999;
}
#login_form {
text-align: left;
}
div#modalOverlay {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: #fff;
z-index: 900;
}
form.login label {
float: <?php echo $left; ?>;
width: 10em;

View File

@ -909,6 +909,28 @@ body#loginform div.container {
margin: 0 auto;
}
div.container.modal_form {
margin: 0 auto;
width: 30em;
text-align: center;
background: #fff;
z-index: 999;
}
#login_form {
text-align: left;
}
div#modalOverlay {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: #fff;
z-index: 900;
}
form.login label {
float: <?php echo $left; ?>;
width: 10em;