Add support for client-side session timeout warning

This commit is contained in:
Lance Edgar 2017-02-16 13:32:20 -06:00
parent 4ae70de339
commit e3ec3be03b
4 changed files with 81 additions and 18 deletions

View file

@ -69,28 +69,69 @@ function get_uuid(obj) {
/*
* get_dialog(id, callback)
*
* Returns a <DIV> element suitable for use as a jQuery dialog.
*
* ``id`` is used to construct a proper ID for the element and allows the
* dialog to be resused if possible.
*
* ``callback``, if specified, should be a callback function for the dialog.
* This function will be called whenever the dialog has been closed
* "successfully" (i.e. data submitted) by the user, and should accept a single
* ``data`` object which is the JSON response returned by the server.
* reference to existing timeout warning dialog, if any
*/
var session_timeout_warning = null;
function get_dialog(id, callback) {
var dialog = $('#'+id+'-dialog');
if (! dialog.length) {
dialog = $('<div class="dialog" id="'+id+'-dialog"></div>');
/**
* Warn user of impending session timeout.
*/
function timeout_warning() {
if (! session_timeout_warning) {
session_timeout_warning = $('<div id="session-timeout-warning">' +
'You will be logged out in <span class="seconds"></span> ' +
'seconds...</div>');
}
if (callback) {
dialog.attr('callback', callback);
session_timeout_warning.find('.seconds').text('60');
session_timeout_warning.dialog({
title: "Session Timeout Warning",
modal: true,
buttons: {
"Stay Logged In": function() {
session_timeout_warning.dialog('close');
$.get(noop_url, set_timeout_warning_timer);
},
"Logout Now": function() {
location.href = logout_url;
}
return dialog;
}
});
window.setTimeout(timeout_warning_update, 1000);
}
/**
* Decrement the 'seconds' counter for the current timeout warning
*/
function timeout_warning_update() {
if (session_timeout_warning.is(':visible')) {
var span = session_timeout_warning.find('.seconds');
var seconds = parseInt(span.text()) - 1;
if (seconds) {
span.text(seconds.toString());
window.setTimeout(timeout_warning_update, 1000);
} else {
location.href = logout_url;
}
}
}
/**
* Warn user of impending session timeout.
*/
function set_timeout_warning_timer() {
// TODO: are we calculating timer ok here? (effective timeout - 60 seconds)
window.setTimeout(timeout_warning, session_timeout * 1000 - 60000);
}
/*
* set initial timer for timeout warning, if applicable
*/
if (session_timeout) {
set_timeout_warning_timer();
}

View file

@ -154,6 +154,13 @@ def context_found(event):
return referrer
request.get_referrer = get_referrer
def get_session_timeout():
"""
Returns the timeout in effect for the current session
"""
return request.session.get('_timeout')
request.get_session_timeout = get_session_timeout
def includeme(config):
config.add_subscriber(add_rattail_config_attribute_to_request, 'pyramid.events.NewRequest')

View file

@ -133,6 +133,11 @@
${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.ui.menubar.js'))}
${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.loadmask.min.js'))}
${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.ui.timepicker.js'))}
<script type="text/javascript">
var session_timeout = ${request.get_session_timeout() or 'null'};
var logout_url = '${request.route_url('logout')}';
var noop_url = '${request.route_url('noop')}';
</script>
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.js'))}
</%def>

View file

@ -149,6 +149,12 @@ class AuthenticationView(View):
def mobile_logout(self):
return self.logout(mobile=True)
def noop(self):
"""
View to serve as "no-op" / ping action to reset current user's session timer
"""
return {'status': 'ok'}
def change_password(self):
"""
Allows a user to change his or her password.
@ -201,6 +207,10 @@ class AuthenticationView(View):
config.add_route('mobile.logout', '/mobile/logout')
config.add_view(cls, attr='mobile_logout', route_name='mobile.logout')
# no-op
config.add_route('noop', '/noop')
config.add_view(cls, attr='noop', route_name='noop', renderer='json')
# change password
config.add_route('change_password', '/change-password')
config.add_view(cls, attr='change_password', route_name='change_password', renderer='/change_password.mako')