diff --git a/session_security/static/session_security/script.js b/session_security/static/session_security/script.js index a9bef38..5f9a579 100644 --- a/session_security/static/session_security/script.js +++ b/session_security/static/session_security/script.js @@ -15,154 +15,185 @@ if (window.yourlabs == undefined) window.yourlabs = {}; // onbeforeunload handler that doesn't block expire(). // - events: a list of event types to watch for activity updates. // - returnToUrl: a url to redirect users to expired sessions to. If this is not defined we just reload the page -yourlabs.SessionSecurity = function(options) { - // **HTML element** that should show to warn the user that his session will - // expire. - this.$warning = $('#session_security_warning'); - - // Last recorded activity datetime. - this.lastActivity = new Date(); +yourlabs.SessionSecurity = function (options) { + // **HTML element** that should show to warn the user that his session will + // expire. + this.warning = document.querySelector("#session_security_warning"); + + // Last recorded activity datetime. + this.lastActivity = new Date(); + + // Events that would trigger an activity + this.events = [ + "mousemove", + "scroll", + "keyup", + "click", + "touchstart", + "touchend", + "touchmove", + ]; + + // Merge the options dict here. + Object.assign(this, options); + + + // Bind activity events to update this.lastActivity. + var m_document = document; + + + for (var i = 0; i < this.events.length; i++) { + if (m_document[this.events[i]] == null) { + m_document.addEventListener(this.events[i], this.activity.bind(this)); + } + } - // Events that would trigger an activity - this.events = ['mousemove', 'scroll', 'keyup', 'click', 'touchstart', 'touchend', 'touchmove']; + // Initialize timers. + this.apply(); - // Merge the options dict here. - $.extend(this, options); + if (this.confirmFormDiscard) { + window.onbeforeunload = this.onbeforeunload.bind(this); + m_document.addEventListener("change", this.formChange.bind(this)); + m_document.addEventListener("submit", this.formClean.bind(this)); + m_document.addEventListener("reset", this.formClean.bind(this)); + } +}; - // Bind activity events to update this.lastActivity. - var $document = $(document); - for(var i=0; i= this.expireAfter) { + // Enforces checking whether a user's session is expired. This + // ensures a user being redirected instead of waiting until nextPing. + this.expire(); } -} + if (!this.warning.hasAttribute('style')) { + // Inform the server that the user came back manually, this should + // block other browser tabs from expiring. + this.ping(); + // The hideWarning should only be called when the warning is visible + this.hideWarning(); + } + }, -yourlabs.SessionSecurity.prototype = { - // Called when there has been no activity for more than expireAfter - // seconds. - expire: function() { - this.expired = true; - if (this.returnToUrl !== undefined) { - window.location.href = this.returnToUrl; - } - else { - window.location.reload(); - } - }, + // Hit the PingView with the number of seconds since last activity. + ping: function () { + var idleFor = Math.floor((new Date() - this.lastActivity) / 1000); - // Called when there has been no activity for more than warnAfter - // seconds. - showWarning: function() { - this.$warning.fadeIn('slow'); - this.$warning.attr('aria-hidden', 'false'); - $('.session_security_modal').focus(); - }, + var xhr = new XMLHttpRequest(); - // Called to hide the warning, for example if there has been activity on - // the server side - in another browser tab. - hideWarning: function() { - this.$warning.hide(); - this.$warning.attr('aria-hidden', 'true'); - }, - - // Called by click, scroll, mousemove, keyup, touchstart, touchend, touchmove - activity: function() { - var now = new Date(); - if (now - this.lastActivity < 1000) - // Throttle these checks to once per second - return; - - var idleFor = Math.floor((now - this.lastActivity) / 1000); - this.lastActivity = now; - - if (idleFor >= this.expireAfter) { - // Enforces checking whether a user's session is expired. This - // ensures a user being redirected instead of waiting until nextPing. - this.expire(); - } - - if (this.$warning.is(':visible')) { - // Inform the server that the user came back manually, this should - // block other browser tabs from expiring. - this.ping(); - // The hideWarning should only be called when the warning is visible - this.hideWarning(); - } - }, - - // Hit the PingView with the number of seconds since last activity. - ping: function() { - var idleFor = Math.floor((new Date() - this.lastActivity) / 1000); - - $.ajax(this.pingUrl, { - data: {idleFor: idleFor}, - cache: false, - success: $.proxy(this.pong, this), - // In case of network error, we still want to hide potentially - // confidential data !! - error: $.proxy(this.apply, this), - dataType: 'json', - type: 'get' - }); - }, - - // Callback to process PingView response. - pong: function(data) { - if (data == 'logout') return this.expire(); - - this.lastActivity = new Date(); - this.lastActivity.setSeconds(this.lastActivity.getSeconds() - data); - this.apply(); - }, - - // Apply warning or expiry, setup next ping - apply: function() { - // Cancel timeout if any, since we're going to make our own - clearTimeout(this.timeout); - - var idleFor = Math.floor((new Date() - this.lastActivity) / 1000); - - if (idleFor >= this.expireAfter) { - return this.expire(); - } else if (idleFor >= this.warnAfter) { - this.showWarning(); - nextPing = this.expireAfter - idleFor; - } else { - this.hideWarning(); - nextPing = this.warnAfter - idleFor; + // For no cache, append _ with current timestamp. + let xUrl = this.pingUrl + "?idleFor=" + String(idleFor) + "&_=" + String(new Date().getTime()); + + xhr.open("GET", xUrl , true); + // set header to JSON? + xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + var self = this; + var ponger = self.pong.bind(self); + var applier = self.apply.bind(self); + xhr.onload = function() { + if (xhr.readyState == xhr.DONE && xhr.status == 200) + { + + ponger(xhr.response); } - - // setTimeout expects the timeout value not to exceed - // a 32-bit unsigned int, so cap the value - var milliseconds = Math.min(nextPing * 1000, 2147483647) - this.timeout = setTimeout($.proxy(this.ping, this), milliseconds); - }, - - // onbeforeunload handler. - onbeforeunload: function(e) { - if ($('form[data-dirty]').length && !this.expired) { - return this.confirmFormDiscard; + else if (xhr.readyState == xhr.DONE && xhr.status == 400) + { + applier(xhr.response ); } - }, + else + { + } + }; + + xhr.onerror = function(){ + console.log("Error connecting to ping view"); + } + xhr.send(JSON.stringify({ + idleFor: idleFor, + })); + }, + + // Callback to process PingView response. + pong: function (data) { + if (String(data) === ""logout\"") return this.expire(); + this.lastActivity = new Date(); + this.lastActivity.setSeconds(this.lastActivity.getSeconds() - data); + this.apply(); + }, + + // Apply warning or expiry, setup next ping + apply: function () { + // Cancel timeout if any, since we're going to make our own + clearTimeout(this.timeout); + + var idleFor = Math.floor((new Date() - this.lastActivity) / 1000); + if (idleFor >= this.expireAfter) { + return this.expire(); + } else if (idleFor >= this.warnAfter) { + this.showWarning(); + nextPing = this.expireAfter - idleFor; + } else { + this.hideWarning(); + nextPing = this.warnAfter - idleFor; + } - // When an input change, set data-dirty attribute on its form. - formChange: function(e) { - $(e.target).closest('form').attr('data-dirty', true); - }, + // setTimeout expects the timeout value not to exceed + // a 32-bit unsigned int, so cap the value + var milliseconds = Math.min(nextPing * 1000, 2147483647); + this.timeout = setTimeout(this.ping.bind(this), milliseconds); + }, - // When a form is submitted or resetted, unset data-dirty attribute. - formClean: function(e) { - $(e.target).removeAttr('data-dirty'); + // onbeforeunload handler. + onbeforeunload: function (e) { + if (document.querySelector("form[data-dirty]").length && !this.expired) { + return this.confirmFormDiscard; } -} + }, + + // When an input change, set data-dirty attribute on its form. + formChange: function (e) { + document.querySelector(e.target).closest("form").attr("data-dirty", true); + }, + + // When a form is submitted or resetted, unset data-dirty attribute. + formClean: function (e) { + document.querySelector(e.target).removeAttr("data-dirty"); + }, +}; \ No newline at end of file