Implement session end for multiple tabs [SCI-7502] (#4632)

This commit is contained in:
ajugo 2022-11-23 20:08:26 +01:00 committed by GitHub
parent d1c664e0ab
commit c44aa2cb9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 109 additions and 38 deletions

View file

@ -3,67 +3,138 @@
(function() { (function() {
'use strict'; 'use strict';
var originalTitle = ''; var expireOn;
var expireIn;
var expireLimit = 900; // 15min
var timeoutID; var timeoutID;
var expirationUrl = $('meta[name=\'expiration-url\']').attr('content'); const oneSecondTimeout = 1000;
const expireLimit = 900000; // 15min in miliseconds
var pad = function(i) { function padTimeStr(i) {
var s = ('0' + Math.floor(i)); var s = ('0' + Math.floor(i));
return s.substring(s.length - 2); return s.substring(s.length - 2);
}; }
var newTimerStr = function(expirationTime) { function newTimerStr(expirationTime) {
var m = (expirationTime / 60) % 60; var m = (expirationTime / 60) % 60;
var s = (expirationTime % 60); var s = (expirationTime % 60);
return [m, s].map(pad).join(':'); return [m, s].map(padTimeStr).join(':');
}; }
function getSessionEnd() { function getCurrentTime() {
var dateNow = new Date();
return dateNow.getTime();
}
function sessionExpireIn() {
expireOn = window.localStorage.getItem('sessionEnd');
return expireOn - getCurrentTime();
}
function setSessionTimeout(functionCallback, timeoutTime) {
if (timeoutID) {
clearTimeout(timeoutID);
}
timeoutID = setTimeout(functionCallback, timeoutTime);
}
function toogleDocumentTitle(timeString = null) {
var sleepEmoticon = String.fromCodePoint(0x1F62A);
var originalTitle = document.title.split(sleepEmoticon).pop().trim();
if (timeString) {
document.title = timeString + ' ' + sleepEmoticon + ' ' + originalTitle;
} else {
document.title = originalTitle;
}
}
function initializeSessionCountdown() {
var expirationUrl = $('meta[name=\'expiration-url\']').attr('content');
var expireIn;
if (expirationUrl) { if (expirationUrl) {
$.get(expirationUrl, function(data) { $.get(expirationUrl, function(data) {
if (data <= 0) { expireOn = Math.max(window.localStorage.getItem('sessionEnd'), getCurrentTime() + parseInt(data, 10));
window.localStorage.setItem('sessionEnd', expireOn);
expireIn = sessionExpireIn();
if (expireIn <= 0) {
$('#session-finished').modal(); $('#session-finished').modal();
} else if (data <= expireLimit + 1) { } else if (expireIn <= expireLimit + oneSecondTimeout) {
expireIn = data;
originalTitle = document.title;
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
timeoutID = setTimeout(expirationInTime, 1000); setSessionTimeout(sessionCountdown, oneSecondTimeout);
} else { } else {
timeoutID = setTimeout(getSessionEnd, (data - expireLimit) * 1000); setSessionTimeout(initializeSessionCountdown, (expireIn - expireLimit));
} }
}); });
} }
} }
function expirationInTime() { function reviveSession() {
var timeString; $.post($('meta[name=\'revive-url\']').attr('content'));
if (expireIn > 0) { toogleDocumentTitle();
timeString = newTimerStr(expireIn); window.localStorage.removeItem('sessionEnd');
document.title = timeString + ' ' + String.fromCodePoint(0x1F62A) + ' ' + originalTitle; setSessionTimeout(initializeSessionCountdown, oneSecondTimeout);
$('.expiring').text(I18n.t('devise.sessions.expire_modal.session_end_in.header', { time: timeString })); }
expireIn -= 1;
if (!$('#session-expire').hasClass('in')) { function initializeSessionReviveCallbacks() {
$('#session-expire').modal().off('hide.bs.modal').on('hide.bs.modal', function() { $('#session-expire').modal().off('hide.bs.modal').on('hide.bs.modal', function() {
if (expireIn > 0) { if (sessionExpireIn() > 0) {
$.post($('meta[name=\'revive-url\']').attr('content')); reviveSession();
document.title = originalTitle;
clearTimeout(timeoutID);
timeoutID = setTimeout(getSessionEnd, 1000);
}
});
} }
timeoutID = setTimeout(expirationInTime, 1000); });
} else {
document.title = originalTitle; // for manual page reload
$(window).off('beforeunload').on('beforeunload', function() {
if (sessionExpireIn() > 0) {
reviveSession();
}
});
}
function sessionCountdown() {
var timeString;
var expireIn = sessionExpireIn();
if (!expireOn) {
initializeSessionCountdown();
} else if (expireIn > 0 && expireIn <= expireLimit) {
timeString = newTimerStr(expireIn / 1000);
toogleDocumentTitle(timeString);
$('.expiring').text(I18n.t('devise.sessions.expire_modal.session_end_in.header', { time: timeString }));
if (!$('#session-expire').hasClass('in')) {
initializeSessionReviveCallbacks();
}
setSessionTimeout(sessionCountdown, oneSecondTimeout);
} else if (expireIn <= 0) {
toogleDocumentTitle();
$('#session-expire').modal('hide'); $('#session-expire').modal('hide');
$('#session-finished').modal(); $('#session-finished').modal();
} }
} }
timeoutID = setTimeout(getSessionEnd, 1000);
window.localStorage.removeItem('sessionEnd');
setSessionTimeout(initializeSessionCountdown, oneSecondTimeout);
$(document).on('click', '.session-login', function() { $(document).on('click', '.session-login', function() {
window.location.reload(); window.location.reload();
}); });
$(window).on('storage', (event) => {
if (event.originalEvent.storageArea !== localStorage) return;
if (event.originalEvent.key === 'sessionEnd') {
if (!expireOn && event.originalEvent.newValue) {
if ($('#session-finished').hasClass('in')) {
$('#session-finished').modal('hide');
}
setSessionTimeout(initializeSessionCountdown, oneSecondTimeout);
} else if (expireOn && !event.originalEvent.newValue) {
toogleDocumentTitle();
}
expireOn = event.originalEvent.newValue;
$('#session-expire').modal().off('hide.bs.modal').modal('hide');
}
});
}()); }());

View file

@ -37,9 +37,9 @@ class Users::SessionsController < Devise::SessionsController
def expire_in def expire_in
if current_user.remember_created_at.nil? if current_user.remember_created_at.nil?
render plain: Devise.timeout_in.to_i - (Time.now.to_i - user_session['last_request_at']).round render plain: (Devise.timeout_in.to_i - (Time.now.to_i - user_session['last_request_at']).round) * 1000
else else
render plain: Devise.remember_for - (Time.now.to_i - current_user.remember_created_at.to_i).round render plain: (Devise.remember_for - (Time.now.to_i - current_user.remember_created_at.to_i).round) * 1000
end end
end end