Browse Source

Fixes #3525 - Show modal infomation 30 seconds before a session expires.

Rolf Schmidt 3 years ago
parent
commit
2858a85050

+ 1 - 0
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

+ 75 - 12
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')

+ 9 - 1
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')

+ 6 - 3
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'
 

+ 9 - 8
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