1 import {Injectable} from '@angular/core';
2 import {Location} from '@angular/common';
3 import {Observable, Observer, of} from 'rxjs';
4 import {Router, Resolve, RouterStateSnapshot,
5 ActivatedRoute, ActivatedRouteSnapshot} from '@angular/router';
6 import {StoreService} from '@eg/core/store.service';
7 import {NetService} from '@eg/core/net.service';
8 import {AuthService} from '@eg/core/auth.service';
9 import {PermService} from '@eg/core/perm.service';
10 import {OrgService} from '@eg/core/org.service';
11 import {FormatService} from '@eg/core/format.service';
12 import {HatchService} from '@eg/core/hatch.service';
14 const LOGIN_PATH = '/staff/login';
15 const WS_MANAGE_PATH = '/staff/admin/workstation/workstations/manage';
17 // Define these at the staff application level so they will be honored
18 // regardless of which interface is loaded / reloaded / etc.
19 const STAFF_LOGIN_SESSION_KEYS = [
20 'eg.circ.patron_hold_target',
21 'eg.catalog.recent_searches',
22 'eg.circ.recent_patrons'
26 * Load data used by all staff modules.
29 export class StaffResolver implements Resolve<Observable<any>> {
31 // Tracks the primary resolve observable.
32 observer: Observer<any>;
35 private router: Router,
36 private route: ActivatedRoute,
37 private ngLocation: Location,
38 private hatch: HatchService,
39 private store: StoreService,
40 private org: OrgService,
41 private net: NetService,
42 private auth: AuthService,
43 private perm: PermService,
44 private format: FormatService
48 route: ActivatedRouteSnapshot,
49 state: RouterStateSnapshot): Observable<any> {
53 STAFF_LOGIN_SESSION_KEYS.forEach(
54 key => this.store.addLoginSessionKey(key));
56 // Staff cookies stay in /$base/staff/
57 // NOTE: storing session data at '/' so it can be shared by
59 this.store.loginSessionBasePath = '/';
60 // ^-- = this.ngLocation.prepareExternalUrl('/staff');
62 // Not sure how to get the path without params... using this for now.
63 const path = state.url.split('?')[0];
64 if (path === '/staff/login' || path === '/staff/login-not-allowed') {
68 const observable: Observable<any>
69 = new Observable(o => this.observer = o);
71 this.auth.testAuthToken().then(
73 this.confirmStaffPerms().then(
75 this.auth.verifyWorkstation().then(
77 this.loadStartupData()
79 // Resolve observable must emit /something/
80 this.observer.next(true);
81 this.observer.complete();
84 wsNotOk => this.handleInvalidWorkstation(path)
88 this.router.navigate(['/staff/login-not-allowed']);
89 this.observer.error('User does not have staff permissions');
93 tokenNotOk => this.handleInvalidToken(state)
100 // Confirm the user has the STAFF_LOGIN permission anywhere before
101 // allowing the staff sub-tree to load. This will prevent users
102 // with valid, non-staff authtokens from attempting to connect and
103 // subsequently getting redirected to the workstation admin page
104 // (since they won't have a valid WS either).
105 confirmStaffPerms(): Promise<any> {
106 return new Promise((resolve, reject) => {
107 this.perm.hasWorkPermAt(['STAFF_LOGIN']).then(
109 if (permMap.STAFF_LOGIN.length) {
110 resolve('perm check OK');
112 reject('perm check faield');
120 // A page that's not the login page was requested without a
121 // valid auth token. Send the caller back to the login page.
122 handleInvalidToken(state: RouterStateSnapshot): void {
123 console.debug('StaffResolver: authtoken is not valid');
124 // state.url is the eg2 path, not a full URL.
125 this.router.navigate([LOGIN_PATH], {queryParams: {routeTo: state.url}});
126 this.observer.error('invalid or no auth token');
129 handleInvalidWorkstation(path: string): void {
131 if (path.startsWith(WS_MANAGE_PATH)) {
132 // user is navigating to the WS admin page.
133 this.observer.next(true);
134 // Resolve observable must emit /something/
135 this.observer.complete();
137 this.router.navigate([WS_MANAGE_PATH]);
138 this.observer.error(`Auth session linked to no
139 workstation or a workstation unknown to this browser`);
144 * Fetches data common to all staff interfaces.
146 loadStartupData(): Promise<any> {
148 // Fetch settings needed globally. This will cache the values
149 // in the org service.
150 return this.org.settings([
152 'webstaff.format.dates',
153 'webstaff.format.date_and_time',
154 'ui.staff.max_recent_patrons',
155 'circ.curbside', // navbar
156 'ui.staff.angular_circ.enabled',
157 'ui.staff.angular_catalog.enabled' // navbar
158 ]).then(settings => {
159 // Avoid clobbering defaults
160 if (settings['lib.timezone']) {
161 this.format.wsOrgTimezone = settings['lib.timezone'];
163 if (settings['webstaff.format.dates']) {
164 this.format.dateFormat = settings['webstaff.format.dates'];
166 if (settings['webstaff.format.date_and_time']) {
167 this.format.dateTimeFormat =
168 settings['webstaff.format.date_and_time'];
170 // TODO remove these once Angular Circ takes over.
171 if (settings['ui.staff.angular_circ.enabled']) {
172 return this.perm.hasWorkPermHere(['ACCESS_ANGULAR_CIRC']);