LP#1467663 webstaff: login requires valid workstation
authorBill Erickson <berickxx@gmail.com>
Wed, 28 Sep 2016 16:22:44 +0000 (12:22 -0400)
committerKathy Lussier <klussier@masslnc.org>
Tue, 22 Nov 2016 19:10:03 +0000 (14:10 -0500)
Hide the workstation selector when no workstations are registered.
After successful login, direct the user to the new workstation admin
page to create a new workstation.

After successful login with an invalid workstation, direct the user
to the workstation admin page, issuing a 'remove' command to un-register
the offending WS.  On the WS admin page, the user can create a new
workstation or select from their existing workstations.

Any attempt to access a browser client interface (minus the WS admin
page) without a valid workstation will cause the page to redirect to
the workstation admin page.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Open-ILS/src/templates/staff/t_login.tt2
Open-ILS/web/js/ui/default/staff/app.js
Open-ILS/web/js/ui/default/staff/services/auth.js

index 135e9da..ce06e6b 100644 (file)
@@ -29,7 +29,7 @@
               </div>
             </div>
 
-            <div class="form-group">
+            <div class="form-group" ng-show="workstations.length > 0">
               <label class="col-md-4 control-label" 
                 for="login-workstation">[% l('Workstation') %]</label>
               <div class="col-md-8">
index 41910b9..4cf388c 100644 (file)
@@ -43,6 +43,8 @@ function($routeProvider , $locationProvider) {
            ['$scope','$location','$window','egCore',
     function($scope , $location , $window , egCore) {
         $scope.focusMe = true;
+        $scope.args = {};
+        $scope.workstations = [];
 
         // if the user is already logged in, jump to splash page
         if (egCore.auth.user()) $location.path('/');
@@ -92,17 +94,37 @@ function($routeProvider , $locationProvider) {
 
             if (! (args.username && args.password) ) return;
 
+            // if at least one workstation exists, it must be used.
+            if (!args.workstation && $scope.workstations.length > 0) return;
+
             args.type = 'staff';
             egCore.auth.login(args).then(
 
-                function() { 
-                    // after login, send the user back to the originally
-                    // requested page or, if none, the home page.
-                    // TODO: this is a little hinky because it causes 2 
-                    // redirects if no route_to is defined.  Improve.
-                    $window.location.href = 
-                        $location.search().route_to || 
-                        $location.path('/').absUrl()
+                function(result) { 
+                    // After login, send the user to:
+                    // 1. The WS admin page for WS maintenance.
+                    // 2. The page originally requested by the caller
+                    // 3. Home page.
+
+                    // NOTE: using $location.path(...) results in
+                    // confusing intermediate page loads, since
+                    // path(...) is a setter function.  Build the URL by
+                    // hand instead from the configured base path.
+                    var route_to = egCore.env.basePath;
+
+                    if (result.invalid_workstation) {
+                        // route to WS admin page to delete the offending
+                        // WS and create a new one.
+                        route_to += 
+                            'admin/workstation/workstations?remove=' 
+                                + encodeURIComponent(args.workstation);
+
+                    } else if ($location.search().route_to) {
+                        // Route to the originally requested page.
+                        route_to = $location.search().route_to;
+                    }
+
+                    $window.location.href = route_to;
                 },
                 function() {
                     $scope.args.password = '';
index a677ff4..3446f4a 100644 (file)
@@ -6,8 +6,8 @@
 angular.module('egCoreMod')
 
 .factory('egAuth', 
-       ['$q','$timeout','$rootScope','egNet','egHatch', 
-function($q , $timeout , $rootScope , egNet , egHatch) {
+       ['$q','$timeout','$rootScope','$window','$location','egNet','egHatch',
+function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) {
 
     var service = {
         // the currently active user (au) object
@@ -49,23 +49,8 @@ function($q , $timeout , $rootScope , egNet , egHatch) {
                     // authtoken test succeeded
                     service._user = user;
                     service.poll();
-                   
-                    if (user.wsid()) {
-                        // user previously logged in with a workstation. 
-                        // Find the workstation name from the list 
-                        // of configured workstations
-                        egHatch.getItem('eg.workstation.all')
-                        .then(function(all) { 
-                            if (all) {
-                                var ws = all.filter(
-                                    function(w) {return w.id == user.wsid()})[0];
-                                if (ws) service.ws = ws.name;
-                            }
-                            deferred.resolve(); // found WS
-                        });
-                    } else {
-                        deferred.resolve(); // no WS
-                    }
+                    service.check_workstation(deferred);
+
                 } else {
                     // authtoken test failed
                     egHatch.clearLoginSessionItems();
@@ -75,60 +60,125 @@ function($q , $timeout , $rootScope , egNet , egHatch) {
 
         } else {
             // no authtoken to test
-            deferred.reject();
+            deferred.reject('No authtoken found');
         }
 
         return deferred.promise;
     };
 
+    service.check_workstation = function(deferred) {
+
+        var user = service.user();
+        var ws_path = '/admin/workstation/workstations';
+
+        return egHatch.getItem('eg.workstation.all')
+        .then(function(workstations) { 
+            if (!workstations) workstations = [];
+
+            // If the user is authenticated with a workstation, get the
+            // name from the locally registered version of the workstation.
+
+            if (user.wsid()) {
+
+                var ws = workstations.filter(
+                    function(w) {return w.id == user.wsid()})[0];
+
+                if (ws) { // success
+                    service.ws = ws.name;
+                    deferred.resolve();
+                    return;
+                }
+            }
+
+            if ($location.path() == ws_path) {
+                // User is on the workstation admin page.  No need
+                // to redirect.
+                deferred.resolve();
+                return;
+            }
+
+            // At this point, the user is trying to access a page
+            // besides the workstation admin page without a valid
+            // registered workstation.  Send them back to the 
+            // workstation admin page.
+
+            // NOTE: egEnv also defines basePath, but we cannot import
+            // egEnv here becuase it creates a circular reference.
+            $window.location.href = '/eg/staff' + ws_path;
+            deferred.resolve();
+        });
+    }
+
     /**
      * Returns a promise, which is resolved on successful 
      * login and rejected on failed login.
      */
-    service.login = function(args) {
-        var deferred = $q.defer();
+    service.login = function(args, ops) {
+        // avoid modifying the caller's data structure.
+        args = angular.copy(args);
+
+        if (!ops) { // only set on redo attempts.
+            ops = {deferred : $q.defer()};
+
+            // Clear old LoginSession keys that were left in localStorage
+            // when the previous user closed the browser without logging
+            // out.  Under normal circumstance, LoginSession data would
+            // have been cleared by now, either during logout or cookie
+            // expiration.  But, if for some reason the user manually
+            // removed the auth token cookie w/o closing the browser
+            // (say, for testing), then this serves double duty to ensure
+            // LoginSession data cannot persist across logins.
+            egHatch.clearLoginSessionItems();
+        }
 
-        // Clear old LoginSession keys that were left in localStorage
-        // when the previous user closed the browser without logging
-        // out.  Under normal circumstance, LoginSession data would
-        // have been cleared by now, either during logout or cookie
-        // expiration.  But, if for some reason the user manually
-        // removed the auth token cookie w/o closing the browser
-        // (say, for testing), then this serves double duty to ensure
-        // LoginSession data cannot persist across logins.
-        egHatch.clearLoginSessionItems();
+        service.login_api(args).then(function(evt) {
 
-        egNet.request(
+            if (evt.textcode == 'SUCCESS') {
+                service.handle_login_ok(args, evt);
+                ops.deferred.resolve({
+                    invalid_workstation : ops.invalid_workstation
+                });
+
+            } else if (evt.textcode == 'WORKSTATION_NOT_FOUND') {
+                ops.invalid_workstation = true;
+                delete args.workstation;
+                service.login(args, ops); // redo w/o workstation
+
+            } else {
+                // note: the likely outcome here is a NO_SESION
+                // server event, which results in broadcasting an 
+                // egInvalidAuth by egNet. 
+                console.error('login failed ' + js2JSON(evt));
+                ops.deferred.reject();
+            }
+        });
+
+        return ops.deferred.promise;
+    }
+
+    service.login_api = function(args) {
+        return egNet.request(
             'open-ils.auth',
-            'open-ils.auth.authenticate.init', args.username).then(
-            function(seed) {
-                args.password = hex_md5(seed + hex_md5(args.password))
-                egNet.request(
+            'open-ils.auth.authenticate.init', args.username)
+        .then(function(seed) {
+                // avoid clobbering the bare password in case
+                // we need it for a login redo attempt.
+                var login_args = angular.copy(args);
+                login_args.password = hex_md5(seed + hex_md5(args.password));
+
+                return egNet.request(
                     'open-ils.auth',
-                    'open-ils.auth.authenticate.complete', args).then(
-                    function(evt) {
-                        if (evt.textcode == 'SUCCESS') {
-                            service.ws = args.workstation; 
-                            service.poll();
-                            egHatch.setLoginSessionItem(
-                                'eg.auth.token', evt.payload.authtoken);
-                            egHatch.setLoginSessionItem(
-                                'eg.auth.time', evt.payload.authtime);
-                            deferred.resolve();
-                        } else {
-                            // note: the likely outcome here is a NO_SESION
-                            // server event, which results in broadcasting an 
-                            // egInvalidAuth by egNet. 
-                            console.error('login failed ' + js2JSON(evt));
-                            deferred.reject();
-                        }
-                    }
-                )
+                    'open-ils.auth.authenticate.complete', login_args)
             }
         );
+    }
 
-        return deferred.promise;
-    };
+    service.handle_login_ok = function(args, evt) {
+        service.ws = args.workstation; 
+        egHatch.setLoginSessionItem('eg.auth.token', evt.payload.authtoken);
+        egHatch.setLoginSessionItem('eg.auth.time', evt.payload.authtime);
+        service.poll();
+    }
 
     /**
      * Force-check the validity of the authtoken on occasion.