angular-aap-auth
Version:
## This library is deprecated. Please, use [ng-ebi-authorization](https://www.npmjs.com/package/ng-ebi-authorization).
698 lines • 59.4 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@auth0/angular-jwt'), require('rxjs'), require('rxjs/operators')) :
typeof define === 'function' && define.amd ? define('angular-aap-auth', ['exports', '@angular/core', '@auth0/angular-jwt', 'rxjs', 'rxjs/operators'], factory) :
(factory((global['angular-aap-auth'] = {}),global.ng.core,global.angularJwt,global.RxJS,global.rxjs.operators));
}(this, (function (exports,core,angularJwt,rxjs,operators) { 'use strict';
/**
* @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 = (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 (e) {
return null;
}
};
/**
* @return {?}
*/
TokenService.prototype.isTokenValid = /**
* @return {?}
*/
function () {
try {
return !this._jwt.isTokenExpired();
}
catch (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 = (this._jwt.decodeToken()[claim]);
if (value === undefined) {
return defaultValue;
}
return value;
}
catch (e) {
return defaultValue;
}
};
TokenService.decorators = [
{ type: core.Injectable },
];
/** @nocollapse */
TokenService.ctorParameters = function () {
return [
{ type: angularJwt.JwtHelperService }
];
};
return TokenService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var /** @type {?} */ AAP_CONFIG = new core.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 = (function () {
function AuthService(_rendererFactory, _tokenService, config) {
var _this = this;
this._rendererFactory = _rendererFactory;
this._tokenService = _tokenService;
this.config = config;
this._credentials = new rxjs.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(operators.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(operators.map(function (credentials) { return credentials ? credentials.realname : null; }));
};
/**
* @return {?}
*/
AuthService.prototype.username = /**
* @return {?}
*/
function () {
return this._credentials.asObservable().pipe(operators.map(function (credentials) { return credentials ? credentials.username : null; }));
};
/**
* @return {?}
*/
AuthService.prototype.token = /**
* @return {?}
*/
function () {
return this._credentials.asObservable().pipe(operators.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 = (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: core.Injectable },
];
/** @nocollapse */
AuthService.ctorParameters = function () {
return [
{ type: core.RendererFactory2 },
{ type: TokenService },
{ type: undefined, decorators: [{ type: core.Inject, args: [AAP_CONFIG,] }] }
];
};
return AuthService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var AuthModule = (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: core.NgModule, args: [{},] },
];
/** @nocollapse */
AuthModule.ctorParameters = function () {
return [
{ type: AuthModule, decorators: [{ type: core.Optional }, { type: core.SkipSelf }] }
];
};
return AuthModule;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
exports.AuthModule = AuthModule;
exports.AuthService = AuthService;
exports.TokenService = TokenService;
exports.ɵb = AAP_CONFIG;
exports.ɵf = DEFAULT_CONF;
exports.ɵc = getToken;
exports.ɵd = removeToken;
exports.ɵe = updateToken;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5ndWxhci1hYXAtYXV0aC51bWQuanMubWFwIiwic291cmNlcyI6WyJuZzovL2FuZ3VsYXItYWFwLWF1dGgvYXBwL21vZHVsZXMvYXV0aC90b2tlbi5zZXJ2aWNlLnRzIiwibmc6Ly9hbmd1bGFyLWFhcC1hdXRoL2FwcC9tb2R1bGVzL2F1dGgvYXV0aC5jb25maWcudHMiLCJuZzovL2FuZ3VsYXItYWFwLWF1dGgvYXBwL21vZHVsZXMvYXV0aC9hdXRoLnNlcnZpY2UudHMiLCJuZzovL2FuZ3VsYXItYWFwLWF1dGgvYXBwL21vZHVsZXMvYXV0aC9hdXRoLm1vZHVsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICAgIEluamVjdGFibGVcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7XG4gICAgSnd0SGVscGVyU2VydmljZVxufSBmcm9tICdAYXV0aDAvYW5ndWxhci1qd3QnO1xuXG4vKipcbiAqIFRoZSBwdXJwb3NlIG9mIHRoaXMgdmVyeSBzaW1wbGUgc2VydmljZSBpcyB0byBpbnRlcmZhY2UgYmV0d2VlbiB0aGVcbiAqIEF1dGhTZXJ2aWNlIGFuZCB0aGUgc3BlY2lmaWMgdG9rZW4gbWFuaXB1bGF0aW9uIHJvdXRpbmcgb2YgSnd0SGVscGVyU2VydmljZS5cbiAqIEluIHRoaXMgd2F5LCBpZiBpbiB0aGUgZnV0dXJlIHdlIHdhbnQgdG8gcmVwbGFjZSBKd3RIZWxwZXJTZXJ2aWNlIGJ5XG4gKiBhbm90aGVyIHNlcnZpY2UsIEF1dGhTZXJ2aWNlIGRvZXNuJ3QgbmVlZCB0byBiZSBtb2RpZmllZCwgb25seSB0aGlzIHNlcnZpY2UuXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBUb2tlblNlcnZpY2Uge1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgX2p3dDogSnd0SGVscGVyU2VydmljZVxuICAgICkge31cblxuICAgIHB1YmxpYyBnZXRUb2tlbigpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2p3dC50b2tlbkdldHRlcigpO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRUb2tlbkV4cGlyYXRpb25EYXRlKCk6IERhdGUgfCBudWxsIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl9qd3QuZ2V0VG9rZW5FeHBpcmF0aW9uRGF0ZSgpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBpc1Rva2VuVmFsaWQoKTogYm9vbGVhbiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICByZXR1cm4gIXRoaXMuX2p3dC5pc1Rva2VuRXhwaXJlZCgpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IGNsYWltcyBmcm9tIHRoZSB0b2tlbi5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBjbGFpbSBUaGUgbmFtZSBvZiB0aGUgY2xhaW1cbiAgICAgKiBAcGFyYW0gZGVmYXVsdFZhbHVlIFRoZSBkZWZhdWx0IHZhbHVlIHJldHVybmVkIGluIGNhc2Ugb2YgZXJyb3JcbiAgICAgKlxuICAgICAqIEByZXR1cm5zIGNsYWltIG9yIGRlZmF1bHQgdmFsdWVcbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0Q2xhaW0gPCBULCBDID4gKGNsYWltOiBzdHJpbmcsIGRlZmF1bHRWYWx1ZTogQyk6IFQgfCBDIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gPCBUID4gdGhpcy5fand0LmRlY29kZVRva2VuKClbY2xhaW1dO1xuICAgICAgICAgICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwiaW1wb3J0IHtcbiAgICBJbmplY3Rpb25Ub2tlblxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuZXhwb3J0IGludGVyZmFjZSBBdXRoQ29uZmlnIHtcbiAgICBhYXBVUkw6IHN0cmluZztcbiAgICB0b2tlbkdldHRlcjogKCkgPT4gc3RyaW5nO1xuICAgIHRva2VuUmVtb3Zlcj86ICgpID0+IHZvaWQ7XG4gICAgdG9rZW5VcGRhdGVyOiAobmV3VG9rZW46IGFueSkgPT4gdm9pZDtcbn1cblxuZXhwb3J0IGNvbnN0IEFBUF9DT05GSUcgPSBuZXcgSW5qZWN0aW9uVG9rZW4gPCBBdXRoQ29uZmlnID4gKCdBQVBfQ09ORklHJyk7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUb2tlbigpOiBzdHJpbmcge1xuICAgIHJldHVybiBsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnaWRfdG9rZW4nKSB8fCAnJztcbn1cbmV4cG9ydCBmdW5jdGlvbiByZW1vdmVUb2tlbigpOiB2b2lkIHtcbiAgICByZXR1cm4gbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oJ2lkX3Rva2VuJyk7XG59XG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlVG9rZW4obmV3VG9rZW46IHN0cmluZyk6IHZvaWQge1xuICAgIHJldHVybiBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnaWRfdG9rZW4nLCBuZXdUb2tlbik7XG59XG5leHBvcnQgY29uc3QgREVGQVVMVF9DT05GOiBBdXRoQ29uZmlnID0ge1xuICAgIGFhcFVSTDogJ2h0dHBzOi8vYXBpLmFhaS5lYmkuYWMudWsnLFxuICAgIHRva2VuR2V0dGVyOiBnZXRUb2tlbixcbiAgICB0b2tlblJlbW92ZXI6IHJlbW92ZVRva2VuLFxuICAgIHRva2VuVXBkYXRlcjogdXBkYXRlVG9rZW5cbn07XG4iLCJpbXBvcnQge1xuICAgIEluamVjdGFibGUsXG4gICAgSW5qZWN0LFxuICAgIFJlbmRlcmVyRmFjdG9yeTIsXG4gICAgUmVuZGVyZXIyXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgICBPYnNlcnZhYmxlXG59IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtcbiAgICBCZWhhdmlvclN1YmplY3QsXG59IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtcbiAgICBtYXBcbn0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQge1xuICAgIEFBUF9DT05GSUcsXG4gICAgQXV0aENvbmZpZ1xufSBmcm9tICcuL2F1dGguY29uZmlnJztcbmltcG9ydCB7XG4gICAgVG9rZW5TZXJ2aWNlXG59IGZyb20gJy4vdG9rZW4uc2VydmljZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTG9naW5PcHRpb25zIHtcbiAgICBba2V5OiBzdHJpbmddOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ3JlZGVudGlhbHMge1xuICAgIHJlYWxuYW1lOiBzdHJpbmc7XG4gICAgdXNlcm5hbWU6IHN0cmluZztcbiAgICB0b2tlbjogc3RyaW5nO1xufVxuXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgQXV0aFNlcnZpY2Uge1xuXG4gICAgcHJpdmF0ZSBfY3JlZGVudGlhbHMgPSBuZXcgQmVoYXZpb3JTdWJqZWN0IDwgQ3JlZGVudGlhbHMgfCBudWxsID4gKG51bGwpO1xuXG4gICAgcHJpdmF0ZSBfbG9naW5DYWxsYmFja3M6IEZ1bmN0aW9uW10gPSBbXTtcbiAgICBwcml2YXRlIF9sb2dvdXRDYWxsYmFja3M6IEZ1bmN0aW9uW10gPSBbXTtcblxuICAgIHByaXZhdGUgX3RpbWVvdXRJRDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG5cbiAgICAvLyBDb25maWd1cmF0aW9uXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZG9tYWluOiBzdHJpbmc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfYXBwVVJMOiBzdHJpbmc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfc3RvcmFnZVVwZGF0ZXI6IChuZXdUb2tlbjogYW55KSA9PiB2b2lkO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3N0b3JhZ2VSZW1vdmVyOiAoKSA9PiB2b2lkO1xuXG4gICAgLy8gVGhpcyB0d28gcHJvcGVydGllcyBhcmUgdXNlZCBmb3IgaW50ZXItd2luZG93IGNvbW11bmNhdGlvbi5cbiAgICAvLyBJdCBpcyBhY2hpZXZlIHRocm91Z2ggdGhlIHVwZGF0ZSBvZiB0aGUgZHVtbXkga2V5IHN0b3JhZ2UgJ19jb21tS2V5TmFtZSdcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jb21tS2V5TmFtZSA9ICdBbmd1bGFyQWFwQXV0aFVwZGF0ZWQnO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2NvbW1LZXlVcGRhdGVyID0gKCkgPT4gbG9jYWxTdG9yYWdlLnNldEl0ZW0odGhpcy5fY29tbUtleU5hbWUsICcnICsgbmV3IERhdGUoKS5nZXRUaW1lKCkpO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgX3JlbmRlcmVyRmFjdG9yeTogUmVuZGVyZXJGYWN0b3J5MixcbiAgICAgICAgcHJpdmF0ZSBfdG9rZW5TZXJ2aWNlOiBUb2tlblNlcnZpY2UsXG4gICAgICAgIEBJbmplY3QoQUFQX0NPTkZJRykgcHJpdmF0ZSBjb25maWc6IEF1dGhDb25maWdcbiAgICApIHtcbiAgICAgICAgdGhpcy5fZG9tYWluID0gZW5jb2RlVVJJQ29tcG9uZW50KHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4pO1xuICAgICAgICB0aGlzLl9hcHBVUkwgPSBjb25maWcuYWFwVVJMLnJlcGxhY2UoL1xcLyQvLCAnJyk7XG4gICAgICAgIHRoaXMuX3N0b3JhZ2VVcGRhdGVyID0gY29uZmlnLnRva2VuVXBkYXRlcjtcbiAgICAgICAgaWYgKGNvbmZpZy50b2tlblJlbW92ZXIpIHtcbiAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyID0gY29uZmlnLnRva2VuUmVtb3ZlcjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyID0gKCkgPT4gY29uZmlnLnRva2VuVXBkYXRlcihudWxsKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHJlbmRlcmVyID0gdGhpcy5fcmVuZGVyZXJGYWN0b3J5LmNyZWF0ZVJlbmRlcmVyKG51bGwsIG51bGwpO1xuICAgICAgICB0aGlzLl9saXN0ZW5Mb2dpbk1lc3NhZ2UocmVuZGVyZXIpO1xuICAgICAgICB0aGlzLl9saXN0ZW5DaGFuZ2VzRnJvbU90aGVyV2luZG93cyhyZW5kZXJlcik7XG5cbiAgICAgICAgdGhpcy5fdXBkYXRlQ3JlZGVudGlhbHMoKTsgLy8gVE9ETzogZXhwZXJpbWVudCB3aXRoIHNldFRpbWVPdXRcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNBdXRoZW50aWNhdGVkKCk6IE9ic2VydmFibGUgPCBib29sZWFuID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHMuYXNPYnNlcnZhYmxlKCkucGlwZShcbiAgICAgICAgICAgIG1hcChjcmVkZW50aWFscyA9PiBjcmVkZW50aWFscyA/IHRydWUgOiBmYWxzZSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY3JlZGVudGlhbHMoKTogT2JzZXJ2YWJsZSA8IENyZWRlbnRpYWxzIHwgbnVsbCA+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzLmFzT2JzZXJ2YWJsZSgpO1xuICAgIH1cblxuICAgIHB1YmxpYyByZWFsbmFtZSgpOiBPYnNlcnZhYmxlIDwgc3RyaW5nIHwgbnVsbCA+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICAgICAgICBtYXAoY3JlZGVudGlhbHMgPT4gY3JlZGVudGlhbHMgPyAgY3JlZGVudGlhbHMucmVhbG5hbWUgOiBudWxsKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyB1c2VybmFtZSgpOiBPYnNlcnZhYmxlIDwgc3RyaW5nIHwgbnVsbCA+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICAgICAgICBtYXAoY3JlZGVudGlhbHMgPT4gY3JlZGVudGlhbHMgPyAgY3JlZGVudGlhbHMudXNlcm5hbWUgOiBudWxsKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyB0b2tlbigpOiBPYnNlcnZhYmxlIDwgc3RyaW5nIHwgbnVsbCA+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICAgICAgICBtYXAoY3JlZGVudGlhbHMgPT4gY3JlZGVudGlhbHMgPyAgY3JlZGVudGlhbHMudG9rZW4gOiBudWxsKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZ1bmN0aW9ucyB0aGF0IG9wZW5zIGEgd2luZG93IGluc3RlYWQgb2YgYSB0YWIuXG4gICAgICpcbiAgICAgKiBTZWUgbWV0aG9kIF9maWx0ZXJMb2dpbk9wdGlvbnMgcmVnYXJkaW5nIHNlY3VyaXR5IHJpc2tzIG9mIGNlcnRhaW5cbiAgICAgKiBMb2dpbk9wdGlvbnMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbG9naW5PcHRpb25zIE9wdGlvbnMgcGFzc2VkIGFzIFVSTCBwYXJhbWV0ZXJzIHRvIHRoZSBTU08uXG4gICAgICogQHBhcmFtIHdpZHRoIFBpeGVsIHdpZHRoIG9mIHRoZSBsb2dpbiB3aW5kb3cuXG4gICAgICogQHBhcmFtIGhlaWdodCBQaXhlbCBoZWlnaHQgb2YgdGhlIGxvZ2luIHdpbmRvdy5cbiAgICAgKiBAcGFyYW0gdG9wIFBvc2l0aW9uIG9mIHRoZSB0b3AgY29ybmVycy4gSWYgaXQgaXMgYSBuZWdhdGl2ZVxuICAgICAqICAgICAgICAgICAgIG51bWJlciBpdCBjZW50cmVzIHRoZSBsb2dpbiB3aW5kb3cgb24gdGhlIHNjcmVlbi5cbiAgICAgKiBAcGFyYW0gbGVmdCBQb3NpdGlvbiBvZiB0aGUgbGVmdCBjb3JuZXJzLiBJZiBpdCBpcyBhIG5lZ2F0aXZlXG4gICAgICogICAgICAgICAgICAgbnVtYmVyIGl0IGNlbnRyZXMgdGhlIGxvZ2luIHdpbmRvdyBvbiB0aGUgc2NyZWVuLlxuICAgICAqL1xuICAgIHB1YmxpYyB3aW5kb3dPcGVuKGxvZ2luT3B0aW9ucz86IExvZ2luT3B0aW9ucywgd2lkdGggPSA2NTAsIGhlaWdodCA9IDEwMDAsIHRvcCA9IC0xLCBsZWZ0ID0gLTEpIHtcbiAgICAgICAgaWYgKGxlZnQgPCAwKSB7XG4gICAgICAgICAgICBjb25zdCBzY3JlZW5XaWR0aCA9IHNjcmVlbi53aWR0aDtcbiAgICAgICAgICAgIGlmIChzY3JlZW5XaWR0aCA+IHdpZHRoKSB7XG4gICAgICAgICAgICAgICAgbGVmdCA9IE1hdGgucm91bmQoc2NyZWVuV2lkdGggLyAyIC0gd2lkdGggLyAyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAodG9wIDwgMCkge1xuICAgICAgICAgICAgY29uc3Qgc2NyZWVuSGVpZ2h0ID0gc2NyZWVuLmhlaWdodDtcbiAgICAgICAgICAgIGlmIChzY3JlZW5IZWlnaHQgPiBoZWlnaHQpIHtcbiAgICAgICAgICAgICAgICB0b3AgPSBNYXRoLnJvdW5kKHNjcmVlbkhlaWdodCAvIDIgLSBoZWlnaHQgLyAyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHdpbmRvd09wdGlvbnMgPSBbXG4gICAgICAgICAgICBgd2lkdGg9JHt3aWR0aH1gLFxuICAgICAgICAgICAgYGhlaWdodD0ke2hlaWdodH1gLFxuICAgICAgICAgICAgYGxlZnQ9JHtsZWZ0fWAsXG4gICAgICAgICAgICBgdG9wPSR7dG9wfWAsXG4gICAgICAgICAgICAncGVyc29uYWxiYXI9bm8nLFxuICAgICAgICAgICAgJ3Rvb2xiYXI9bm8nLFxuICAgICAgICAgICAgJ3Njcm9sbGJhcnM9eWVzJyxcbiAgICAgICAgICAgICdyZXNpemFibGU9eWVzJyxcbiAgICAgICAgICAgICdkaXJlY3Rvcmllcz1ubycsXG4gICAgICAgICAgICAnbG9jYXRpb249bm8nLFxuICAgICAgICAgICAgJ21lbnViYXI9bm8nLFxuICAgICAgICAgICAgJ3RpdGxlYmFyPW5vJyxcbiAgICAgICAgICAgICd0b29sYmFyPW5vJ1xuICAgICAgICBdO1xuXG4gICAgICAgIGNvbnN0IGxvZ2luV2luZG93ID0gd2luZG93Lm9wZW4odGhpcy5nZXRTU09VUkwobG9naW5PcHRpb25zKSwgJ1NpZ24gaW4gdG8gRWxpeGlyJywgd2luZG93T3B0aW9ucy5qb2luKCcsJykpO1xuICAgICAgICBpZiAobG9naW5XaW5kb3cpIHtcbiAgICAgICAgICAgIGxvZ2luV2luZG93LmZvY3VzKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBvcGVucyBhIHRhYiAoaW4gbW9kZXJuIGJyb3dzZXIpLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqL1xuICAgIHB1YmxpYyB0YWJPcGVuKGxvZ2luT3B0aW9ucz86IExvZ2luT3B0aW9ucykge1xuICAgICAgICBjb25zdCBsb2dpbldpbmRvdyA9IHdpbmRvdy5vcGVuKHRoaXMuZ2V0U1NPVVJMKGxvZ2luT3B0aW9ucyksICdTaWduIGluIHRvIEVsaXhpcicpO1xuICAgICAgICBpZiAobG9naW5XaW5kb3cpIHtcbiAgICAgICAgICAgIGxvZ2luV2luZG93LmZvY3VzKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBQcm9kdWNlcyBhIFVSTCB0aGF0IGFsbG93cyBsb2dnaW5nIGludG8gdGhlIHNpbmdsZSBzaWduIG9uIChTU08pIHBhZ2UuXG4gICAgICogVGhlIFVSTCBjYW5zIGJlIG9wZW5lZCBpbiBhIG5ldyB0YWIgdXNpbmcgdGFyZ2V0PVwiX2JsYW5rXCIsXG4gICAgICogb3IgaW4gYSBuZXcgd2luZG93IHVzaW5nIHdpbmRvdy5vcGVuKCkuXG4gICAgICpcbiAgICAgKiBTZWUgbWV0aG9kIF9maWx0ZXJMb2dpbk9wdGlvbnMgcmVnYXJkaW5nIHNlY3VyaXR5IHJpc2tzIG9mIGNlcnRhaW5cbiAgICAgKiBMb2dpbk9wdGlvbnMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqXG4gICAgICogQHJldHVybnMgVGhlIFNTTyBVUkwuXG4gICAgICpcbiAgICAgKi9cbiAgICAgICAgcHVibGljIGdldFNTT1VSTChvcHRpb25zPzogTG9naW5PcHRpb25zKTogc3RyaW5nIHtcbiAgICAgICAgbGV0IGV4dHJhID0gJyc7XG4gICAgICAgIGlmIChvcHRpb25zKSB7XG4gICAgICAgICAgICB0aGlzLl9maWx0ZXJMb2dpbk9wdGlvbnMob3B0aW9ucyk7XG4gICAgICAgICAgICBleHRyYSA9IE9iamVjdC5rZXlzKG9wdGlvbnMpXG4gICAgICAgICAgICAgICAgLm1hcChrZXkgPT4gW2tleSwgb3B0aW9uc1trZXldXSlcbiAgICAgICAgICAgICAgICAucmVkdWNlKChhY2N1bXVsYXRvciwga2V5dmFsdWUpID0+IGAke2FjY3VtdWxhdG9yfSYke2tleXZhbHVlWzBdfT0ke2tleXZhbHVlWzFdfWAsICcnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYCR7dGhpcy5fYXBwVVJMfS9zc28/ZnJvbT0ke3RoaXMuX2RvbWFpbn0ke2V4dHJhfWA7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRmlsdGVycyBvcHRpb25zIHRoYXQgYXJlIHVuc2VjdXJlLlxuICAgICAqXG4gICAgICogU2VlIHRoZSBhZHZhbmNlIG9wdGlvbnMgdGhhdCBjYW4gYmUgcmVxdWVzdGVkIHRocm91Z2ggdGhlIG9wdGlvbnMgcGFyYW1ldGVyOlxuICAgICAqIGh0dHBzOi8vYXBpLmFhaS5lYmkuYWMudWsvZG9jcy9hdXRoZW50aWNhdGlvbi9hdXRoZW50aWNhdGlvbi5pbmRleC5odG1sI19jb21tb25fYXR0cmlidXRlc1xuICAgICAqXG4gICAgICogVGhlIHRpbWUgdG8gbGl2ZSBwYXJhbWVudGVyICh0dGwpIGRlZmF1bHQgdmFsdWUgaXMgNjAgbWludXRlcy4gSXQgaXMgYVxuICAgICAqIGJpZyBzZWN1cml0eSByaXNrIHRvIHJlcXVlc3QgbG9uZ2VyIHR0bC4gSWYgYSB0aGlyZCBwYXJ0eSBnZXRzIGhvbGQgb2ZcbiAgICAgKiBzdWNoIHRva2VuLCBtZWFucyB0aGF0IHRoZXkgY291bGQgdXNlIGl0IGZvciBhIGRheSwgd2VlaywgeWVhclxuICAgICAqIChlc3NlbnRpYWxseSwgbGlrZSBoYXZpbmcgdGhlIHVzZXJuYW1lL3Bhc3N3b3JkKS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSAgbG9naW5PcHRpb25zIE9wdGlvbnMgcGFzc2VkIGFzIFVSTCBwYXJhbWV0ZXJzIHRvIHRoZSBTU08uXG4gICAgICpcbiAgICAgKlxuICAgICAqL1xuICAgIHB1YmxpYyBfZmlsdGVyTG9naW5PcHRpb25zKG9wdGlvbnM6IExvZ2luT3B0aW9ucykge1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMob3B0aW9ucykuaW5kZXhPZigndHRsJykgPiAtMSkge1xuICAgICAgICAgICAgY29uc3QgdHRsOiBudW1iZXIgPSArb3B0aW9uc1sndHRsJ107XG4gICAgICAgICAgICBjb25zdCBzb2Z0TGltaXQgPSA2MDtcbiAgICAgICAgICAgIGNvbnN0IGhhcmRMaW1pdCA9IDYwICogMjQ7XG4gICAgICAgICAgICBpZiAodHRsID4gaGFyZExpbWl0KSB7XG4gICAgICAgICAgICAgICAgd2luZG93LmNvbnNvbGUuZXJyb3IoYExvZ2luIHJlcXVlc3RlZCB3aXRoIGFuIGV4cGlyYXRpb24gbG9uZ2VyIHRoYW4gJHtoYXJkTGltaXR9IG1pbnV0ZXMhIFRoaXMgaXMgbm90IGFsbG93ZWQuYCk7XG4gICAgICAgICAgICAgICAgd2luZG93LmNvbnNvbGUuZXJyb3IoYEV4cGlyYXRpb24gcmVxdWVzdCByZXNldCB0byAke2hhcmRMaW1pdH0gbWludXRlcy5gKTtcbiAgICAgICAgICAgICAgICBvcHRpb25zWyd0dGwnXSA9ICcnICsgaGFyZExpbWl0O1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0dGwgPiBzb2Z0TGltaXQpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuY29uc29sZS53YXJuKGBMb2dpbiByZXF1ZXN0ZWQgd2l0aCBhbiBleHBpcmF0aW9uIGxvbmdlciB0aGFuICR7c29mdExpbWl0fSBtaW51dGVzIWApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRnVuY3Rpb25zIHRoYXQgbG9ncyBvdXQgdGhlIHVzZXIuXG4gICAgICogSXQgdHJpZ2dlcnMgdGhlIGxvZ291dCBjYWxsYmFja3MuXG4gICAgICogSXQgaXMgYW4gYXJyb3cgZnVuY3Rpb24gKGxhbWJkYSkgYmVjYXVzZSBpbiB0aGF0IHdheSBpdCBoYXMgYSByZWZlcmVuY2VcbiAgICAgKiB0byAndGhpcycgd2hlbiB1c2VkIGluIHNldFRpbWVvdXQgY2FsbC5cbiAgICAgKi9cbiAgICBwdWJsaWMgbG9nT3V0KCkge1xuICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlcigpO1xuICAgICAgICB0aGlzLl91cGRhdGVDcmVkZW50aWFscygpO1xuXG4gICAgICAgIC8vIFRyaWdnZXJzIHVwZGF0aW5nIG90aGVyIHdpbmRvd3NcbiAgICAgICAgdGhpcy5fY29tbUtleVVwZGF0ZXIoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgYSBjYWxsYmFjayB0byB0aGUgTG9nSW4gZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gY2FsbGJhY2sgVGhlIEZ1bmN0aW9uIGNhbGxlZCB3aGVuIHRoZSBsb2dpbiBldmVudCBpcyB0cmlnZ2VyZWQgYW5kIHRoZVxuICAgICAqICAgIEpXVCB0b2tlbiBpcyByZWNlaXZlZCBhbmQgYWNjZXB0ZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyBUaGUgZXZlbnQgcmVnaXN0cmF0aW9uIGlkIChuZWNlc3NhcnkgdG8gdW5yZWdpc3RlciB0aGUgZXZlbnQpLlxuICAgICAqL1xuICAgIHB1YmxpYyBhZGRMb2dJbkV2ZW50TGlzdGVuZXIoY2FsbGJhY2s6IEZ1bmN0aW9uKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvZ2luQ2FsbGJhY2tzLnB1c2goY2FsbGJhY2spO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIGNhbGxiYWNrIGZyb20gdGhlIExvZ0luIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIFRoZSBpZCBnaXZlbiB3aGVuIGV2ZW50IGxpc3RlbmVyIHdhcyBhZGRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHRydWUgd2hlbiByZW1vdmUgc3VjY2Vzc2Z1bGx5LCBmYWxzZSBvdGhlcndpc2UuXG4gICAgICovXG4gICAgcHVibGljIHJlbW92ZUxvZ0luRXZlbnRMaXN0ZW5lcihpZDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBkZWxldGUgdGhpcy5fbG9naW5DYWxsYmFja3NbaWQgLSAxXTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgYSBjYWxsYmFjayB0byB0aGUgTG9nT3V0IGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGNhbGxiYWNrIFRoZSBGdW5jdGlvbiBjYWxsZWQgd2hlbiB0aGUgbG9nb3V0IGV2ZW50IGlzIHRyaWdnZXJlZCBhbmQgdGhlXG4gICAgICogICAgSldUIHRva2VuIGlzIHJlY2VpdmVkIGFuZCBhY2NlcHRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIFRoZSByZWdpc3RyYXRpb24gaWQgKG5lY2Vzc2FyeSB0byB1bnJlZ2lzdGVyIHRoZSBldmVudCkuXG4gICAgICovXG4gICAgcHVibGljIGFkZExvZ091dEV2ZW50TGlzdGVuZXIoY2FsbGJhY2s6IEZ1bmN0aW9uKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvZ291dENhbGxiYWNrcy5wdXNoKGNhbGxiYWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmUgYSBjYWxsYmFjayBmcm9tIHRoZSBMb2dPdXQgZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gaWQgVGhlIGlkIGdpdmVuIHdoZW4gZXZlbnQgbGlzdGVuZXIgd2FzIGFkZGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgdHJ1ZSB3aGVuIHJlbW92ZSBzdWNjZXNzZnVsbHksIGZhbHNlIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgcmVtb3ZlTG9nT3V0RXZlbnRMaXN0ZW5lcihpZDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBkZWxldGUgdGhpcy5fbG9nb3V0Q2FsbGJhY2tzW2lkIC0gMV07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTGlzdGVuIGZvciBsb2dpbiBtZXNzYWdlcyBmcm9tIG90aGVyIHdpbmRvd3MuXG4gICAgICogVGhlc2UgbWVzc2FnZXMgY29udGFpbiB0aGUgdG9rZW5zIGZyb20gdGhlIEFBUC5cbiAgICAgKiBJZiBhIHRva2VuIGlzIHJlY2VpdmVkIHRoZW4gdGhlIGNhbGxiYWNrcyBhcmUgdHJpZ2dlcmVkLlxuICAgICAqL1xuICAgIHByaXZhdGUgX2xpc3RlbkxvZ2luTWVzc2FnZShyZW5kZXJlcjogUmVuZGVyZXIyKSB7XG4gICAgICAgIHJlbmRlcmVyLmxpc3Rlbignd2luZG93JywgJ21lc3NhZ2UnLCAoZXZlbnQ6IE1lc3NhZ2VFdmVudCkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLm1lc3NhZ2VJc0FjY2VwdGFibGUoZXZlbnQpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVVwZGF0ZXIoZXZlbnQuZGF0YSk7XG4gICAgICAgICAgICBldmVudC5zb3VyY2UuY2xvc2UoKTtcbiAgICAgICAgICAgIHRoaXMuX3VwZGF0ZUNyZWRlbnRpYWxzKCk7XG5cbiAgICAgICAgICAgIC8vIFRyaWdnZXJzIHVwZGF0aW5nIG90aGVyIHdpbmRvd3NcbiAgICAgICAgICAgIHRoaXMuX2NvbW1LZXlVcGRhdGVyKCk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKiBMaXN0ZW4gdG8gY2hhbmdlcyBpbiB0aGUgdG9rZW4gZnJvbSAqb3RoZXIqIHdpbmRvd3MuXG4gICAgICpcbiAgICAgKiBGb3IgaW50ZXItd2luZG93IGNvbW11bmljYXRpb24gbWVzc2FnZXMgYXJlIHRyYW5zbWl0dGVkIHRyb3VnaCBjaGFuZ2VzXG4gICAgICogb24gYSBkdW1teSBzdG9yYWdlIGtleSBwcm9wZXJ0eTogJ19jb21tS2V5TmFtZScuXG4gICAgICpcbiAgICAgKiBOb3RpY2UgdGhhdCBjaGFuZ2VzIGluIHRoZSAnX2NvbW1LZXlOYW1lJyBwcm9kdWNlZCBieSB0aGlzIGNsYXNzIGRvZXNuJ3RcbiAgICAgKiB0cmlnZ2VyIHRoaXMgZXZlbnQuXG4gICAgICovXG4gICAgcHJpdmF0ZSBfbGlzdGVuQ2hhbmdlc0Zyb21PdGhlcldpbmRvd3MocmVuZGVyZXI6IFJlbmRlcmVyMikge1xuICAgICAgICByZW5kZXJlci5saXN0ZW4oJ3dpbmRvdycsICdzdG9yYWdlJywgKGV2ZW50OiBTdG9yYWdlRXZlbnQpID0+IHtcbiAgICAgICAgICAgIGlmIChldmVudC5rZXkgPT09IHRoaXMuX2NvbW1LZXlOYW1lKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlQ3JlZGVudGlhbHMoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2sgaWYgdGhlIG1lc3NhZ2UgaXMgY29taW5nIGZyb20gdGhlIHNhbWUgZG9tYWluIHdlIHVzZSB0byBnZW5lcmF0ZVxuICAgICAqIHRoZSBTU08gVVJMLCBvdGhlcndpc2UgaXQncyBpZmZ5IGFuZCBzaG91bGRuJ3QgdHJ1c3QgaXQuXG4gICAgICovXG4gICAgcHJpdmF0ZSBtZXNzYWdlSXNBY2NlcHRhYmxlKGV2ZW50OiBNZXNzYWdlRXZlbnQpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGV2ZW50Lm9yaWdpbiA9PT0gdGhpcy5fYXBwVVJMO1xuICAgIH1cblxuICAgIHByaXZhdGUgX3VwZGF0ZUNyZWRlbnRpYWxzKCkge1xuICAgICAgICBjb25zdCBpc0F1dGhlbnRpY2F0ZWQgPSB0aGlzLl9sb2dnZWRJbigpO1xuXG4gICAgICAgIGlmICh0aGlzLl90aW1lb3V0SUQpIHtcbiAgICAgICAgICAgIHdpbmRvdy5jbGVhclRpbWVvdXQodGhpcy5fdGltZW91dElEKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpc0F1dGhlbnRpY2F0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMuX2NyZWRlbnRpYWxzLm5leHQoe1xuICAgICAgICAgICAgICAgIHJlYWxuYW1lOiA8IHN0cmluZyA+IHRoaXMuX2dldFJlYWxOYW1lKCksXG4gICAgICAgICAgICAgICAgdXNlcm5hbWU6IDwgc3RyaW5nID4gdGhpcy5fZ2V0VXNlck5hbWUoKSxcbiAgICAgICAgICAgICAgICB0b2tlbjogPCBzdHJpbmcgPiB0aGlzLl9nZXRUb2tlbigpXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdGhpcy5fbG9naW5DYWxsYmFja3MubWFwKGNhbGxiYWNrID0+IGNhbGxiYWNrICYmIGNhbGxiYWNrKCkpO1xuXG4gICAgICAgICAgICAvLyBTY2hlZHVsZSBmdXR1cmUgbG9nb3V0IGV2ZW50IGJhc2Ugb24gdG9rZW4gZXhwaXJhdGlvblxuICAgICAgICAgICAgY29uc3QgZXhwaXJlRGF0ZSA9IDwgRGF0ZSA+IHRoaXMuX3Rva2VuU2VydmljZS5nZXRUb2tlbkV4cGlyYXRpb25EYXRlKCk7XG4gICAgICAgICAgICAvLyBDb2VyY2luZyBkYXRlcyB0byBudW1iZXJzIHdpdGggdGhlIHVuYXJ5IG9wZXJhdG9yICcrJ1xuICAgICAgICAgICAgY29uc3QgZGVsYXkgPSArZXhwaXJlRGF0ZSAtICtuZXcgRGF0ZSgpO1xuICAgICAgICAgICAgdGhpcy5fdGltZW91dElEID0gd2luZG93LnNldFRpbWVvdXQoKCkgPT4gdGhpcy5sb2dPdXQoKSwgZGVsYXkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIoKTsgLy8gQ2xlYW51cCBwb3NzaWJsZSBsZWZ0IGJlaGluZCB0b2tlblxuICAgICAgICAgICAgdGhpcy5fY3JlZGVudGlhbHMubmV4dChudWxsKTtcbiAgICAgICAgICAgIHRoaXMuX2xvZ291dENhbGxiYWNrcy5tYXAoY2FsbGJhY2sgPT4gY2FsbGJhY2sgJiYgY2FsbGJhY2soKSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVjayBpZiB0aGVyZSdzIGEgdXNlciBsb2dnaW5nIG9uIGFuZCB3aGV0aGVyIHRoZSB0b2tlbiBpcyBzdGlsbCB2YWxpZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zICBXaGV0aGVyIHRoZSB1c2VyIHVzZXIgaXMgYXV0aGVudGljYXRlZCBvciBub3QuXG4gICAgICovXG4gICAgcHJpdmF0ZSBfbG9nZ2VkSW4oKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLl90b2tlblNlcnZpY2UuaXNUb2tlblZhbGlkKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0VG9rZW4oKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0VG9rZW4oKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9nZXRVc2VyTmFtZSgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Rva2VuU2VydmljZS5nZXRDbGFpbSA8IHN0cmluZywgbnVsbCA+ICgnZW1haWwnLCBudWxsKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9nZXRSZWFsTmFtZSgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Rva2VuU2VydmljZS5nZXRDbGFpbSA8IHN0cmluZywgbnVsbCA+ICgnbmFtZScsIG51bGwpO1xuICAgIH1cblxufVxuIiwiaW1wb3J0IHtcbiAgICBOZ01vZHVsZSxcbiAgICBPcHRpb25hbCxcbiAgICBTa2lwU2VsZixcbiAgICBNb2R1bGVXaXRoUHJvdmlkZXJzLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHtcbiAgICBKV1RfT1BUSU9OUyxcbiAgICBKd3RIZWxwZXJTZXJ2aWNlXG59IGZyb20gJ0BhdXRoMC9hbmd1bGFyLWp3dCc7XG5pbXBvcnQge1xuICAgIFRva2VuU2VydmljZVxufSBmcm9tICcuL3Rva2VuLnNlcnZpY2UnO1xuaW1wb3J0IHtcbiAgICBBdXRoQ29uZmlnLFxuICAgIEFBUF9DT05GSUcsXG4gICAgREVGQVVMVF9DT05GXG59IGZyb20gJy4vYXV0aC5jb25maWcnO1xuaW1wb3J0IHtcbiAgICBBdXRoU2VydmljZVxufSBmcm9tICcuL2F1dGguc2VydmljZSc7XG5cbkBOZ01vZHVsZSh7fSlcbmV4cG9ydCBjbGFzcyBBdXRoTW9kdWxlIHtcblxuICAgIGNvbnN0cnVjdG9yKEBPcHRpb25hbCgpIEBTa2lwU2VsZigpIHBhcmVudE1vZHVsZTogQXV0aE1vZHVsZSkge1xuICAgICAgICBpZiAocGFyZW50TW9kdWxlKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F1dGhNb2R1bGUgaXMgYWxyZWFkeSBsb2FkZWQuIEl0IHNob3VsZCBvbmx5IGJlIGltcG9ydGVkIGluIHlvdXIgYXBwbGljYXRpb25cXCdzIG1haW4gbW9kdWxlLicpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGZvclJvb3Qob3B0aW9ucz86IEF1dGhDb25maWcpOiBNb2R1bGVXaXRoUHJvdmlkZXJzIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIG5nTW9kdWxlOiBBdXRoTW9kdWxlLFxuICAgICAgICAgICAgcHJvdmlkZXJzOiBbXG4gICAgICAgICAgICAgICAgVG9rZW5TZXJ2aWNlLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgcHJvdmlkZTogQUFQX0NPTkZJRyxcbiAgICAgICAgICAgICAgICAgICAgdXNlVmFsdWU6IG9wdGlvbnMgPyBvcHRpb25zIDogREVGQVVMVF9DT05GXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBBdXRoU2VydmljZVxuICAgICAgICAgICAgXVxuICAgICAgICB9O1xuICAgIH1cbn1cbiJdLCJuYW1lcyI6WyJJbmplY3RhYmxlIiwiSnd0SGVscGVyU2VydmljZSIsIkluamVjdGlvblRva2VuIiwiQmVoYXZpb3JTdWJqZWN0IiwibWFwIiwiUmVuZGVyZXJGYWN0b3J5MiIsIkluamVjdCIsIk5nTW9kdWxlIiwiT3B0aW9uYWwiLCJTa2lwU2VsZiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBOzs7Ozs7O1FBaUJJLHNCQUNZO1lBQUEsU0FBSSxHQUFKLElBQUk7U0FDWjs7OztRQUVHLCtCQUFROzs7O2dCQUNYLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQzs7Ozs7UUFHNUIsNkNBQXNCOzs7O2dCQUN6QixJQUFJO29CQUNBLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO2lCQUM3QztnQkFBQyxPQUFPLENBQUMsRUFBRTtvQkFDUixPQUFPLElBQUksQ0FBQztpQkFDZjs7Ozs7UUFHRSxtQ0FBWTs7OztnQkFDZixJQUFJO29CQUNBLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO2lCQUN0QztnQkFBQyxPQUFPLEtBQUssRUFBRTtvQkFDWixPQUFPLEtBQUss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