1 /* Core Sevice - egAuth
3 * Manages login and auth session retrieval.
6 angular.module('egCoreMod')
9 ['$q','$timeout','$rootScope','$window','$location','egNet','egHatch',
10 function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) {
13 // the currently active user (au) object
21 // the user hidden by an operator change
22 OCuser : function(u) {
29 // the Op Change hidden auth token string
30 OCtoken : function() {
31 return egHatch.getLoginSessionItem('eg.auth.token.oc');
34 // Op Change hidden authtime in seconds
35 OCauthtime : function() {
36 return egHatch.getLoginSessionItem('eg.auth.time.oc');
39 // the currently active auth token string
41 return egHatch.getLoginSessionItem('eg.auth.token');
44 // authtime in seconds
45 authtime : function() {
46 return egHatch.getLoginSessionItem('eg.auth.time');
49 // the currently active workstation name
50 // For ws_ou or wsid(), see egAuth.user().ws_ou(), etc.
51 workstation : function() {
56 /* Returns a promise, which is resolved if valid
57 * authtoken is found, otherwise rejected */
58 service.testAuthToken = function() {
59 var deferred = $q.defer();
60 var token = service.token();
66 'open-ils.auth.session.retrieve', token)
68 .then(function(user) {
69 if (user && user.classname) {
70 // authtoken test succeeded
73 service.check_workstation(deferred);
76 // authtoken test failed
77 egHatch.clearLoginSessionItems();
83 // no authtoken to test
84 deferred.reject('No authtoken found');
87 return deferred.promise;
90 service.check_workstation = function(deferred) {
92 var user = service.user();
93 var ws_path = '/admin/workstation/workstations';
95 return egHatch.getItem('eg.workstation.all')
96 .then(function(workstations) {
97 if (!workstations) workstations = [];
99 // If the user is authenticated with a workstation, get the
100 // name from the locally registered version of the workstation.
104 var ws = workstations.filter(
105 function(w) {return w.id == user.wsid()})[0];
108 service.ws = ws.name;
114 if ($location.path() == ws_path) {
115 // User is on the workstation admin page. No need
121 // At this point, the user is trying to access a page
122 // besides the workstation admin page without a valid
123 // registered workstation. Send them back to the
124 // workstation admin page.
126 // NOTE: egEnv also defines basePath, but we cannot import
127 // egEnv here becuase it creates a circular reference.
128 $window.location.href = '/eg/staff' + ws_path;
134 * Returns a promise, which is resolved on successful
135 * login and rejected on failed login.
137 service.login = function(args, ops) {
138 // avoid modifying the caller's data structure.
139 args = angular.copy(args);
141 if (!ops) { // only set on redo attempts.
142 ops = {deferred : $q.defer()};
144 // Clear old LoginSession keys that were left in localStorage
145 // when the previous user closed the browser without logging
146 // out. Under normal circumstance, LoginSession data would
147 // have been cleared by now, either during logout or cookie
148 // expiration. But, if for some reason the user manually
149 // removed the auth token cookie w/o closing the browser
150 // (say, for testing), then this serves double duty to ensure
151 // LoginSession data cannot persist across logins.
152 egHatch.clearLoginSessionItems();
155 service.login_api(args).then(function(evt) {
157 if (evt.textcode == 'SUCCESS') {
158 service.handle_login_ok(args, evt);
159 ops.deferred.resolve({
160 invalid_workstation : ops.invalid_workstation
163 } else if (evt.textcode == 'WORKSTATION_NOT_FOUND') {
164 ops.invalid_workstation = true;
165 delete args.workstation;
166 service.login(args, ops); // redo w/o workstation
169 // note: the likely outcome here is a NO_SESION
170 // server event, which results in broadcasting an
171 // egInvalidAuth by egNet.
172 console.error('login failed ' + js2JSON(evt));
173 ops.deferred.reject();
177 return ops.deferred.promise;
181 * Returns a promise, which is resolved on successful
182 * login and rejected on failed login.
184 service.opChange = function(args) {
185 // avoid modifying the caller's data structure.
186 args = angular.copy(args);
187 args.workstation = service.workstation();
189 var deferred = $q.defer();
191 service.login_api(args).then(function(evt) {
193 if (evt.textcode == 'SUCCESS') {
194 service.OCuser(service.user());
195 egHatch.setLoginSessionItem('eg.auth.token.oc', service.token());
196 egHatch.setLoginSessionItem('eg.auth.time.oc', service.authtime());
197 service.handle_login_ok(args, evt);
201 // note: the likely outcome here is a NO_SESION
202 // server event, which results in broadcasting an
203 // egInvalidAuth by egNet.
204 console.error('operator change failed ' + js2JSON(evt));
209 return deferred.promise;
212 service.opChangeUndo = function() {
213 if (service.OCtoken()) {
214 service.user(service.OCuser());
215 egHatch.setLoginSessionItem('eg.auth.token', service.OCtoken());
216 egHatch.setLoginSessionItem('eg.auth.time', service.OCauthtime());
217 egHatch.removeLoginSessionItem('eg.auth.token.oc');
218 egHatch.removeLoginSessionItem('eg.auth.time.oc');
220 return service.testAuthToken();
223 service.login_api = function(args) {
224 return egNet.request(
226 'open-ils.auth.authenticate.init', args.username)
227 .then(function(seed) {
228 // avoid clobbering the bare password in case
229 // we need it for a login redo attempt.
230 var login_args = angular.copy(args);
231 login_args.password = hex_md5(seed + hex_md5(args.password));
233 return egNet.request(
235 'open-ils.auth.authenticate.complete', login_args)
240 service.handle_login_ok = function(args, evt) {
241 service.ws = args.workstation;
242 egHatch.setLoginSessionItem('eg.auth.token', evt.payload.authtoken);
243 egHatch.setLoginSessionItem('eg.auth.time', evt.payload.authtime);
248 * Force-check the validity of the authtoken on occasion.
249 * This allows us to redirect an idle staff client back to the login
250 * page after the session times out. Otherwise, the UI would stay
251 * open with potentially sensitive data visible.
252 * TODO: What is the practical difference (for a browser) between
253 * checking auth validity and the ui.general.idle_timeout setting?
254 * Does that setting serve a purpose in a browser environment?
256 service.poll = function() {
257 if (!service.authtime()) return;
261 if (!service.authtime()) return;
264 'open-ils.auth.session.retrieve', service.token())
265 .then(function(user) {
266 if (user && user.classname) { // all good
269 $rootScope.$broadcast('egAuthExpired')
273 // add a 5 second delay to give the token plenty of time
274 // to expire on the server.
275 service.authtime() * 1000 + 5000
279 service.logout = function() {
280 if (service.token()) {
283 'open-ils.auth.session.delete',
284 service.token()); // fire and forget
285 egHatch.clearLoginSessionItems();
287 service._user = null;
295 * Service for testing user permissions.
296 * Note: this cannot live within egAuth, because it creates a circular
297 * dependency of egOrg -> egEnv -> egAuth -> egOrg
300 ['$q','egNet','egAuth','egOrg',
301 function($q , egNet , egAuth , egOrg) {
305 * Returns the full list of org unit objects at which the currently
306 * logged in user has the selected permissions.
307 * @permList - list or string. If a list, the response object is a
308 * hash of perm => orgList maps. If a string, the response is the
309 * org list for the requested perm.
311 service.hasPermAt = function(permList, asId) {
312 var deferred = $q.defer();
314 if (!angular.isArray(permList)) {
316 permList = [permList];
318 // as called, this method will return the top-most org unit of the
319 // sub-tree at which this user has the selected permission.
320 // From there, flesh the descendant orgs locally.
323 'open-ils.actor.user.has_work_perm_at.batch',
324 egAuth.token(), permList
325 ).then(function(resp) {
327 angular.forEach(permList, function(perm) {
329 angular.forEach(resp[perm], function(oneOrg) {
330 all = all.concat(egOrg.descendants(oneOrg, asId));
334 if (!isArray) answer = answer[permList[0]];
335 deferred.resolve(answer);
337 return deferred.promise;
342 * Returns a hash of perm => hasPermBool for each requested permission.
343 * If the authenticated user has no workstation, no checks are made
344 * and all permissions return false.
346 service.hasPermHere = function(permList) {
350 if (!angular.isArray(permList)) {
352 permList = [permList];
355 // no workstation, all are false
356 if (egAuth.user().wsid() === null) {
357 console.warn("egPerm.hasPermHere() called with no workstation");
359 response = permList.map(function(perm) {
360 return response[perm] = false;
365 return $q.when(response);
368 ws_ou = Number(egAuth.user().ws_ou()); // from string
370 return service.hasPermAt(permList, true)
371 .then(function(orgMap) {
372 angular.forEach(orgMap, function(orgIds, perm) {
373 // each permission is mapped to a flat list of org unit ids,
374 // including descendants. See if our workstation org unit
376 response[perm] = orgIds.indexOf(ws_ou) > -1;
378 if (!isArray) response = response[permList[0]];