From 2858a85050f9e55f0a1f2731220190acf4d6117d Mon Sep 17 00:00:00 2001 From: Rolf Schmidt Date: Mon, 10 May 2021 13:43:50 +0000 Subject: [PATCH] Fixes #3525 - Show modal infomation 30 seconds before a session expires. --- .../_application_controller/_base.coffee | 1 + .../_plugin/session_timeout.coffee | 87 ++++++++++++++++--- .../javascripts/app/controllers/login.coffee | 10 ++- .../javascripts/app/lib/app_post/auth.coffee | 9 +- spec/system/dashboard_spec.rb | 17 ++-- 5 files changed, 100 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_application_controller/_base.coffee b/app/assets/javascripts/app/controllers/_application_controller/_base.coffee index 5d55fbdc6..5e6a85268 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/_base.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/_base.coffee @@ -273,6 +273,7 @@ class App.Controller extends Spine.Controller return if location is '#' return if location is '#login' return if location is '#logout' + return if location is '#session_timeout' return if location is '#keyboard_shortcuts' # remember requested url diff --git a/app/assets/javascripts/app/controllers/_plugin/session_timeout.coffee b/app/assets/javascripts/app/controllers/_plugin/session_timeout.coffee index 56529eafe..3b184e3a4 100644 --- a/app/assets/javascripts/app/controllers/_plugin/session_timeout.coffee +++ b/app/assets/javascripts/app/controllers/_plugin/session_timeout.coffee @@ -1,31 +1,67 @@ class SessionTimeout extends App.Controller - lastEvent = 0 + lastEvent: 0 + warningDialog: undefined + intervalCheck: 5000 + showLogoutWarningBefore: -(30 * 1000) + timeTillLogout: undefined constructor: -> super - lastEvent = new Date().getTime() - checkTimeout = => - return if new Date().getTime() - 1000 < lastEvent - lastEvent = new Date().getTime() - @checkLogout() + @lastEvent = @currentTime() # reset timeout on mouse move - $(document).off('keyup.session_timeout').on('keyup.session_timeout', checkTimeout) - $(document).off('mousemove.session_timeout').on('mousemove.session_timeout', checkTimeout) + $(document).off('keyup.session_timeout').on('keyup.session_timeout', @checkTimeout) + $(document).off('mousemove.session_timeout').on('mousemove.session_timeout', @checkTimeout) - @controllerBind('config_update', checkTimeout) + # lisen to remote events + @controllerBind('config_update', @checkTimeout) @controllerBind('session_timeout', @quitApp) - @interval(@checkLogout, 5000, 'session_timeout') + + # check interfall of session timeouts + @interval(@checkLogout, @intervalCheck, 'session_timeout') + + checkTimeout: => + getTime = @currentTime() + return if getTime - 2000 < @lastEvent + + @lastEvent = getTime + + # return if time till logout is far away + return if @timeTillLogout && @timeTillLogout > 20000 + + @checkLogout() checkLogout: => return if App.Session.get() is undefined - return if lastEvent + @getTimeout() > new Date().getTime() + + @timeTillLogout = @currentTime() - (@lastEvent + @getTimeout()) + + # close logut warning + if @timeTillLogout < @showLogoutWarningBefore + return if !@logoutWarningExists() + + @logoutWarningClose() + return + + # show logut warning + if @timeTillLogout <= 0 + @logoutWarningShow() + return + @quitApp() + currentTime: -> + new Date().getTime() + quitApp: => return if App.Session.get() is undefined - @navigate '#logout' + + @logoutWarningClose() + + App.Auth.logout(false, => + @navigate '#session_timeout' + ) getTimeout: -> user = App.User.find(App.Session.get().id) @@ -43,4 +79,31 @@ class SessionTimeout extends App.Controller return timeout * 1000 + logoutWarningExists: => + return true if @warningDialog + false + + logoutWarningClose: => + return false if !@warningDialog + @warningDialog.close() + @warningDialog = undefined + + logoutWarningShow: => + return if @warningDialog + + @warningDialog = new App.ControllerModal( + head: 'Session' + message: 'Due to inactivity are automatically logged out within the next 30 seconds.' + keyboard: true + backdrop: true + buttonClose: true + buttonSubmit: 'Continue session' + onSubmit: => + @lastEvent = @currentTime() + @checkLogout() + ) + + release: -> + @logoutWarningClose() + App.Config.set('session_timeout', SessionTimeout, 'Plugins') diff --git a/app/assets/javascripts/app/controllers/login.coffee b/app/assets/javascripts/app/controllers/login.coffee index bb921dfe4..c470accd9 100644 --- a/app/assets/javascripts/app/controllers/login.coffee +++ b/app/assets/javascripts/app/controllers/login.coffee @@ -16,8 +16,15 @@ class Login extends App.ControllerFullPage @navigate '#' return + # show session timeout message on login screen + data = {} + if window.location.hash is '#session_timeout' + data = { + errorMessage: App.i18n.translateContent('Due to inactivity you are automatically logged out.') + } + @title 'Sign in' - @render() + @render(data) @navupdate '#login' # observe config changes related to login page @@ -102,3 +109,4 @@ class Login extends App.ControllerFullPage ) App.Config.set('login', Login, 'Routes') +App.Config.set('session_timeout', Login, 'Routes') diff --git a/app/assets/javascripts/app/lib/app_post/auth.coffee b/app/assets/javascripts/app/lib/app_post/auth.coffee index 426c3782e..233e18749 100644 --- a/app/assets/javascripts/app/lib/app_post/auth.coffee +++ b/app/assets/javascripts/app/lib/app_post/auth.coffee @@ -43,7 +43,7 @@ class App.Auth @_loginError() ) - @logout: -> + @logout: (rerender = true, callback) -> App.Log.debug 'Auth', 'logout' # abort all AJAX requests @@ -66,7 +66,7 @@ class App.Auth success: => # set logout (config, session, ...) - @_logout() + @_logout(rerender, callback) error: (xhr, statusText, error) => @_loginError() @@ -147,7 +147,7 @@ class App.Auth if _.isFunction(App[model].updateAttributes) App[model].updateAttributes(attributes) - @_logout: (rerender = true) -> + @_logout: (rerender = true, callback) -> App.Log.debug 'Auth', '_logout' App.TaskManager.reset() @@ -169,6 +169,9 @@ class App.Auth ) App.Event.trigger('clearStore') + if callback + callback() + @_loginError: -> App.Log.debug 'Auth', '_loginError:error' diff --git a/spec/system/dashboard_spec.rb b/spec/system/dashboard_spec.rb index fc1174179..f076a7759 100644 --- a/spec/system/dashboard_spec.rb +++ b/spec/system/dashboard_spec.rb @@ -49,20 +49,21 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do end it 'does logout user' do - expect(page).to have_text('Sign in', wait: 20) + expect(page).to have_text('Due to inactivity are automatically logged out within the next 30 seconds.', wait: 20) + expect(page).to have_text('Due to inactivity you are automatically logged out.', wait: 20) end it 'does not logout user', authenticated_as: :admin do sleep 1.5 - expect(page).to have_no_text('Sign in', wait: 0) + expect(page).to have_no_text('Due to inactivity you are automatically logged out.', wait: 0) end end context 'Logout by frontend plugin - Setting change', authenticated_as: :admin do it 'does logout user' do - expect(page).to have_no_text('Sign in') + expect(page).to have_no_text('Due to inactivity you are automatically logged out.') Setting.set('session_timeout', { default: '1' }) - expect(page).to have_text('Sign in', wait: 20) + expect(page).to have_text('Due to inactivity you are automatically logged out.', wait: 20) end end @@ -73,7 +74,7 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do end it 'does logout user' do - expect(page).to have_text('Sign in', wait: 20) + expect(page).to have_text('Due to inactivity you are automatically logged out.', wait: 20) end end @@ -84,7 +85,7 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do end it 'does logout user' do - expect(page).to have_text('Sign in', wait: 20) + expect(page).to have_text('Due to inactivity you are automatically logged out.', wait: 20) end end @@ -95,7 +96,7 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do end it 'does logout user' do - expect(page).to have_text('Sign in', wait: 20) + expect(page).to have_text('Due to inactivity you are automatically logged out.', wait: 20) end end @@ -109,7 +110,7 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do # backend tests for the rest session = ActiveRecord::SessionStore::Session.all.detect { |s| s.data['user_id'] == admin.id } SessionTimeoutJob.destroy_session(admin, session) - expect(page).to have_text('Sign in', wait: 20) + expect(page).to have_text('Due to inactivity you are automatically logged out.', wait: 20) end end end