angular-aap-auth
Version:
## This library is deprecated. Please, use [ng-ebi-authorization](https://www.npmjs.com/package/ng-ebi-authorization).
398 lines (397 loc) • 39.9 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
import { Injectable, Inject, RendererFactory2 } from '@angular/core';
import { BehaviorSubject, } from 'rxjs';
import { map } from 'rxjs/operators';
import { AAP_CONFIG } from './auth.config';
import { TokenService } from './token.service';
/**
* @record
*/
export function LoginOptions() { }
function LoginOptions_tsickle_Closure_declarations() {
/* TODO: handle strange member:
[key: string]: string;
*/
}
/**
* @record
*/
export function Credentials() { }
function Credentials_tsickle_Closure_declarations() {
/** @type {?} */
Credentials.prototype.realname;
/** @type {?} */
Credentials.prototype.username;
/** @type {?} */
Credentials.prototype.token;
}
export class AuthService {
/**
* @param {?} _rendererFactory
* @param {?} _tokenService
* @param {?} config
*/
constructor(_rendererFactory, _tokenService, config) {
this._rendererFactory = _rendererFactory;
this._tokenService = _tokenService;
this.config = config;
this._credentials = new BehaviorSubject(null);
this._loginCallbacks = [];
this._logoutCallbacks = [];
this._timeoutID = null;
this._commKeyName = 'AngularAapAuthUpdated';
this._commKeyUpdater = () => localStorage.setItem(this._commKeyName, '' + new Date().getTime());
this._domain = encodeURIComponent(window.location.origin);
this._appURL = config.aapURL.replace(/\/$/, '');
this._storageUpdater = config.tokenUpdater;
if (config.tokenRemover) {
this._storageRemover = config.tokenRemover;
}
else {
this._storageRemover = () => config.tokenUpdater(null);
}
const /** @type {?} */ renderer = this._rendererFactory.createRenderer(null, null);
this._listenLoginMessage(renderer);
this._listenChangesFromOtherWindows(renderer);
this._updateCredentials(); // TODO: experiment with setTimeOut
}
/**
* @return {?}
*/
isAuthenticated() {
return this._credentials.asObservable().pipe(map(credentials => credentials ? true : false));
}
/**
* @return {?}
*/
credentials() {
return this._credentials.asObservable();
}
/**
* @return {?}
*/
realname() {
return this._credentials.asObservable().pipe(map(credentials => credentials ? credentials.realname : null));
}
/**
* @return {?}
*/
username() {
return this._credentials.asObservable().pipe(map(credentials => credentials ? credentials.username : null));
}
/**
* @return {?}
*/
token() {
return this._credentials.asObservable().pipe(map(credentials => credentials ? credentials.token : null));
}
/**
* Functions that opens a window instead of a tab.
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?=} loginOptions Options passed as URL parameters to the SSO.
* @param {?=} width Pixel width of the login window.
* @param {?=} height Pixel height of the login window.
* @param {?=} top Position of the top corners. If it is a negative
* number it centres the login window on the screen.
* @param {?=} left Position of the left corners. If it is a negative
* number it centres the login window on the screen.
* @return {?}
*/
windowOpen(loginOptions, width = 650, height = 1000, top = -1, left = -1) {
if (left < 0) {
const /** @type {?} */ screenWidth = screen.width;
if (screenWidth > width) {
left = Math.round(screenWidth / 2 - width / 2);
}
}
if (top < 0) {
const /** @type {?} */ screenHeight = screen.height;
if (screenHeight > height) {
top = Math.round(screenHeight / 2 - height / 2);
}
}
const /** @type {?} */ windowOptions = [
`width=${width}`,
`height=${height}`,
`left=${left}`,
`top=${top}`,
'personalbar=no',
'toolbar=no',
'scrollbars=yes',
'resizable=yes',
'directories=no',
'location=no',
'menubar=no',
'titlebar=no',
'toolbar=no'
];
const /** @type {?} */ loginWindow = window.open(this.getSSOURL(loginOptions), 'Sign in to Elixir', windowOptions.join(','));
if (loginWindow) {
loginWindow.focus();
}
}
/**
* Functions that opens a tab (in modern browser).
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?=} loginOptions Options passed as URL parameters to the SSO.
* @return {?}
*/
tabOpen(loginOptions) {
const /** @type {?} */ loginWindow = window.open(this.getSSOURL(loginOptions), 'Sign in to Elixir');
if (loginWindow) {
loginWindow.focus();
}
}
/**
* Produces a URL that allows logging into the single sign on (SSO) page.
* The URL cans be opened in a new tab using target="_blank",
* or in a new window using window.open().
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?=} options
* @return {?} The SSO URL.
*
*/
getSSOURL(options) {
let /** @type {?} */ extra = '';
if (options) {
this._filterLoginOptions(options);
extra = Object.keys(options)
.map(key => [key, options[key]])
.reduce((accumulator, keyvalue) => `${accumulator}&${keyvalue[0]}=${keyvalue[1]}`, '');
}
return `${this._appURL}/sso?from=${this._domain}${extra}`;
}
/**
* Filters options that are unsecure.
*
* See the advance options that can be requested through the options parameter:
* https://api.aai.ebi.ac.uk/docs/authentication/authentication.index.html#_common_attributes
*
* The time to live paramenter (ttl) default value is 60 minutes. It is a
* big security risk to request longer ttl. If a third party gets hold of
* such token, means that they could use it for a day, week, year
* (essentially, like having the username/password).
*
* @param {?} options
* @return {?}
*/
_filterLoginOptions(options) {
if (Object.keys(options).indexOf('ttl') > -1) {
const /** @type {?} */ ttl = +options['ttl'];
const /** @type {?} */ softLimit = 60;
const /** @type {?} */ hardLimit = 60 * 24;
if (ttl > hardLimit) {
window.console.error(`Login requested with an expiration longer than ${hardLimit} minutes! This is not allowed.`);
window.console.error(`Expiration request reset to ${hardLimit} minutes.`);
options['ttl'] = '' + hardLimit;
}
else if (ttl > softLimit) {
window.console.warn(`Login requested with an expiration longer than ${softLimit} minutes!`);
}
}
}
/**
* Functions that logs out the user.
* It triggers the logout callbacks.
* It is an arrow function (lambda) because in that way it has a reference
* to 'this' when used in setTimeout call.
* @return {?}
*/
logOut() {
this._storageRemover();
this._updateCredentials();
// Triggers updating other windows
this._commKeyUpdater();
}
/**
* Add a callback to the LogIn event.
*
* @param {?} callback The Function called when the login event is triggered and the
* JWT token is received and accepted.
*
* @return {?} The event registration id (necessary to unregister the event).
*/
addLogInEventListener(callback) {
return this._loginCallbacks.push(callback);
}
/**
* Remove a callback from the LogIn event.
*
* @param {?} id The id given when event listener was added.
*
* @return {?} true when remove successfully, false otherwise.
*/
removeLogInEventListener(id) {
return delete this._loginCallbacks[id - 1];
}
/**
* Add a callback to the LogOut event.
*
* @param {?} callback The Function called when the logout event is triggered and the
* JWT token is received and accepted.
*
* @return {?} The registration id (necessary to unregister the event).
*/
addLogOutEventListener(callback) {
return this._logoutCallbacks.push(callback);
}
/**
* Remove a callback from the LogOut event.
*
* @param {?} id The id given when event listener was added.
*
* @return {?} true when remove successfully, false otherwise.
*/
removeLogOutEventListener(id) {
return delete this._logoutCallbacks[id - 1];
}
/**
* Listen for login messages from other windows.
* These messages contain the tokens from the AAP.
* If a token is received then the callbacks are triggered.
* @param {?} renderer
* @return {?}
*/
_listenLoginMessage(renderer) {
renderer.listen('window', 'message', (event) => {
if (!this.messageIsAcceptable(event)) {
return;
}
this._storageUpdater(event.data);
event.source.close();
this._updateCredentials();
// Triggers updating other windows
this._commKeyUpdater();
});
}
/**
* Listen to changes in the token from *other* windows.
*
* For inter-window communication messages are transmitted trough changes
* on a dummy storage key property: '_commKeyName'.
*
* Notice that changes in the '_commKeyName' produced by this class doesn't
* trigger this event.
* @param {?} renderer
* @return {?}
*/
_listenChangesFromOtherWindows(renderer) {
renderer.listen('window', 'storage', (event) => {
if (event.key === this._commKeyName) {
this._updateCredentials();
}
});
}
/**
* Check if the message is coming from the same domain we use to generate
* the SSO URL, otherwise it's iffy and shouldn't trust it.
* @param {?} event
* @return {?}
*/
messageIsAcceptable(event) {
return event.origin === this._appURL;
}
/**
* @return {?}
*/
_updateCredentials() {
const /** @type {?} */ isAuthenticated = this._loggedIn();
if (this._timeoutID) {
window.clearTimeout(this._timeoutID);
}
if (isAuthenticated) {
this._credentials.next({
realname: /** @type {?} */ (this._getRealName()),
username: /** @type {?} */ (this._getUserName()),
token: /** @type {?} */ (this._getToken())
});
this._loginCallbacks.map(callback => callback && callback());
// Schedule future logout event base on token expiration
const /** @type {?} */ expireDate = /** @type {?} */ (this._tokenService.getTokenExpirationDate());
// Coercing dates to numbers with the unary operator '+'
const /** @type {?} */ delay = +expireDate - +new Date();
this._timeoutID = window.setTimeout(() => this.logOut(), delay);
}
else {
this._storageRemover(); // Cleanup possible left behind token
this._credentials.next(null);
this._logoutCallbacks.map(callback => callback && callback());
}
}
/**
* Check if there's a user logging on and whether the token is still valid.
*
* @return {?} Whether the user user is authenticated or not.
*/
_loggedIn() {
return this._tokenService.isTokenValid();
}
/**
* @return {?}
*/
_getToken() {
return this._tokenService.getToken();
}
/**
* @return {?}
*/
_getUserName() {
return this._tokenService.getClaim('email', null);
}
/**
* @return {?}
*/
_getRealName() {
return this._tokenService.getClaim('name', null);
}
}
AuthService.decorators = [
{ type: Injectable },
];
/** @nocollapse */
AuthService.ctorParameters = () => [
{ type: RendererFactory2 },
{ type: TokenService },
{ type: undefined, decorators: [{ type: Inject, args: [AAP_CONFIG,] }] }
];
function AuthService_tsickle_Closure_declarations() {
/** @type {?} */
AuthService.prototype._credentials;
/** @type {?} */
AuthService.prototype._loginCallbacks;
/** @type {?} */
AuthService.prototype._logoutCallbacks;
/** @type {?} */
AuthService.prototype._timeoutID;
/** @type {?} */
AuthService.prototype._domain;
/** @type {?} */
AuthService.prototype._appURL;
/** @type {?} */
AuthService.prototype._storageUpdater;
/** @type {?} */
AuthService.prototype._storageRemover;
/** @type {?} */
AuthService.prototype._commKeyName;
/** @type {?} */
AuthService.prototype._commKeyUpdater;
/** @type {?} */
AuthService.prototype._rendererFactory;
/** @type {?} */
AuthService.prototype._tokenService;
/** @type {?} */
AuthService.prototype.config;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8vYW5ndWxhci1hYXAtYXV0aC8iLCJzb3VyY2VzIjpbImFwcC9tb2R1bGVzL2F1dGgvYXV0aC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQ0gsVUFBVSxFQUNWLE1BQU0sRUFDTixnQkFBZ0IsRUFFbkIsTUFBTSxlQUFlLENBQUM7QUFJdkIsT0FBTyxFQUNILGVBQWUsR0FDbEIsTUFBTSxNQUFNLENBQUM7QUFDZCxPQUFPLEVBQ0gsR0FBRyxFQUNOLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEIsT0FBTyxFQUNILFVBQVUsRUFFYixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQ0gsWUFBWSxFQUNmLE1BQU0saUJBQWlCLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFhekIsTUFBTTs7Ozs7O0lBb0JGLFlBQ1ksa0JBQ0EsZUFDb0IsTUFBa0I7UUFGdEMscUJBQWdCLEdBQWhCLGdCQUFnQjtRQUNoQixrQkFBYSxHQUFiLGFBQWE7UUFDTyxXQUFNLEdBQU4sTUFBTSxDQUFZOzRCQXJCM0IsSUFBSSxlQUFlLENBQXlCLElBQUksQ0FBQzsrQkFFbEMsRUFBRTtnQ0FDRCxFQUFFOzBCQUVMLElBQUk7NEJBVVIsdUJBQXVCOytCQUNwQixHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsRUFBRSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUM7UUFPdkcsSUFBSSxDQUFDLE9BQU8sR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzFELElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ2hELElBQUksQ0FBQyxlQUFlLEdBQUcsTUFBTSxDQUFDLFlB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LEVBQUUsS0FBSyxDQUFDLENBQUM7U0FDbkU7UUFBQyxJQUFJLENBQUMsQ0FBQztZQUNKLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM3QixJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsUUFBUSxJQUFJLFFBQVEsRUFBRSxDQUFDLENBQUM7U0FDakU7Ozs7Ozs7SUFRRyxTQUFTO1FBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLENBQUM7Ozs7O0lBR3JDLFNBQVM7UUFDYixNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLEVBQUUsQ0FBQzs7Ozs7SUFHakMsWUFBWTtRQUNoQixNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQW1CLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQzs7Ozs7SUFHaEUsWUFBWTtRQUNoQixNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQW1CLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQzs7OztZQWxWMUUsVUFBVTs7OztZQS9CUCxnQkFBZ0I7WUFrQmhCLFlBQVk7NENBcUNQLE1BQU0sU0FBQyxVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgICBJbmplY3RhYmxlLFxuICAgIEluamVjdCxcbiAgICBSZW5kZXJlckZhY3RvcnkyLFxuICAgIFJlbmRlcmVyMlxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gICAgT2JzZXJ2YWJsZVxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7XG4gICAgQmVoYXZpb3JTdWJqZWN0LFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7XG4gICAgbWFwXG59IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHtcbiAgICBBQVBfQ09ORklHLFxuICAgIEF1dGhDb25maWdcbn0gZnJvbSAnLi9hdXRoLmNvbmZpZyc7XG5pbXBvcnQge1xuICAgIFRva2VuU2VydmljZVxufSBmcm9tICcuL3Rva2VuLnNlcnZpY2UnO1xuXG5leHBvcnQgaW50ZXJmYWNlIExvZ2luT3B0aW9ucyB7XG4gICAgW2tleTogc3RyaW5nXTogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENyZWRlbnRpYWxzIHtcbiAgICByZWFsbmFtZTogc3RyaW5nO1xuICAgIHVzZXJuYW1lOiBzdHJpbmc7XG4gICAgdG9rZW46IHN0cmluZztcbn1cblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEF1dGhTZXJ2aWNlIHtcblxuICAgIHByaXZhdGUgX2NyZWRlbnRpYWxzID0gbmV3IEJlaGF2aW9yU3ViamVjdCA8IENyZWRlbnRpYWxzIHwgbnVsbCA+IChudWxsKTtcblxuICAgIHByaXZhdGUgX2xvZ2luQ2FsbGJhY2tzOiBGdW5jdGlvbltdID0gW107XG4gICAgcHJpdmF0ZSBfbG9nb3V0Q2FsbGJhY2tzOiBGdW5jdGlvbltdID0gW107XG5cbiAgICBwcml2YXRlIF90aW1lb3V0SUQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xuXG4gICAgLy8gQ29uZmlndXJhdGlvblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2RvbWFpbjogc3RyaW5nO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2FwcFVSTDogc3RyaW5nO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3N0b3JhZ2VVcGRhdGVyOiAobmV3VG9rZW46IGFueSkgPT4gdm9pZDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9zdG9yYWdlUmVtb3ZlcjogKCkgPT4gdm9pZDtcblxuICAgIC8vIFRoaXMgdHdvIHByb3BlcnRpZXMgYXJlIHVzZWQgZm9yIGludGVyLXdpbmRvdyBjb21tdW5jYXRpb24uXG4gICAgLy8gSXQgaXMgYWNoaWV2ZSB0aHJvdWdoIHRoZSB1cGRhdGUgb2YgdGhlIGR1bW15IGtleSBzdG9yYWdlICdfY29tbUtleU5hbWUnXG4gICAgcHJpdmF0ZSByZWFkb25seSBfY29tbUtleU5hbWUgPSAnQW5ndWxhckFhcEF1dGhVcGRhdGVkJztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jb21tS2V5VXBkYXRlciA9ICgpID0+IGxvY2FsU3RvcmFnZS5zZXRJdGVtKHRoaXMuX2NvbW1LZXlOYW1lLCAnJyArIG5ldyBEYXRlKCkuZ2V0VGltZSgpKTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIF9yZW5kZXJlckZhY3Rvcnk6IFJlbmRlcmVyRmFjdG9yeTIsXG4gICAgICAgIHByaXZhdGUgX3Rva2VuU2VydmljZTogVG9rZW5TZXJ2aWNlLFxuICAgICAgICBASW5qZWN0KEFBUF9DT05GSUcpIHByaXZhdGUgY29uZmlnOiBBdXRoQ29uZmlnXG4gICAgKSB7XG4gICAgICAgIHRoaXMuX2RvbWFpbiA9IGVuY29kZVVSSUNvbXBvbmVudCh3aW5kb3cubG9jYXRpb24ub3JpZ2luKTtcbiAgICAgICAgdGhpcy5fYXBwVVJMID0gY29uZmlnLmFhcFVSTC5yZXBsYWNlKC9cXC8kLywgJycpO1xuICAgICAgICB0aGlzLl9zdG9yYWdlVXBkYXRlciA9IGNvbmZpZy50b2tlblVwZGF0ZXI7XG4gICAgICAgIGlmIChjb25maWcudG9rZW5SZW1vdmVyKSB7XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlciA9IGNvbmZpZy50b2tlblJlbW92ZXI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlciA9ICgpID0+IGNvbmZpZy50b2tlblVwZGF0ZXIobnVsbCk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCByZW5kZXJlciA9IHRoaXMuX3JlbmRlcmVyRmFjdG9yeS5jcmVhdGVSZW5kZXJlcihudWxsLCBudWxsKTtcbiAgICAgICAgdGhpcy5fbGlzdGVuTG9naW5NZXNzYWdlKHJlbmRlcmVyKTtcbiAgICAgICAgdGhpcy5fbGlzdGVuQ2hhbmdlc0Zyb21PdGhlcldpbmRvd3MocmVuZGVyZXIpO1xuXG4gICAgICAgIHRoaXMuX3VwZGF0ZUNyZWRlbnRpYWxzKCk7IC8vIFRPRE86IGV4cGVyaW1lbnQgd2l0aCBzZXRUaW1lT3V0XG4gICAgfVxuXG4gICAgcHVibGljIGlzQXV0aGVudGljYXRlZCgpOiBPYnNlcnZhYmxlIDwgYm9vbGVhbiA+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICAgICAgICBtYXAoY3JlZGVudGlhbHMgPT4gY3JlZGVudGlhbHMgPyB0cnVlIDogZmFsc2UpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIGNyZWRlbnRpYWxzKCk6IE9ic2VydmFibGUgPCBDcmVkZW50aWFscyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgcmVhbG5hbWUoKTogT2JzZXJ2YWJsZSA8IHN0cmluZyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gIGNyZWRlbnRpYWxzLnJlYWxuYW1lIDogbnVsbClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgdXNlcm5hbWUoKTogT2JzZXJ2YWJsZSA8IHN0cmluZyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gIGNyZWRlbnRpYWxzLnVzZXJuYW1lIDogbnVsbClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgdG9rZW4oKTogT2JzZXJ2YWJsZSA8IHN0cmluZyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gIGNyZWRlbnRpYWxzLnRva2VuIDogbnVsbClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBvcGVucyBhIHdpbmRvdyBpbnN0ZWFkIG9mIGEgdGFiLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqIEBwYXJhbSB3aWR0aCBQaXhlbCB3aWR0aCBvZiB0aGUgbG9naW4gd2luZG93LlxuICAgICAqIEBwYXJhbSBoZWlnaHQgUGl4ZWwgaGVpZ2h0IG9mIHRoZSBsb2dpbiB3aW5kb3cuXG4gICAgICogQHBhcmFtIHRvcCBQb3NpdGlvbiBvZiB0aGUgdG9wIGNvcm5lcnMuIElmIGl0IGlzIGEgbmVnYXRpdmVcbiAgICAgKiAgICAgICAgICAgICBudW1iZXIgaXQgY2VudHJlcyB0aGUgbG9naW4gd2luZG93IG9uIHRoZSBzY3JlZW4uXG4gICAgICogQHBhcmFtIGxlZnQgUG9zaXRpb24gb2YgdGhlIGxlZnQgY29ybmVycy4gSWYgaXQgaXMgYSBuZWdhdGl2ZVxuICAgICAqICAgICAgICAgICAgIG51bWJlciBpdCBjZW50cmVzIHRoZSBsb2dpbiB3aW5kb3cgb24gdGhlIHNjcmVlbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgd2luZG93T3Blbihsb2dpbk9wdGlvbnM/OiBMb2dpbk9wdGlvbnMsIHdpZHRoID0gNjUwLCBoZWlnaHQgPSAxMDAwLCB0b3AgPSAtMSwgbGVmdCA9IC0xKSB7XG4gICAgICAgIGlmIChsZWZ0IDwgMCkge1xuICAgICAgICAgICAgY29uc3Qgc2NyZWVuV2lkdGggPSBzY3JlZW4ud2lkdGg7XG4gICAgICAgICAgICBpZiAoc2NyZWVuV2lkdGggPiB3aWR0aCkge1xuICAgICAgICAgICAgICAgIGxlZnQgPSBNYXRoLnJvdW5kKHNjcmVlbldpZHRoIC8gMiAtIHdpZHRoIC8gMik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRvcCA8IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHNjcmVlbkhlaWdodCA9IHNjcmVlbi5oZWlnaHQ7XG4gICAgICAgICAgICBpZiAoc2NyZWVuSGVpZ2h0ID4gaGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgdG9wID0gTWF0aC5yb3VuZChzY3JlZW5IZWlnaHQgLyAyIC0gaGVpZ2h0IC8gMik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB3aW5kb3dPcHRpb25zID0gW1xuICAgICAgICAgICAgYHdpZHRoPSR7d2lkdGh9YCxcbiAgICAgICAgICAgIGBoZWlnaHQ9JHtoZWlnaHR9YCxcbiAgICAgICAgICAgIGBsZWZ0PSR7bGVmdH1gLFxuICAgICAgICAgICAgYHRvcD0ke3RvcH1gLFxuICAgICAgICAgICAgJ3BlcnNvbmFsYmFyPW5vJyxcbiAgICAgICAgICAgICd0b29sYmFyPW5vJyxcbiAgICAgICAgICAgICdzY3JvbGxiYXJzPXllcycsXG4gICAgICAgICAgICAncmVzaXphYmxlPXllcycsXG4gICAgICAgICAgICAnZGlyZWN0b3JpZXM9bm8nLFxuICAgICAgICAgICAgJ2xvY2F0aW9uPW5vJyxcbiAgICAgICAgICAgICdtZW51YmFyPW5vJyxcbiAgICAgICAgICAgICd0aXRsZWJhcj1ubycsXG4gICAgICAgICAgICAndG9vbGJhcj1ubydcbiAgICAgICAgXTtcblxuICAgICAgICBjb25zdCBsb2dpbldpbmRvdyA9IHdpbmRvdy5vcGVuKHRoaXMuZ2V0U1NPVVJMKGxvZ2luT3B0aW9ucyksICdTaWduIGluIHRvIEVsaXhpcicsIHdpbmRvd09wdGlvbnMuam9pbignLCcpKTtcbiAgICAgICAgaWYgKGxvZ2luV2luZG93KSB7XG4gICAgICAgICAgICBsb2dpbldpbmRvdy5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRnVuY3Rpb25zIHRoYXQgb3BlbnMgYSB0YWIgKGluIG1vZGVybiBicm93c2VyKS5cbiAgICAgKlxuICAgICAqIFNlZSBtZXRob2QgX2ZpbHRlckxvZ2luT3B0aW9ucyByZWdhcmRpbmcgc2VjdXJpdHkgcmlza3Mgb2YgY2VydGFpblxuICAgICAqIExvZ2luT3B0aW9ucy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKi9cbiAgICBwdWJsaWMgdGFiT3Blbihsb2dpbk9wdGlvbnM/OiBMb2dpbk9wdGlvbnMpIHtcbiAgICAgICAgY29uc3QgbG9naW5XaW5kb3cgPSB3aW5kb3cub3Blbih0aGlzLmdldFNTT1VSTChsb2dpbk9wdGlvbnMpLCAnU2lnbiBpbiB0byBFbGl4aXInKTtcbiAgICAgICAgaWYgKGxvZ2luV2luZG93KSB7XG4gICAgICAgICAgICBsb2dpbldpbmRvdy5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUHJvZHVjZXMgYSBVUkwgdGhhdCBhbGxvd3MgbG9nZ2luZyBpbnRvIHRoZSBzaW5nbGUgc2lnbiBvbiAoU1NPKSBwYWdlLlxuICAgICAqIFRoZSBVUkwgY2FucyBiZSBvcGVuZWQgaW4gYSBuZXcgdGFiIHVzaW5nIHRhcmdldD1cIl9ibGFua1wiLFxuICAgICAqIG9yIGluIGEgbmV3IHdpbmRvdyB1c2luZyB3aW5kb3cub3BlbigpLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtICBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIFRoZSBTU08gVVJMLlxuICAgICAqXG4gICAgICovXG4gICAgICAgIHB1YmxpYyBnZXRTU09VUkwob3B0aW9ucz86IExvZ2luT3B0aW9ucyk6IHN0cmluZyB7XG4gICAgICAgIGxldCBleHRyYSA9ICcnO1xuICAgICAgICBpZiAob3B0aW9ucykge1xuICAgICAgICAgICAgdGhpcy5fZmlsdGVyTG9naW5PcHRpb25zKG9wdGlvbnMpO1xuICAgICAgICAgICAgZXh0cmEgPSBPYmplY3Qua2V5cyhvcHRpb25zKVxuICAgICAgICAgICAgICAgIC5tYXAoa2V5ID0+IFtrZXksIG9wdGlvbnNba2V5XV0pXG4gICAgICAgICAgICAgICAgLnJlZHVjZSgoYWNjdW11bGF0b3IsIGtleXZhbHVlKSA9PiBgJHthY2N1bXVsYXRvcn0mJHtrZXl2YWx1ZVswXX09JHtrZXl2YWx1ZVsxXX1gLCAnJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGAke3RoaXMuX2FwcFVSTH0vc3NvP2Zyb209JHt0aGlzLl9kb21haW59JHtleHRyYX1gO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZpbHRlcnMgb3B0aW9ucyB0aGF0IGFyZSB1bnNlY3VyZS5cbiAgICAgKlxuICAgICAqIFNlZSB0aGUgYWR2YW5jZSBvcHRpb25zIHRoYXQgY2FuIGJlIHJlcXVlc3RlZCB0aHJvdWdoIHRoZSBvcHRpb25zIHBhcmFtZXRlcjpcbiAgICAgKiBodHRwczovL2FwaS5hYWkuZWJpLmFjLnVrL2RvY3MvYXV0aGVudGljYXRpb24vYXV0aGVudGljYXRpb24uaW5kZXguaHRtbCNfY29tbW9uX2F0dHJpYnV0ZXNcbiAgICAgKlxuICAgICAqIFRoZSB0aW1lIHRvIGxpdmUgcGFyYW1lbnRlciAodHRsKSBkZWZhdWx0IHZhbHVlIGlzIDYwIG1pbnV0ZXMuIEl0IGlzIGFcbiAgICAgKiBiaWcgc2VjdXJpdHkgcmlzayB0byByZXF1ZXN0IGxvbmdlciB0dGwuIElmIGEgdGhpcmQgcGFydHkgZ2V0cyBob2xkIG9mXG4gICAgICogc3VjaCB0b2tlbiwgbWVhbnMgdGhhdCB0aGV5IGNvdWxkIHVzZSBpdCBmb3IgYSBkYXksIHdlZWssIHllYXJcbiAgICAgKiAoZXNzZW50aWFsbHksIGxpa2UgaGF2aW5nIHRoZSB1c2VybmFtZS9wYXNzd29yZCkuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqXG4gICAgICpcbiAgICAgKi9cbiAgICBwdWJsaWMgX2ZpbHRlckxvZ2luT3B0aW9ucyhvcHRpb25zOiBMb2dpbk9wdGlvbnMpIHtcbiAgICAgICAgaWYgKE9iamVjdC5rZXlzKG9wdGlvbnMpLmluZGV4T2YoJ3R0bCcpID4gLTEpIHtcbiAgICAgICAgICAgIGNvbnN0IHR0bDogbnVtYmVyID0gK29wdGlvbnNbJ3R0bCddO1xuICAgICAgICAgICAgY29uc3Qgc29mdExpbWl0ID0gNjA7XG4gICAgICAgICAgICBjb25zdCBoYXJkTGltaXQgPSA2MCAqIDI0O1xuICAgICAgICAgICAgaWYgKHR0bCA+IGhhcmRMaW1pdCkge1xuICAgICAgICAgICAgICAgIHdpbmRvdy5jb25zb2xlLmVycm9yKGBMb2dpbiByZXF1ZXN0ZWQgd2l0aCBhbiBleHBpcmF0aW9uIGxvbmdlciB0aGFuICR7aGFyZExpbWl0fSBtaW51dGVzISBUaGlzIGlzIG5vdCBhbGxvd2VkLmApO1xuICAgICAgICAgICAgICAgIHdpbmRvdy5jb25zb2xlLmVycm9yKGBFeHBpcmF0aW9uIHJlcXVlc3QgcmVzZXQgdG8gJHtoYXJkTGltaXR9IG1pbnV0ZXMuYCk7XG4gICAgICAgICAgICAgICAgb3B0aW9uc1sndHRsJ10gPSAnJyArIGhhcmRMaW1pdDtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodHRsID4gc29mdExpbWl0KSB7XG4gICAgICAgICAgICAgICAgd2luZG93LmNvbnNvbGUud2FybihgTG9naW4gcmVxdWVzdGVkIHdpdGggYW4gZXhwaXJhdGlvbiBsb25nZXIgdGhhbiAke3NvZnRMaW1pdH0gbWludXRlcyFgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZ1bmN0aW9ucyB0aGF0IGxvZ3Mgb3V0IHRoZSB1c2VyLlxuICAgICAqIEl0IHRyaWdnZXJzIHRoZSBsb2dvdXQgY2FsbGJhY2tzLlxuICAgICAqIEl0IGlzIGFuIGFycm93IGZ1bmN0aW9uIChsYW1iZGEpIGJlY2F1c2UgaW4gdGhhdCB3YXkgaXQgaGFzIGEgcmVmZXJlbmNlXG4gICAgICogdG8gJ3RoaXMnIHdoZW4gdXNlZCBpbiBzZXRUaW1lb3V0IGNhbGwuXG4gICAgICovXG4gICAgcHVibGljIGxvZ091dCgpIHtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIoKTtcbiAgICAgICAgdGhpcy5fdXBkYXRlQ3JlZGVudGlhbHMoKTtcblxuICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgIHRoaXMuX2NvbW1LZXlVcGRhdGVyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIGEgY2FsbGJhY2sgdG8gdGhlIExvZ0luIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGNhbGxiYWNrIFRoZSBGdW5jdGlvbiBjYWxsZWQgd2hlbiB0aGUgbG9naW4gZXZlbnQgaXMgdHJpZ2dlcmVkIGFuZCB0aGVcbiAgICAgKiAgICBKV1QgdG9rZW4gaXMgcmVjZWl2ZWQgYW5kIGFjY2VwdGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgVGhlIGV2ZW50IHJlZ2lzdHJhdGlvbiBpZCAobmVjZXNzYXJ5IHRvIHVucmVnaXN0ZXIgdGhlIGV2ZW50KS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYWRkTG9nSW5FdmVudExpc3RlbmVyKGNhbGxiYWNrOiBGdW5jdGlvbik6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLl9sb2dpbkNhbGxiYWNrcy5wdXNoKGNhbGxiYWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmUgYSBjYWxsYmFjayBmcm9tIHRoZSBMb2dJbiBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBpZCBUaGUgaWQgZ2l2ZW4gd2hlbiBldmVudCBsaXN0ZW5lciB3YXMgYWRkZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB0cnVlIHdoZW4gcmVtb3ZlIHN1Y2Nlc3NmdWxseSwgZmFsc2Ugb3RoZXJ3aXNlLlxuICAgICAqL1xuICAgIHB1YmxpYyByZW1vdmVMb2dJbkV2ZW50TGlzdGVuZXIoaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZGVsZXRlIHRoaXMuX2xvZ2luQ2FsbGJhY2tzW2lkIC0gMV07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIGEgY2FsbGJhY2sgdG8gdGhlIExvZ091dCBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBjYWxsYmFjayBUaGUgRnVuY3Rpb24gY2FsbGVkIHdoZW4gdGhlIGxvZ291dCBldmVudCBpcyB0cmlnZ2VyZWQgYW5kIHRoZVxuICAgICAqICAgIEpXVCB0b2tlbiBpcyByZWNlaXZlZCBhbmQgYWNjZXB0ZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyBUaGUgcmVnaXN0cmF0aW9uIGlkIChuZWNlc3NhcnkgdG8gdW5yZWdpc3RlciB0aGUgZXZlbnQpLlxuICAgICAqL1xuICAgIHB1YmxpYyBhZGRMb2dPdXRFdmVudExpc3RlbmVyKGNhbGxiYWNrOiBGdW5jdGlvbik6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLl9sb2dvdXRDYWxsYmFja3MucHVzaChjYWxsYmFjayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGEgY2FsbGJhY2sgZnJvbSB0aGUgTG9nT3V0IGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIFRoZSBpZCBnaXZlbiB3aGVuIGV2ZW50IGxpc3RlbmVyIHdhcyBhZGRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHRydWUgd2hlbiByZW1vdmUgc3VjY2Vzc2Z1bGx5LCBmYWxzZSBvdGhlcndpc2UuXG4gICAgICovXG4gICAgcHVibGljIHJlbW92ZUxvZ091dEV2ZW50TGlzdGVuZXIoaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZGVsZXRlIHRoaXMuX2xvZ291dENhbGxiYWNrc1tpZCAtIDFdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIExpc3RlbiBmb3IgbG9naW4gbWVzc2FnZXMgZnJvbSBvdGhlciB3aW5kb3dzLlxuICAgICAqIFRoZXNlIG1lc3NhZ2VzIGNvbnRhaW4gdGhlIHRva2VucyBmcm9tIHRoZSBBQVAuXG4gICAgICogSWYgYSB0b2tlbiBpcyByZWNlaXZlZCB0aGVuIHRoZSBjYWxsYmFja3MgYXJlIHRyaWdnZXJlZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIF9saXN0ZW5Mb2dpbk1lc3NhZ2UocmVuZGVyZXI6IFJlbmRlcmVyMikge1xuICAgICAgICByZW5kZXJlci5saXN0ZW4oJ3dpbmRvdycsICdtZXNzYWdlJywgKGV2ZW50OiBNZXNzYWdlRXZlbnQpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5tZXNzYWdlSXNBY2NlcHRhYmxlKGV2ZW50KSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VVcGRhdGVyKGV2ZW50LmRhdGEpO1xuICAgICAgICAgICAgZXZlbnQuc291cmNlLmNsb3NlKCk7XG4gICAgICAgICAgICB0aGlzLl91cGRhdGVDcmVkZW50aWFscygpO1xuXG4gICAgICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgICAgICB0aGlzLl9jb21tS2V5VXBkYXRlcigpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKiogTGlzdGVuIHRvIGNoYW5nZXMgaW4gdGhlIHRva2VuIGZyb20gKm90aGVyKiB3aW5kb3dzLlxuICAgICAqXG4gICAgICogRm9yIGludGVyLXdpbmRvdyBjb21tdW5pY2F0aW9uIG1lc3NhZ2VzIGFyZSB0cmFuc21pdHRlZCB0cm91Z2ggY2hhbmdlc1xuICAgICAqIG9uIGEgZHVtbXkgc3RvcmFnZSBrZXkgcHJvcGVydHk6ICdfY29tbUtleU5hbWUnLlxuICAgICAqXG4gICAgICogTm90aWNlIHRoYXQgY2hhbmdlcyBpbiB0aGUgJ19jb21tS2V5TmFtZScgcHJvZHVjZWQgYnkgdGhpcyBjbGFzcyBkb2Vzbid0XG4gICAgICogdHJpZ2dlciB0aGlzIGV2ZW50LlxuICAgICAqL1xuICAgIHByaXZhdGUgX2xpc3RlbkNoYW5nZXNGcm9tT3RoZXJXaW5kb3dzKHJlbmRlcmVyOiBSZW5kZXJlcjIpIHtcbiAgICAgICAgcmVuZGVyZXIubGlzdGVuKCd3aW5kb3cnLCAnc3RvcmFnZScsIChldmVudDogU3RvcmFnZUV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSB0aGlzLl9jb21tS2V5TmFtZSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZUNyZWRlbnRpYWxzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIHRoZSBtZXNzYWdlIGlzIGNvbWluZyBmcm9tIHRoZSBzYW1lIGRvbWFpbiB3ZSB1c2UgdG8gZ2VuZXJhdGVcbiAgICAgKiB0aGUgU1NPIFVSTCwgb3RoZXJ3aXNlIGl0J3MgaWZmeSBhbmQgc2hvdWxkbid0IHRydXN0IGl0LlxuICAgICAqL1xuICAgIHByaXZhdGUgbWVzc2FnZUlzQWNjZXB0YWJsZShldmVudDogTWVzc2FnZUV2ZW50KTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBldmVudC5vcmlnaW4gPT09IHRoaXMuX2FwcFVSTDtcbiAgICB9XG5cbiAgICBwcml2YXRlIF91cGRhdGVDcmVkZW50aWFscygpIHtcbiAgICAgICAgY29uc3QgaXNBdXRoZW50aWNhdGVkID0gdGhpcy5fbG9nZ2VkSW4oKTtcblxuICAgICAgICBpZiAodGhpcy5fdGltZW91dElEKSB7XG4gICAgICAgICAgICB3aW5kb3cuY2xlYXJUaW1lb3V0KHRoaXMuX3RpbWVvdXRJRCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaXNBdXRoZW50aWNhdGVkKSB7XG4gICAgICAgICAgICB0aGlzLl9jcmVkZW50aWFscy5uZXh0KHtcbiAgICAgICAgICAgICAgICByZWFsbmFtZTogPCBzdHJpbmcgPiB0aGlzLl9nZXRSZWFsTmFtZSgpLFxuICAgICAgICAgICAgICAgIHVzZXJuYW1lOiA8IHN0cmluZyA+IHRoaXMuX2dldFVzZXJOYW1lKCksXG4gICAgICAgICAgICAgICAgdG9rZW46IDwgc3RyaW5nID4gdGhpcy5fZ2V0VG9rZW4oKVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHRoaXMuX2xvZ2luQ2FsbGJhY2tzLm1hcChjYWxsYmFjayA9PiBjYWxsYmFjayAmJiBjYWxsYmFjaygpKTtcblxuICAgICAgICAgICAgLy8gU2NoZWR1bGUgZnV0dXJlIGxvZ291dCBldmVudCBiYXNlIG9uIHRva2VuIGV4cGlyYXRpb25cbiAgICAgICAgICAgIGNvbnN0IGV4cGlyZURhdGUgPSA8IERhdGUgPiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0VG9rZW5FeHBpcmF0aW9uRGF0ZSgpO1xuICAgICAgICAgICAgLy8gQ29lcmNpbmcgZGF0ZXMgdG8gbnVtYmVycyB3aXRoIHRoZSB1bmFyeSBvcGVyYXRvciAnKydcbiAgICAgICAgICAgIGNvbnN0IGRlbGF5ID0gK2V4cGlyZURhdGUgLSArbmV3IERhdGUoKTtcbiAgICAgICAgICAgIHRoaXMuX3RpbWVvdXRJRCA9IHdpbmRvdy5zZXRUaW1lb3V0KCgpID0+IHRoaXMubG9nT3V0KCksIGRlbGF5KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyKCk7IC8vIENsZWFudXAgcG9zc2libGUgbGVmdCBiZWhpbmQgdG9rZW5cbiAgICAgICAgICAgIHRoaXMuX2NyZWRlbnRpYWxzLm5leHQobnVsbCk7XG4gICAgICAgICAgICB0aGlzLl9sb2dvdXRDYWxsYmFja3MubWFwKGNhbGxiYWNrID0+IGNhbGxiYWNrICYmIGNhbGxiYWNrKCkpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2sgaWYgdGhlcmUncyBhIHVzZXIgbG9nZ2luZyBvbiBhbmQgd2hldGhlciB0aGUgdG9rZW4gaXMgc3RpbGwgdmFsaWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyAgV2hldGhlciB0aGUgdXNlciB1c2VyIGlzIGF1dGhlbnRpY2F0ZWQgb3Igbm90LlxuICAgICAqL1xuICAgIHByaXZhdGUgX2xvZ2dlZEluKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5TZXJ2aWNlLmlzVG9rZW5WYWxpZCgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX2dldFRva2VuKCk6IHN0cmluZyB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5TZXJ2aWNlLmdldFRva2VuKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0VXNlck5hbWUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0Q2xhaW0gPCBzdHJpbmcsIG51bGwgPiAoJ2VtYWlsJywgbnVsbCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0UmVhbE5hbWUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0Q2xhaW0gPCBzdHJpbmcsIG51bGwgPiAoJ25hbWUnLCBudWxsKTtcbiAgICB9XG5cbn1cbiJdfQ==