From b10fd571f5993691f332f1a0579babf0ae836c9a Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 19 Oct 2017 14:46:01 -0400 Subject: [PATCH] LP#1724915 Webstaff auth timeout works w/ multiple tabs Adds a new API parameter to open-ils.session.retrieve which allows the session to be fetched without extending the auth session timeout. Teach the browser client to use the new API. Teach the browser client to notify all webstaff tabs when a logout event has occurred, so every tab can immediately log out. To test ------- [0] Apply the patch. [1] Log in the web staff client, then open a new window/tab and navigate to the web staff client. [2] Log out of the web staff client in one window. Verify that the second window automatically refreshes and goes to the login page. [3] Set a low staff idle timeout (optional). [4] Repeat step 1, then wait for the timeout. Verify that the staff client is logged out in both windows. Signed-off-by: Bill Erickson Signed-off-by: Galen Charlton Signed-off-by: Mike Rylander --- Open-ILS/src/c-apps/oils_auth.c | 21 +++++++++--- Open-ILS/web/js/ui/default/staff/services/auth.js | 37 ++++++++++++++++++---- .../web/js/ui/default/staff/services/startup.js | 9 ++++-- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/Open-ILS/src/c-apps/oils_auth.c b/Open-ILS/src/c-apps/oils_auth.c index 4afb36cf50..b87d1ca6a1 100644 --- a/Open-ILS/src/c-apps/oils_auth.c +++ b/Open-ILS/src/c-apps/oils_auth.c @@ -98,10 +98,13 @@ int osrfAppInitialize() { MODULENAME, "open-ils.auth.session.retrieve", "oilsAuthSessionRetrieve", - "Pass in the auth token and this retrieves the user object. The auth " - "timeout is reset when this call is made " + "Pass in the auth token and this retrieves the user object. By " + "default, the auth timeout is reset when this call is made. If " + "a second non-zero parameter is passed, the auth timeout info is " + "returned to the caller along with the user object. If a 3rd " + "non-zero parameter is passed, the auth timeout will not be reset." "Returns the user object (password blanked) for the given login session " - "PARAMS( authToken )", 1, 0 ); + "PARAMS( authToken[, returnTime[, doNotResetSession]] )", 1, 0 ); osrfAppRegisterMethod( MODULENAME, @@ -1206,6 +1209,7 @@ int oilsAuthResetTimeout( osrfMethodContext* ctx ) { int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) { OSRF_METHOD_VERIFY_CONTEXT(ctx); bool returnFull = false; + bool noTimeoutReset = false; const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0)); @@ -1214,6 +1218,14 @@ int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) { const char* rt = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 1)); if(rt && strcmp(rt, "0") != 0) returnFull = true; + + if (ctx->params->size > 2) { + // Avoid resetting the auth session timeout. + const char* noReset = + jsonObjectGetString(jsonObjectGetIndex(ctx->params, 2)); + if (noReset && strcmp(noReset, "0") != 0) + noTimeoutReset = true; + } } jsonObject* cacheObj = NULL; @@ -1222,7 +1234,8 @@ int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) { if( authToken ){ // Reset the timeout to keep the session alive - evt = _oilsAuthResetTimeout(authToken, 0); + if (!noTimeoutReset) + evt = _oilsAuthResetTimeout(authToken, 0); if( evt && strcmp(evt->event, OILS_EVENT_SUCCESS) ) { osrfAppRespondComplete( ctx, oilsEventToJSON( evt )); // can't reset timeout diff --git a/Open-ILS/web/js/ui/default/staff/services/auth.js b/Open-ILS/web/js/ui/default/staff/services/auth.js index 3ee2296923..8912c78a4b 100644 --- a/Open-ILS/web/js/ui/default/staff/services/auth.js +++ b/Open-ILS/web/js/ui/default/staff/services/auth.js @@ -50,7 +50,10 @@ function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) { // For ws_ou or wsid(), see egAuth.user().ws_ou(), etc. workstation : function() { return this.ws; - } + }, + + // Listen for logout events in other tabs + authChannel : new BroadcastChannel('eg.auth') }; /* Returns a promise, which is resolved if valid @@ -268,19 +271,33 @@ function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) { * Does that setting serve a purpose in a browser environment? */ service.poll = function() { - if (!service.authtime()) return; + + if (!service.authChannel.onmessage) { + // Now that we have an authtoken, listen for logout events + // initiated by other tabs. + service.authChannel.onmessage = function(e) { + if (e.data.action == 'logout') { + $rootScope.$broadcast( + 'egAuthExpired', {startedElsewhere : true}); + } + } + } $timeout( function() { - if (!service.authtime()) return; egNet.request( 'open-ils.auth', - 'open-ils.auth.session.retrieve', service.token()) - .then(function(user) { + 'open-ils.auth.session.retrieve', + service.token(), + 0, // return extra auth details, unneeded here. + 1 // avoid extending the auth timeout + ).then(function(user) { if (user && user.classname) { // all good service.poll(); } else { - $rootScope.$broadcast('egAuthExpired') + // NOTE: we should never get here, since egNet + // filters responses for NO_SESSION events. + $rootScope.$broadcast('egAuthExpired'); } }) }, @@ -290,7 +307,13 @@ function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) { ); } - service.logout = function() { + service.logout = function(broadcast) { + + if (broadcast) { + // Tell the other tabs to shut it all down. + service.authChannel.postMessage({action : 'logout'}); + } + if (service.token()) { egNet.request( 'open-ils.auth', diff --git a/Open-ILS/web/js/ui/default/staff/services/startup.js b/Open-ILS/web/js/ui/default/staff/services/startup.js index 7792eb2036..d947d751ce 100644 --- a/Open-ILS/web/js/ui/default/staff/services/startup.js +++ b/Open-ILS/web/js/ui/default/staff/services/startup.js @@ -52,11 +52,16 @@ function($q, $rootScope, $location, $window, egIDL, egAuth, egEnv , egOrg // returns true if we are staying on the current page // false if we are redirecting to login - service.expiredAuthHandler = function() { + service.expiredAuthHandler = function(data) { if (lf.isOffline) return true; // Only set by the offline UI console.debug('egStartup.expiredAuthHandler()'); - egAuth.logout(); // clean up + + // Only notify other tabs the auth session has expired + // when this tab was the first tab to know it. + var broadcast = !(data && data.startedElsewhere); + + egAuth.logout(broadcast); // clean up // no need to redirect if we're on the /login page if ($location.path() == '/login') return true; -- 2.11.0