LP1904036 Login session keys expire; hold patron uses login key
[evergreen-equinox.git] / Open-ILS / src / eg2 / src / app / core / store.service.ts
1 /**
2  * Store and retrieve data from various sources.
3  *
4  * Data Types:
5  * 1. LocalItem: Stored in window.localStorage and persist indefinitely.
6  * 2. SessionItem: Stored in window.sessionStorage and persist until
7  *    the end of the current browser tab/window.  Data is only available
8  *    to the tab/window where the data was set.
9  * 3. LoginItem: Stored as session cookies and persist until the browser
10  *    is closed.  These values are avalable to all browser windows/tabs.
11  */
12 import {Injectable} from '@angular/core';
13 import {CookieService} from 'ngx-cookie';
14 import {HatchService} from './hatch.service';
15
16 const WS_ALL_KEY = 'eg.workstation.all';
17 const WS_DEF_KEY = 'eg.workstation.default';
18
19 @Injectable({providedIn: 'root'})
20 export class StoreService {
21
22     // Base path for cookie-based storage.
23     // Useful for limiting cookies to subsections of the application.
24     // Store cookies globally by default.
25     // Note cookies shared with /eg/staff must be stored at "/"
26     loginSessionBasePath = '/';
27
28     // Set of keys whose values should disappear at logout.
29     loginSessionKeys: string[] = [
30         'eg.auth.token',
31         'eg.auth.time',
32         'eg.auth.token.oc',
33         'eg.auth.time.oc'
34     ];
35
36     constructor(
37         private cookieService: CookieService,
38         private hatch: HatchService) {
39     }
40
41     private parseJson(valJson: string): any {
42         if (valJson === undefined || valJson === null || valJson === '') {
43             return null;
44         }
45         try {
46             return JSON.parse(valJson);
47         } catch (E) {
48             console.error(`Failure to parse JSON: ${E} => ${valJson}`);
49             return null;
50         }
51     }
52
53     /**
54      * Add a an app-local login session key
55      */
56     addLoginSessionKey(key: string): void {
57         if (!this.loginSessionKeys.includes(key)) {
58             this.loginSessionKeys.push(key);
59         }
60     }
61
62     setLocalItem(key: string, val: any, isJson?: boolean): void {
63         if (!isJson) {
64             val = JSON.stringify(val);
65         }
66         window.localStorage.setItem(key, val);
67     }
68
69     setSessionItem(key: string, val: any, isJson?: boolean): void {
70         if (!isJson) {
71             val = JSON.stringify(val);
72         }
73         window.sessionStorage.setItem(key, val);
74     }
75
76     setLoginSessionItem(key: string, val: any, isJson?: boolean): void {
77         if (!isJson) {
78             val = JSON.stringify(val);
79         }
80         this.cookieService.put(key, val,
81             {path : this.loginSessionBasePath, secure: true});
82     }
83
84     setWorkstations(val: any, isJson?: boolean): Promise<any> {
85         if (this.hatch.isAvailable) {
86             return this.hatch.setItem(WS_ALL_KEY, val).then(
87                 ok => {
88                     // When clearing workstations, remove the default.
89                     if (!val || val.length === 0) {
90                         return this.hatch.removeItem(WS_DEF_KEY);
91                     }
92                 }
93             );
94         } else {
95             return Promise.resolve(
96                 this.setLocalItem(WS_ALL_KEY, val, isJson));
97         }
98     }
99
100     setDefaultWorkstation(val: string, isJson?: boolean): Promise<any> {
101         if (this.hatch.isAvailable) {
102             return this.hatch.setItem(WS_DEF_KEY, val);
103         } else {
104             return Promise.resolve(
105                 this.setLocalItem(WS_DEF_KEY, val, isJson));
106         }
107     }
108
109     getLocalItem(key: string): any {
110         return this.parseJson(window.localStorage.getItem(key));
111     }
112
113     getSessionItem(key: string): any {
114         return this.parseJson(window.sessionStorage.getItem(key));
115     }
116
117     getLoginSessionItem(key: string): any {
118         return this.parseJson(this.cookieService.get(key));
119     }
120
121     getWorkstations(): Promise<any> {
122         if (this.hatch.isAvailable) {
123             return this.mergeWorkstations().then(ok => {
124                 this.removeLocalItem(WS_ALL_KEY);
125                 return this.hatch.getItem(WS_ALL_KEY);
126             });
127         } else {
128             return Promise.resolve(this.getLocalItem(WS_ALL_KEY));
129         }
130     }
131
132     // See if any workstatoins are stored in local storage.  If so, also
133     // see if we have any stored in Hatch.  If both, merged workstations
134     // from localStorage in Hatch storage, skipping any whose name
135     // collide with a workstation in Hatch.  If none exist in Hatch,
136     // copy the localStorage workstations over wholesale.
137     mergeWorkstations(): Promise<any> {
138         const existing = this.getLocalItem(WS_ALL_KEY);
139
140         if (!existing || existing.length === 0) {
141             return Promise.resolve();
142         }
143
144         return this.hatch.getItem(WS_ALL_KEY).then(inHatch => {
145
146             if (!inHatch || inHatch.length === 0) {
147                 // Nothing to merge, copy the data over directly
148                 return this.hatch.setItem('eg.workstation.all', existing);
149             }
150
151             const addMe: any = [];
152             existing.forEach(ws => {
153                 const match = inHatch.filter(w => w.name === ws.name)[0];
154                 if (!match) {
155                     console.log(
156                         'Migrating workstation from local storage to hatch: '
157                         + ws.name
158                     );
159                     addMe.push(ws);
160                 }
161             });
162             inHatch = inHatch.concat(addMe);
163             return this.hatch.setItem(WS_ALL_KEY, inHatch);
164         });
165     }
166
167     getDefaultWorkstation(): Promise<any> {
168         if (this.hatch.isAvailable) {
169             return this.hatch.getItem(WS_DEF_KEY).then(name => {
170                 if (name) {
171                     // We have a default in Hatch, remove any lingering
172                     // value from localStorage.
173                     this.removeLocalItem(WS_DEF_KEY);
174                     return name;
175                 } else {
176                     // Nothing in Hatch, see if we have a localStorage
177                     // value to migrate to Hatch
178                     name = this.getLocalItem(WS_DEF_KEY);
179                     if (name) {
180                         console.debug(
181                             'Migrating default workstation to Hatch ' + name);
182                         return this.hatch.setItem(WS_DEF_KEY, name)
183                         .then(ok => name);
184                     } else {
185                         return null;
186                     }
187                 }
188             });
189         } else {
190             return Promise.resolve(this.getLocalItem(WS_DEF_KEY));
191         }
192     }
193
194     removeLocalItem(key: string): void {
195         window.localStorage.removeItem(key);
196     }
197
198     removeSessionItem(key: string): void {
199         window.sessionStorage.removeItem(key);
200     }
201
202     removeLoginSessionItem(key: string): void {
203         this.cookieService.remove(key, {path : this.loginSessionBasePath});
204     }
205
206     removeDefaultWorkstation(val: string, isJson?: boolean): Promise<any> {
207         if (this.hatch.isAvailable) {
208             return this.hatch.removeItem(WS_DEF_KEY);
209         } else {
210             return Promise.resolve(
211                 this.removeLocalItem(WS_DEF_KEY));
212         }
213     }
214
215
216     clearLoginSessionItems(): void {
217         this.loginSessionKeys.forEach(
218             key => this.removeLoginSessionItem(key)
219         );
220     }
221 }
222