angular-aap-auth
Version:
## This library is deprecated. Please, use [ng-ebi-authorization](https://www.npmjs.com/package/ng-ebi-authorization).
672 lines • 54.8 kB
JavaScript
import { Injectable, InjectionToken, Inject, RendererFactory2, NgModule, Optional, SkipSelf } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* The purpose of this very simple service is to interface between the
* AuthService and the specific token manipulation routing of JwtHelperService.
* In this way, if in the future we want to replace JwtHelperService by
* another service, AuthService doesn't need to be modified, only this service.
*/
var TokenService = /** @class */ (function () {
function TokenService(_jwt) {
this._jwt = _jwt;
}
/**
* @return {?}
*/
TokenService.prototype.getToken = /**
* @return {?}
*/
function () {
return this._jwt.tokenGetter();
};
/**
* @return {?}
*/
TokenService.prototype.getTokenExpirationDate = /**
* @return {?}
*/
function () {
try {
return this._jwt.getTokenExpirationDate();
}
catch (/** @type {?} */ e) {
return null;
}
};
/**
* @return {?}
*/
TokenService.prototype.isTokenValid = /**
* @return {?}
*/
function () {
try {
return !this._jwt.isTokenExpired();
}
catch (/** @type {?} */ error) {
return false;
}
};
/**
* Get claims from the token.
*
* @template T, C
* @param {?} claim The name of the claim
* @param {?} defaultValue The default value returned in case of error
*
* @return {?} claim or default value
*/
TokenService.prototype.getClaim = /**
* Get claims from the token.
*
* @template T, C
* @param {?} claim The name of the claim
* @param {?} defaultValue The default value returned in case of error
*
* @return {?} claim or default value
*/
function (claim, defaultValue) {
try {
var /** @type {?} */ value = /** @type {?} */ (this._jwt.decodeToken()[claim]);
if (value === undefined) {
return defaultValue;
}
return value;
}
catch (/** @type {?} */ e) {
return defaultValue;
}
};
TokenService.decorators = [
{ type: Injectable },
];
/** @nocollapse */
TokenService.ctorParameters = function () { return [
{ type: JwtHelperService }
]; };
return TokenService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var /** @type {?} */ AAP_CONFIG = new InjectionToken('AAP_CONFIG');
/**
* @return {?}
*/
function getToken() {
return localStorage.getItem('id_token') || '';
}
/**
* @return {?}
*/
function removeToken() {
return localStorage.removeItem('id_token');
}
/**
* @param {?} newToken
* @return {?}
*/
function updateToken(newToken) {
return localStorage.setItem('id_token', newToken);
}
var /** @type {?} */ DEFAULT_CONF = {
aapURL: 'https://api.aai.ebi.ac.uk',
tokenGetter: getToken,
tokenRemover: removeToken,
tokenUpdater: updateToken
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var AuthService = /** @class */ (function () {
function AuthService(_rendererFactory, _tokenService, config) {
var _this = this;
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 = function () { return 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 = function () { return config.tokenUpdater(null); };
}
var /** @type {?} */ renderer = this._rendererFactory.createRenderer(null, null);
this._listenLoginMessage(renderer);
this._listenChangesFromOtherWindows(renderer);
this._updateCredentials(); // TODO: experiment with setTimeOut
}
/**
* @return {?}
*/
AuthService.prototype.isAuthenticated = /**
* @return {?}
*/
function () {
return this._credentials.asObservable().pipe(map(function (credentials) { return credentials ? true : false; }));
};
/**
* @return {?}
*/
AuthService.prototype.credentials = /**
* @return {?}
*/
function () {
return this._credentials.asObservable();
};
/**
* @return {?}
*/
AuthService.prototype.realname = /**
* @return {?}
*/
function () {
return this._credentials.asObservable().pipe(map(function (credentials) { return credentials ? credentials.realname : null; }));
};
/**
* @return {?}
*/
AuthService.prototype.username = /**
* @return {?}
*/
function () {
return this._credentials.asObservable().pipe(map(function (credentials) { return credentials ? credentials.username : null; }));
};
/**
* @return {?}
*/
AuthService.prototype.token = /**
* @return {?}
*/
function () {
return this._credentials.asObservable().pipe(map(function (credentials) { return 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 {?}
*/
AuthService.prototype.windowOpen = /**
* 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 {?}
*/
function (loginOptions, width, height, top, left) {
if (width === void 0) { width = 650; }
if (height === void 0) { height = 1000; }
if (top === void 0) { top = -1; }
if (left === void 0) { left = -1; }
if (left < 0) {
var /** @type {?} */ screenWidth = screen.width;
if (screenWidth > width) {
left = Math.round(screenWidth / 2 - width / 2);
}
}
if (top < 0) {
var /** @type {?} */ screenHeight = screen.height;
if (screenHeight > height) {
top = Math.round(screenHeight / 2 - height / 2);
}
}
var /** @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'
];
var /** @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 {?}
*/
AuthService.prototype.tabOpen = /**
* 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 {?}
*/
function (loginOptions) {
var /** @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.
*
*/
AuthService.prototype.getSSOURL = /**
* 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.
*
*/
function (options) {
var /** @type {?} */ extra = '';
if (options) {
this._filterLoginOptions(options);
extra = Object.keys(options)
.map(function (key) { return [key, options[key]]; })
.reduce(function (accumulator, keyvalue) { return 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 {?}
*/
AuthService.prototype._filterLoginOptions = /**
* 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 {?}
*/
function (options) {
if (Object.keys(options).indexOf('ttl') > -1) {
var /** @type {?} */ ttl = +options['ttl'];
var /** @type {?} */ softLimit = 60;
var /** @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 {?}
*/
AuthService.prototype.logOut = /**
* 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 {?}
*/
function () {
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).
*/
AuthService.prototype.addLogInEventListener = /**
* 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).
*/
function (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.
*/
AuthService.prototype.removeLogInEventListener = /**
* Remove a callback from the LogIn event.
*
* @param {?} id The id given when event listener was added.
*
* @return {?} true when remove successfully, false otherwise.
*/
function (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).
*/
AuthService.prototype.addLogOutEventListener = /**
* 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).
*/
function (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.
*/
AuthService.prototype.removeLogOutEventListener = /**
* Remove a callback from the LogOut event.
*
* @param {?} id The id given when event listener was added.
*
* @return {?} true when remove successfully, false otherwise.
*/
function (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 {?}
*/
AuthService.prototype._listenLoginMessage = /**
* 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 {?}
*/
function (renderer) {
var _this = this;
renderer.listen('window', 'message', function (event) {
if (!_this.messageIsAcceptable(event)) {
return;
}
_this._storageUpdater(event.data);
event.source.close();
_this._updateCredentials();
// Triggers updating other windows
// 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 {?}
*/
AuthService.prototype._listenChangesFromOtherWindows = /**
* 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 {?}
*/
function (renderer) {
var _this = this;
renderer.listen('window', 'storage', function (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 {?}
*/
AuthService.prototype.messageIsAcceptable = /**
* 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 {?}
*/
function (event) {
return event.origin === this._appURL;
};
/**
* @return {?}
*/
AuthService.prototype._updateCredentials = /**
* @return {?}
*/
function () {
var _this = this;
var /** @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(function (callback) { return callback && callback(); });
// Schedule future logout event base on token expiration
var /** @type {?} */ expireDate = /** @type {?} */ (this._tokenService.getTokenExpirationDate());
// Coercing dates to numbers with the unary operator '+'
var /** @type {?} */ delay = +expireDate - +new Date();
this._timeoutID = window.setTimeout(function () { return _this.logOut(); }, delay);
}
else {
this._storageRemover(); // Cleanup possible left behind token
this._credentials.next(null);
this._logoutCallbacks.map(function (callback) { return 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.
*/
AuthService.prototype._loggedIn = /**
* Check if there's a user logging on and whether the token is still valid.
*
* @return {?} Whether the user user is authenticated or not.
*/
function () {
return this._tokenService.isTokenValid();
};
/**
* @return {?}
*/
AuthService.prototype._getToken = /**
* @return {?}
*/
function () {
return this._tokenService.getToken();
};
/**
* @return {?}
*/
AuthService.prototype._getUserName = /**
* @return {?}
*/
function () {
return this._tokenService.getClaim('email', null);
};
/**
* @return {?}
*/
AuthService.prototype._getRealName = /**
* @return {?}
*/
function () {
return this._tokenService.getClaim('name', null);
};
AuthService.decorators = [
{ type: Injectable },
];
/** @nocollapse */
AuthService.ctorParameters = function () { return [
{ type: RendererFactory2 },
{ type: TokenService },
{ type: undefined, decorators: [{ type: Inject, args: [AAP_CONFIG,] }] }
]; };
return AuthService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var AuthModule = /** @class */ (function () {
function AuthModule(parentModule) {
if (parentModule) {
throw new Error('AuthModule is already loaded. It should only be imported in your application\'s main module.');
}
}
/**
* @param {?=} options
* @return {?}
*/
AuthModule.forRoot = /**
* @param {?=} options
* @return {?}
*/
function (options) {
return {
ngModule: AuthModule,
providers: [
TokenService,
{
provide: AAP_CONFIG,
useValue: options ? options : DEFAULT_CONF
},
AuthService
]
};
};
AuthModule.decorators = [
{ type: NgModule, args: [{},] },
];
/** @nocollapse */
AuthModule.ctorParameters = function () { return [
{ type: AuthModule, decorators: [{ type: Optional }, { type: SkipSelf }] }
]; };
return AuthModule;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
export { AuthModule, AuthService, TokenService, AAP_CONFIG as ɵb, DEFAULT_CONF as ɵf, getToken as ɵc, removeToken as ɵd, updateToken as ɵe };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5ndWxhci1hYXAtYXV0aC5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vYW5ndWxhci1hYXAtYXV0aC9hcHAvbW9kdWxlcy9hdXRoL3Rva2VuLnNlcnZpY2UudHMiLCJuZzovL2FuZ3VsYXItYWFwLWF1dGgvYXBwL21vZHVsZXMvYXV0aC9hdXRoLmNvbmZpZy50cyIsIm5nOi8vYW5ndWxhci1hYXAtYXV0aC9hcHAvbW9kdWxlcy9hdXRoL2F1dGguc2VydmljZS50cyIsIm5nOi8vYW5ndWxhci1hYXAtYXV0aC9hcHAvbW9kdWxlcy9hdXRoL2F1dGgubW9kdWxlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gICAgSW5qZWN0YWJsZVxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHtcbiAgICBKd3RIZWxwZXJTZXJ2aWNlXG59IGZyb20gJ0BhdXRoMC9hbmd1bGFyLWp3dCc7XG5cbi8qKlxuICogVGhlIHB1cnBvc2Ugb2YgdGhpcyB2ZXJ5IHNpbXBsZSBzZXJ2aWNlIGlzIHRvIGludGVyZmFjZSBiZXR3ZWVuIHRoZVxuICogQXV0aFNlcnZpY2UgYW5kIHRoZSBzcGVjaWZpYyB0b2tlbiBtYW5pcHVsYXRpb24gcm91dGluZyBvZiBKd3RIZWxwZXJTZXJ2aWNlLlxuICogSW4gdGhpcyB3YXksIGlmIGluIHRoZSBmdXR1cmUgd2Ugd2FudCB0byByZXBsYWNlIEp3dEhlbHBlclNlcnZpY2UgYnlcbiAqIGFub3RoZXIgc2VydmljZSwgQXV0aFNlcnZpY2UgZG9lc24ndCBuZWVkIHRvIGJlIG1vZGlmaWVkLCBvbmx5IHRoaXMgc2VydmljZS5cbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFRva2VuU2VydmljZSB7XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBfand0OiBKd3RIZWxwZXJTZXJ2aWNlXG4gICAgKSB7fVxuXG4gICAgcHVibGljIGdldFRva2VuKCk6IHN0cmluZyB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5fand0LnRva2VuR2V0dGVyKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFRva2VuRXhwaXJhdGlvbkRhdGUoKTogRGF0ZSB8IG51bGwge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2p3dC5nZXRUb2tlbkV4cGlyYXRpb25EYXRlKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGlzVG9rZW5WYWxpZCgpOiBib29sZWFuIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiAhdGhpcy5fand0LmlzVG9rZW5FeHBpcmVkKCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgY2xhaW1zIGZyb20gdGhlIHRva2VuLlxuICAgICAqXG4gICAgICogQHBhcmFtIGNsYWltIFRoZSBuYW1lIG9mIHRoZSBjbGFpbVxuICAgICAqIEBwYXJhbSBkZWZhdWx0VmFsdWUgVGhlIGRlZmF1bHQgdmFsdWUgcmV0dXJuZWQgaW4gY2FzZSBvZiBlcnJvclxuICAgICAqXG4gICAgICogQHJldHVybnMgY2xhaW0gb3IgZGVmYXVsdCB2YWx1ZVxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDbGFpbSA8IFQsIEMgPiAoY2xhaW06IHN0cmluZywgZGVmYXVsdFZhbHVlOiBDKTogVCB8IEMge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgdmFsdWUgPSA8IFQgPiB0aGlzLl9qd3QuZGVjb2RlVG9rZW4oKVtjbGFpbV07XG4gICAgICAgICAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBkZWZhdWx0VmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHJldHVybiBkZWZhdWx0VmFsdWU7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCJpbXBvcnQge1xuICAgIEluamVjdGlvblRva2VuXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEF1dGhDb25maWcge1xuICAgIGFhcFVSTDogc3RyaW5nO1xuICAgIHRva2VuR2V0dGVyOiAoKSA9PiBzdHJpbmc7XG4gICAgdG9rZW5SZW1vdmVyPzogKCkgPT4gdm9pZDtcbiAgICB0b2tlblVwZGF0ZXI6IChuZXdUb2tlbjogYW55KSA9PiB2b2lkO1xufVxuXG5leHBvcnQgY29uc3QgQUFQX0NPTkZJRyA9IG5ldyBJbmplY3Rpb25Ub2tlbiA8IEF1dGhDb25maWcgPiAoJ0FBUF9DT05GSUcnKTtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldFRva2VuKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdpZF90b2tlbicpIHx8ICcnO1xufVxuZXhwb3J0IGZ1bmN0aW9uIHJlbW92ZVRva2VuKCk6IHZvaWQge1xuICAgIHJldHVybiBsb2NhbFN0b3JhZ2UucmVtb3ZlSXRlbSgnaWRfdG9rZW4nKTtcbn1cbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVUb2tlbihuZXdUb2tlbjogc3RyaW5nKTogdm9pZCB7XG4gICAgcmV0dXJuIGxvY2FsU3RvcmFnZS5zZXRJdGVtKCdpZF90b2tlbicsIG5ld1Rva2VuKTtcbn1cbmV4cG9ydCBjb25zdCBERUZBVUxUX0NPTkY6IEF1dGhDb25maWcgPSB7XG4gICAgYWFwVVJMOiAnaHR0cHM6Ly9hcGkuYWFpLmViaS5hYy51aycsXG4gICAgdG9rZW5HZXR0ZXI6IGdldFRva2VuLFxuICAgIHRva2VuUmVtb3ZlcjogcmVtb3ZlVG9rZW4sXG4gICAgdG9rZW5VcGRhdGVyOiB1cGRhdGVUb2tlblxufTtcbiIsImltcG9ydCB7XG4gICAgSW5qZWN0YWJsZSxcbiAgICBJbmplY3QsXG4gICAgUmVuZGVyZXJGYWN0b3J5MixcbiAgICBSZW5kZXJlcjJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICAgIE9ic2VydmFibGVcbn0gZnJvbSAncnhqcyc7XG5pbXBvcnQge1xuICAgIEJlaGF2aW9yU3ViamVjdCxcbn0gZnJvbSAncnhqcyc7XG5pbXBvcnQge1xuICAgIG1hcFxufSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7XG4gICAgQUFQX0NPTkZJRyxcbiAgICBBdXRoQ29uZmlnXG59IGZyb20gJy4vYXV0aC5jb25maWcnO1xuaW1wb3J0IHtcbiAgICBUb2tlblNlcnZpY2Vcbn0gZnJvbSAnLi90b2tlbi5zZXJ2aWNlJztcblxuZXhwb3J0IGludGVyZmFjZSBMb2dpbk9wdGlvbnMge1xuICAgIFtrZXk6IHN0cmluZ106IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDcmVkZW50aWFscyB7XG4gICAgcmVhbG5hbWU6IHN0cmluZztcbiAgICB1c2VybmFtZTogc3RyaW5nO1xuICAgIHRva2VuOiBzdHJpbmc7XG59XG5cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBBdXRoU2VydmljZSB7XG5cbiAgICBwcml2YXRlIF9jcmVkZW50aWFscyA9IG5ldyBCZWhhdmlvclN1YmplY3QgPCBDcmVkZW50aWFscyB8IG51bGwgPiAobnVsbCk7XG5cbiAgICBwcml2YXRlIF9sb2dpbkNhbGxiYWNrczogRnVuY3Rpb25bXSA9IFtdO1xuICAgIHByaXZhdGUgX2xvZ291dENhbGxiYWNrczogRnVuY3Rpb25bXSA9IFtdO1xuXG4gICAgcHJpdmF0ZSBfdGltZW91dElEOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAgIC8vIENvbmZpZ3VyYXRpb25cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9kb21haW46IHN0cmluZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9hcHBVUkw6IHN0cmluZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9zdG9yYWdlVXBkYXRlcjogKG5ld1Rva2VuOiBhbnkpID0+IHZvaWQ7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfc3RvcmFnZVJlbW92ZXI6ICgpID0+IHZvaWQ7XG5cbiAgICAvLyBUaGlzIHR3byBwcm9wZXJ0aWVzIGFyZSB1c2VkIGZvciBpbnRlci13aW5kb3cgY29tbXVuY2F0aW9uLlxuICAgIC8vIEl0IGlzIGFjaGlldmUgdGhyb3VnaCB0aGUgdXBkYXRlIG9mIHRoZSBkdW1teSBrZXkgc3RvcmFnZSAnX2NvbW1LZXlOYW1lJ1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2NvbW1LZXlOYW1lID0gJ0FuZ3VsYXJBYXBBdXRoVXBkYXRlZCc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfY29tbUtleVVwZGF0ZXIgPSAoKSA9PiBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSh0aGlzLl9jb21tS2V5TmFtZSwgJycgKyBuZXcgRGF0ZSgpLmdldFRpbWUoKSk7XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBfcmVuZGVyZXJGYWN0b3J5OiBSZW5kZXJlckZhY3RvcnkyLFxuICAgICAgICBwcml2YXRlIF90b2tlblNlcnZpY2U6IFRva2VuU2VydmljZSxcbiAgICAgICAgQEluamVjdChBQVBfQ09ORklHKSBwcml2YXRlIGNvbmZpZzogQXV0aENvbmZpZ1xuICAgICkge1xuICAgICAgICB0aGlzLl9kb21haW4gPSBlbmNvZGVVUklDb21wb25lbnQod2luZG93LmxvY2F0aW9uLm9yaWdpbik7XG4gICAgICAgIHRoaXMuX2FwcFVSTCA9IGNvbmZpZy5hYXBVUkwucmVwbGFjZSgvXFwvJC8sICcnKTtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVVwZGF0ZXIgPSBjb25maWcudG9rZW5VcGRhdGVyO1xuICAgICAgICBpZiAoY29uZmlnLnRva2VuUmVtb3Zlcikge1xuICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIgPSBjb25maWcudG9rZW5SZW1vdmVyO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIgPSAoKSA9PiBjb25maWcudG9rZW5VcGRhdGVyKG51bGwpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcmVuZGVyZXIgPSB0aGlzLl9yZW5kZXJlckZhY3RvcnkuY3JlYXRlUmVuZGVyZXIobnVsbCwgbnVsbCk7XG4gICAgICAgIHRoaXMuX2xpc3RlbkxvZ2luTWVzc2FnZShyZW5kZXJlcik7XG4gICAgICAgIHRoaXMuX2xpc3RlbkNoYW5nZXNGcm9tT3RoZXJXaW5kb3dzKHJlbmRlcmVyKTtcblxuICAgICAgICB0aGlzLl91cGRhdGVDcmVkZW50aWFscygpOyAvLyBUT0RPOiBleHBlcmltZW50IHdpdGggc2V0VGltZU91dFxuICAgIH1cblxuICAgIHB1YmxpYyBpc0F1dGhlbnRpY2F0ZWQoKTogT2JzZXJ2YWJsZSA8IGJvb2xlYW4gPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gdHJ1ZSA6IGZhbHNlKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyBjcmVkZW50aWFscygpOiBPYnNlcnZhYmxlIDwgQ3JlZGVudGlhbHMgfCBudWxsID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHMuYXNPYnNlcnZhYmxlKCk7XG4gICAgfVxuXG4gICAgcHVibGljIHJlYWxuYW1lKCk6IE9ic2VydmFibGUgPCBzdHJpbmcgfCBudWxsID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHMuYXNPYnNlcnZhYmxlKCkucGlwZShcbiAgICAgICAgICAgIG1hcChjcmVkZW50aWFscyA9PiBjcmVkZW50aWFscyA/ICBjcmVkZW50aWFscy5yZWFsbmFtZSA6IG51bGwpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIHVzZXJuYW1lKCk6IE9ic2VydmFibGUgPCBzdHJpbmcgfCBudWxsID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHMuYXNPYnNlcnZhYmxlKCkucGlwZShcbiAgICAgICAgICAgIG1hcChjcmVkZW50aWFscyA9PiBjcmVkZW50aWFscyA/ICBjcmVkZW50aWFscy51c2VybmFtZSA6IG51bGwpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIHRva2VuKCk6IE9ic2VydmFibGUgPCBzdHJpbmcgfCBudWxsID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHMuYXNPYnNlcnZhYmxlKCkucGlwZShcbiAgICAgICAgICAgIG1hcChjcmVkZW50aWFscyA9PiBjcmVkZW50aWFscyA/ICBjcmVkZW50aWFscy50b2tlbiA6IG51bGwpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRnVuY3Rpb25zIHRoYXQgb3BlbnMgYSB3aW5kb3cgaW5zdGVhZCBvZiBhIHRhYi5cbiAgICAgKlxuICAgICAqIFNlZSBtZXRob2QgX2ZpbHRlckxvZ2luT3B0aW9ucyByZWdhcmRpbmcgc2VjdXJpdHkgcmlza3Mgb2YgY2VydGFpblxuICAgICAqIExvZ2luT3B0aW9ucy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKiBAcGFyYW0gd2lkdGggUGl4ZWwgd2lkdGggb2YgdGhlIGxvZ2luIHdpbmRvdy5cbiAgICAgKiBAcGFyYW0gaGVpZ2h0IFBpeGVsIGhlaWdodCBvZiB0aGUgbG9naW4gd2luZG93LlxuICAgICAqIEBwYXJhbSB0b3AgUG9zaXRpb24gb2YgdGhlIHRvcCBjb3JuZXJzLiBJZiBpdCBpcyBhIG5lZ2F0aXZlXG4gICAgICogICAgICAgICAgICAgbnVtYmVyIGl0IGNlbnRyZXMgdGhlIGxvZ2luIHdpbmRvdyBvbiB0aGUgc2NyZWVuLlxuICAgICAqIEBwYXJhbSBsZWZ0IFBvc2l0aW9uIG9mIHRoZSBsZWZ0IGNvcm5lcnMuIElmIGl0IGlzIGEgbmVnYXRpdmVcbiAgICAgKiAgICAgICAgICAgICBudW1iZXIgaXQgY2VudHJlcyB0aGUgbG9naW4gd2luZG93IG9uIHRoZSBzY3JlZW4uXG4gICAgICovXG4gICAgcHVibGljIHdpbmRvd09wZW4obG9naW5PcHRpb25zPzogTG9naW5PcHRpb25zLCB3aWR0aCA9IDY1MCwgaGVpZ2h0ID0gMTAwMCwgdG9wID0gLTEsIGxlZnQgPSAtMSkge1xuICAgICAgICBpZiAobGVmdCA8IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHNjcmVlbldpZHRoID0gc2NyZWVuLndpZHRoO1xuICAgICAgICAgICAgaWYgKHNjcmVlbldpZHRoID4gd2lkdGgpIHtcbiAgICAgICAgICAgICAgICBsZWZ0ID0gTWF0aC5yb3VuZChzY3JlZW5XaWR0aCAvIDIgLSB3aWR0aCAvIDIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0b3AgPCAwKSB7XG4gICAgICAgICAgICBjb25zdCBzY3JlZW5IZWlnaHQgPSBzY3JlZW4uaGVpZ2h0O1xuICAgICAgICAgICAgaWYgKHNjcmVlbkhlaWdodCA+IGhlaWdodCkge1xuICAgICAgICAgICAgICAgIHRvcCA9IE1hdGgucm91bmQoc2NyZWVuSGVpZ2h0IC8gMiAtIGhlaWdodCAvIDIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgd2luZG93T3B0aW9ucyA9IFtcbiAgICAgICAgICAgIGB3aWR0aD0ke3dpZHRofWAsXG4gICAgICAgICAgICBgaGVpZ2h0PSR7aGVpZ2h0fWAsXG4gICAgICAgICAgICBgbGVmdD0ke2xlZnR9YCxcbiAgICAgICAgICAgIGB0b3A9JHt0b3B9YCxcbiAgICAgICAgICAgICdwZXJzb25hbGJhcj1ubycsXG4gICAgICAgICAgICAndG9vbGJhcj1ubycsXG4gICAgICAgICAgICAnc2Nyb2xsYmFycz15ZXMnLFxuICAgICAgICAgICAgJ3Jlc2l6YWJsZT15ZXMnLFxuICAgICAgICAgICAgJ2RpcmVjdG9yaWVzPW5vJyxcbiAgICAgICAgICAgICdsb2NhdGlvbj1ubycsXG4gICAgICAgICAgICAnbWVudWJhcj1ubycsXG4gICAgICAgICAgICAndGl0bGViYXI9bm8nLFxuICAgICAgICAgICAgJ3Rvb2xiYXI9bm8nXG4gICAgICAgIF07XG5cbiAgICAgICAgY29uc3QgbG9naW5XaW5kb3cgPSB3aW5kb3cub3Blbih0aGlzLmdldFNTT1VSTChsb2dpbk9wdGlvbnMpLCAnU2lnbiBpbiB0byBFbGl4aXInLCB3aW5kb3dPcHRpb25zLmpvaW4oJywnKSk7XG4gICAgICAgIGlmIChsb2dpbldpbmRvdykge1xuICAgICAgICAgICAgbG9naW5XaW5kb3cuZm9jdXMoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZ1bmN0aW9ucyB0aGF0IG9wZW5zIGEgdGFiIChpbiBtb2Rlcm4gYnJvd3NlcikuXG4gICAgICpcbiAgICAgKiBTZWUgbWV0aG9kIF9maWx0ZXJMb2dpbk9wdGlvbnMgcmVnYXJkaW5nIHNlY3VyaXR5IHJpc2tzIG9mIGNlcnRhaW5cbiAgICAgKiBMb2dpbk9wdGlvbnMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbG9naW5PcHRpb25zIE9wdGlvbnMgcGFzc2VkIGFzIFVSTCBwYXJhbWV0ZXJzIHRvIHRoZSBTU08uXG4gICAgICovXG4gICAgcHVibGljIHRhYk9wZW4obG9naW5PcHRpb25zPzogTG9naW5PcHRpb25zKSB7XG4gICAgICAgIGNvbnN0IGxvZ2luV2luZG93ID0gd2luZG93Lm9wZW4odGhpcy5nZXRTU09VUkwobG9naW5PcHRpb25zKSwgJ1NpZ24gaW4gdG8gRWxpeGlyJyk7XG4gICAgICAgIGlmIChsb2dpbldpbmRvdykge1xuICAgICAgICAgICAgbG9naW5XaW5kb3cuZm9jdXMoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFByb2R1Y2VzIGEgVVJMIHRoYXQgYWxsb3dzIGxvZ2dpbmcgaW50byB0aGUgc2luZ2xlIHNpZ24gb24gKFNTTykgcGFnZS5cbiAgICAgKiBUaGUgVVJMIGNhbnMgYmUgb3BlbmVkIGluIGEgbmV3IHRhYiB1c2luZyB0YXJnZXQ9XCJfYmxhbmtcIixcbiAgICAgKiBvciBpbiBhIG5ldyB3aW5kb3cgdXNpbmcgd2luZG93Lm9wZW4oKS5cbiAgICAgKlxuICAgICAqIFNlZSBtZXRob2QgX2ZpbHRlckxvZ2luT3B0aW9ucyByZWdhcmRpbmcgc2VjdXJpdHkgcmlza3Mgb2YgY2VydGFpblxuICAgICAqIExvZ2luT3B0aW9ucy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSAgbG9naW5PcHRpb25zIE9wdGlvbnMgcGFzc2VkIGFzIFVSTCBwYXJhbWV0ZXJzIHRvIHRoZSBTU08uXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyBUaGUgU1NPIFVSTC5cbiAgICAgKlxuICAgICAqL1xuICAgICAgICBwdWJsaWMgZ2V0U1NPVVJMKG9wdGlvbnM/OiBMb2dpbk9wdGlvbnMpOiBzdHJpbmcge1xuICAgICAgICBsZXQgZXh0cmEgPSAnJztcbiAgICAgICAgaWYgKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHRoaXMuX2ZpbHRlckxvZ2luT3B0aW9ucyhvcHRpb25zKTtcbiAgICAgICAgICAgIGV4dHJhID0gT2JqZWN0LmtleXMob3B0aW9ucylcbiAgICAgICAgICAgICAgICAubWFwKGtleSA9PiBba2V5LCBvcHRpb25zW2tleV1dKVxuICAgICAgICAgICAgICAgIC5yZWR1Y2UoKGFjY3VtdWxhdG9yLCBrZXl2YWx1ZSkgPT4gYCR7YWNjdW11bGF0b3J9JiR7a2V5dmFsdWVbMF19PSR7a2V5dmFsdWVbMV19YCwgJycpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBgJHt0aGlzLl9hcHBVUkx9L3Nzbz9mcm9tPSR7dGhpcy5fZG9tYWlufSR7ZXh0cmF9YDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGaWx0ZXJzIG9wdGlvbnMgdGhhdCBhcmUgdW5zZWN1cmUuXG4gICAgICpcbiAgICAgKiBTZWUgdGhlIGFkdmFuY2Ugb3B0aW9ucyB0aGF0IGNhbiBiZSByZXF1ZXN0ZWQgdGhyb3VnaCB0aGUgb3B0aW9ucyBwYXJhbWV0ZXI6XG4gICAgICogaHR0cHM6Ly9hcGkuYWFpLmViaS5hYy51ay9kb2NzL2F1dGhlbnRpY2F0aW9uL2F1dGhlbnRpY2F0aW9uLmluZGV4Lmh0bWwjX2NvbW1vbl9hdHRyaWJ1dGVzXG4gICAgICpcbiAgICAgKiBUaGUgdGltZSB0byBsaXZlIHBhcmFtZW50ZXIgKHR0bCkgZGVmYXVsdCB2YWx1ZSBpcyA2MCBtaW51dGVzLiBJdCBpcyBhXG4gICAgICogYmlnIHNlY3VyaXR5IHJpc2sgdG8gcmVxdWVzdCBsb25nZXIgdHRsLiBJZiBhIHRoaXJkIHBhcnR5IGdldHMgaG9sZCBvZlxuICAgICAqIHN1Y2ggdG9rZW4sIG1lYW5zIHRoYXQgdGhleSBjb3VsZCB1c2UgaXQgZm9yIGEgZGF5LCB3ZWVrLCB5ZWFyXG4gICAgICogKGVzc2VudGlhbGx5LCBsaWtlIGhhdmluZyB0aGUgdXNlcm5hbWUvcGFzc3dvcmQpLlxuICAgICAqXG4gICAgICogQHBhcmFtICBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKlxuICAgICAqXG4gICAgICovXG4gICAgcHVibGljIF9maWx0ZXJMb2dpbk9wdGlvbnMob3B0aW9uczogTG9naW5PcHRpb25zKSB7XG4gICAgICAgIGlmIChPYmplY3Qua2V5cyhvcHRpb25zKS5pbmRleE9mKCd0dGwnKSA+IC0xKSB7XG4gICAgICAgICAgICBjb25zdCB0dGw6IG51bWJlciA9ICtvcHRpb25zWyd0dGwnXTtcbiAgICAgICAgICAgIGNvbnN0IHNvZnRMaW1pdCA9IDYwO1xuICAgICAgICAgICAgY29uc3QgaGFyZExpbWl0ID0gNjAgKiAyNDtcbiAgICAgICAgICAgIGlmICh0dGwgPiBoYXJkTGltaXQpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuY29uc29sZS5lcnJvcihgTG9naW4gcmVxdWVzdGVkIHdpdGggYW4gZXhwaXJhdGlvbiBsb25nZXIgdGhhbiAke2hhcmRMaW1pdH0gbWludXRlcyEgVGhpcyBpcyBub3QgYWxsb3dlZC5gKTtcbiAgICAgICAgICAgICAgICB3aW5kb3cuY29uc29sZS5lcnJvcihgRXhwaXJhdGlvbiByZXF1ZXN0IHJlc2V0IHRvICR7aGFyZExpbWl0fSBtaW51dGVzLmApO1xuICAgICAgICAgICAgICAgIG9wdGlvbnNbJ3R0bCddID0gJycgKyBoYXJkTGltaXQ7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHR0bCA+IHNvZnRMaW1pdCkge1xuICAgICAgICAgICAgICAgIHdpbmRvdy5jb25zb2xlLndhcm4oYExvZ2luIHJlcXVlc3RlZCB3aXRoIGFuIGV4cGlyYXRpb24gbG9uZ2VyIHRoYW4gJHtzb2Z0TGltaXR9IG1pbnV0ZXMhYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBsb2dzIG91dCB0aGUgdXNlci5cbiAgICAgKiBJdCB0cmlnZ2VycyB0aGUgbG9nb3V0IGNhbGxiYWNrcy5cbiAgICAgKiBJdCBpcyBhbiBhcnJvdyBmdW5jdGlvbiAobGFtYmRhKSBiZWNhdXNlIGluIHRoYXQgd2F5IGl0IGhhcyBhIHJlZmVyZW5jZVxuICAgICAqIHRvICd0aGlzJyB3aGVuIHVzZWQgaW4gc2V0VGltZW91dCBjYWxsLlxuICAgICAqL1xuICAgIHB1YmxpYyBsb2dPdXQoKSB7XG4gICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyKCk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZUNyZWRlbnRpYWxzKCk7XG5cbiAgICAgICAgLy8gVHJpZ2dlcnMgdXBkYXRpbmcgb3RoZXIgd2luZG93c1xuICAgICAgICB0aGlzLl9jb21tS2V5VXBkYXRlcigpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZCBhIGNhbGxiYWNrIHRvIHRoZSBMb2dJbiBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBjYWxsYmFjayBUaGUgRnVuY3Rpb24gY2FsbGVkIHdoZW4gdGhlIGxvZ2luIGV2ZW50IGlzIHRyaWdnZXJlZCBhbmQgdGhlXG4gICAgICogICAgSldUIHRva2VuIGlzIHJlY2VpdmVkIGFuZCBhY2NlcHRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIFRoZSBldmVudCByZWdpc3RyYXRpb24gaWQgKG5lY2Vzc2FyeSB0byB1bnJlZ2lzdGVyIHRoZSBldmVudCkuXG4gICAgICovXG4gICAgcHVibGljIGFkZExvZ0luRXZlbnRMaXN0ZW5lcihjYWxsYmFjazogRnVuY3Rpb24pOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fbG9naW5DYWxsYmFja3MucHVzaChjYWxsYmFjayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGEgY2FsbGJhY2sgZnJvbSB0aGUgTG9nSW4gZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gaWQgVGhlIGlkIGdpdmVuIHdoZW4gZXZlbnQgbGlzdGVuZXIgd2FzIGFkZGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgdHJ1ZSB3aGVuIHJlbW92ZSBzdWNjZXNzZnVsbHksIGZhbHNlIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgcmVtb3ZlTG9nSW5FdmVudExpc3RlbmVyKGlkOiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGRlbGV0ZSB0aGlzLl9sb2dpbkNhbGxiYWNrc1tpZCAtIDFdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZCBhIGNhbGxiYWNrIHRvIHRoZSBMb2dPdXQgZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gY2FsbGJhY2sgVGhlIEZ1bmN0aW9uIGNhbGxlZCB3aGVuIHRoZSBsb2dvdXQgZXZlbnQgaXMgdHJpZ2dlcmVkIGFuZCB0aGVcbiAgICAgKiAgICBKV1QgdG9rZW4gaXMgcmVjZWl2ZWQgYW5kIGFjY2VwdGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgVGhlIHJlZ2lzdHJhdGlvbiBpZCAobmVjZXNzYXJ5IHRvIHVucmVnaXN0ZXIgdGhlIGV2ZW50KS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYWRkTG9nT3V0RXZlbnRMaXN0ZW5lcihjYWxsYmFjazogRnVuY3Rpb24pOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fbG9nb3V0Q2FsbGJhY2tzLnB1c2goY2FsbGJhY2spO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIGNhbGxiYWNrIGZyb20gdGhlIExvZ091dCBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBpZCBUaGUgaWQgZ2l2ZW4gd2hlbiBldmVudCBsaXN0ZW5lciB3YXMgYWRkZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB0cnVlIHdoZW4gcmVtb3ZlIHN1Y2Nlc3NmdWxseSwgZmFsc2Ugb3RoZXJ3aXNlLlxuICAgICAqL1xuICAgIHB1YmxpYyByZW1vdmVMb2dPdXRFdmVudExpc3RlbmVyKGlkOiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGRlbGV0ZSB0aGlzLl9sb2dvdXRDYWxsYmFja3NbaWQgLSAxXTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMaXN0ZW4gZm9yIGxvZ2luIG1lc3NhZ2VzIGZyb20gb3RoZXIgd2luZG93cy5cbiAgICAgKiBUaGVzZSBtZXNzYWdlcyBjb250YWluIHRoZSB0b2tlbnMgZnJvbSB0aGUgQUFQLlxuICAgICAqIElmIGEgdG9rZW4gaXMgcmVjZWl2ZWQgdGhlbiB0aGUgY2FsbGJhY2tzIGFyZSB0cmlnZ2VyZWQuXG4gICAgICovXG4gICAgcHJpdmF0ZSBfbGlzdGVuTG9naW5NZXNzYWdlKHJlbmRlcmVyOiBSZW5kZXJlcjIpIHtcbiAgICAgICAgcmVuZGVyZXIubGlzdGVuKCd3aW5kb3cnLCAnbWVzc2FnZScsIChldmVudDogTWVzc2FnZUV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMubWVzc2FnZUlzQWNjZXB0YWJsZShldmVudCkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlVXBkYXRlcihldmVudC5kYXRhKTtcbiAgICAgICAgICAgIGV2ZW50LnNvdXJjZS5jbG9zZSgpO1xuICAgICAgICAgICAgdGhpcy5fdXBkYXRlQ3JlZGVudGlhbHMoKTtcblxuICAgICAgICAgICAgLy8gVHJpZ2dlcnMgdXBkYXRpbmcgb3RoZXIgd2luZG93c1xuICAgICAgICAgICAgdGhpcy5fY29tbUtleVVwZGF0ZXIoKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqIExpc3RlbiB0byBjaGFuZ2VzIGluIHRoZSB0b2tlbiBmcm9tICpvdGhlciogd2luZG93cy5cbiAgICAgKlxuICAgICAqIEZvciBpbnRlci13aW5kb3cgY29tbXVuaWNhdGlvbiBtZXNzYWdlcyBhcmUgdHJhbnNtaXR0ZWQgdHJvdWdoIGNoYW5nZXNcbiAgICAgKiBvbiBhIGR1bW15IHN0b3JhZ2Uga2V5IHByb3BlcnR5OiAnX2NvbW1LZXlOYW1lJy5cbiAgICAgKlxuICAgICAqIE5vdGljZSB0aGF0IGNoYW5nZXMgaW4gdGhlICdfY29tbUtleU5hbWUnIHByb2R1Y2VkIGJ5IHRoaXMgY2xhc3MgZG9lc24ndFxuICAgICAqIHRyaWdnZXIgdGhpcyBldmVudC5cbiAgICAgKi9cbiAgICBwcml2YXRlIF9saXN0ZW5DaGFuZ2VzRnJvbU90aGVyV2luZG93cyhyZW5kZXJlcjogUmVuZGVyZXIyKSB7XG4gICAgICAgIHJlbmRlcmVyLmxpc3Rlbignd2luZG93JywgJ3N0b3JhZ2UnLCAoZXZlbnQ6IFN0b3JhZ2VFdmVudCkgPT4ge1xuICAgICAgICAgICAgaWYgKGV2ZW50LmtleSA9PT0gdGhpcy5fY29tbUtleU5hbWUpIHtcbiAgICAgICAgICAgICAgICB0aGlzLl91cGRhdGVDcmVkZW50aWFscygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVjayBpZiB0aGUgbWVzc2FnZSBpcyBjb21pbmcgZnJvbSB0aGUgc2FtZSBkb21haW4gd2UgdXNlIHRvIGdlbmVyYXRlXG4gICAgICogdGhlIFNTTyBVUkwsIG90aGVyd2lzZSBpdCdzIGlmZnkgYW5kIHNob3VsZG4ndCB0cnVzdCBpdC5cbiAgICAgKi9cbiAgICBwcml2YXRlIG1lc3NhZ2VJc0FjY2VwdGFibGUoZXZlbnQ6IE1lc3NhZ2VFdmVudCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZXZlbnQub3JpZ2luID09PSB0aGlzLl9hcHBVUkw7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfdXBkYXRlQ3JlZGVudGlhbHMoKSB7XG4gICAgICAgIGNvbnN0IGlzQXV0aGVudGljYXRlZCA9IHRoaXMuX2xvZ2dlZEluKCk7XG5cbiAgICAgICAgaWYgKHRoaXMuX3RpbWVvdXRJRCkge1xuICAgICAgICAgICAgd2luZG93LmNsZWFyVGltZW91dCh0aGlzLl90aW1lb3V0SUQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGlzQXV0aGVudGljYXRlZCkge1xuICAgICAgICAgICAgdGhpcy5fY3JlZGVudGlhbHMubmV4dCh7XG4gICAgICAgICAgICAgICAgcmVhbG5hbWU6IDwgc3RyaW5nID4gdGhpcy5fZ2V0UmVhbE5hbWUoKSxcbiAgICAgICAgICAgICAgICB1c2VybmFtZTogPCBzdHJpbmcgPiB0aGlzLl9nZXRVc2VyTmFtZSgpLFxuICAgICAgICAgICAgICAgIHRva2VuOiA8IHN0cmluZyA+IHRoaXMuX2dldFRva2VuKClcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB0aGlzLl9sb2dpbkNhbGxiYWNrcy5tYXAoY2FsbGJhY2sgPT4gY2FsbGJhY2sgJiYgY2FsbGJhY2soKSk7XG5cbiAgICAgICAgICAgIC8vIFNjaGVkdWxlIGZ1dHVyZSBsb2dvdXQgZXZlbnQgYmFzZSBvbiB0b2tlbiBleHBpcmF0aW9uXG4gICAgICAgICAgICBjb25zdCBleHBpcmVEYXRlID0gPCBEYXRlID4gdGhpcy5fdG9rZW5TZXJ2aWNlLmdldFRva2VuRXhwaXJhdGlvbkRhdGUoKTtcbiAgICAgICAgICAgIC8vIENvZXJjaW5nIGRhdGVzIHRvIG51bWJlcnMgd2l0aCB0aGUgdW5hcnkgb3BlcmF0b3IgJysnXG4gICAgICAgICAgICBjb25zdCBkZWxheSA9ICtleHBpcmVEYXRlIC0gK25ldyBEYXRlKCk7XG4gICAgICAgICAgICB0aGlzLl90aW1lb3V0SUQgPSB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB0aGlzLmxvZ091dCgpLCBkZWxheSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlcigpOyAvLyBDbGVhbnVwIHBvc3NpYmxlIGxlZnQgYmVoaW5kIHRva2VuXG4gICAgICAgICAgICB0aGlzLl9jcmVkZW50aWFscy5uZXh0KG51bGwpO1xuICAgICAgICAgICAgdGhpcy5fbG9nb3V0Q2FsbGJhY2tzLm1hcChjYWxsYmFjayA9PiBjYWxsYmFjayAmJiBjYWxsYmFjaygpKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIHRoZXJlJ3MgYSB1c2VyIGxvZ2dpbmcgb24gYW5kIHdoZXRoZXIgdGhlIHRva2VuIGlzIHN0aWxsIHZhbGlkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgIFdoZXRoZXIgdGhlIHVzZXIgdXNlciBpcyBhdXRoZW50aWNhdGVkIG9yIG5vdC5cbiAgICAgKi9cbiAgICBwcml2YXRlIF9sb2dnZWRJbigpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Rva2VuU2VydmljZS5pc1Rva2VuVmFsaWQoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9nZXRUb2tlbigpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Rva2VuU2VydmljZS5nZXRUb2tlbigpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX2dldFVzZXJOYW1lKCk6IHN0cmluZyB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5TZXJ2aWNlLmdldENsYWltIDwgc3RyaW5nLCBudWxsID4gKCdlbWFpbCcsIG51bGwpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX2dldFJlYWxOYW1lKCk6IHN0cmluZyB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5TZXJ2aWNlLmdldENsYWltIDwgc3RyaW5nLCBudWxsID4gKCduYW1lJywgbnVsbCk7XG4gICAgfVxuXG59XG4iLCJpbXBvcnQge1xuICAgIE5nTW9kdWxlLFxuICAgIE9wdGlvbmFsLFxuICAgIFNraXBTZWxmLFxuICAgIE1vZHVsZVdpdGhQcm92aWRlcnMsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQge1xuICAgIEpXVF9PUFRJT05TLFxuICAgIEp3dEhlbHBlclNlcnZpY2Vcbn0gZnJvbSAnQGF1dGgwL2FuZ3VsYXItand0JztcbmltcG9ydCB7XG4gICAgVG9rZW5TZXJ2aWNlXG59IGZyb20gJy4vdG9rZW4uc2VydmljZSc7XG5pbXBvcnQge1xuICAgIEF1dGhDb25maWcsXG4gICAgQUFQX0NPTkZJRyxcbiAgICBERUZBVUxUX0NPTkZcbn0gZnJvbSAnLi9hdXRoLmNvbmZpZyc7XG5pbXBvcnQge1xuICAgIEF1dGhTZXJ2aWNlXG59IGZyb20gJy4vYXV0aC5zZXJ2aWNlJztcblxuQE5nTW9kdWxlKHt9KVxuZXhwb3J0IGNsYXNzIEF1dGhNb2R1bGUge1xuXG4gICAgY29uc3RydWN0b3IoQE9wdGlvbmFsKCkgQFNraXBTZWxmKCkgcGFyZW50TW9kdWxlOiBBdXRoTW9kdWxlKSB7XG4gICAgICAgIGlmIChwYXJlbnRNb2R1bGUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQXV0aE1vZHVsZSBpcyBhbHJlYWR5IGxvYWRlZC4gSXQgc2hvdWxkIG9ubHkgYmUgaW1wb3J0ZWQgaW4geW91ciBhcHBsaWNhdGlvblxcJ3MgbWFpbiBtb2R1bGUuJyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgZm9yUm9vdChvcHRpb25zPzogQXV0aENvbmZpZyk6IE1vZHVsZVdpdGhQcm92aWRlcnMge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgbmdNb2R1bGU6IEF1dGhNb2R1bGUsXG4gICAgICAgICAgICBwcm92aWRlcnM6IFtcbiAgICAgICAgICAgICAgICBUb2tlblNlcnZpY2UsXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBwcm92aWRlOiBBQVBfQ09ORklHLFxuICAgICAgICAgICAgICAgICAgICB1c2VWYWx1ZTogb3B0aW9ucyA/IG9wdGlvbnMgOiBERUZBVUxUX0NPTkZcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIEF1dGhTZXJ2aWNlXG4gICAgICAgICAgICBdXG4gICAgICAgIH07XG4gICAgfVxufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBOzs7Ozs7O0lBaUJJLHNCQUNZO1FBQUEsU0FBSSxHQUFKLElBQUk7S0FDWjs7OztJQUVHLCtCQUFROzs7O1FBQ1gsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDOzs7OztJQUc1Qiw2Q0FBc0I7Ozs7UUFDekIsSUFBSTtZQUNBLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1NBQzdDO1FBQUMsd0JBQU8sQ0FBQyxFQUFFO1lBQ1IsT0FBTyxJQUFJLENBQUM7U0FDZjs7Ozs7SUFHRSxtQ0FBWTs7OztRQUNmLElBQUk7WUFDQSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztTQUN0QztRQUFDLHdCQUFPLEtBQUssRUFBRTtZQUNaLE9BQU8sS0FBSyxDQUFDO1NBQ2hCOzs7Ozs7Ozs7OztJQVdFLCtCQUFROzs7Ozs7Ozs7Y0FBVyxLQUFhLEVBQUUsWUFBZTtRQUNwRCxJQUFJO1lBQ0EscUJBQU0sS0FBSyxxQkFBUyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFBLENBQUM7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