angular-aap-auth
Version:
## This library is deprecated. Please, use [ng-ebi-authorization](https://www.npmjs.com/package/ng-ebi-authorization).
547 lines (546 loc) • 46.1 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;
}
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;
}());
export { AuthService };
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8vYW5ndWxhci1hYXAtYXV0aC8iLCJzb3VyY2VzIjpbImFwcC9tb2R1bGVzL2F1dGgvYXV0aC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQ0gsVUFBVSxFQUNWLE1BQU0sRUFDTixnQkFBZ0IsRUFFbkIsTUFBTSxlQUFlLENBQUM7QUFJdkIsT0FBTyxFQUNILGVBQWUsR0FDbEIsTUFBTSxNQUFNLENBQUM7QUFDZCxPQUFPLEVBQ0gsR0FBRyxFQUNOLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEIsT0FBTyxFQUNILFVBQVUsRUFFYixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQ0gsWUFBWSxFQUNmLE1BQU0saUJBQWlCLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0lBaUNyQixxQkFDWSxrQkFDQSxlQUNvQixNQUFrQjtRQUhsRCxpQkFtQkM7UUFsQlcscUJBQWdCLEdBQWhCLGdCQUFnQjtRQUNoQixrQkFBYSxHQUFiLGFBQWE7UUFDTyxXQUFNLEdBQU4sTUFBTSxDQUFZOzRCQXJCM0IsSUFBSSxlQUFlLENBQXlCLElBQUksQ0FBQzsrQkFFbEMsRUFBRTtnQ0FDRCxFQUFFOzBCQUVMLElBQUk7NEJBVVIsdUJBQXVCOytCQUNwQixjQUFNLE9BQUEsWUFBWSxDQUFDLE9BQU8sQ0FBQyxLQUFJLENBQUMsWUFBWSxFQUFFLEVBQUUsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEVBQWxFLENBQWtFO1FBT3ZHLElBQUksQ0FBQyxPQUFPLEdBQUcsa0JBQWtCLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxRCxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNoRCxJQUFJLENBQUMsZUFBZSxHQUFHLE1BQU0s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b3J0IHtcbiAgICBJbmplY3RhYmxlLFxuICAgIEluamVjdCxcbiAgICBSZW5kZXJlckZhY3RvcnkyLFxuICAgIFJlbmRlcmVyMlxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gICAgT2JzZXJ2YWJsZVxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7XG4gICAgQmVoYXZpb3JTdWJqZWN0LFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7XG4gICAgbWFwXG59IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHtcbiAgICBBQVBfQ09ORklHLFxuICAgIEF1dGhDb25maWdcbn0gZnJvbSAnLi9hdXRoLmNvbmZpZyc7XG5pbXBvcnQge1xuICAgIFRva2VuU2VydmljZVxufSBmcm9tICcuL3Rva2VuLnNlcnZpY2UnO1xuXG5leHBvcnQgaW50ZXJmYWNlIExvZ2luT3B0aW9ucyB7XG4gICAgW2tleTogc3RyaW5nXTogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENyZWRlbnRpYWxzIHtcbiAgICByZWFsbmFtZTogc3RyaW5nO1xuICAgIHVzZXJuYW1lOiBzdHJpbmc7XG4gICAgdG9rZW46IHN0cmluZztcbn1cblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEF1dGhTZXJ2aWNlIHtcblxuICAgIHByaXZhdGUgX2NyZWRlbnRpYWxzID0gbmV3IEJlaGF2aW9yU3ViamVjdCA8IENyZWRlbnRpYWxzIHwgbnVsbCA+IChudWxsKTtcblxuICAgIHByaXZhdGUgX2xvZ2luQ2FsbGJhY2tzOiBGdW5jdGlvbltdID0gW107XG4gICAgcHJpdmF0ZSBfbG9nb3V0Q2FsbGJhY2tzOiBGdW5jdGlvbltdID0gW107XG5cbiAgICBwcml2YXRlIF90aW1lb3V0SUQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xuXG4gICAgLy8gQ29uZmlndXJhdGlvblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2RvbWFpbjogc3RyaW5nO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2FwcFVSTDogc3RyaW5nO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3N0b3JhZ2VVcGRhdGVyOiAobmV3VG9rZW46IGFueSkgPT4gdm9pZDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9zdG9yYWdlUmVtb3ZlcjogKCkgPT4gdm9pZDtcblxuICAgIC8vIFRoaXMgdHdvIHByb3BlcnRpZXMgYXJlIHVzZWQgZm9yIGludGVyLXdpbmRvdyBjb21tdW5jYXRpb24uXG4gICAgLy8gSXQgaXMgYWNoaWV2ZSB0aHJvdWdoIHRoZSB1cGRhdGUgb2YgdGhlIGR1bW15IGtleSBzdG9yYWdlICdfY29tbUtleU5hbWUnXG4gICAgcHJpdmF0ZSByZWFkb25seSBfY29tbUtleU5hbWUgPSAnQW5ndWxhckFhcEF1dGhVcGRhdGVkJztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jb21tS2V5VXBkYXRlciA9ICgpID0+IGxvY2FsU3RvcmFnZS5zZXRJdGVtKHRoaXMuX2NvbW1LZXlOYW1lLCAnJyArIG5ldyBEYXRlKCkuZ2V0VGltZSgpKTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIF9yZW5kZXJlckZhY3Rvcnk6IFJlbmRlcmVyRmFjdG9yeTIsXG4gICAgICAgIHByaXZhdGUgX3Rva2VuU2VydmljZTogVG9rZW5TZXJ2aWNlLFxuICAgICAgICBASW5qZWN0KEFBUF9DT05GSUcpIHByaXZhdGUgY29uZmlnOiBBdXRoQ29uZmlnXG4gICAgKSB7XG4gICAgICAgIHRoaXMuX2RvbWFpbiA9IGVuY29kZVVSSUNvbXBvbmVudCh3aW5kb3cubG9jYXRpb24ub3JpZ2luKTtcbiAgICAgICAgdGhpcy5fYXBwVVJMID0gY29uZmlnLmFhcFVSTC5yZXBsYWNlKC9cXC8kLywgJycpO1xuICAgICAgICB0aGlzLl9zdG9yYWdlVXBkYXRlciA9IGNvbmZpZy50b2tlblVwZGF0ZXI7XG4gICAgICAgIGlmIChjb25maWcudG9rZW5SZW1vdmVyKSB7XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlciA9IGNvbmZpZy50b2tlblJlbW92ZXI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlciA9ICgpID0+IGNvbmZpZy50b2tlblVwZGF0ZXIobnVsbCk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCByZW5kZXJlciA9IHRoaXMuX3JlbmRlcmVyRmFjdG9yeS5jcmVhdGVSZW5kZXJlcihudWxsLCBudWxsKTtcbiAgICAgICAgdGhpcy5fbGlzdGVuTG9naW5NZXNzYWdlKHJlbmRlcmVyKTtcbiAgICAgICAgdGhpcy5fbGlzdGVuQ2hhbmdlc0Zyb21PdGhlcldpbmRvd3MocmVuZGVyZXIpO1xuXG4gICAgICAgIHRoaXMuX3VwZGF0ZUNyZWRlbnRpYWxzKCk7IC8vIFRPRE86IGV4cGVyaW1lbnQgd2l0aCBzZXRUaW1lT3V0XG4gICAgfVxuXG4gICAgcHVibGljIGlzQXV0aGVudGljYXRlZCgpOiBPYnNlcnZhYmxlIDwgYm9vbGVhbiA+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICAgICAgICBtYXAoY3JlZGVudGlhbHMgPT4gY3JlZGVudGlhbHMgPyB0cnVlIDogZmFsc2UpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIGNyZWRlbnRpYWxzKCk6IE9ic2VydmFibGUgPCBDcmVkZW50aWFscyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgcmVhbG5hbWUoKTogT2JzZXJ2YWJsZSA8IHN0cmluZyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gIGNyZWRlbnRpYWxzLnJlYWxuYW1lIDogbnVsbClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgdXNlcm5hbWUoKTogT2JzZXJ2YWJsZSA8IHN0cmluZyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gIGNyZWRlbnRpYWxzLnVzZXJuYW1lIDogbnVsbClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgdG9rZW4oKTogT2JzZXJ2YWJsZSA8IHN0cmluZyB8IG51bGwgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFscy5hc09ic2VydmFibGUoKS5waXBlKFxuICAgICAgICAgICAgbWFwKGNyZWRlbnRpYWxzID0+IGNyZWRlbnRpYWxzID8gIGNyZWRlbnRpYWxzLnRva2VuIDogbnVsbClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBvcGVucyBhIHdpbmRvdyBpbnN0ZWFkIG9mIGEgdGFiLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqIEBwYXJhbSB3aWR0aCBQaXhlbCB3aWR0aCBvZiB0aGUgbG9naW4gd2luZG93LlxuICAgICAqIEBwYXJhbSBoZWlnaHQgUGl4ZWwgaGVpZ2h0IG9mIHRoZSBsb2dpbiB3aW5kb3cuXG4gICAgICogQHBhcmFtIHRvcCBQb3NpdGlvbiBvZiB0aGUgdG9wIGNvcm5lcnMuIElmIGl0IGlzIGEgbmVnYXRpdmVcbiAgICAgKiAgICAgICAgICAgICBudW1iZXIgaXQgY2VudHJlcyB0aGUgbG9naW4gd2luZG93IG9uIHRoZSBzY3JlZW4uXG4gICAgICogQHBhcmFtIGxlZnQgUG9zaXRpb24gb2YgdGhlIGxlZnQgY29ybmVycy4gSWYgaXQgaXMgYSBuZWdhdGl2ZVxuICAgICAqICAgICAgICAgICAgIG51bWJlciBpdCBjZW50cmVzIHRoZSBsb2dpbiB3aW5kb3cgb24gdGhlIHNjcmVlbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgd2luZG93T3Blbihsb2dpbk9wdGlvbnM/OiBMb2dpbk9wdGlvbnMsIHdpZHRoID0gNjUwLCBoZWlnaHQgPSAxMDAwLCB0b3AgPSAtMSwgbGVmdCA9IC0xKSB7XG4gICAgICAgIGlmIChsZWZ0IDwgMCkge1xuICAgICAgICAgICAgY29uc3Qgc2NyZWVuV2lkdGggPSBzY3JlZW4ud2lkdGg7XG4gICAgICAgICAgICBpZiAoc2NyZWVuV2lkdGggPiB3aWR0aCkge1xuICAgICAgICAgICAgICAgIGxlZnQgPSBNYXRoLnJvdW5kKHNjcmVlbldpZHRoIC8gMiAtIHdpZHRoIC8gMik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRvcCA8IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHNjcmVlbkhlaWdodCA9IHNjcmVlbi5oZWlnaHQ7XG4gICAgICAgICAgICBpZiAoc2NyZWVuSGVpZ2h0ID4gaGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgdG9wID0gTWF0aC5yb3VuZChzY3JlZW5IZWlnaHQgLyAyIC0gaGVpZ2h0IC8gMik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB3aW5kb3dPcHRpb25zID0gW1xuICAgICAgICAgICAgYHdpZHRoPSR7d2lkdGh9YCxcbiAgICAgICAgICAgIGBoZWlnaHQ9JHtoZWlnaHR9YCxcbiAgICAgICAgICAgIGBsZWZ0PSR7bGVmdH1gLFxuICAgICAgICAgICAgYHRvcD0ke3RvcH1gLFxuICAgICAgICAgICAgJ3BlcnNvbmFsYmFyPW5vJyxcbiAgICAgICAgICAgICd0b29sYmFyPW5vJyxcbiAgICAgICAgICAgICdzY3JvbGxiYXJzPXllcycsXG4gICAgICAgICAgICAncmVzaXphYmxlPXllcycsXG4gICAgICAgICAgICAnZGlyZWN0b3JpZXM9bm8nLFxuICAgICAgICAgICAgJ2xvY2F0aW9uPW5vJyxcbiAgICAgICAgICAgICdtZW51YmFyPW5vJyxcbiAgICAgICAgICAgICd0aXRsZWJhcj1ubycsXG4gICAgICAgICAgICAndG9vbGJhcj1ubydcbiAgICAgICAgXTtcblxuICAgICAgICBjb25zdCBsb2dpbldpbmRvdyA9IHdpbmRvdy5vcGVuKHRoaXMuZ2V0U1NPVVJMKGxvZ2luT3B0aW9ucyksICdTaWduIGluIHRvIEVsaXhpcicsIHdpbmRvd09wdGlvbnMuam9pbignLCcpKTtcbiAgICAgICAgaWYgKGxvZ2luV2luZG93KSB7XG4gICAgICAgICAgICBsb2dpbldpbmRvdy5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRnVuY3Rpb25zIHRoYXQgb3BlbnMgYSB0YWIgKGluIG1vZGVybiBicm93c2VyKS5cbiAgICAgKlxuICAgICAqIFNlZSBtZXRob2QgX2ZpbHRlckxvZ2luT3B0aW9ucyByZWdhcmRpbmcgc2VjdXJpdHkgcmlza3Mgb2YgY2VydGFpblxuICAgICAqIExvZ2luT3B0aW9ucy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKi9cbiAgICBwdWJsaWMgdGFiT3Blbihsb2dpbk9wdGlvbnM/OiBMb2dpbk9wdGlvbnMpIHtcbiAgICAgICAgY29uc3QgbG9naW5XaW5kb3cgPSB3aW5kb3cub3Blbih0aGlzLmdldFNTT1VSTChsb2dpbk9wdGlvbnMpLCAnU2lnbiBpbiB0byBFbGl4aXInKTtcbiAgICAgICAgaWYgKGxvZ2luV2luZG93KSB7XG4gICAgICAgICAgICBsb2dpbldpbmRvdy5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUHJvZHVjZXMgYSBVUkwgdGhhdCBhbGxvd3MgbG9nZ2luZyBpbnRvIHRoZSBzaW5nbGUgc2lnbiBvbiAoU1NPKSBwYWdlLlxuICAgICAqIFRoZSBVUkwgY2FucyBiZSBvcGVuZWQgaW4gYSBuZXcgdGFiIHVzaW5nIHRhcmdldD1cIl9ibGFua1wiLFxuICAgICAqIG9yIGluIGEgbmV3IHdpbmRvdyB1c2luZyB3aW5kb3cub3BlbigpLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtICBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIFRoZSBTU08gVVJMLlxuICAgICAqXG4gICAgICovXG4gICAgICAgIHB1YmxpYyBnZXRTU09VUkwob3B0aW9ucz86IExvZ2luT3B0aW9ucyk6IHN0cmluZyB7XG4gICAgICAgIGxldCBleHRyYSA9ICcnO1xuICAgICAgICBpZiAob3B0aW9ucykge1xuICAgICAgICAgICAgdGhpcy5fZmlsdGVyTG9naW5PcHRpb25zKG9wdGlvbnMpO1xuICAgICAgICAgICAgZXh0cmEgPSBPYmplY3Qua2V5cyhvcHRpb25zKVxuICAgICAgICAgICAgICAgIC5tYXAoa2V5ID0+IFtrZXksIG9wdGlvbnNba2V5XV0pXG4gICAgICAgICAgICAgICAgLnJlZHVjZSgoYWNjdW11bGF0b3IsIGtleXZhbHVlKSA9PiBgJHthY2N1bXVsYXRvcn0mJHtrZXl2YWx1ZVswXX09JHtrZXl2YWx1ZVsxXX1gLCAnJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGAke3RoaXMuX2FwcFVSTH0vc3NvP2Zyb209JHt0aGlzLl9kb21haW59JHtleHRyYX1gO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZpbHRlcnMgb3B0aW9ucyB0aGF0IGFyZSB1bnNlY3VyZS5cbiAgICAgKlxuICAgICAqIFNlZSB0aGUgYWR2YW5jZSBvcHRpb25zIHRoYXQgY2FuIGJlIHJlcXVlc3RlZCB0aHJvdWdoIHRoZSBvcHRpb25zIHBhcmFtZXRlcjpcbiAgICAgKiBodHRwczovL2FwaS5hYWkuZWJpLmFjLnVrL2RvY3MvYXV0aGVudGljYXRpb24vYXV0aGVudGljYXRpb24uaW5kZXguaHRtbCNfY29tbW9uX2F0dHJpYnV0ZXNcbiAgICAgKlxuICAgICAqIFRoZSB0aW1lIHRvIGxpdmUgcGFyYW1lbnRlciAodHRsKSBkZWZhdWx0IHZhbHVlIGlzIDYwIG1pbnV0ZXMuIEl0IGlzIGFcbiAgICAgKiBiaWcgc2VjdXJpdHkgcmlzayB0byByZXF1ZXN0IGxvbmdlciB0dGwuIElmIGEgdGhpcmQgcGFydHkgZ2V0cyBob2xkIG9mXG4gICAgICogc3VjaCB0b2tlbiwgbWVhbnMgdGhhdCB0aGV5IGNvdWxkIHVzZSBpdCBmb3IgYSBkYXksIHdlZWssIHllYXJcbiAgICAgKiAoZXNzZW50aWFsbHksIGxpa2UgaGF2aW5nIHRoZSB1c2VybmFtZS9wYXNzd29yZCkuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqXG4gICAgICpcbiAgICAgKi9cbiAgICBwdWJsaWMgX2ZpbHRlckxvZ2luT3B0aW9ucyhvcHRpb25zOiBMb2dpbk9wdGlvbnMpIHtcbiAgICAgICAgaWYgKE9iamVjdC5rZXlzKG9wdGlvbnMpLmluZGV4T2YoJ3R0bCcpID4gLTEpIHtcbiAgICAgICAgICAgIGNvbnN0IHR0bDogbnVtYmVyID0gK29wdGlvbnNbJ3R0bCddO1xuICAgICAgICAgICAgY29uc3Qgc29mdExpbWl0ID0gNjA7XG4gICAgICAgICAgICBjb25zdCBoYXJkTGltaXQgPSA2MCAqIDI0O1xuICAgICAgICAgICAgaWYgKHR0bCA+IGhhcmRMaW1pdCkge1xuICAgICAgICAgICAgICAgIHdpbmRvdy5jb25zb2xlLmVycm9yKGBMb2dpbiByZXF1ZXN0ZWQgd2l0aCBhbiBleHBpcmF0aW9uIGxvbmdlciB0aGFuICR7aGFyZExpbWl0fSBtaW51dGVzISBUaGlzIGlzIG5vdCBhbGxvd2VkLmApO1xuICAgICAgICAgICAgICAgIHdpbmRvdy5jb25zb2xlLmVycm9yKGBFeHBpcmF0aW9uIHJlcXVlc3QgcmVzZXQgdG8gJHtoYXJkTGltaXR9IG1pbnV0ZXMuYCk7XG4gICAgICAgICAgICAgICAgb3B0aW9uc1sndHRsJ10gPSAnJyArIGhhcmRMaW1pdDtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodHRsID4gc29mdExpbWl0KSB7XG4gICAgICAgICAgICAgICAgd2luZG93LmNvbnNvbGUud2FybihgTG9naW4gcmVxdWVzdGVkIHdpdGggYW4gZXhwaXJhdGlvbiBsb25nZXIgdGhhbiAke3NvZnRMaW1pdH0gbWludXRlcyFgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZ1bmN0aW9ucyB0aGF0IGxvZ3Mgb3V0IHRoZSB1c2VyLlxuICAgICAqIEl0IHRyaWdnZXJzIHRoZSBsb2dvdXQgY2FsbGJhY2tzLlxuICAgICAqIEl0IGlzIGFuIGFycm93IGZ1bmN0aW9uIChsYW1iZGEpIGJlY2F1c2UgaW4gdGhhdCB3YXkgaXQgaGFzIGEgcmVmZXJlbmNlXG4gICAgICogdG8gJ3RoaXMnIHdoZW4gdXNlZCBpbiBzZXRUaW1lb3V0IGNhbGwuXG4gICAgICovXG4gICAgcHVibGljIGxvZ091dCgpIHtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIoKTtcbiAgICAgICAgdGhpcy5fdXBkYXRlQ3JlZGVudGlhbHMoKTtcblxuICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgIHRoaXMuX2NvbW1LZXlVcGRhdGVyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIGEgY2FsbGJhY2sgdG8gdGhlIExvZ0luIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGNhbGxiYWNrIFRoZSBGdW5jdGlvbiBjYWxsZWQgd2hlbiB0aGUgbG9naW4gZXZlbnQgaXMgdHJpZ2dlcmVkIGFuZCB0aGVcbiAgICAgKiAgICBKV1QgdG9rZW4gaXMgcmVjZWl2ZWQgYW5kIGFjY2VwdGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgVGhlIGV2ZW50IHJlZ2lzdHJhdGlvbiBpZCAobmVjZXNzYXJ5IHRvIHVucmVnaXN0ZXIgdGhlIGV2ZW50KS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYWRkTG9nSW5FdmVudExpc3RlbmVyKGNhbGxiYWNrOiBGdW5jdGlvbik6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLl9sb2dpbkNhbGxiYWNrcy5wdXNoKGNhbGxiYWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmUgYSBjYWxsYmFjayBmcm9tIHRoZSBMb2dJbiBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBpZCBUaGUgaWQgZ2l2ZW4gd2hlbiBldmVudCBsaXN0ZW5lciB3YXMgYWRkZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB0cnVlIHdoZW4gcmVtb3ZlIHN1Y2Nlc3NmdWxseSwgZmFsc2Ugb3RoZXJ3aXNlLlxuICAgICAqL1xuICAgIHB1YmxpYyByZW1vdmVMb2dJbkV2ZW50TGlzdGVuZXIoaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZGVsZXRlIHRoaXMuX2xvZ2luQ2FsbGJhY2tzW2lkIC0gMV07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIGEgY2FsbGJhY2sgdG8gdGhlIExvZ091dCBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBjYWxsYmFjayBUaGUgRnVuY3Rpb24gY2FsbGVkIHdoZW4gdGhlIGxvZ291dCBldmVudCBpcyB0cmlnZ2VyZWQgYW5kIHRoZVxuICAgICAqICAgIEpXVCB0b2tlbiBpcyByZWNlaXZlZCBhbmQgYWNjZXB0ZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyBUaGUgcmVnaXN0cmF0aW9uIGlkIChuZWNlc3NhcnkgdG8gdW5yZWdpc3RlciB0aGUgZXZlbnQpLlxuICAgICAqL1xuICAgIHB1YmxpYyBhZGRMb2dPdXRFdmVudExpc3RlbmVyKGNhbGxiYWNrOiBGdW5jdGlvbik6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLl9sb2dvdXRDYWxsYmFja3MucHVzaChjYWxsYmFjayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGEgY2FsbGJhY2sgZnJvbSB0aGUgTG9nT3V0IGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIFRoZSBpZCBnaXZlbiB3aGVuIGV2ZW50IGxpc3RlbmVyIHdhcyBhZGRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHRydWUgd2hlbiByZW1vdmUgc3VjY2Vzc2Z1bGx5LCBmYWxzZSBvdGhlcndpc2UuXG4gICAgICovXG4gICAgcHVibGljIHJlbW92ZUxvZ091dEV2ZW50TGlzdGVuZXIoaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZGVsZXRlIHRoaXMuX2xvZ291dENhbGxiYWNrc1tpZCAtIDFdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIExpc3RlbiBmb3IgbG9naW4gbWVzc2FnZXMgZnJvbSBvdGhlciB3aW5kb3dzLlxuICAgICAqIFRoZXNlIG1lc3NhZ2VzIGNvbnRhaW4gdGhlIHRva2VucyBmcm9tIHRoZSBBQVAuXG4gICAgICogSWYgYSB0b2tlbiBpcyByZWNlaXZlZCB0aGVuIHRoZSBjYWxsYmFja3MgYXJlIHRyaWdnZXJlZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIF9saXN0ZW5Mb2dpbk1lc3NhZ2UocmVuZGVyZXI6IFJlbmRlcmVyMikge1xuICAgICAgICByZW5kZXJlci5saXN0ZW4oJ3dpbmRvdycsICdtZXNzYWdlJywgKGV2ZW50OiBNZXNzYWdlRXZlbnQpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5tZXNzYWdlSXNBY2NlcHRhYmxlKGV2ZW50KSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VVcGRhdGVyKGV2ZW50LmRhdGEpO1xuICAgICAgICAgICAgZXZlbnQuc291cmNlLmNsb3NlKCk7XG4gICAgICAgICAgICB0aGlzLl91cGRhdGVDcmVkZW50aWFscygpO1xuXG4gICAgICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgICAgICB0aGlzLl9jb21tS2V5VXBkYXRlcigpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKiogTGlzdGVuIHRvIGNoYW5nZXMgaW4gdGhlIHRva2VuIGZyb20gKm90aGVyKiB3aW5kb3dzLlxuICAgICAqXG4gICAgICogRm9yIGludGVyLXdpbmRvdyBjb21tdW5pY2F0aW9uIG1lc3NhZ2VzIGFyZSB0cmFuc21pdHRlZCB0cm91Z2ggY2hhbmdlc1xuICAgICAqIG9uIGEgZHVtbXkgc3RvcmFnZSBrZXkgcHJvcGVydHk6ICdfY29tbUtleU5hbWUnLlxuICAgICAqXG4gICAgICogTm90aWNlIHRoYXQgY2hhbmdlcyBpbiB0aGUgJ19jb21tS2V5TmFtZScgcHJvZHVjZWQgYnkgdGhpcyBjbGFzcyBkb2Vzbid0XG4gICAgICogdHJpZ2dlciB0aGlzIGV2ZW50LlxuICAgICAqL1xuICAgIHByaXZhdGUgX2xpc3RlbkNoYW5nZXNGcm9tT3RoZXJXaW5kb3dzKHJlbmRlcmVyOiBSZW5kZXJlcjIpIHtcbiAgICAgICAgcmVuZGVyZXIubGlzdGVuKCd3aW5kb3cnLCAnc3RvcmFnZScsIChldmVudDogU3RvcmFnZUV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSB0aGlzLl9jb21tS2V5TmFtZSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZUNyZWRlbnRpYWxzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIHRoZSBtZXNzYWdlIGlzIGNvbWluZyBmcm9tIHRoZSBzYW1lIGRvbWFpbiB3ZSB1c2UgdG8gZ2VuZXJhdGVcbiAgICAgKiB0aGUgU1NPIFVSTCwgb3RoZXJ3aXNlIGl0J3MgaWZmeSBhbmQgc2hvdWxkbid0IHRydXN0IGl0LlxuICAgICAqL1xuICAgIHByaXZhdGUgbWVzc2FnZUlzQWNjZXB0YWJsZShldmVudDogTWVzc2FnZUV2ZW50KTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBldmVudC5vcmlnaW4gPT09IHRoaXMuX2FwcFVSTDtcbiAgICB9XG5cbiAgICBwcml2YXRlIF91cGRhdGVDcmVkZW50aWFscygpIHtcbiAgICAgICAgY29uc3QgaXNBdXRoZW50aWNhdGVkID0gdGhpcy5fbG9nZ2VkSW4oKTtcblxuICAgICAgICBpZiAodGhpcy5fdGltZW91dElEKSB7XG4gICAgICAgICAgICB3aW5kb3cuY2xlYXJUaW1lb3V0KHRoaXMuX3RpbWVvdXRJRCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaXNBdXRoZW50aWNhdGVkKSB7XG4gICAgICAgICAgICB0aGlzLl9jcmVkZW50aWFscy5uZXh0KHtcbiAgICAgICAgICAgICAgICByZWFsbmFtZTogPCBzdHJpbmcgPiB0aGlzLl9nZXRSZWFsTmFtZSgpLFxuICAgICAgICAgICAgICAgIHVzZXJuYW1lOiA8IHN0cmluZyA+IHRoaXMuX2dldFVzZXJOYW1lKCksXG4gICAgICAgICAgICAgICAgdG9rZW46IDwgc3RyaW5nID4gdGhpcy5fZ2V0VG9rZW4oKVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHRoaXMuX2xvZ2luQ2FsbGJhY2tzLm1hcChjYWxsYmFjayA9PiBjYWxsYmFjayAmJiBjYWxsYmFjaygpKTtcblxuICAgICAgICAgICAgLy8gU2NoZWR1bGUgZnV0dXJlIGxvZ291dCBldmVudCBiYXNlIG9uIHRva2VuIGV4cGlyYXRpb25cbiAgICAgICAgICAgIGNvbnN0IGV4cGlyZURhdGUgPSA8IERhdGUgPiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0VG9rZW5FeHBpcmF0aW9uRGF0ZSgpO1xuICAgICAgICAgICAgLy8gQ29lcmNpbmcgZGF0ZXMgdG8gbnVtYmVycyB3aXRoIHRoZSB1bmFyeSBvcGVyYXRvciAnKydcbiAgICAgICAgICAgIGNvbnN0IGRlbGF5ID0gK2V4cGlyZURhdGUgLSArbmV3IERhdGUoKTtcbiAgICAgICAgICAgIHRoaXMuX3RpbWVvdXRJRCA9IHdpbmRvdy5zZXRUaW1lb3V0KCgpID0+IHRoaXMubG9nT3V0KCksIGRlbGF5KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyKCk7IC8vIENsZWFudXAgcG9zc2libGUgbGVmdCBiZWhpbmQgdG9rZW5cbiAgICAgICAgICAgIHRoaXMuX2NyZWRlbnRpYWxzLm5leHQobnVsbCk7XG4gICAgICAgICAgICB0aGlzLl9sb2dvdXRDYWxsYmFja3MubWFwKGNhbGxiYWNrID0+IGNhbGxiYWNrICYmIGNhbGxiYWNrKCkpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2sgaWYgdGhlcmUncyBhIHVzZXIgbG9nZ2luZyBvbiBhbmQgd2hldGhlciB0aGUgdG9rZW4gaXMgc3RpbGwgdmFsaWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyAgV2hldGhlciB0aGUgdXNlciB1c2VyIGlzIGF1dGhlbnRpY2F0ZWQgb3Igbm90LlxuICAgICAqL1xuICAgIHByaXZhdGUgX2xvZ2dlZEluKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5TZXJ2aWNlLmlzVG9rZW5WYWxpZCgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX2dldFRva2VuKCk6IHN0cmluZyB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5TZXJ2aWNlLmdldFRva2VuKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0VXNlck5hbWUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0Q2xhaW0gPCBzdHJpbmcsIG51bGwgPiAoJ2VtYWlsJywgbnVsbCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0UmVhbE5hbWUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLl90b2tlblNlcnZpY2UuZ2V0Q2xhaW0gPCBzdHJpbmcsIG51bGwgPiAoJ25hbWUnLCBudWxsKTtcbiAgICB9XG5cbn1cbiJdfQ==