@huntsman-cancer-institute/authentication 16.0.1 → 17.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/authentication.component.d.ts +27 -27
- package/authentication.module.d.ts +24 -24
- package/authentication.provider.d.ts +16 -16
- package/authentication.service.d.ts +123 -123
- package/authorization.interceptor.d.ts +11 -11
- package/directlogin.component.d.ts +21 -21
- package/esm2022/authentication.component.mjs +112 -112
- package/esm2022/authentication.module.mjs +103 -103
- package/esm2022/authentication.provider.mjs +36 -36
- package/esm2022/authentication.service.mjs +393 -393
- package/esm2022/authorization.interceptor.mjs +79 -79
- package/esm2022/directlogin.component.mjs +47 -47
- package/esm2022/huntsman-cancer-institute-authentication.mjs +4 -4
- package/esm2022/index.mjs +12 -12
- package/esm2022/route-guard.service.mjs +22 -22
- package/esm2022/timeout-notification.component.mjs +117 -117
- package/fesm2022/huntsman-cancer-institute-authentication.mjs +831 -831
- package/fesm2022/huntsman-cancer-institute-authentication.mjs.map +1 -1
- package/index.d.ts +12 -12
- package/package.json +4 -4
- package/route-guard.service.d.ts +1 -1
- package/timeout-notification.component.d.ts +14 -14
|
@@ -17,502 +17,502 @@ import { map, catchError, first, takeWhile } from 'rxjs/operators';
|
|
|
17
17
|
import * as i2$1 from '@angular/platform-browser';
|
|
18
18
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
|
19
19
|
|
|
20
|
-
let AUTHENTICATION_TOKEN_KEY = new InjectionToken("authentication_token_key");
|
|
21
|
-
class AuthenticationProvider {
|
|
22
|
-
constructor(_localStorageService, _authenticationTokenKey) {
|
|
23
|
-
this._localStorageService = _localStorageService;
|
|
24
|
-
this._authenticationTokenKey = _authenticationTokenKey;
|
|
25
|
-
this.whitelistedDomains = [
|
|
26
|
-
"localhost",
|
|
27
|
-
new RegExp(".*[.]utah[.]edu")
|
|
28
|
-
];
|
|
29
|
-
this.tokenGetter = () => {
|
|
30
|
-
return this.authToken;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
get authenticationTokenKey() {
|
|
34
|
-
return this._authenticationTokenKey;
|
|
35
|
-
}
|
|
36
|
-
set authenticationTokenKey(_authenticationTokenKey) {
|
|
37
|
-
this._authenticationTokenKey = _authenticationTokenKey;
|
|
38
|
-
}
|
|
39
|
-
get authToken() {
|
|
40
|
-
return this._localStorageService.getItem(this._authenticationTokenKey);
|
|
41
|
-
}
|
|
42
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
43
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
44
|
-
}
|
|
45
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
46
|
-
type: Injectable
|
|
47
|
-
}], ctorParameters:
|
|
48
|
-
type: Inject,
|
|
49
|
-
args: [AUTHENTICATION_TOKEN_KEY]
|
|
50
|
-
}] }]
|
|
20
|
+
let AUTHENTICATION_TOKEN_KEY = new InjectionToken("authentication_token_key");
|
|
21
|
+
class AuthenticationProvider {
|
|
22
|
+
constructor(_localStorageService, _authenticationTokenKey) {
|
|
23
|
+
this._localStorageService = _localStorageService;
|
|
24
|
+
this._authenticationTokenKey = _authenticationTokenKey;
|
|
25
|
+
this.whitelistedDomains = [
|
|
26
|
+
"localhost",
|
|
27
|
+
new RegExp(".*[.]utah[.]edu")
|
|
28
|
+
];
|
|
29
|
+
this.tokenGetter = () => {
|
|
30
|
+
return this.authToken;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
get authenticationTokenKey() {
|
|
34
|
+
return this._authenticationTokenKey;
|
|
35
|
+
}
|
|
36
|
+
set authenticationTokenKey(_authenticationTokenKey) {
|
|
37
|
+
this._authenticationTokenKey = _authenticationTokenKey;
|
|
38
|
+
}
|
|
39
|
+
get authToken() {
|
|
40
|
+
return this._localStorageService.getItem(this._authenticationTokenKey);
|
|
41
|
+
}
|
|
42
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationProvider, deps: [{ token: i1.CoolLocalStorage }, { token: AUTHENTICATION_TOKEN_KEY }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
43
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationProvider }); }
|
|
44
|
+
}
|
|
45
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationProvider, decorators: [{
|
|
46
|
+
type: Injectable
|
|
47
|
+
}], ctorParameters: () => [{ type: i1.CoolLocalStorage }, { type: undefined, decorators: [{
|
|
48
|
+
type: Inject,
|
|
49
|
+
args: [AUTHENTICATION_TOKEN_KEY]
|
|
50
|
+
}] }] });
|
|
51
51
|
|
|
52
|
-
/*
|
|
53
|
-
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
54
|
-
*/
|
|
55
|
-
/**
|
|
56
|
-
* The token used for injection of the server side endpoint for the currently authenticated subject.
|
|
57
|
-
*
|
|
58
|
-
* @type {InjectionToken}
|
|
59
|
-
*/
|
|
60
|
-
let AUTHENTICATION_SERVER_URL = new InjectionToken("authentication_server_rest_api");
|
|
61
|
-
let AUTHENTICATION_LOGOUT_PATH = new InjectionToken("authentication_logout_path");
|
|
62
|
-
let AUTHENTICATION_DIRECT_ENDPOINT = new InjectionToken("authentication_direct_endpoint");
|
|
63
|
-
let AUTHENTICATION_TOKEN_ENDPOINT = new InjectionToken("authentication_token_endpoint");
|
|
64
|
-
let AUTHENTICATION_ROUTE = new InjectionToken("authentication_route");
|
|
65
|
-
let AUTHENTICATION_MAX_INACTIVITY_MINUTES = new InjectionToken("authentication_max_inactivity");
|
|
66
|
-
let AUTHENTICATION_USER_COUNTDOWN_SECONDS = new InjectionToken("authentication_user_countdown_seconds");
|
|
67
|
-
let AUTHENTICATION_IDP_INACTIVITY_MINUTES = new InjectionToken("authentication_idp_inactivity_minutes");
|
|
68
|
-
/**
|
|
69
|
-
* @since 1.0.0
|
|
70
|
-
*/
|
|
71
|
-
class AuthenticationService {
|
|
72
|
-
/**
|
|
73
|
-
* The generic error message used when a server error is thrown without a status.
|
|
74
|
-
*
|
|
75
|
-
* @type {string}
|
|
76
|
-
*/
|
|
77
|
-
static { this.GENERIC_ERR_MSG = "Server error"; }
|
|
78
|
-
static { this.CONTENT_TYPE = "Content-Type"; }
|
|
79
|
-
static { this.SEC_GOV_CLASS_HEADER = "SecurityGovernorClass"; }
|
|
80
|
-
static { this.SEC_GOV_ID_HEADER = "SecurityGovernorId"; }
|
|
81
|
-
static { this.DEIDENT_HEADER = "DeidentifiedContext"; }
|
|
82
|
-
static { this.LIMITED_HEADER = "LimitedContext"; }
|
|
83
|
-
constructor(_http, _router, _localStorageService, _jwtHelper, authenticationProvider, _authenticationRoute, _logoutPath, _tokenEndpoint, _serverUrl, _directEndpoint, _maxInactivity, _userCountdownSeconds, _idpInactivityMinutes, locationStrategy) {
|
|
84
|
-
this._http = _http;
|
|
85
|
-
this._router = _router;
|
|
86
|
-
this._localStorageService = _localStorageService;
|
|
87
|
-
this._jwtHelper = _jwtHelper;
|
|
88
|
-
this.authenticationProvider = authenticationProvider;
|
|
89
|
-
this._authenticationRoute = _authenticationRoute;
|
|
90
|
-
this._logoutPath = _logoutPath;
|
|
91
|
-
this._tokenEndpoint = _tokenEndpoint;
|
|
92
|
-
this._serverUrl = _serverUrl;
|
|
93
|
-
this._directEndpoint = _directEndpoint;
|
|
94
|
-
this._maxInactivity = _maxInactivity;
|
|
95
|
-
this._userCountdownSeconds = _userCountdownSeconds;
|
|
96
|
-
this._idpInactivityMinutes = _idpInactivityMinutes;
|
|
97
|
-
this.locationStrategy = locationStrategy;
|
|
98
|
-
this.userCountdownSeconds = 60;
|
|
99
|
-
this.idpInactivityMinutes = 5;
|
|
100
|
-
this.contentType = "application/json";
|
|
101
|
-
this.limitedContext = false;
|
|
102
|
-
this.deidentifiedContext = false;
|
|
103
|
-
this.maxViewPermission = new BehaviorSubject("viewident");
|
|
104
|
-
this._isAuthenticatedSubject = new BehaviorSubject(false);
|
|
105
|
-
this._userIsAboutToTimeOut = new BehaviorSubject(false);
|
|
106
|
-
this._maxInactivityMinutes = 120;
|
|
107
|
-
this.contextRoot = "";
|
|
108
|
-
if (isDevMode()) {
|
|
109
|
-
console.debug("window.location.href: " + window.location.href);
|
|
110
|
-
}
|
|
111
|
-
if (window.location) {
|
|
112
|
-
let parts = window.location.href.split("/");
|
|
113
|
-
this.baseUrl = parts[0] + "//" + parts[2];
|
|
114
|
-
if (parts.length > 3) {
|
|
115
|
-
this.contextRoot = parts[3];
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (this._localStorageService.getItem("maxViewPermission")) {
|
|
119
|
-
this.maxViewPermission.next(this._localStorageService.getItem("maxViewPermission"));
|
|
120
|
-
}
|
|
121
|
-
if (_maxInactivity) {
|
|
122
|
-
this._maxInactivityMinutes = _maxInactivity;
|
|
123
|
-
}
|
|
124
|
-
if (_userCountdownSeconds) {
|
|
125
|
-
this.userCountdownSeconds = _userCountdownSeconds;
|
|
126
|
-
}
|
|
127
|
-
if (_idpInactivityMinutes) {
|
|
128
|
-
this.idpInactivityMinutes = _idpInactivityMinutes;
|
|
129
|
-
}
|
|
130
|
-
this.hasValidConfig();
|
|
131
|
-
//There could be a non-expired token in local storage.
|
|
132
|
-
let token = this.authenticationProvider.authToken;
|
|
133
|
-
this.storeToken(token);
|
|
134
|
-
}
|
|
135
|
-
getBaseUrl() {
|
|
136
|
-
return (this.baseUrl) ? this.baseUrl : "";
|
|
137
|
-
}
|
|
138
|
-
getContextRoot() {
|
|
139
|
-
return this.contextRoot;
|
|
140
|
-
}
|
|
141
|
-
getHeaders(req) {
|
|
142
|
-
let headers = req.headers;
|
|
143
|
-
//Don't set content type if already set
|
|
144
|
-
if (!req.headers.get(AuthenticationService.CONTENT_TYPE)) {
|
|
145
|
-
headers = headers.set(AuthenticationService.CONTENT_TYPE, this.contentType.toString());
|
|
146
|
-
}
|
|
147
|
-
if (headers.get(AuthenticationService.SEC_GOV_CLASS_HEADER) === "") {
|
|
148
|
-
headers = headers.delete(AuthenticationService.SEC_GOV_CLASS_HEADER);
|
|
149
|
-
}
|
|
150
|
-
else if (this.securityGovernorClass && !headers.get(AuthenticationService.SEC_GOV_CLASS_HEADER)) {
|
|
151
|
-
headers = headers.set(AuthenticationService.SEC_GOV_CLASS_HEADER, this.securityGovernorClass);
|
|
152
|
-
}
|
|
153
|
-
if (headers.get(AuthenticationService.SEC_GOV_ID_HEADER) === "") {
|
|
154
|
-
headers = headers.delete(AuthenticationService.SEC_GOV_ID_HEADER);
|
|
155
|
-
}
|
|
156
|
-
else if (this.securityGovernorId && !headers.get(AuthenticationService.SEC_GOV_ID_HEADER)) {
|
|
157
|
-
headers = headers.set(AuthenticationService.SEC_GOV_ID_HEADER, this.securityGovernorId.toString());
|
|
158
|
-
}
|
|
159
|
-
headers = headers.set(AuthenticationService.DEIDENT_HEADER, this.deidentifiedContext.toString());
|
|
160
|
-
headers = headers.set(AuthenticationService.LIMITED_HEADER, this.limitedContext.toString());
|
|
161
|
-
return headers;
|
|
162
|
-
}
|
|
163
|
-
get authenticationTokenKey() {
|
|
164
|
-
return this.authenticationProvider.authenticationTokenKey;
|
|
165
|
-
}
|
|
166
|
-
get authToken() {
|
|
167
|
-
return this.authenticationProvider.authToken;
|
|
168
|
-
}
|
|
169
|
-
updateUserActivity() {
|
|
170
|
-
if (this._isAuthenticatedSubject.value) {
|
|
171
|
-
this._lastUserInteraction = new Date();
|
|
172
|
-
this._userIsAboutToTimeOut.next(false);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* A mutator for identifying the clients original request location. Setting this value will influence the end location
|
|
177
|
-
* navigated to by {@link #navigateToPath}.
|
|
178
|
-
*
|
|
179
|
-
* @param redirectUrl location of the users request before authentication
|
|
180
|
-
*/
|
|
181
|
-
set redirectUrl(redirectUrl) {
|
|
182
|
-
this._redirectUrl = redirectUrl;
|
|
183
|
-
}
|
|
184
|
-
get redirectUrl() {
|
|
185
|
-
return this._redirectUrl;
|
|
186
|
-
}
|
|
187
|
-
requestAccessToken(redirectOnSuccess) {
|
|
188
|
-
this._http.get(this.tokenLocation(), { withCredentials: true })
|
|
189
|
-
.subscribe((response) => {
|
|
190
|
-
this.storeToken(response.auth_token);
|
|
191
|
-
if (redirectOnSuccess) {
|
|
192
|
-
this.proceedIfAuthenticated();
|
|
193
|
-
}
|
|
194
|
-
}, (error) => {
|
|
195
|
-
//Token refresh failed.
|
|
196
|
-
this.logout(true);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Verifies whether or not a current user session exists.
|
|
201
|
-
*
|
|
202
|
-
* @returns {Observable<boolean>} evaluates to true if the user is authenticated, false otherwise.
|
|
203
|
-
*/
|
|
204
|
-
isAuthenticated() {
|
|
205
|
-
return this._isAuthenticatedSubject.asObservable();
|
|
206
|
-
}
|
|
207
|
-
isAboutToTimeOut() {
|
|
208
|
-
return this._userIsAboutToTimeOut.asObservable();
|
|
209
|
-
}
|
|
210
|
-
getTimeoutStart() {
|
|
211
|
-
if (this._lastUserInteraction) {
|
|
212
|
-
return this._lastUserInteraction.valueOf() + (((this._maxInactivityMinutes * 60) - this.userCountdownSeconds) * 1000);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
tokenLocation() {
|
|
216
|
-
if (this._serverUrl) {
|
|
217
|
-
return this._serverUrl + this._tokenEndpoint;
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
return this._tokenEndpoint;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
directLoginLocation() {
|
|
224
|
-
if (this._serverUrl) {
|
|
225
|
-
return this._serverUrl + this._directEndpoint;
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
return this._directEndpoint;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
logoutLocation() {
|
|
232
|
-
if (this._serverUrl) {
|
|
233
|
-
return this._serverUrl + this._logoutPath;
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
return this._logoutPath;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* A function to authenticated the user with the provided credentials. Failure results in an error that describes the
|
|
241
|
-
* server response (status and status message) and should be actionable by the client application.
|
|
242
|
-
*
|
|
243
|
-
* @param username of the authenticating user to verify
|
|
244
|
-
* @param password of the authenticating user to verify
|
|
245
|
-
* @returns {Observable<R>} describing the result of the login action, true or an error
|
|
246
|
-
*/
|
|
247
|
-
login(_username, _password) {
|
|
248
|
-
return this._http.post(this.directLoginLocation(), { username: _username, password: _password }, { observe: "response" }).pipe(map((resp) => {
|
|
249
|
-
if (resp.status === 201) {
|
|
250
|
-
return true;
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
throw new Error("Authentication failed. " + resp.status + ": " + resp.statusText);
|
|
254
|
-
}
|
|
255
|
-
}), catchError(this.handleError));
|
|
256
|
-
}
|
|
257
|
-
clearLogin() {
|
|
258
|
-
//Front-end logout
|
|
259
|
-
try {
|
|
260
|
-
this._localStorageService.removeItem(this.authenticationProvider.authenticationTokenKey);
|
|
261
|
-
this.unsubscribeFromTokenRefresh();
|
|
262
|
-
this._isAuthenticatedSubject.next(false);
|
|
263
|
-
this._userIsAboutToTimeOut.next(false);
|
|
264
|
-
}
|
|
265
|
-
catch (Error) {
|
|
266
|
-
}
|
|
267
|
-
//Back-end logout
|
|
268
|
-
let headers = new HttpHeaders().set(AuthenticationService.CONTENT_TYPE, "text/plain");
|
|
269
|
-
return this._http.get(this.logoutLocation(), { headers: headers });
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* A function to signal the termination of the current session. Invoking this function will clean up any relevant state
|
|
273
|
-
* related to the last active session.
|
|
274
|
-
*/
|
|
275
|
-
logout(keepCurrentRoute = false) {
|
|
276
|
-
//Prevent logout if already on authentication route. Doing otherwise screws up SAML
|
|
277
|
-
if (!this._router.routerState || this._router.routerState.snapshot.url !== this._authenticationRoute) {
|
|
278
|
-
this._redirectUrl = (keepCurrentRoute && this._router.routerState && this._router.routerState.snapshot) ? this._router.routerState.snapshot.url : "";
|
|
279
|
-
if (this._redirectUrl.startsWith("/")) {
|
|
280
|
-
this._redirectUrl = this._redirectUrl.substring(1);
|
|
281
|
-
}
|
|
282
|
-
this.clearLogin().subscribe((response) => {
|
|
283
|
-
window.location.replace(this._redirectUrl);
|
|
284
|
-
}, (error) => {
|
|
285
|
-
window.location.replace(this._redirectUrl);
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
storeToken(token) {
|
|
290
|
-
let valid = this.validateToken(token);
|
|
291
|
-
// unsubscribe from refesh before we decide wether to resubscribe
|
|
292
|
-
this.unsubscribeFromTokenRefresh();
|
|
293
|
-
if (valid) {
|
|
294
|
-
this._localStorageService.setItem(this.authenticationProvider.authenticationTokenKey, token);
|
|
295
|
-
this.subscribeToTokenRefresh(token);
|
|
296
|
-
//Change the BehaviorSubject if the user was not previously authenticated.
|
|
297
|
-
//Since other code may be subscribing to this observable, we don't want to cause new events to fire if just refreshing the JWT.
|
|
298
|
-
if (!this._isAuthenticatedSubject.value) {
|
|
299
|
-
this._isAuthenticatedSubject.next(true);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
this._localStorageService.removeItem(this.authenticationProvider.authenticationTokenKey);
|
|
304
|
-
this._isAuthenticatedSubject.next(false);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
proceedIfAuthenticated() {
|
|
308
|
-
if (isDevMode()) {
|
|
309
|
-
console.debug("AuthenticationService.proceedIfAuthenticated: " + this._redirectUrl);
|
|
310
|
-
}
|
|
311
|
-
if (this._isAuthenticatedSubject.value) {
|
|
312
|
-
//Login counts as user activity, too
|
|
313
|
-
this.updateUserActivity();
|
|
314
|
-
if (this._redirectUrl && this._redirectUrl && this._redirectUrl !== "") {
|
|
315
|
-
this._router.navigateByUrl(this._redirectUrl);
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
this._router.navigate([""]);
|
|
319
|
-
}
|
|
320
|
-
return true;
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
validateToken(token) {
|
|
327
|
-
return (token && !this._jwtHelper.isTokenExpired(token));
|
|
328
|
-
}
|
|
329
|
-
subscribeToTokenRefresh(token) {
|
|
330
|
-
let exp = this._jwtHelper.getTokenExpirationDate(token);
|
|
331
|
-
// Use a timer to periodically check timeouts
|
|
332
|
-
this._refreshSubscription = interval(1000)
|
|
333
|
-
.subscribe(() => {
|
|
334
|
-
// If a tab is inactive we can't know if our timer is accurate
|
|
335
|
-
// so when the interval hits check against timestamps
|
|
336
|
-
if (this._isAuthenticatedSubject.value && Date.now() > this.getTimeoutStart()) {
|
|
337
|
-
//Don't update the subject more than once! Doing so initializes more than one countdown timer!
|
|
338
|
-
if (this._userIsAboutToTimeOut.getValue() !== true) {
|
|
339
|
-
this._userIsAboutToTimeOut.next(true);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
// check for refresh token
|
|
343
|
-
let msToExpiry = (exp.valueOf() - new Date().valueOf());
|
|
344
|
-
// Refresh 60 seconds before expiry
|
|
345
|
-
if (msToExpiry <= 60000) {
|
|
346
|
-
this.refreshTokenIfUserIsActive();
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
unsubscribeFromTokenRefresh() {
|
|
351
|
-
if (this._refreshSubscription && !this._refreshSubscription.closed) {
|
|
352
|
-
this._refreshSubscription.unsubscribe();
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
getMaxViewPermission() {
|
|
356
|
-
return this.maxViewPermission.getValue();
|
|
357
|
-
}
|
|
358
|
-
getMaxViewPermissionSubject() {
|
|
359
|
-
return this.maxViewPermission;
|
|
360
|
-
}
|
|
361
|
-
setMaxViewPermission(maxViewPermission) {
|
|
362
|
-
this._localStorageService.setItem("maxViewPermission", maxViewPermission);
|
|
363
|
-
this.maxViewPermission.next(maxViewPermission);
|
|
364
|
-
}
|
|
365
|
-
refreshTokenIfUserIsActive() {
|
|
366
|
-
//Only refresh if the user has been active
|
|
367
|
-
if (this._lastUserInteraction && ((new Date().valueOf() - this._lastUserInteraction.valueOf()) <= (this._maxInactivityMinutes * 60 * 1000))) {
|
|
368
|
-
this.requestAccessToken(false);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
hasValidConfig() {
|
|
372
|
-
if (this._tokenEndpoint == null && (this._serverUrl === null || this._logoutPath === null)) {
|
|
373
|
-
throw new Error("BUG ALERT! Invalid AuthenticationService configuration. No valid configuration for authentication endpoint(s).");
|
|
374
|
-
}
|
|
375
|
-
if (this._localStorageService === null || this.authenticationProvider.authenticationTokenKey === null) {
|
|
376
|
-
throw new Error("BUG ALERT! Invalid AuthenticationService configuration. No valid configuration for local storage");
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
handleError(error) {
|
|
380
|
-
let errMsg = (error.message) ? error.message : AuthenticationService.GENERIC_ERR_MSG;
|
|
381
|
-
return throwError(errMsg);
|
|
382
|
-
}
|
|
383
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
384
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
385
|
-
}
|
|
386
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
387
|
-
type: Injectable
|
|
388
|
-
}], ctorParameters:
|
|
389
|
-
type: Inject,
|
|
390
|
-
args: [AUTHENTICATION_ROUTE]
|
|
391
|
-
}] }, { type: undefined, decorators: [{
|
|
392
|
-
type: Inject,
|
|
393
|
-
args: [AUTHENTICATION_LOGOUT_PATH]
|
|
394
|
-
}] }, { type: undefined, decorators: [{
|
|
395
|
-
type: Inject,
|
|
396
|
-
args: [AUTHENTICATION_TOKEN_ENDPOINT]
|
|
397
|
-
}] }, { type: undefined, decorators: [{
|
|
398
|
-
type: Optional
|
|
399
|
-
}, {
|
|
400
|
-
type: Inject,
|
|
401
|
-
args: [AUTHENTICATION_SERVER_URL]
|
|
402
|
-
}] }, { type: undefined, decorators: [{
|
|
403
|
-
type: Optional
|
|
404
|
-
}, {
|
|
405
|
-
type: Inject,
|
|
406
|
-
args: [AUTHENTICATION_DIRECT_ENDPOINT]
|
|
407
|
-
}] }, { type: undefined, decorators: [{
|
|
408
|
-
type: Optional
|
|
409
|
-
}, {
|
|
410
|
-
type: Inject,
|
|
411
|
-
args: [AUTHENTICATION_MAX_INACTIVITY_MINUTES]
|
|
412
|
-
}] }, { type: undefined, decorators: [{
|
|
413
|
-
type: Optional
|
|
414
|
-
}, {
|
|
415
|
-
type: Inject,
|
|
416
|
-
args: [AUTHENTICATION_USER_COUNTDOWN_SECONDS]
|
|
417
|
-
}] }, { type: undefined, decorators: [{
|
|
418
|
-
type: Optional
|
|
419
|
-
}, {
|
|
420
|
-
type: Inject,
|
|
421
|
-
args: [AUTHENTICATION_IDP_INACTIVITY_MINUTES]
|
|
422
|
-
}] }, { type: i4$1.LocationStrategy, decorators: [{
|
|
423
|
-
type: Optional
|
|
424
|
-
}, {
|
|
425
|
-
type: Inject,
|
|
426
|
-
args: [LocationStrategy]
|
|
427
|
-
}] }]
|
|
52
|
+
/*
|
|
53
|
+
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* The token used for injection of the server side endpoint for the currently authenticated subject.
|
|
57
|
+
*
|
|
58
|
+
* @type {InjectionToken}
|
|
59
|
+
*/
|
|
60
|
+
let AUTHENTICATION_SERVER_URL = new InjectionToken("authentication_server_rest_api");
|
|
61
|
+
let AUTHENTICATION_LOGOUT_PATH = new InjectionToken("authentication_logout_path");
|
|
62
|
+
let AUTHENTICATION_DIRECT_ENDPOINT = new InjectionToken("authentication_direct_endpoint");
|
|
63
|
+
let AUTHENTICATION_TOKEN_ENDPOINT = new InjectionToken("authentication_token_endpoint");
|
|
64
|
+
let AUTHENTICATION_ROUTE = new InjectionToken("authentication_route");
|
|
65
|
+
let AUTHENTICATION_MAX_INACTIVITY_MINUTES = new InjectionToken("authentication_max_inactivity");
|
|
66
|
+
let AUTHENTICATION_USER_COUNTDOWN_SECONDS = new InjectionToken("authentication_user_countdown_seconds");
|
|
67
|
+
let AUTHENTICATION_IDP_INACTIVITY_MINUTES = new InjectionToken("authentication_idp_inactivity_minutes");
|
|
68
|
+
/**
|
|
69
|
+
* @since 1.0.0
|
|
70
|
+
*/
|
|
71
|
+
class AuthenticationService {
|
|
72
|
+
/**
|
|
73
|
+
* The generic error message used when a server error is thrown without a status.
|
|
74
|
+
*
|
|
75
|
+
* @type {string}
|
|
76
|
+
*/
|
|
77
|
+
static { this.GENERIC_ERR_MSG = "Server error"; }
|
|
78
|
+
static { this.CONTENT_TYPE = "Content-Type"; }
|
|
79
|
+
static { this.SEC_GOV_CLASS_HEADER = "SecurityGovernorClass"; }
|
|
80
|
+
static { this.SEC_GOV_ID_HEADER = "SecurityGovernorId"; }
|
|
81
|
+
static { this.DEIDENT_HEADER = "DeidentifiedContext"; }
|
|
82
|
+
static { this.LIMITED_HEADER = "LimitedContext"; }
|
|
83
|
+
constructor(_http, _router, _localStorageService, _jwtHelper, authenticationProvider, _authenticationRoute, _logoutPath, _tokenEndpoint, _serverUrl, _directEndpoint, _maxInactivity, _userCountdownSeconds, _idpInactivityMinutes, locationStrategy) {
|
|
84
|
+
this._http = _http;
|
|
85
|
+
this._router = _router;
|
|
86
|
+
this._localStorageService = _localStorageService;
|
|
87
|
+
this._jwtHelper = _jwtHelper;
|
|
88
|
+
this.authenticationProvider = authenticationProvider;
|
|
89
|
+
this._authenticationRoute = _authenticationRoute;
|
|
90
|
+
this._logoutPath = _logoutPath;
|
|
91
|
+
this._tokenEndpoint = _tokenEndpoint;
|
|
92
|
+
this._serverUrl = _serverUrl;
|
|
93
|
+
this._directEndpoint = _directEndpoint;
|
|
94
|
+
this._maxInactivity = _maxInactivity;
|
|
95
|
+
this._userCountdownSeconds = _userCountdownSeconds;
|
|
96
|
+
this._idpInactivityMinutes = _idpInactivityMinutes;
|
|
97
|
+
this.locationStrategy = locationStrategy;
|
|
98
|
+
this.userCountdownSeconds = 60;
|
|
99
|
+
this.idpInactivityMinutes = 5;
|
|
100
|
+
this.contentType = "application/json";
|
|
101
|
+
this.limitedContext = false;
|
|
102
|
+
this.deidentifiedContext = false;
|
|
103
|
+
this.maxViewPermission = new BehaviorSubject("viewident");
|
|
104
|
+
this._isAuthenticatedSubject = new BehaviorSubject(false);
|
|
105
|
+
this._userIsAboutToTimeOut = new BehaviorSubject(false);
|
|
106
|
+
this._maxInactivityMinutes = 120;
|
|
107
|
+
this.contextRoot = "";
|
|
108
|
+
if (isDevMode()) {
|
|
109
|
+
console.debug("window.location.href: " + window.location.href);
|
|
110
|
+
}
|
|
111
|
+
if (window.location) {
|
|
112
|
+
let parts = window.location.href.split("/");
|
|
113
|
+
this.baseUrl = parts[0] + "//" + parts[2];
|
|
114
|
+
if (parts.length > 3) {
|
|
115
|
+
this.contextRoot = parts[3];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (this._localStorageService.getItem("maxViewPermission")) {
|
|
119
|
+
this.maxViewPermission.next(this._localStorageService.getItem("maxViewPermission"));
|
|
120
|
+
}
|
|
121
|
+
if (_maxInactivity) {
|
|
122
|
+
this._maxInactivityMinutes = _maxInactivity;
|
|
123
|
+
}
|
|
124
|
+
if (_userCountdownSeconds) {
|
|
125
|
+
this.userCountdownSeconds = _userCountdownSeconds;
|
|
126
|
+
}
|
|
127
|
+
if (_idpInactivityMinutes) {
|
|
128
|
+
this.idpInactivityMinutes = _idpInactivityMinutes;
|
|
129
|
+
}
|
|
130
|
+
this.hasValidConfig();
|
|
131
|
+
//There could be a non-expired token in local storage.
|
|
132
|
+
let token = this.authenticationProvider.authToken;
|
|
133
|
+
this.storeToken(token);
|
|
134
|
+
}
|
|
135
|
+
getBaseUrl() {
|
|
136
|
+
return (this.baseUrl) ? this.baseUrl : "";
|
|
137
|
+
}
|
|
138
|
+
getContextRoot() {
|
|
139
|
+
return this.contextRoot;
|
|
140
|
+
}
|
|
141
|
+
getHeaders(req) {
|
|
142
|
+
let headers = req.headers;
|
|
143
|
+
//Don't set content type if already set
|
|
144
|
+
if (!req.headers.get(AuthenticationService.CONTENT_TYPE)) {
|
|
145
|
+
headers = headers.set(AuthenticationService.CONTENT_TYPE, this.contentType.toString());
|
|
146
|
+
}
|
|
147
|
+
if (headers.get(AuthenticationService.SEC_GOV_CLASS_HEADER) === "") {
|
|
148
|
+
headers = headers.delete(AuthenticationService.SEC_GOV_CLASS_HEADER);
|
|
149
|
+
}
|
|
150
|
+
else if (this.securityGovernorClass && !headers.get(AuthenticationService.SEC_GOV_CLASS_HEADER)) {
|
|
151
|
+
headers = headers.set(AuthenticationService.SEC_GOV_CLASS_HEADER, this.securityGovernorClass);
|
|
152
|
+
}
|
|
153
|
+
if (headers.get(AuthenticationService.SEC_GOV_ID_HEADER) === "") {
|
|
154
|
+
headers = headers.delete(AuthenticationService.SEC_GOV_ID_HEADER);
|
|
155
|
+
}
|
|
156
|
+
else if (this.securityGovernorId && !headers.get(AuthenticationService.SEC_GOV_ID_HEADER)) {
|
|
157
|
+
headers = headers.set(AuthenticationService.SEC_GOV_ID_HEADER, this.securityGovernorId.toString());
|
|
158
|
+
}
|
|
159
|
+
headers = headers.set(AuthenticationService.DEIDENT_HEADER, this.deidentifiedContext.toString());
|
|
160
|
+
headers = headers.set(AuthenticationService.LIMITED_HEADER, this.limitedContext.toString());
|
|
161
|
+
return headers;
|
|
162
|
+
}
|
|
163
|
+
get authenticationTokenKey() {
|
|
164
|
+
return this.authenticationProvider.authenticationTokenKey;
|
|
165
|
+
}
|
|
166
|
+
get authToken() {
|
|
167
|
+
return this.authenticationProvider.authToken;
|
|
168
|
+
}
|
|
169
|
+
updateUserActivity() {
|
|
170
|
+
if (this._isAuthenticatedSubject.value) {
|
|
171
|
+
this._lastUserInteraction = new Date();
|
|
172
|
+
this._userIsAboutToTimeOut.next(false);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* A mutator for identifying the clients original request location. Setting this value will influence the end location
|
|
177
|
+
* navigated to by {@link #navigateToPath}.
|
|
178
|
+
*
|
|
179
|
+
* @param redirectUrl location of the users request before authentication
|
|
180
|
+
*/
|
|
181
|
+
set redirectUrl(redirectUrl) {
|
|
182
|
+
this._redirectUrl = redirectUrl;
|
|
183
|
+
}
|
|
184
|
+
get redirectUrl() {
|
|
185
|
+
return this._redirectUrl;
|
|
186
|
+
}
|
|
187
|
+
requestAccessToken(redirectOnSuccess) {
|
|
188
|
+
this._http.get(this.tokenLocation(), { withCredentials: true })
|
|
189
|
+
.subscribe((response) => {
|
|
190
|
+
this.storeToken(response.auth_token);
|
|
191
|
+
if (redirectOnSuccess) {
|
|
192
|
+
this.proceedIfAuthenticated();
|
|
193
|
+
}
|
|
194
|
+
}, (error) => {
|
|
195
|
+
//Token refresh failed.
|
|
196
|
+
this.logout(true);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Verifies whether or not a current user session exists.
|
|
201
|
+
*
|
|
202
|
+
* @returns {Observable<boolean>} evaluates to true if the user is authenticated, false otherwise.
|
|
203
|
+
*/
|
|
204
|
+
isAuthenticated() {
|
|
205
|
+
return this._isAuthenticatedSubject.asObservable();
|
|
206
|
+
}
|
|
207
|
+
isAboutToTimeOut() {
|
|
208
|
+
return this._userIsAboutToTimeOut.asObservable();
|
|
209
|
+
}
|
|
210
|
+
getTimeoutStart() {
|
|
211
|
+
if (this._lastUserInteraction) {
|
|
212
|
+
return this._lastUserInteraction.valueOf() + (((this._maxInactivityMinutes * 60) - this.userCountdownSeconds) * 1000);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
tokenLocation() {
|
|
216
|
+
if (this._serverUrl) {
|
|
217
|
+
return this._serverUrl + this._tokenEndpoint;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
return this._tokenEndpoint;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
directLoginLocation() {
|
|
224
|
+
if (this._serverUrl) {
|
|
225
|
+
return this._serverUrl + this._directEndpoint;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
return this._directEndpoint;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
logoutLocation() {
|
|
232
|
+
if (this._serverUrl) {
|
|
233
|
+
return this._serverUrl + this._logoutPath;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
return this._logoutPath;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A function to authenticated the user with the provided credentials. Failure results in an error that describes the
|
|
241
|
+
* server response (status and status message) and should be actionable by the client application.
|
|
242
|
+
*
|
|
243
|
+
* @param username of the authenticating user to verify
|
|
244
|
+
* @param password of the authenticating user to verify
|
|
245
|
+
* @returns {Observable<R>} describing the result of the login action, true or an error
|
|
246
|
+
*/
|
|
247
|
+
login(_username, _password) {
|
|
248
|
+
return this._http.post(this.directLoginLocation(), { username: _username, password: _password }, { observe: "response" }).pipe(map((resp) => {
|
|
249
|
+
if (resp.status === 201) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
throw new Error("Authentication failed. " + resp.status + ": " + resp.statusText);
|
|
254
|
+
}
|
|
255
|
+
}), catchError(this.handleError));
|
|
256
|
+
}
|
|
257
|
+
clearLogin() {
|
|
258
|
+
//Front-end logout
|
|
259
|
+
try {
|
|
260
|
+
this._localStorageService.removeItem(this.authenticationProvider.authenticationTokenKey);
|
|
261
|
+
this.unsubscribeFromTokenRefresh();
|
|
262
|
+
this._isAuthenticatedSubject.next(false);
|
|
263
|
+
this._userIsAboutToTimeOut.next(false);
|
|
264
|
+
}
|
|
265
|
+
catch (Error) {
|
|
266
|
+
}
|
|
267
|
+
//Back-end logout
|
|
268
|
+
let headers = new HttpHeaders().set(AuthenticationService.CONTENT_TYPE, "text/plain");
|
|
269
|
+
return this._http.get(this.logoutLocation(), { headers: headers });
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A function to signal the termination of the current session. Invoking this function will clean up any relevant state
|
|
273
|
+
* related to the last active session.
|
|
274
|
+
*/
|
|
275
|
+
logout(keepCurrentRoute = false) {
|
|
276
|
+
//Prevent logout if already on authentication route. Doing otherwise screws up SAML
|
|
277
|
+
if (!this._router.routerState || this._router.routerState.snapshot.url !== this._authenticationRoute) {
|
|
278
|
+
this._redirectUrl = (keepCurrentRoute && this._router.routerState && this._router.routerState.snapshot) ? this._router.routerState.snapshot.url : "";
|
|
279
|
+
if (this._redirectUrl.startsWith("/")) {
|
|
280
|
+
this._redirectUrl = this._redirectUrl.substring(1);
|
|
281
|
+
}
|
|
282
|
+
this.clearLogin().subscribe((response) => {
|
|
283
|
+
window.location.replace(this._redirectUrl);
|
|
284
|
+
}, (error) => {
|
|
285
|
+
window.location.replace(this._redirectUrl);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
storeToken(token) {
|
|
290
|
+
let valid = this.validateToken(token);
|
|
291
|
+
// unsubscribe from refesh before we decide wether to resubscribe
|
|
292
|
+
this.unsubscribeFromTokenRefresh();
|
|
293
|
+
if (valid) {
|
|
294
|
+
this._localStorageService.setItem(this.authenticationProvider.authenticationTokenKey, token);
|
|
295
|
+
this.subscribeToTokenRefresh(token);
|
|
296
|
+
//Change the BehaviorSubject if the user was not previously authenticated.
|
|
297
|
+
//Since other code may be subscribing to this observable, we don't want to cause new events to fire if just refreshing the JWT.
|
|
298
|
+
if (!this._isAuthenticatedSubject.value) {
|
|
299
|
+
this._isAuthenticatedSubject.next(true);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
this._localStorageService.removeItem(this.authenticationProvider.authenticationTokenKey);
|
|
304
|
+
this._isAuthenticatedSubject.next(false);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
proceedIfAuthenticated() {
|
|
308
|
+
if (isDevMode()) {
|
|
309
|
+
console.debug("AuthenticationService.proceedIfAuthenticated: " + this._redirectUrl);
|
|
310
|
+
}
|
|
311
|
+
if (this._isAuthenticatedSubject.value) {
|
|
312
|
+
//Login counts as user activity, too
|
|
313
|
+
this.updateUserActivity();
|
|
314
|
+
if (this._redirectUrl && this._redirectUrl && this._redirectUrl !== "") {
|
|
315
|
+
this._router.navigateByUrl(this._redirectUrl);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
this._router.navigate([""]);
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
validateToken(token) {
|
|
327
|
+
return (token && !this._jwtHelper.isTokenExpired(token));
|
|
328
|
+
}
|
|
329
|
+
subscribeToTokenRefresh(token) {
|
|
330
|
+
let exp = this._jwtHelper.getTokenExpirationDate(token);
|
|
331
|
+
// Use a timer to periodically check timeouts
|
|
332
|
+
this._refreshSubscription = interval(1000)
|
|
333
|
+
.subscribe(() => {
|
|
334
|
+
// If a tab is inactive we can't know if our timer is accurate
|
|
335
|
+
// so when the interval hits check against timestamps
|
|
336
|
+
if (this._isAuthenticatedSubject.value && Date.now() > this.getTimeoutStart()) {
|
|
337
|
+
//Don't update the subject more than once! Doing so initializes more than one countdown timer!
|
|
338
|
+
if (this._userIsAboutToTimeOut.getValue() !== true) {
|
|
339
|
+
this._userIsAboutToTimeOut.next(true);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// check for refresh token
|
|
343
|
+
let msToExpiry = (exp.valueOf() - new Date().valueOf());
|
|
344
|
+
// Refresh 60 seconds before expiry
|
|
345
|
+
if (msToExpiry <= 60000) {
|
|
346
|
+
this.refreshTokenIfUserIsActive();
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
unsubscribeFromTokenRefresh() {
|
|
351
|
+
if (this._refreshSubscription && !this._refreshSubscription.closed) {
|
|
352
|
+
this._refreshSubscription.unsubscribe();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
getMaxViewPermission() {
|
|
356
|
+
return this.maxViewPermission.getValue();
|
|
357
|
+
}
|
|
358
|
+
getMaxViewPermissionSubject() {
|
|
359
|
+
return this.maxViewPermission;
|
|
360
|
+
}
|
|
361
|
+
setMaxViewPermission(maxViewPermission) {
|
|
362
|
+
this._localStorageService.setItem("maxViewPermission", maxViewPermission);
|
|
363
|
+
this.maxViewPermission.next(maxViewPermission);
|
|
364
|
+
}
|
|
365
|
+
refreshTokenIfUserIsActive() {
|
|
366
|
+
//Only refresh if the user has been active
|
|
367
|
+
if (this._lastUserInteraction && ((new Date().valueOf() - this._lastUserInteraction.valueOf()) <= (this._maxInactivityMinutes * 60 * 1000))) {
|
|
368
|
+
this.requestAccessToken(false);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
hasValidConfig() {
|
|
372
|
+
if (this._tokenEndpoint == null && (this._serverUrl === null || this._logoutPath === null)) {
|
|
373
|
+
throw new Error("BUG ALERT! Invalid AuthenticationService configuration. No valid configuration for authentication endpoint(s).");
|
|
374
|
+
}
|
|
375
|
+
if (this._localStorageService === null || this.authenticationProvider.authenticationTokenKey === null) {
|
|
376
|
+
throw new Error("BUG ALERT! Invalid AuthenticationService configuration. No valid configuration for local storage");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
handleError(error) {
|
|
380
|
+
let errMsg = (error.message) ? error.message : AuthenticationService.GENERIC_ERR_MSG;
|
|
381
|
+
return throwError(errMsg);
|
|
382
|
+
}
|
|
383
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationService, deps: [{ token: i1$1.HttpClient }, { token: i2.Router }, { token: i1.CoolLocalStorage }, { token: i4.JwtHelperService }, { token: AuthenticationProvider }, { token: AUTHENTICATION_ROUTE }, { token: AUTHENTICATION_LOGOUT_PATH }, { token: AUTHENTICATION_TOKEN_ENDPOINT }, { token: AUTHENTICATION_SERVER_URL, optional: true }, { token: AUTHENTICATION_DIRECT_ENDPOINT, optional: true }, { token: AUTHENTICATION_MAX_INACTIVITY_MINUTES, optional: true }, { token: AUTHENTICATION_USER_COUNTDOWN_SECONDS, optional: true }, { token: AUTHENTICATION_IDP_INACTIVITY_MINUTES, optional: true }, { token: LocationStrategy, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
384
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationService }); }
|
|
385
|
+
}
|
|
386
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationService, decorators: [{
|
|
387
|
+
type: Injectable
|
|
388
|
+
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2.Router }, { type: i1.CoolLocalStorage }, { type: i4.JwtHelperService }, { type: AuthenticationProvider }, { type: undefined, decorators: [{
|
|
389
|
+
type: Inject,
|
|
390
|
+
args: [AUTHENTICATION_ROUTE]
|
|
391
|
+
}] }, { type: undefined, decorators: [{
|
|
392
|
+
type: Inject,
|
|
393
|
+
args: [AUTHENTICATION_LOGOUT_PATH]
|
|
394
|
+
}] }, { type: undefined, decorators: [{
|
|
395
|
+
type: Inject,
|
|
396
|
+
args: [AUTHENTICATION_TOKEN_ENDPOINT]
|
|
397
|
+
}] }, { type: undefined, decorators: [{
|
|
398
|
+
type: Optional
|
|
399
|
+
}, {
|
|
400
|
+
type: Inject,
|
|
401
|
+
args: [AUTHENTICATION_SERVER_URL]
|
|
402
|
+
}] }, { type: undefined, decorators: [{
|
|
403
|
+
type: Optional
|
|
404
|
+
}, {
|
|
405
|
+
type: Inject,
|
|
406
|
+
args: [AUTHENTICATION_DIRECT_ENDPOINT]
|
|
407
|
+
}] }, { type: undefined, decorators: [{
|
|
408
|
+
type: Optional
|
|
409
|
+
}, {
|
|
410
|
+
type: Inject,
|
|
411
|
+
args: [AUTHENTICATION_MAX_INACTIVITY_MINUTES]
|
|
412
|
+
}] }, { type: undefined, decorators: [{
|
|
413
|
+
type: Optional
|
|
414
|
+
}, {
|
|
415
|
+
type: Inject,
|
|
416
|
+
args: [AUTHENTICATION_USER_COUNTDOWN_SECONDS]
|
|
417
|
+
}] }, { type: undefined, decorators: [{
|
|
418
|
+
type: Optional
|
|
419
|
+
}, {
|
|
420
|
+
type: Inject,
|
|
421
|
+
args: [AUTHENTICATION_IDP_INACTIVITY_MINUTES]
|
|
422
|
+
}] }, { type: i4$1.LocationStrategy, decorators: [{
|
|
423
|
+
type: Optional
|
|
424
|
+
}, {
|
|
425
|
+
type: Inject,
|
|
426
|
+
args: [LocationStrategy]
|
|
427
|
+
}] }] });
|
|
428
428
|
|
|
429
|
-
/*
|
|
430
|
-
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
431
|
-
*/
|
|
432
|
-
class AuthenticationComponent {
|
|
433
|
-
constructor(authenticationService, domSanitizer, router, location, renderer, authenticationRoute) {
|
|
434
|
-
this.authenticationService = authenticationService;
|
|
435
|
-
this.domSanitizer = domSanitizer;
|
|
436
|
-
this.router = router;
|
|
437
|
-
this.location = location;
|
|
438
|
-
this.renderer = renderer;
|
|
439
|
-
this.authenticationRoute = authenticationRoute;
|
|
440
|
-
}
|
|
441
|
-
ngOnInit() {
|
|
442
|
-
/*
|
|
443
|
-
* Fix back bug
|
|
444
|
-
* Issue is that the browser will go back to the previous route. If it's guarded, the route guard will just load the login again
|
|
445
|
-
* Eventually the browser gets to the /authenticate route and going back from there loads the iframe history and Shibboleth displays
|
|
446
|
-
* an error relating to navigating back.
|
|
447
|
-
*/
|
|
448
|
-
history.pushState(null, null, this.location.prepareExternalUrl(this.authenticationRoute));
|
|
449
|
-
this.popstateSubscription = this.location.subscribe((value) => {
|
|
450
|
-
//This is going to prevent back from working from the login component
|
|
451
|
-
history.go(1);
|
|
452
|
-
});
|
|
453
|
-
this.beginAuthenticationProcess();
|
|
454
|
-
}
|
|
455
|
-
handleChanges() {
|
|
456
|
-
if (!this.iframe.nativeElement.contentDocument) {
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
try {
|
|
460
|
-
let element = this.iframe.nativeElement.contentDocument.body;
|
|
461
|
-
if (element.querySelector("pre")) {
|
|
462
|
-
element = element.querySelector("pre");
|
|
463
|
-
}
|
|
464
|
-
this._errorMsg = null;
|
|
465
|
-
var jsonText = element.innerText;
|
|
466
|
-
var json = JSON.parse(jsonText);
|
|
467
|
-
this.authenticationService.storeToken(json.auth_token);
|
|
468
|
-
var authenticated = this.authenticationService.proceedIfAuthenticated();
|
|
469
|
-
if (!authenticated) {
|
|
470
|
-
this.resetSubscription.unsubscribe();
|
|
471
|
-
this.beginAuthenticationProcess();
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
catch (error) {
|
|
475
|
-
if (this.iframe.nativeElement.contentDocument.title.toUpperCase() === "ERROR") {
|
|
476
|
-
if (this.iframe.nativeElement.contentDocument.body.innerHTML.toUpperCase() === "FORBIDDEN") {
|
|
477
|
-
this._errorMsg = "You do not have permission to log into this application";
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
this._errorMsg = null;
|
|
481
|
-
}
|
|
482
|
-
//A bit of a workaround for a WildFly issue. Success on Pac4j authentication, but failure on DB load of user put things in a weird state. Just logout, and redo the login.
|
|
483
|
-
this.clearLoginAndRetry();
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
// After the iframe loads, make the background transparent so we use the implementation's background and not the sso background.
|
|
487
|
-
this.renderer.setStyle(this.iframe.nativeElement.contentDocument.body, "background-color", "transparent");
|
|
488
|
-
}
|
|
489
|
-
ngOnDestroy() {
|
|
490
|
-
this.resetSubscription.unsubscribe();
|
|
491
|
-
this.popstateSubscription.unsubscribe();
|
|
492
|
-
}
|
|
493
|
-
clearLoginAndRetry() {
|
|
494
|
-
this.resetSubscription.unsubscribe();
|
|
495
|
-
this.authenticationService.clearLogin().subscribe(() => { this.beginAuthenticationProcess(); }, (error) => { this.beginAuthenticationProcess(); });
|
|
496
|
-
}
|
|
497
|
-
beginAuthenticationProcess() {
|
|
498
|
-
var tokenEndpoint = this.authenticationService.tokenLocation();
|
|
499
|
-
if (tokenEndpoint !== "") {
|
|
500
|
-
this.url = this.domSanitizer.bypassSecurityTrustResourceUrl(tokenEndpoint);
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* If the user doesn't complete authentication before the IdP session times out, that will be a problem when they eventually
|
|
504
|
-
* attampt to log in. It is likely that users will do this often when they log out or are timed out in the evening, leave
|
|
505
|
-
* their browser open, then attempt to log back in in the morning. In order to work around this, this component will re-request
|
|
506
|
-
* the token prior to IdP timeout, which will reset the process. This will happen 1 minute before idpInactivityMinutes
|
|
507
|
-
**/
|
|
508
|
-
this.resetSubscription = interval((this.authenticationService.idpInactivityMinutes - 1) * 60 * 1000)
|
|
509
|
-
.pipe(first())
|
|
510
|
-
.subscribe((value) => {
|
|
511
|
-
this.beginAuthenticationProcess();
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
515
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
429
|
+
/*
|
|
430
|
+
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
431
|
+
*/
|
|
432
|
+
class AuthenticationComponent {
|
|
433
|
+
constructor(authenticationService, domSanitizer, router, location, renderer, authenticationRoute) {
|
|
434
|
+
this.authenticationService = authenticationService;
|
|
435
|
+
this.domSanitizer = domSanitizer;
|
|
436
|
+
this.router = router;
|
|
437
|
+
this.location = location;
|
|
438
|
+
this.renderer = renderer;
|
|
439
|
+
this.authenticationRoute = authenticationRoute;
|
|
440
|
+
}
|
|
441
|
+
ngOnInit() {
|
|
442
|
+
/*
|
|
443
|
+
* Fix back bug
|
|
444
|
+
* Issue is that the browser will go back to the previous route. If it's guarded, the route guard will just load the login again
|
|
445
|
+
* Eventually the browser gets to the /authenticate route and going back from there loads the iframe history and Shibboleth displays
|
|
446
|
+
* an error relating to navigating back.
|
|
447
|
+
*/
|
|
448
|
+
history.pushState(null, null, this.location.prepareExternalUrl(this.authenticationRoute));
|
|
449
|
+
this.popstateSubscription = this.location.subscribe((value) => {
|
|
450
|
+
//This is going to prevent back from working from the login component
|
|
451
|
+
history.go(1);
|
|
452
|
+
});
|
|
453
|
+
this.beginAuthenticationProcess();
|
|
454
|
+
}
|
|
455
|
+
handleChanges() {
|
|
456
|
+
if (!this.iframe.nativeElement.contentDocument) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
try {
|
|
460
|
+
let element = this.iframe.nativeElement.contentDocument.body;
|
|
461
|
+
if (element.querySelector("pre")) {
|
|
462
|
+
element = element.querySelector("pre");
|
|
463
|
+
}
|
|
464
|
+
this._errorMsg = null;
|
|
465
|
+
var jsonText = element.innerText;
|
|
466
|
+
var json = JSON.parse(jsonText);
|
|
467
|
+
this.authenticationService.storeToken(json.auth_token);
|
|
468
|
+
var authenticated = this.authenticationService.proceedIfAuthenticated();
|
|
469
|
+
if (!authenticated) {
|
|
470
|
+
this.resetSubscription.unsubscribe();
|
|
471
|
+
this.beginAuthenticationProcess();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
if (this.iframe.nativeElement.contentDocument.title.toUpperCase() === "ERROR") {
|
|
476
|
+
if (this.iframe.nativeElement.contentDocument.body.innerHTML.toUpperCase() === "FORBIDDEN") {
|
|
477
|
+
this._errorMsg = "You do not have permission to log into this application";
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
this._errorMsg = null;
|
|
481
|
+
}
|
|
482
|
+
//A bit of a workaround for a WildFly issue. Success on Pac4j authentication, but failure on DB load of user put things in a weird state. Just logout, and redo the login.
|
|
483
|
+
this.clearLoginAndRetry();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// After the iframe loads, make the background transparent so we use the implementation's background and not the sso background.
|
|
487
|
+
this.renderer.setStyle(this.iframe.nativeElement.contentDocument.body, "background-color", "transparent");
|
|
488
|
+
}
|
|
489
|
+
ngOnDestroy() {
|
|
490
|
+
this.resetSubscription.unsubscribe();
|
|
491
|
+
this.popstateSubscription.unsubscribe();
|
|
492
|
+
}
|
|
493
|
+
clearLoginAndRetry() {
|
|
494
|
+
this.resetSubscription.unsubscribe();
|
|
495
|
+
this.authenticationService.clearLogin().subscribe(() => { this.beginAuthenticationProcess(); }, (error) => { this.beginAuthenticationProcess(); });
|
|
496
|
+
}
|
|
497
|
+
beginAuthenticationProcess() {
|
|
498
|
+
var tokenEndpoint = this.authenticationService.tokenLocation();
|
|
499
|
+
if (tokenEndpoint !== "") {
|
|
500
|
+
this.url = this.domSanitizer.bypassSecurityTrustResourceUrl(tokenEndpoint);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* If the user doesn't complete authentication before the IdP session times out, that will be a problem when they eventually
|
|
504
|
+
* attampt to log in. It is likely that users will do this often when they log out or are timed out in the evening, leave
|
|
505
|
+
* their browser open, then attempt to log back in in the morning. In order to work around this, this component will re-request
|
|
506
|
+
* the token prior to IdP timeout, which will reset the process. This will happen 1 minute before idpInactivityMinutes
|
|
507
|
+
**/
|
|
508
|
+
this.resetSubscription = interval((this.authenticationService.idpInactivityMinutes - 1) * 60 * 1000)
|
|
509
|
+
.pipe(first())
|
|
510
|
+
.subscribe((value) => {
|
|
511
|
+
this.beginAuthenticationProcess();
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationComponent, deps: [{ token: AuthenticationService }, { token: i2$1.DomSanitizer }, { token: i2.Router }, { token: i4$1.Location }, { token: i0.Renderer2 }, { token: AUTHENTICATION_ROUTE }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
515
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: AuthenticationComponent, selector: "authentication-iframe", host: { classAttribute: "outlet-row" }, viewQueries: [{ propertyName: "iframe", first: true, predicate: ["iframe"], descendants: true, static: true }], ngImport: i0, template: `
|
|
516
516
|
<div class="container">
|
|
517
517
|
<iframe #iframe class="frame" [src]="url" (load)="handleChanges()"></iframe>
|
|
518
518
|
<div *ngIf="_errorMsg" class="alert-box">
|
|
@@ -522,10 +522,10 @@ class AuthenticationComponent {
|
|
|
522
522
|
</div>
|
|
523
523
|
</div>
|
|
524
524
|
</div>
|
|
525
|
-
`, isInline: true, styles: [":host{background-color:#fff}.container{max-width:100%;margin-top:60px;padding-top:15px}.frame{width:100%;height:100%;border:0px}\n"], dependencies: [{ kind: "directive", type: i4$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
|
|
526
|
-
}
|
|
527
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
528
|
-
type: Component,
|
|
525
|
+
`, isInline: true, styles: [":host{background-color:#fff}.container{max-width:100%;margin-top:60px;padding-top:15px}.frame{width:100%;height:100%;border:0px}\n"], dependencies: [{ kind: "directive", type: i4$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
|
|
526
|
+
}
|
|
527
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationComponent, decorators: [{
|
|
528
|
+
type: Component,
|
|
529
529
|
args: [{ selector: "authentication-iframe", template: `
|
|
530
530
|
<div class="container">
|
|
531
531
|
<iframe #iframe class="frame" [src]="url" (load)="handleChanges()"></iframe>
|
|
@@ -536,48 +536,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
536
536
|
</div>
|
|
537
537
|
</div>
|
|
538
538
|
</div>
|
|
539
|
-
`, host: { class: "outlet-row" }, styles: [":host{background-color:#fff}.container{max-width:100%;margin-top:60px;padding-top:15px}.frame{width:100%;height:100%;border:0px}\n"] }]
|
|
540
|
-
}], ctorParameters:
|
|
541
|
-
type: Inject,
|
|
542
|
-
args: [AUTHENTICATION_ROUTE]
|
|
543
|
-
}] }]
|
|
544
|
-
type: ViewChild,
|
|
545
|
-
args: ["iframe", { static: true }]
|
|
539
|
+
`, host: { class: "outlet-row" }, styles: [":host{background-color:#fff}.container{max-width:100%;margin-top:60px;padding-top:15px}.frame{width:100%;height:100%;border:0px}\n"] }]
|
|
540
|
+
}], ctorParameters: () => [{ type: AuthenticationService }, { type: i2$1.DomSanitizer }, { type: i2.Router }, { type: i4$1.Location }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
|
|
541
|
+
type: Inject,
|
|
542
|
+
args: [AUTHENTICATION_ROUTE]
|
|
543
|
+
}] }], propDecorators: { iframe: [{
|
|
544
|
+
type: ViewChild,
|
|
545
|
+
args: ["iframe", { static: true }]
|
|
546
546
|
}] } });
|
|
547
547
|
|
|
548
|
-
/*
|
|
549
|
-
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
550
|
-
*/
|
|
551
|
-
class DirectLoginComponent {
|
|
552
|
-
constructor(_authenticationService, _formBuilder) {
|
|
553
|
-
this._authenticationService = _authenticationService;
|
|
554
|
-
this._formBuilder = _formBuilder;
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* Initializes the authentication form.
|
|
558
|
-
*/
|
|
559
|
-
ngOnInit() {
|
|
560
|
-
this._loginForm = this._formBuilder.group({
|
|
561
|
-
username: ["", Validators.required],
|
|
562
|
-
password: ["", Validators.required]
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* A function to submit the login form the the {@link UserService}.
|
|
567
|
-
*/
|
|
568
|
-
login() {
|
|
569
|
-
this._authenticationService.login(this._loginForm.value.username, this._loginForm.value.password)
|
|
570
|
-
.subscribe((res) => {
|
|
571
|
-
if (res) {
|
|
572
|
-
this._errorMsg = null;
|
|
573
|
-
this._authenticationService.requestAccessToken(true);
|
|
574
|
-
}
|
|
575
|
-
}, (error) => {
|
|
576
|
-
this._errorMsg = "Please check your username and password.";
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
580
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
548
|
+
/*
|
|
549
|
+
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
550
|
+
*/
|
|
551
|
+
class DirectLoginComponent {
|
|
552
|
+
constructor(_authenticationService, _formBuilder) {
|
|
553
|
+
this._authenticationService = _authenticationService;
|
|
554
|
+
this._formBuilder = _formBuilder;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Initializes the authentication form.
|
|
558
|
+
*/
|
|
559
|
+
ngOnInit() {
|
|
560
|
+
this._loginForm = this._formBuilder.group({
|
|
561
|
+
username: ["", Validators.required],
|
|
562
|
+
password: ["", Validators.required]
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* A function to submit the login form the the {@link UserService}.
|
|
567
|
+
*/
|
|
568
|
+
login() {
|
|
569
|
+
this._authenticationService.login(this._loginForm.value.username, this._loginForm.value.password)
|
|
570
|
+
.subscribe((res) => {
|
|
571
|
+
if (res) {
|
|
572
|
+
this._errorMsg = null;
|
|
573
|
+
this._authenticationService.requestAccessToken(true);
|
|
574
|
+
}
|
|
575
|
+
}, (error) => {
|
|
576
|
+
this._errorMsg = "Please check your username and password.";
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DirectLoginComponent, deps: [{ token: AuthenticationService }, { token: i2$2.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
580
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: DirectLoginComponent, selector: "hci-login-form", ngImport: i0, template: `
|
|
581
581
|
<div class="container">
|
|
582
582
|
<div class="login-box" id="hci-login-form-box">
|
|
583
583
|
<div class="login-heading" id="hci-login-form-heading">
|
|
@@ -602,10 +602,10 @@ class DirectLoginComponent {
|
|
|
602
602
|
</div>
|
|
603
603
|
</div>
|
|
604
604
|
</div>
|
|
605
|
-
`, isInline: true, styles: [".container{max-width:400px;margin-top:20px;padding-top:15px}.login-box{border-radius:10px;box-shadow:0 0 2px #ccc;padding:15px}.login-box .login-heading h3{line-height:1.5;margin:0 0 10px}.login-box .form-control{padding:10px;border:1px solid #ccc}.login-box input[type=password]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}.login-box input[type=text]{margin-bottom:-1px;border-bottom-right-radius:0;border-bottom-left-radius:0}.login-box .alert-box{margin:10px 0 -5px}.login-box .alert-text{font-size:small}.login-box .btn-box{margin:10px 0 0}\n"], dependencies: [{ kind: "directive", type: i4$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
|
|
606
|
-
}
|
|
607
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
608
|
-
type: Component,
|
|
605
|
+
`, isInline: true, styles: [".container{max-width:400px;margin-top:20px;padding-top:15px}.login-box{border-radius:10px;box-shadow:0 0 2px #ccc;padding:15px}.login-box .login-heading h3{line-height:1.5;margin:0 0 10px}.login-box .form-control{padding:10px;border:1px solid #ccc}.login-box input[type=password]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}.login-box input[type=text]{margin-bottom:-1px;border-bottom-right-radius:0;border-bottom-left-radius:0}.login-box .alert-box{margin:10px 0 -5px}.login-box .alert-text{font-size:small}.login-box .btn-box{margin:10px 0 0}\n"], dependencies: [{ kind: "directive", type: i4$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
|
|
606
|
+
}
|
|
607
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DirectLoginComponent, decorators: [{
|
|
608
|
+
type: Component,
|
|
609
609
|
args: [{ selector: "hci-login-form", template: `
|
|
610
610
|
<div class="container">
|
|
611
611
|
<div class="login-box" id="hci-login-form-box">
|
|
@@ -631,51 +631,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
631
631
|
</div>
|
|
632
632
|
</div>
|
|
633
633
|
</div>
|
|
634
|
-
`, styles: [".container{max-width:400px;margin-top:20px;padding-top:15px}.login-box{border-radius:10px;box-shadow:0 0 2px #ccc;padding:15px}.login-box .login-heading h3{line-height:1.5;margin:0 0 10px}.login-box .form-control{padding:10px;border:1px solid #ccc}.login-box input[type=password]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}.login-box input[type=text]{margin-bottom:-1px;border-bottom-right-radius:0;border-bottom-left-radius:0}.login-box .alert-box{margin:10px 0 -5px}.login-box .alert-text{font-size:small}.login-box .btn-box{margin:10px 0 0}\n"] }]
|
|
635
|
-
}], ctorParameters:
|
|
634
|
+
`, styles: [".container{max-width:400px;margin-top:20px;padding-top:15px}.login-box{border-radius:10px;box-shadow:0 0 2px #ccc;padding:15px}.login-box .login-heading h3{line-height:1.5;margin:0 0 10px}.login-box .form-control{padding:10px;border:1px solid #ccc}.login-box input[type=password]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}.login-box input[type=text]{margin-bottom:-1px;border-bottom-right-radius:0;border-bottom-left-radius:0}.login-box .alert-box{margin:10px 0 -5px}.login-box .alert-text{font-size:small}.login-box .btn-box{margin:10px 0 0}\n"] }]
|
|
635
|
+
}], ctorParameters: () => [{ type: AuthenticationService }, { type: i2$2.UntypedFormBuilder }] });
|
|
636
636
|
|
|
637
|
-
/*
|
|
638
|
-
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
639
|
-
*/
|
|
640
|
-
class TimeoutNotificationComponent {
|
|
641
|
-
constructor(authenticationService) {
|
|
642
|
-
this.authenticationService = authenticationService;
|
|
643
|
-
this.openState = "hidden";
|
|
644
|
-
authenticationService.isAboutToTimeOut().subscribe((isAboutToTimeOut) => {
|
|
645
|
-
if (isAboutToTimeOut) {
|
|
646
|
-
this.openState = "opened";
|
|
647
|
-
this.startCountdown();
|
|
648
|
-
}
|
|
649
|
-
else {
|
|
650
|
-
this.openState = "hidden";
|
|
651
|
-
//If something changed mid-timeout, cancel the timeout/logout.
|
|
652
|
-
if (this.subscription != null && !this.subscription.closed) {
|
|
653
|
-
this.subscription.unsubscribe();
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
startCountdown() {
|
|
659
|
-
this.seconds = timer(0, 1000)
|
|
660
|
-
.pipe(map(() => {
|
|
661
|
-
const elapsed = Math.round((Date.now() - this.authenticationService.getTimeoutStart()) / 1000);
|
|
662
|
-
return this.authenticationService.userCountdownSeconds - elapsed;
|
|
663
|
-
}),
|
|
664
|
-
// The true argument emits the final value that completed the observable
|
|
665
|
-
takeWhile((value) => value > 0, true));
|
|
666
|
-
this.subscription = this.seconds.subscribe((value) => {
|
|
667
|
-
if (value < 1) {
|
|
668
|
-
this.subscription.unsubscribe();
|
|
669
|
-
this.authenticationService.logout(true);
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
click() {
|
|
674
|
-
this.subscription.unsubscribe();
|
|
675
|
-
this.authenticationService.updateUserActivity();
|
|
676
|
-
}
|
|
677
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
678
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
637
|
+
/*
|
|
638
|
+
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
639
|
+
*/
|
|
640
|
+
class TimeoutNotificationComponent {
|
|
641
|
+
constructor(authenticationService) {
|
|
642
|
+
this.authenticationService = authenticationService;
|
|
643
|
+
this.openState = "hidden";
|
|
644
|
+
authenticationService.isAboutToTimeOut().subscribe((isAboutToTimeOut) => {
|
|
645
|
+
if (isAboutToTimeOut) {
|
|
646
|
+
this.openState = "opened";
|
|
647
|
+
this.startCountdown();
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
this.openState = "hidden";
|
|
651
|
+
//If something changed mid-timeout, cancel the timeout/logout.
|
|
652
|
+
if (this.subscription != null && !this.subscription.closed) {
|
|
653
|
+
this.subscription.unsubscribe();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
startCountdown() {
|
|
659
|
+
this.seconds = timer(0, 1000)
|
|
660
|
+
.pipe(map(() => {
|
|
661
|
+
const elapsed = Math.round((Date.now() - this.authenticationService.getTimeoutStart()) / 1000);
|
|
662
|
+
return this.authenticationService.userCountdownSeconds - elapsed;
|
|
663
|
+
}),
|
|
664
|
+
// The true argument emits the final value that completed the observable
|
|
665
|
+
takeWhile((value) => value > 0, true));
|
|
666
|
+
this.subscription = this.seconds.subscribe((value) => {
|
|
667
|
+
if (value < 1) {
|
|
668
|
+
this.subscription.unsubscribe();
|
|
669
|
+
this.authenticationService.logout(true);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
click() {
|
|
674
|
+
this.subscription.unsubscribe();
|
|
675
|
+
this.authenticationService.updateUserActivity();
|
|
676
|
+
}
|
|
677
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TimeoutNotificationComponent, deps: [{ token: AuthenticationService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
678
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: TimeoutNotificationComponent, selector: "timeout-notification", ngImport: i0, template: `
|
|
679
679
|
<div class="flyout-max" [@openBacksplash]="openState">
|
|
680
680
|
<div class="modal-dialog" [@openModal]="openState" role="document">
|
|
681
681
|
<div class="modal-header">
|
|
@@ -691,40 +691,40 @@ class TimeoutNotificationComponent {
|
|
|
691
691
|
</div>
|
|
692
692
|
</div>
|
|
693
693
|
</div>
|
|
694
|
-
`, isInline: true, styles: [".flyout-max{position:fixed;z-index:9999;top:0;bottom:0;background-color:#0006;width:100vw}.modal-dialog{position:fixed;max-width:50vw;min-width:50vw;left:-50vw;top:25vw;margin:0;background-color:#fff;border:black 1px solid;border-left:none;border-radius:20px;pointer-events:all}.modal-body{width:100%;display:inline-block}.modal-body-left{display:inline-block;overflow-y:auto;overflow-x:hidden;min-height:300px;max-height:300px}.modal-body-right{width:70%;vertical-align:top;padding-left:15px;border-left:black 1px solid;margin-left:15px;display:inline-block;overflow-y:auto;min-height:300px;max-height:300px}\n"], dependencies: [{ kind: "pipe", type: i4$1.AsyncPipe, name: "async" }], animations: [
|
|
695
|
-
trigger("openBacksplash", [
|
|
696
|
-
state("in", style({
|
|
697
|
-
"display": "none"
|
|
698
|
-
})),
|
|
699
|
-
state("hidden", style({
|
|
700
|
-
"display": "none"
|
|
701
|
-
})),
|
|
702
|
-
state("opened", style({
|
|
703
|
-
"display": "inherit"
|
|
704
|
-
})),
|
|
705
|
-
transition("hidden => opened", animate(100)),
|
|
706
|
-
transition("opened => hidden", animate(200))
|
|
707
|
-
]),
|
|
708
|
-
trigger("openModal", [
|
|
709
|
-
state("in", style({
|
|
710
|
-
"opacity": "0",
|
|
711
|
-
"left": "-50vw"
|
|
712
|
-
})),
|
|
713
|
-
state("hidden", style({
|
|
714
|
-
"opacity": "0",
|
|
715
|
-
"left": "-50vw"
|
|
716
|
-
})),
|
|
717
|
-
state("opened", style({
|
|
718
|
-
"opacity": "1",
|
|
719
|
-
"left": "25vw"
|
|
720
|
-
})),
|
|
721
|
-
transition("hidden => opened", animate(500)),
|
|
722
|
-
transition("opened => hidden", animate(300))
|
|
723
|
-
])
|
|
724
|
-
] }); }
|
|
725
|
-
}
|
|
726
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
727
|
-
type: Component,
|
|
694
|
+
`, isInline: true, styles: [".flyout-max{position:fixed;z-index:9999;top:0;bottom:0;background-color:#0006;width:100vw}.modal-dialog{position:fixed;max-width:50vw;min-width:50vw;left:-50vw;top:25vw;margin:0;background-color:#fff;border:black 1px solid;border-left:none;border-radius:20px;pointer-events:all}.modal-body{width:100%;display:inline-block}.modal-body-left{display:inline-block;overflow-y:auto;overflow-x:hidden;min-height:300px;max-height:300px}.modal-body-right{width:70%;vertical-align:top;padding-left:15px;border-left:black 1px solid;margin-left:15px;display:inline-block;overflow-y:auto;min-height:300px;max-height:300px}\n"], dependencies: [{ kind: "pipe", type: i4$1.AsyncPipe, name: "async" }], animations: [
|
|
695
|
+
trigger("openBacksplash", [
|
|
696
|
+
state("in", style({
|
|
697
|
+
"display": "none"
|
|
698
|
+
})),
|
|
699
|
+
state("hidden", style({
|
|
700
|
+
"display": "none"
|
|
701
|
+
})),
|
|
702
|
+
state("opened", style({
|
|
703
|
+
"display": "inherit"
|
|
704
|
+
})),
|
|
705
|
+
transition("hidden => opened", animate(100)),
|
|
706
|
+
transition("opened => hidden", animate(200))
|
|
707
|
+
]),
|
|
708
|
+
trigger("openModal", [
|
|
709
|
+
state("in", style({
|
|
710
|
+
"opacity": "0",
|
|
711
|
+
"left": "-50vw"
|
|
712
|
+
})),
|
|
713
|
+
state("hidden", style({
|
|
714
|
+
"opacity": "0",
|
|
715
|
+
"left": "-50vw"
|
|
716
|
+
})),
|
|
717
|
+
state("opened", style({
|
|
718
|
+
"opacity": "1",
|
|
719
|
+
"left": "25vw"
|
|
720
|
+
})),
|
|
721
|
+
transition("hidden => opened", animate(500)),
|
|
722
|
+
transition("opened => hidden", animate(300))
|
|
723
|
+
])
|
|
724
|
+
] }); }
|
|
725
|
+
}
|
|
726
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TimeoutNotificationComponent, decorators: [{
|
|
727
|
+
type: Component,
|
|
728
728
|
args: [{ selector: "timeout-notification", template: `
|
|
729
729
|
<div class="flyout-max" [@openBacksplash]="openState">
|
|
730
730
|
<div class="modal-dialog" [@openModal]="openState" role="document">
|
|
@@ -741,222 +741,222 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
741
741
|
</div>
|
|
742
742
|
</div>
|
|
743
743
|
</div>
|
|
744
|
-
`, animations: [
|
|
745
|
-
trigger("openBacksplash", [
|
|
746
|
-
state("in", style({
|
|
747
|
-
"display": "none"
|
|
748
|
-
})),
|
|
749
|
-
state("hidden", style({
|
|
750
|
-
"display": "none"
|
|
751
|
-
})),
|
|
752
|
-
state("opened", style({
|
|
753
|
-
"display": "inherit"
|
|
754
|
-
})),
|
|
755
|
-
transition("hidden => opened", animate(100)),
|
|
756
|
-
transition("opened => hidden", animate(200))
|
|
757
|
-
]),
|
|
758
|
-
trigger("openModal", [
|
|
759
|
-
state("in", style({
|
|
760
|
-
"opacity": "0",
|
|
761
|
-
"left": "-50vw"
|
|
762
|
-
})),
|
|
763
|
-
state("hidden", style({
|
|
764
|
-
"opacity": "0",
|
|
765
|
-
"left": "-50vw"
|
|
766
|
-
})),
|
|
767
|
-
state("opened", style({
|
|
768
|
-
"opacity": "1",
|
|
769
|
-
"left": "25vw"
|
|
770
|
-
})),
|
|
771
|
-
transition("hidden => opened", animate(500)),
|
|
772
|
-
transition("opened => hidden", animate(300))
|
|
773
|
-
])
|
|
774
|
-
], styles: [".flyout-max{position:fixed;z-index:9999;top:0;bottom:0;background-color:#0006;width:100vw}.modal-dialog{position:fixed;max-width:50vw;min-width:50vw;left:-50vw;top:25vw;margin:0;background-color:#fff;border:black 1px solid;border-left:none;border-radius:20px;pointer-events:all}.modal-body{width:100%;display:inline-block}.modal-body-left{display:inline-block;overflow-y:auto;overflow-x:hidden;min-height:300px;max-height:300px}.modal-body-right{width:70%;vertical-align:top;padding-left:15px;border-left:black 1px solid;margin-left:15px;display:inline-block;overflow-y:auto;min-height:300px;max-height:300px}\n"] }]
|
|
775
|
-
}], ctorParameters:
|
|
744
|
+
`, animations: [
|
|
745
|
+
trigger("openBacksplash", [
|
|
746
|
+
state("in", style({
|
|
747
|
+
"display": "none"
|
|
748
|
+
})),
|
|
749
|
+
state("hidden", style({
|
|
750
|
+
"display": "none"
|
|
751
|
+
})),
|
|
752
|
+
state("opened", style({
|
|
753
|
+
"display": "inherit"
|
|
754
|
+
})),
|
|
755
|
+
transition("hidden => opened", animate(100)),
|
|
756
|
+
transition("opened => hidden", animate(200))
|
|
757
|
+
]),
|
|
758
|
+
trigger("openModal", [
|
|
759
|
+
state("in", style({
|
|
760
|
+
"opacity": "0",
|
|
761
|
+
"left": "-50vw"
|
|
762
|
+
})),
|
|
763
|
+
state("hidden", style({
|
|
764
|
+
"opacity": "0",
|
|
765
|
+
"left": "-50vw"
|
|
766
|
+
})),
|
|
767
|
+
state("opened", style({
|
|
768
|
+
"opacity": "1",
|
|
769
|
+
"left": "25vw"
|
|
770
|
+
})),
|
|
771
|
+
transition("hidden => opened", animate(500)),
|
|
772
|
+
transition("opened => hidden", animate(300))
|
|
773
|
+
])
|
|
774
|
+
], styles: [".flyout-max{position:fixed;z-index:9999;top:0;bottom:0;background-color:#0006;width:100vw}.modal-dialog{position:fixed;max-width:50vw;min-width:50vw;left:-50vw;top:25vw;margin:0;background-color:#fff;border:black 1px solid;border-left:none;border-radius:20px;pointer-events:all}.modal-body{width:100%;display:inline-block}.modal-body-left{display:inline-block;overflow-y:auto;overflow-x:hidden;min-height:300px;max-height:300px}.modal-body-right{width:70%;vertical-align:top;padding-left:15px;border-left:black 1px solid;margin-left:15px;display:inline-block;overflow-y:auto;min-height:300px;max-height:300px}\n"] }]
|
|
775
|
+
}], ctorParameters: () => [{ type: AuthenticationService }] });
|
|
776
776
|
|
|
777
|
-
class AuthorizationInterceptor {
|
|
778
|
-
constructor(injector) {
|
|
779
|
-
this.injector = injector;
|
|
780
|
-
}
|
|
781
|
-
intercept(req, next) {
|
|
782
|
-
if (isDevMode()) {
|
|
783
|
-
console.debug("AuthorizationInterceptor.intercept");
|
|
784
|
-
}
|
|
785
|
-
let authService = this.injector.get(AuthenticationService);
|
|
786
|
-
//Don't want to include background token refreshes in considering the user 'active'
|
|
787
|
-
if (req.url !== authService.tokenLocation()) {
|
|
788
|
-
//Update user activity. Done here instead of the previous method using a subscription to a subject in AuthenticationProvider
|
|
789
|
-
authService.updateUserActivity();
|
|
790
|
-
}
|
|
791
|
-
let headers = authService.getHeaders(req);
|
|
792
|
-
let url = req.url;
|
|
793
|
-
if (url.startsWith("/")) {
|
|
794
|
-
url = authService.getBaseUrl() + url;
|
|
795
|
-
}
|
|
796
|
-
else if (!url.startsWith("http")) {
|
|
797
|
-
if (authService.getContextRoot().length > 0) {
|
|
798
|
-
url = authService.getBaseUrl() + "/" + authService.getContextRoot() + "/" + url;
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
url = authService.getBaseUrl() + "/" + url;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
let params = req.params;
|
|
805
|
-
if (url.indexOf("/crud/") > 0) {
|
|
806
|
-
params = params.set("maxViewPermission", authService.getMaxViewPermission());
|
|
807
|
-
}
|
|
808
|
-
let reqClone = req.clone({
|
|
809
|
-
url: url,
|
|
810
|
-
withCredentials: true,
|
|
811
|
-
headers: headers,
|
|
812
|
-
params: params
|
|
813
|
-
});
|
|
814
|
-
return next.handle(reqClone)
|
|
815
|
-
.pipe(catchError((error) => {
|
|
816
|
-
if (isDevMode()) {
|
|
817
|
-
console.error("AuthorizationInterceptor.error");
|
|
818
|
-
console.error(error);
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* If the token is not authenticated which angular does not know about, then a REST request to the backend will
|
|
822
|
-
* return a 401. To duplicate this, open Core in two tabs. In one tab, logout, in the other, perform a request
|
|
823
|
-
* that hits a protected resource.
|
|
824
|
-
*/
|
|
825
|
-
if (error.status === 401) {
|
|
826
|
-
authService.isAuthenticated().subscribe((authenticated) => {
|
|
827
|
-
if (authenticated) {
|
|
828
|
-
// If authenticated, then logout which will redirect.
|
|
829
|
-
authService.logout(true);
|
|
830
|
-
return throwError(error.message);
|
|
831
|
-
}
|
|
832
|
-
else {
|
|
833
|
-
// Otherwise, for example, when the user first opens Core, 401s are expected.
|
|
834
|
-
return throwError(error);
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
if (error.status === 403) {
|
|
839
|
-
// TODO: Trigger notification for unauthorized.
|
|
840
|
-
}
|
|
841
|
-
return throwError(error);
|
|
842
|
-
}));
|
|
843
|
-
}
|
|
844
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
845
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
846
|
-
}
|
|
847
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
848
|
-
type: Injectable
|
|
849
|
-
}], ctorParameters:
|
|
777
|
+
class AuthorizationInterceptor {
|
|
778
|
+
constructor(injector) {
|
|
779
|
+
this.injector = injector;
|
|
780
|
+
}
|
|
781
|
+
intercept(req, next) {
|
|
782
|
+
if (isDevMode()) {
|
|
783
|
+
console.debug("AuthorizationInterceptor.intercept");
|
|
784
|
+
}
|
|
785
|
+
let authService = this.injector.get(AuthenticationService);
|
|
786
|
+
//Don't want to include background token refreshes in considering the user 'active'
|
|
787
|
+
if (req.url !== authService.tokenLocation()) {
|
|
788
|
+
//Update user activity. Done here instead of the previous method using a subscription to a subject in AuthenticationProvider
|
|
789
|
+
authService.updateUserActivity();
|
|
790
|
+
}
|
|
791
|
+
let headers = authService.getHeaders(req);
|
|
792
|
+
let url = req.url;
|
|
793
|
+
if (url.startsWith("/")) {
|
|
794
|
+
url = authService.getBaseUrl() + url;
|
|
795
|
+
}
|
|
796
|
+
else if (!url.startsWith("http")) {
|
|
797
|
+
if (authService.getContextRoot().length > 0) {
|
|
798
|
+
url = authService.getBaseUrl() + "/" + authService.getContextRoot() + "/" + url;
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
url = authService.getBaseUrl() + "/" + url;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
let params = req.params;
|
|
805
|
+
if (url.indexOf("/crud/") > 0) {
|
|
806
|
+
params = params.set("maxViewPermission", authService.getMaxViewPermission());
|
|
807
|
+
}
|
|
808
|
+
let reqClone = req.clone({
|
|
809
|
+
url: url,
|
|
810
|
+
withCredentials: true,
|
|
811
|
+
headers: headers,
|
|
812
|
+
params: params
|
|
813
|
+
});
|
|
814
|
+
return next.handle(reqClone)
|
|
815
|
+
.pipe(catchError((error) => {
|
|
816
|
+
if (isDevMode()) {
|
|
817
|
+
console.error("AuthorizationInterceptor.error");
|
|
818
|
+
console.error(error);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* If the token is not authenticated which angular does not know about, then a REST request to the backend will
|
|
822
|
+
* return a 401. To duplicate this, open Core in two tabs. In one tab, logout, in the other, perform a request
|
|
823
|
+
* that hits a protected resource.
|
|
824
|
+
*/
|
|
825
|
+
if (error.status === 401) {
|
|
826
|
+
authService.isAuthenticated().subscribe((authenticated) => {
|
|
827
|
+
if (authenticated) {
|
|
828
|
+
// If authenticated, then logout which will redirect.
|
|
829
|
+
authService.logout(true);
|
|
830
|
+
return throwError(error.message);
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
// Otherwise, for example, when the user first opens Core, 401s are expected.
|
|
834
|
+
return throwError(error);
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
if (error.status === 403) {
|
|
839
|
+
// TODO: Trigger notification for unauthorized.
|
|
840
|
+
}
|
|
841
|
+
return throwError(error);
|
|
842
|
+
}));
|
|
843
|
+
}
|
|
844
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthorizationInterceptor, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
845
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthorizationInterceptor }); }
|
|
846
|
+
}
|
|
847
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthorizationInterceptor, decorators: [{
|
|
848
|
+
type: Injectable
|
|
849
|
+
}], ctorParameters: () => [{ type: i0.Injector }] });
|
|
850
850
|
|
|
851
|
-
/*
|
|
852
|
-
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
853
|
-
*/
|
|
854
|
-
/**
|
|
855
|
-
* Provide a single auth service and interceptor for the implementing application. Also provide everything
|
|
856
|
-
* from the angular-jwt library.
|
|
857
|
-
*
|
|
858
|
-
* @since 1.0.0
|
|
859
|
-
*/
|
|
860
|
-
class AuthenticationModule {
|
|
861
|
-
constructor(parentModule) {
|
|
862
|
-
if (parentModule) {
|
|
863
|
-
throw new Error("AuthenticationModule is already loaded.");
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
static forRoot() {
|
|
867
|
-
return {
|
|
868
|
-
providers: [
|
|
869
|
-
AuthenticationProvider,
|
|
870
|
-
JwtHelperService,
|
|
871
|
-
AuthenticationService,
|
|
872
|
-
{
|
|
873
|
-
provide: HTTP_INTERCEPTORS,
|
|
874
|
-
useClass: AuthorizationInterceptor,
|
|
875
|
-
multi: true
|
|
876
|
-
},
|
|
877
|
-
{
|
|
878
|
-
provide: HTTP_INTERCEPTORS,
|
|
879
|
-
useClass: JwtInterceptor,
|
|
880
|
-
multi: true
|
|
881
|
-
},
|
|
882
|
-
{
|
|
883
|
-
provide: JWT_OPTIONS,
|
|
884
|
-
useClass: AuthenticationProvider
|
|
885
|
-
}
|
|
886
|
-
],
|
|
887
|
-
ngModule: AuthenticationModule
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
891
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
892
|
-
DirectLoginComponent,
|
|
893
|
-
TimeoutNotificationComponent], imports: [CommonModule,
|
|
894
|
-
HttpClientModule,
|
|
895
|
-
//JwtModule,
|
|
896
|
-
RouterModule,
|
|
897
|
-
FormsModule,
|
|
898
|
-
ReactiveFormsModule,
|
|
899
|
-
CoolStorageModule], exports: [AuthenticationComponent,
|
|
900
|
-
DirectLoginComponent,
|
|
901
|
-
TimeoutNotificationComponent] }); }
|
|
902
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
903
|
-
HttpClientModule,
|
|
904
|
-
//JwtModule,
|
|
905
|
-
RouterModule,
|
|
906
|
-
FormsModule,
|
|
907
|
-
ReactiveFormsModule,
|
|
908
|
-
CoolStorageModule] }); }
|
|
909
|
-
}
|
|
910
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
911
|
-
type: NgModule,
|
|
912
|
-
args: [{
|
|
913
|
-
imports: [
|
|
914
|
-
CommonModule,
|
|
915
|
-
HttpClientModule,
|
|
916
|
-
//JwtModule,
|
|
917
|
-
RouterModule,
|
|
918
|
-
FormsModule,
|
|
919
|
-
ReactiveFormsModule,
|
|
920
|
-
CoolStorageModule
|
|
921
|
-
],
|
|
922
|
-
declarations: [
|
|
923
|
-
AuthenticationComponent,
|
|
924
|
-
DirectLoginComponent,
|
|
925
|
-
TimeoutNotificationComponent
|
|
926
|
-
],
|
|
927
|
-
exports: [
|
|
928
|
-
AuthenticationComponent,
|
|
929
|
-
DirectLoginComponent,
|
|
930
|
-
TimeoutNotificationComponent
|
|
931
|
-
]
|
|
932
|
-
}]
|
|
933
|
-
}], ctorParameters:
|
|
934
|
-
type: Optional
|
|
935
|
-
}, {
|
|
936
|
-
type: SkipSelf
|
|
937
|
-
}] }]
|
|
851
|
+
/*
|
|
852
|
+
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
853
|
+
*/
|
|
854
|
+
/**
|
|
855
|
+
* Provide a single auth service and interceptor for the implementing application. Also provide everything
|
|
856
|
+
* from the angular-jwt library.
|
|
857
|
+
*
|
|
858
|
+
* @since 1.0.0
|
|
859
|
+
*/
|
|
860
|
+
class AuthenticationModule {
|
|
861
|
+
constructor(parentModule) {
|
|
862
|
+
if (parentModule) {
|
|
863
|
+
throw new Error("AuthenticationModule is already loaded.");
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
static forRoot() {
|
|
867
|
+
return {
|
|
868
|
+
providers: [
|
|
869
|
+
AuthenticationProvider,
|
|
870
|
+
JwtHelperService,
|
|
871
|
+
AuthenticationService,
|
|
872
|
+
{
|
|
873
|
+
provide: HTTP_INTERCEPTORS,
|
|
874
|
+
useClass: AuthorizationInterceptor,
|
|
875
|
+
multi: true
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
provide: HTTP_INTERCEPTORS,
|
|
879
|
+
useClass: JwtInterceptor,
|
|
880
|
+
multi: true
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
provide: JWT_OPTIONS,
|
|
884
|
+
useClass: AuthenticationProvider
|
|
885
|
+
}
|
|
886
|
+
],
|
|
887
|
+
ngModule: AuthenticationModule
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationModule, deps: [{ token: i4.JwtModule, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
891
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationModule, declarations: [AuthenticationComponent,
|
|
892
|
+
DirectLoginComponent,
|
|
893
|
+
TimeoutNotificationComponent], imports: [CommonModule,
|
|
894
|
+
HttpClientModule,
|
|
895
|
+
//JwtModule,
|
|
896
|
+
RouterModule,
|
|
897
|
+
FormsModule,
|
|
898
|
+
ReactiveFormsModule,
|
|
899
|
+
CoolStorageModule], exports: [AuthenticationComponent,
|
|
900
|
+
DirectLoginComponent,
|
|
901
|
+
TimeoutNotificationComponent] }); }
|
|
902
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationModule, imports: [CommonModule,
|
|
903
|
+
HttpClientModule,
|
|
904
|
+
//JwtModule,
|
|
905
|
+
RouterModule,
|
|
906
|
+
FormsModule,
|
|
907
|
+
ReactiveFormsModule,
|
|
908
|
+
CoolStorageModule] }); }
|
|
909
|
+
}
|
|
910
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AuthenticationModule, decorators: [{
|
|
911
|
+
type: NgModule,
|
|
912
|
+
args: [{
|
|
913
|
+
imports: [
|
|
914
|
+
CommonModule,
|
|
915
|
+
HttpClientModule,
|
|
916
|
+
//JwtModule,
|
|
917
|
+
RouterModule,
|
|
918
|
+
FormsModule,
|
|
919
|
+
ReactiveFormsModule,
|
|
920
|
+
CoolStorageModule
|
|
921
|
+
],
|
|
922
|
+
declarations: [
|
|
923
|
+
AuthenticationComponent,
|
|
924
|
+
DirectLoginComponent,
|
|
925
|
+
TimeoutNotificationComponent
|
|
926
|
+
],
|
|
927
|
+
exports: [
|
|
928
|
+
AuthenticationComponent,
|
|
929
|
+
DirectLoginComponent,
|
|
930
|
+
TimeoutNotificationComponent
|
|
931
|
+
]
|
|
932
|
+
}]
|
|
933
|
+
}], ctorParameters: () => [{ type: i4.JwtModule, decorators: [{
|
|
934
|
+
type: Optional
|
|
935
|
+
}, {
|
|
936
|
+
type: SkipSelf
|
|
937
|
+
}] }] });
|
|
938
938
|
|
|
939
|
-
/*
|
|
940
|
-
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
941
|
-
*/
|
|
942
|
-
const RouteGuardService = () => {
|
|
943
|
-
const authenticationService = inject(AuthenticationService);
|
|
944
|
-
const router = inject(Router);
|
|
945
|
-
const state = inject(RouterStateSnapshot);
|
|
946
|
-
const authenticationRoute = inject(AUTHENTICATION_ROUTE);
|
|
947
|
-
if (authenticationService.isAuthenticated()) {
|
|
948
|
-
return true;
|
|
949
|
-
}
|
|
950
|
-
else {
|
|
951
|
-
// Store the attempted URL for redirecting
|
|
952
|
-
authenticationService.redirectUrl = state.url;
|
|
953
|
-
// Navigate to the login page
|
|
954
|
-
return router.navigate([authenticationRoute]);
|
|
955
|
-
}
|
|
939
|
+
/*
|
|
940
|
+
* Copyright (c) 2016 Huntsman Cancer Institute at the University of Utah, Confidential and Proprietary
|
|
941
|
+
*/
|
|
942
|
+
const RouteGuardService = () => {
|
|
943
|
+
const authenticationService = inject(AuthenticationService);
|
|
944
|
+
const router = inject(Router);
|
|
945
|
+
const state = inject(RouterStateSnapshot);
|
|
946
|
+
const authenticationRoute = inject(AUTHENTICATION_ROUTE);
|
|
947
|
+
if (authenticationService.isAuthenticated()) {
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
// Store the attempted URL for redirecting
|
|
952
|
+
authenticationService.redirectUrl = state.url;
|
|
953
|
+
// Navigate to the login page
|
|
954
|
+
return router.navigate([authenticationRoute]);
|
|
955
|
+
}
|
|
956
956
|
};
|
|
957
957
|
|
|
958
|
-
/**
|
|
959
|
-
* Generated bundle index. Do not edit.
|
|
958
|
+
/**
|
|
959
|
+
* Generated bundle index. Do not edit.
|
|
960
960
|
*/
|
|
961
961
|
|
|
962
962
|
export { AUTHENTICATION_DIRECT_ENDPOINT, AUTHENTICATION_IDP_INACTIVITY_MINUTES, AUTHENTICATION_LOGOUT_PATH, AUTHENTICATION_MAX_INACTIVITY_MINUTES, AUTHENTICATION_ROUTE, AUTHENTICATION_SERVER_URL, AUTHENTICATION_TOKEN_ENDPOINT, AUTHENTICATION_TOKEN_KEY, AUTHENTICATION_USER_COUNTDOWN_SECONDS, AuthenticationComponent, AuthenticationModule, AuthenticationService, AuthorizationInterceptor, DirectLoginComponent, RouteGuardService, TimeoutNotificationComponent };
|