ng-ebi-authorization
Version:
The ng-ebi-authorization is a simple authentication Angular library that relies on EBI's Authentication and Authorization Profile (AAP) infrastructure. After successful login, a JWT token is stored on the browser (via cookie, local or session storage).
601 lines • 53.9 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { Injectable, Inject, RendererFactory2 } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { BehaviorSubject, } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { AAP_CONFIG } from './auth.config';
import { TokenService } from './token.service';
/**
* @record
*/
export function LoginOptions() { }
/**
* @record
*/
export function User() { }
if (false) {
/** @type {?} */
User.prototype.uid;
/** @type {?} */
User.prototype.name;
/** @type {?} */
User.prototype.nickname;
/** @type {?} */
User.prototype.email;
/** @type {?} */
User.prototype.token;
}
export class AuthService {
/**
* @param {?} _rendererFactory
* @param {?} _tokenService
* @param {?} _http
* @param {?} config
*/
constructor(_rendererFactory, _tokenService, _http, config) {
this._rendererFactory = _rendererFactory;
this._tokenService = _tokenService;
this._http = _http;
this.config = config;
this._user = new BehaviorSubject(null);
this._currentState = null; // stores the string token or null otherwise (logout)
// stores the string token or null otherwise (logout)
this._loginCallbacks = [];
this._logoutCallbacks = [];
this._timeoutID = null;
// This two properties are used for inter-window communication.
// It is achieve through the update of the dummy key storage '_commKeyName'
this._commKeyName = 'AngularAapAuthUpdated';
this._commKeyUpdater = () => localStorage.setItem(this._commKeyName, '' + new Date().getTime());
this._domain = encodeURIComponent(window.location.origin);
this._appURL = config.aapURL.replace(/\/$/, '');
this._authURL = `${this._appURL}/auth`;
this._tokenURL = `${this._appURL}/token`;
this._storageUpdater = config.tokenUpdater;
if (config.tokenRemover) {
this._storageRemover = config.tokenRemover;
}
else {
this._storageRemover = () => config.tokenUpdater(null);
}
/** @type {?} */
const renderer = this._rendererFactory.createRenderer(null, null);
this._unlistenEvents = [
this._listenLoginMessage(renderer),
this._listenChangesFromOtherWindows(renderer)
];
this._currentState = null;
this._updateUser(); // TODO: experiment with setTimeOut
}
/**
* @return {?}
*/
ngOnDestroy() {
this._unlistenEvents.forEach(fn => fn());
}
/**
* @return {?}
*/
user() {
return this._user.asObservable();
}
/**
* Functions that opens a window instead of a tab.
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?=} options
* @param {?=} width Pixel width of the login window.
* @param {?=} height Pixel height of the login window.
* @param {?=} left Position of the left corners. If it is a negative
* number it centres the login window on the screen.
* @param {?=} top Position of the top corners. If it is a negative
* number it centres the login window on the screen.
* @return {?}
*/
openLoginWindow(options, width = 650, height = 1000, left = -1, top = -1) {
if (left < 0) {
/** @type {?} */
const screenWidth = screen.width;
if (screenWidth > width) {
left = Math.round((screenWidth - width) / 2);
}
}
if (top < 0) {
/** @type {?} */
const screenHeight = screen.height;
if (screenHeight > height) {
top = Math.round((screenHeight - height) / 2);
}
}
/** @type {?} */
const 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'
];
/** @type {?} */
const loginWindow = window.open(this.getSSOURL(options), 'Sign in to Elixir', windowOptions.join(','));
if (loginWindow) {
loginWindow.focus();
}
}
/**
* @deprecated use openLoginWindow method instead (top and left arguments are inverted in the new method)
* since version 1.0.0-beta.5.
* windowOpen will be deleted in version 1.0.0
* @param {?=} options
* @param {?=} width
* @param {?=} height
* @param {?=} top
* @param {?=} left
* @return {?}
*/
windowOpen(options, width = 650, height = 1000, top = -1, left = -1) {
this._deprecationWarning('windowOpen', 'openLoginWindow');
this.openLoginWindow(options, width, height, left, top);
}
/**
* Functions that opens a tab (in modern browser).
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?=} options
* @return {?}
*/
openLoginTab(options) {
/** @type {?} */
const loginWindow = window.open(this.getSSOURL(options), 'Sign in to Elixir');
if (loginWindow) {
loginWindow.focus();
}
}
/**
* @deprecated use openLoginTab method instead
* since version 1.0.0-beta.5.
* tabOpen will be deleted in version 1.0.0
* @param {?=} options
* @return {?}
*/
tabOpen(options) {
this._deprecationWarning('tabOpen', 'openLoginTab');
this.openLoginTab(options);
}
/**
* Produces a URL that allows logging into the single sign on (SSO) page.
* The URL cans be opened in a new tab using target="_blank",
* or in a new window using window.open().
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?=} options
* @return {?} The SSO URL.
*
*/
getSSOURL(options) {
/** @type {?} */
const fragments = this._formatFragments(Object.assign({ 'from': this._domain }, options));
return `${this._appURL}/sso${fragments}`;
}
/**
* Functions that logs out the user.
* It triggers the logout callbacks.
* It is an arrow function (lambda) because in that way it has a reference
* to 'this' when used in setTimeout call.
* @return {?}
*/
logOut() {
this._storageRemover();
this._updateUser();
// Triggers updating other windows
this._commKeyUpdater();
}
/**
* Create AAP account
*
* @param {?} newUser
* @return {?} uid of the new user
*/
createAAPaccount(newUser) {
return this._http.post(this._authURL, newUser, {
responseType: 'text'
});
}
/**
* Login directly through the AAP
*
* See method _filterLoginOptions regarding security risks of certain
* LoginOptions.
*
* @param {?} user
* @param {?=} options
* @return {?} true if the user successfully login, otherwise false
*/
loginAAP(user, options) {
/** @type {?} */
const fragments = this._formatFragments(options);
return this._http.get(`${this._authURL}${fragments}`, {
headers: this._createAuthHeader(user),
responseType: 'text'
}).pipe(tap(token => {
this._storageRemover();
this._storageUpdater(token);
this._updateUser();
// Triggers updating other windows
this._commKeyUpdater();
}), map(Boolean));
}
/**
* Change password AAP account
*
* @param {?} __0
* @return {?} true when password is successfully changed
*/
changePasswordAAP({ username, oldPassword, newPassword }) {
return this._http.patch(this._authURL, {
username,
password: newPassword
}, {
headers: this._createAuthHeader({
username,
password: oldPassword
})
}).pipe(map(response => true) // response is empty, but if it reaches this point the request has successfully completed
);
}
/**
* Add a callback to the LogIn event.
*
* @param {?} callback The Function called when the login event is triggered and the
* JWT token is received and accepted.
*
* @return {?} The event registration id (necessary to unregister the event).
*/
addLogInEventListener(callback) {
return this._loginCallbacks.push(callback);
}
/**
* Remove a callback from the LogIn event.
*
* @param {?} id The id given when event listener was added.
*
* @return {?} true when remove successfully, false otherwise.
*/
removeLogInEventListener(id) {
return delete this._loginCallbacks[id - 1];
}
/**
* Add a callback to the LogOut event.
*
* @param {?} callback The Function called when the logout event is triggered and the
* JWT token is received and accepted.
*
* @return {?} The registration id (necessary to unregister the event).
*/
addLogOutEventListener(callback) {
return this._logoutCallbacks.push(callback);
}
/**
* Remove a callback from the LogOut event.
*
* @param {?} id The id given when event listener was added.
*
* @return {?} true when remove successfully, false otherwise.
*/
removeLogOutEventListener(id) {
return delete this._logoutCallbacks[id - 1];
}
/**
* Refresh token
*
* @return {?} true when token is successfully refreshed
*/
refresh() {
return this._http.get(this._tokenURL, {
responseType: 'text'
}).pipe(tap(token => {
this._storageRemover();
this._storageUpdater(token);
this._updateUser();
// Triggers updating other windows
this._commKeyUpdater();
}), map(Boolean));
}
/**
* Format and filter fragment options
*
* \@params options
*
* @private
* @param {?=} options
* @return {?} fragment string
*/
_formatFragments(options) {
if (options) {
this._filterLoginOptions(options);
return '?' + Object.entries(options)
.map(([key, value]) => `${key}=${value}`).join('&');
}
return '';
}
/**
* 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).
*
* @private
* @param {?} options
* @return {?}
*/
_filterLoginOptions(options) {
if (Object.keys(options).indexOf('ttl') > -1) {
/** @type {?} */
const ttl = +options['ttl'];
/** @type {?} */
const softLimit = 60;
/** @type {?} */
const hardLimit = 60 * 24;
if (ttl > hardLimit) {
throw (new Error(`Login requested with an expiration longer than ${hardLimit} minutes! This is not allowed.`));
}
if (ttl > softLimit) {
window.console.warn(`Login requested with an expiration longer than ${softLimit} minutes!`);
}
}
}
/**
* Creates Authorization header
*
* @private
* @param {?} __0
* @return {?} New authorization header
*/
_createAuthHeader({ username, password }) {
/** @type {?} */
const authToken = btoa(`${username}:${password}`);
return new HttpHeaders({
'Authorization': `Basic ${authToken}`
});
}
/**
* 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.
* @private
* @param {?} renderer
* @return {?}
*/
_listenLoginMessage(renderer) {
return renderer.listen('window', 'message', (event) => {
if (!this._messageIsAcceptable(event)) {
return;
}
this._storageUpdater(event.data);
// I don't know how to type guard event.source
// This doesn't work
// if (event.source instanceof Window) {
if (event.source) {
((/** @type {?} */ (event.source))).close();
}
this._updateUser();
// 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.
* @private
* @param {?} renderer
* @return {?}
*/
_listenChangesFromOtherWindows(renderer) {
return renderer.listen('window', 'storage', (event) => {
if (event.key === this._commKeyName) {
this._updateUser();
}
});
}
/**
* 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.
* @private
* @param {?} event
* @return {?}
*/
_messageIsAcceptable(event) {
return event.origin === this._appURL;
}
/**
* @private
* @return {?}
*/
_updateUser() {
if (this._timeoutID) {
window.clearTimeout(this._timeoutID);
}
if (this._tokenService.isTokenValid()) {
/** @type {?} */
const token = (/** @type {?} */ (this._tokenService.getToken()));
this._user.next({
uid: this._getClaim('sub'),
name: this._getClaim('name'),
nickname: this._getClaim('nickname'),
email: this._getClaim('email'),
token
});
if (this._currentState === null) {
// this._loginCallbacks is an empty list when first called from the constructor.
// Latter it could be filled as clients of the library call `this.addLogInEventListener(myfunction)`
this._loginCallbacks.forEach(callback => callback());
}
// Schedule future logout event base on token expiration
/** @type {?} */
const expireDate = (/** @type {?} */ (this._tokenService.getTokenExpirationDate()));
// Coercing dates to numbers with the unary operator '+'
/** @type {?} */
const delay = +expireDate - +new Date();
this._timeoutID = window.setTimeout(() => this.logOut(), delay);
if (this._currentState !== token) {
this._currentState = token;
}
}
else {
this._storageRemover(); // Cleanup possible left behind token
if (this._currentState !== null) {
// this._logoutCallbacks is an empty list when first called from the constructor.
// Latter it could be filled as clients of the library call `this.addLogOutEventListener(myfunction)`
this._logoutCallbacks.forEach(callback => callback());
this._user.next(null);
}
this._currentState = null;
}
}
/**
* @private
* @param {?} claim
* @return {?}
*/
_getClaim(claim) {
return this._tokenService.getClaim(claim, '');
}
/**
* @private
* @param {?} oldMethod
* @param {?} newMethod
* @return {?}
*/
_deprecationWarning(oldMethod, newMethod) {
window.console.warn(`Method '${oldMethod}' has been deprecated, please use '${newMethod}' method instead`);
}
}
AuthService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
AuthService.ctorParameters = () => [
{ type: RendererFactory2 },
{ type: TokenService },
{ type: HttpClient },
{ type: undefined, decorators: [{ type: Inject, args: [AAP_CONFIG,] }] }
];
if (false) {
/**
* @type {?}
* @private
*/
AuthService.prototype._user;
/**
* @type {?}
* @private
*/
AuthService.prototype._currentState;
/**
* @type {?}
* @private
*/
AuthService.prototype._loginCallbacks;
/**
* @type {?}
* @private
*/
AuthService.prototype._logoutCallbacks;
/**
* @type {?}
* @private
*/
AuthService.prototype._unlistenEvents;
/**
* @type {?}
* @private
*/
AuthService.prototype._timeoutID;
/**
* @type {?}
* @private
*/
AuthService.prototype._domain;
/**
* @type {?}
* @private
*/
AuthService.prototype._appURL;
/**
* @type {?}
* @private
*/
AuthService.prototype._tokenURL;
/**
* @type {?}
* @private
*/
AuthService.prototype._authURL;
/**
* @type {?}
* @private
*/
AuthService.prototype._storageUpdater;
/**
* @type {?}
* @private
*/
AuthService.prototype._storageRemover;
/**
* @type {?}
* @private
*/
AuthService.prototype._commKeyName;
/**
* @type {?}
* @private
*/
AuthService.prototype._commKeyUpdater;
/**
* @type {?}
* @private
*/
AuthService.prototype._rendererFactory;
/**
* @type {?}
* @private
*/
AuthService.prototype._tokenService;
/**
* @type {?}
* @private
*/
AuthService.prototype._http;
/**
* @type {?}
* @private
*/
AuthService.prototype.config;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8vbmctZWJpLWF1dGhvcml6YXRpb24vIiwic291cmNlcyI6WyJhdXRoL2F1dGguc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsT0FBTyxFQUNILFVBQVUsRUFDVixNQUFNLEVBRU4sZ0JBQWdCLEVBRW5CLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFDSCxVQUFVLEVBQ1YsV0FBVyxFQUVkLE1BQU0sc0JBQXNCLENBQUM7QUFJOUIsT0FBTyxFQUNILGVBQWUsR0FDbEIsTUFBTSxNQUFNLENBQUM7QUFDZCxPQUFPLEVBRUgsR0FBRyxFQUNILEdBQUcsRUFDTixNQUFNLGdCQUFnQixDQUFDO0FBRXhCLE9BQU8sRUFDSCxVQUFVLEVBRWIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNILFlBQVksRUFDZixNQUFNLGlCQUFpQixDQUFDOzs7O0FBRXpCLGtDQUVDOzs7O0FBRUQsMEJBTUM7OztJQUxHLG1CQUFZOztJQUNaLG9CQUFhOztJQUNiLHdCQUFpQjs7SUFDakIscUJBQWM7O0lBQ2QscUJBQWM7O0FBSWxCLE1BQU0sT0FBTyxXQUFXOzs7Ozs7O0lBeUJwQixZQUNZLGdCQUFrQyxFQUNsQyxhQUEyQixFQUMzQixLQUFpQixFQUNHLE1BQWtCO1FBSHRDLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUFDbEMsa0JBQWEsR0FBYixhQUFhLENBQWM7UUFDM0IsVUFBSyxHQUFMLEtBQUssQ0FBWTtRQUNHLFdBQU0sR0FBTixNQUFNLENBQVk7UUEzQjFDLFVBQUssR0FBRyxJQUFJLGVBQWUsQ0FBa0IsSUFBSSxDQUFDLENBQUM7UUFDbkQsa0JBQWEsR0FBa0IsSUFBSSxDQUFDLENBQUMscURBQXFEOztRQUUxRixvQkFBZSxHQUFlLEVBQUUsQ0FBQztRQUNqQyxxQkFBZ0IsR0FBZSxFQUFFLENBQUM7UUFJbEMsZUFBVSxHQUFrQixJQUFJLENBQUM7OztRQVl4Qixp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Q25DLDRCQUF5Qjs7Ozs7SUFDekIsNkJBQThDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgICBJbmplY3RhYmxlLFxuICAgIEluamVjdCxcbiAgICBPbkRlc3Ryb3ksXG4gICAgUmVuZGVyZXJGYWN0b3J5MixcbiAgICBSZW5kZXJlcjJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICAgIEh0dHBDbGllbnQsXG4gICAgSHR0cEhlYWRlcnMsXG4gICAgSHR0cFJlc3BvbnNlXG59IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7XG4gICAgT2JzZXJ2YWJsZVxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7XG4gICAgQmVoYXZpb3JTdWJqZWN0LFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7XG4gICAgZmlsdGVyLFxuICAgIG1hcCxcbiAgICB0YXBcbn0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQge1xuICAgIEFBUF9DT05GSUcsXG4gICAgQXV0aENvbmZpZ1xufSBmcm9tICcuL2F1dGguY29uZmlnJztcbmltcG9ydCB7XG4gICAgVG9rZW5TZXJ2aWNlXG59IGZyb20gJy4vdG9rZW4uc2VydmljZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTG9naW5PcHRpb25zIHtcbiAgICBba2V5OiBzdHJpbmddOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVXNlciB7XG4gICAgdWlkOiBzdHJpbmc7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIG5pY2tuYW1lOiBzdHJpbmc7XG4gICAgZW1haWw6IHN0cmluZztcbiAgICB0b2tlbjogc3RyaW5nO1xufVxuXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgQXV0aFNlcnZpY2UgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuXG4gICAgcHJpdmF0ZSBfdXNlciA9IG5ldyBCZWhhdmlvclN1YmplY3QgPCBVc2VyIHwgbnVsbCA+IChudWxsKTtcbiAgICBwcml2YXRlIF9jdXJyZW50U3RhdGU6IHN0cmluZyB8IG51bGwgPSBudWxsOyAvLyBzdG9yZXMgdGhlIHN0cmluZyB0b2tlbiBvciBudWxsIG90aGVyd2lzZSAobG9nb3V0KVxuXG4gICAgcHJpdmF0ZSBfbG9naW5DYWxsYmFja3M6IEZ1bmN0aW9uW10gPSBbXTtcbiAgICBwcml2YXRlIF9sb2dvdXRDYWxsYmFja3M6IEZ1bmN0aW9uW10gPSBbXTtcblxuICAgIHByaXZhdGUgX3VubGlzdGVuRXZlbnRzOiBGdW5jdGlvbltdO1xuXG4gICAgcHJpdmF0ZSBfdGltZW91dElEOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAgIC8vIENvbmZpZ3VyYXRpb25cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9kb21haW46IHN0cmluZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9hcHBVUkw6IHN0cmluZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF90b2tlblVSTDogc3RyaW5nO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2F1dGhVUkw6IHN0cmluZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9zdG9yYWdlVXBkYXRlcjogKG5ld1Rva2VuOiBhbnkpID0+IHZvaWQ7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfc3RvcmFnZVJlbW92ZXI6ICgpID0+IHZvaWQ7XG5cbiAgICAvLyBUaGlzIHR3byBwcm9wZXJ0aWVzIGFyZSB1c2VkIGZvciBpbnRlci13aW5kb3cgY29tbXVuaWNhdGlvbi5cbiAgICAvLyBJdCBpcyBhY2hpZXZlIHRocm91Z2ggdGhlIHVwZGF0ZSBvZiB0aGUgZHVtbXkga2V5IHN0b3JhZ2UgJ19jb21tS2V5TmFtZSdcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jb21tS2V5TmFtZSA9ICdBbmd1bGFyQWFwQXV0aFVwZGF0ZWQnO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2NvbW1LZXlVcGRhdGVyID0gKCkgPT4gbG9jYWxTdG9yYWdlLnNldEl0ZW0odGhpcy5fY29tbUtleU5hbWUsICcnICsgbmV3IERhdGUoKS5nZXRUaW1lKCkpO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgX3JlbmRlcmVyRmFjdG9yeTogUmVuZGVyZXJGYWN0b3J5MixcbiAgICAgICAgcHJpdmF0ZSBfdG9rZW5TZXJ2aWNlOiBUb2tlblNlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgX2h0dHA6IEh0dHBDbGllbnQsXG4gICAgICAgIEBJbmplY3QoQUFQX0NPTkZJRykgcHJpdmF0ZSBjb25maWc6IEF1dGhDb25maWdcbiAgICApIHtcbiAgICAgICAgdGhpcy5fZG9tYWluID0gZW5jb2RlVVJJQ29tcG9uZW50KHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4pO1xuXG4gICAgICAgIHRoaXMuX2FwcFVSTCA9IGNvbmZpZy5hYXBVUkwucmVwbGFjZSgvXFwvJC8sICcnKTtcbiAgICAgICAgdGhpcy5fYXV0aFVSTCA9IGAke3RoaXMuX2FwcFVSTH0vYXV0aGA7XG4gICAgICAgIHRoaXMuX3Rva2VuVVJMID0gYCR7dGhpcy5fYXBwVVJMfS90b2tlbmA7XG5cbiAgICAgICAgdGhpcy5fc3RvcmFnZVVwZGF0ZXIgPSBjb25maWcudG9rZW5VcGRhdGVyO1xuICAgICAgICBpZiAoY29uZmlnLnRva2VuUmVtb3Zlcikge1xuICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIgPSBjb25maWcudG9rZW5SZW1vdmVyO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVJlbW92ZXIgPSAoKSA9PiBjb25maWcudG9rZW5VcGRhdGVyKG51bGwpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcmVuZGVyZXIgPSB0aGlzLl9yZW5kZXJlckZhY3RvcnkuY3JlYXRlUmVuZGVyZXIobnVsbCwgbnVsbCk7XG4gICAgICAgIHRoaXMuX3VubGlzdGVuRXZlbnRzID0gW1xuICAgICAgICAgICAgdGhpcy5fbGlzdGVuTG9naW5NZXNzYWdlKHJlbmRlcmVyKSxcbiAgICAgICAgICAgIHRoaXMuX2xpc3RlbkNoYW5nZXNGcm9tT3RoZXJXaW5kb3dzKHJlbmRlcmVyKVxuICAgICAgICBdO1xuXG4gICAgICAgIHRoaXMuX2N1cnJlbnRTdGF0ZSA9IG51bGw7XG4gICAgICAgIHRoaXMuX3VwZGF0ZVVzZXIoKTsgLy8gVE9ETzogZXhwZXJpbWVudCB3aXRoIHNldFRpbWVPdXRcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkRlc3Ryb3koKSB7XG4gICAgICAgIHRoaXMuX3VubGlzdGVuRXZlbnRzLmZvckVhY2goZm4gPT4gZm4oKSk7XG4gICAgfVxuXG4gICAgcHVibGljIHVzZXIoKTogT2JzZXJ2YWJsZSA8IFVzZXIgfCBudWxsID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fdXNlci5hc09ic2VydmFibGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBvcGVucyBhIHdpbmRvdyBpbnN0ZWFkIG9mIGEgdGFiLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqIEBwYXJhbSB3aWR0aCBQaXhlbCB3aWR0aCBvZiB0aGUgbG9naW4gd2luZG93LlxuICAgICAqIEBwYXJhbSBoZWlnaHQgUGl4ZWwgaGVpZ2h0IG9mIHRoZSBsb2dpbiB3aW5kb3cuXG4gICAgICogQHBhcmFtIHRvcCBQb3NpdGlvbiBvZiB0aGUgdG9wIGNvcm5lcnMuIElmIGl0IGlzIGEgbmVnYXRpdmVcbiAgICAgKiAgICAgICAgICAgICBudW1iZXIgaXQgY2VudHJlcyB0aGUgbG9naW4gd2luZG93IG9uIHRoZSBzY3JlZW4uXG4gICAgICogQHBhcmFtIGxlZnQgUG9zaXRpb24gb2YgdGhlIGxlZnQgY29ybmVycy4gSWYgaXQgaXMgYSBuZWdhdGl2ZVxuICAgICAqICAgICAgICAgICAgIG51bWJlciBpdCBjZW50cmVzIHRoZSBsb2dpbiB3aW5kb3cgb24gdGhlIHNjcmVlbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgb3BlbkxvZ2luV2luZG93KG9wdGlvbnM/OiBMb2dpbk9wdGlvbnMsIHdpZHRoID0gNjUwLCBoZWlnaHQgPSAxMDAwLCBsZWZ0ID0gLTEsIHRvcCA9IC0xKSB7XG4gICAgICAgIGlmIChsZWZ0IDwgMCkge1xuICAgICAgICAgICAgY29uc3Qgc2NyZWVuV2lkdGggPSBzY3JlZW4ud2lkdGg7XG4gICAgICAgICAgICBpZiAoc2NyZWVuV2lkdGggPiB3aWR0aCkge1xuICAgICAgICAgICAgICAgIGxlZnQgPSBNYXRoLnJvdW5kKChzY3JlZW5XaWR0aCAtIHdpZHRoKSAvIDIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0b3AgPCAwKSB7XG4gICAgICAgICAgICBjb25zdCBzY3JlZW5IZWlnaHQgPSBzY3JlZW4uaGVpZ2h0O1xuICAgICAgICAgICAgaWYgKHNjcmVlbkhlaWdodCA+IGhlaWdodCkge1xuICAgICAgICAgICAgICAgIHRvcCA9IE1hdGgucm91bmQoKHNjcmVlbkhlaWdodCAtIGhlaWdodCkgLyAyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHdpbmRvd09wdGlvbnMgPSBbXG4gICAgICAgICAgICBgd2lkdGg9JHt3aWR0aH1gLFxuICAgICAgICAgICAgYGhlaWdodD0ke2hlaWdodH1gLFxuICAgICAgICAgICAgYGxlZnQ9JHtsZWZ0fWAsXG4gICAgICAgICAgICBgdG9wPSR7dG9wfWAsXG4gICAgICAgICAgICAncGVyc29uYWxiYXI9bm8nLFxuICAgICAgICAgICAgJ3Rvb2xiYXI9bm8nLFxuICAgICAgICAgICAgJ3Njcm9sbGJhcnM9eWVzJyxcbiAgICAgICAgICAgICdyZXNpemFibGU9eWVzJyxcbiAgICAgICAgICAgICdkaXJlY3Rvcmllcz1ubycsXG4gICAgICAgICAgICAnbG9jYXRpb249bm8nLFxuICAgICAgICAgICAgJ21lbnViYXI9bm8nLFxuICAgICAgICAgICAgJ3RpdGxlYmFyPW5vJyxcbiAgICAgICAgICAgICd0b29sYmFyPW5vJ1xuICAgICAgICBdO1xuXG4gICAgICAgIGNvbnN0IGxvZ2luV2luZG93ID0gd2luZG93Lm9wZW4odGhpcy5nZXRTU09VUkwob3B0aW9ucyksICdTaWduIGluIHRvIEVsaXhpcicsIHdpbmRvd09wdGlvbnMuam9pbignLCcpKTtcbiAgICAgICAgaWYgKGxvZ2luV2luZG93KSB7XG4gICAgICAgICAgICBsb2dpbldpbmRvdy5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQGRlcHJlY2F0ZWQgdXNlIG9wZW5Mb2dpbldpbmRvdyBtZXRob2QgaW5zdGVhZCAodG9wIGFuZCBsZWZ0IGFyZ3VtZW50cyBhcmUgaW52ZXJ0ZWQgaW4gdGhlIG5ldyBtZXRob2QpXG4gICAgICogc2luY2UgdmVyc2lvbiAxLjAuMC1iZXRhLjUuXG4gICAgICogd2luZG93T3BlbiB3aWxsIGJlIGRlbGV0ZWQgaW4gdmVyc2lvbiAxLjAuMFxuICAgICAqL1xuICAgIHB1YmxpYyB3aW5kb3dPcGVuKG9wdGlvbnM/OiBMb2dpbk9wdGlvbnMsIHdpZHRoID0gNjUwLCBoZWlnaHQgPSAxMDAwLCB0b3AgPSAtMSwgbGVmdCA9IC0xKSB7XG4gICAgICAgIHRoaXMuX2RlcHJlY2F0aW9uV2FybmluZygnd2luZG93T3BlbicsICdvcGVuTG9naW5XaW5kb3cnKTtcbiAgICAgICAgdGhpcy5vcGVuTG9naW5XaW5kb3cob3B0aW9ucywgd2lkdGgsIGhlaWdodCwgbGVmdCwgdG9wKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBvcGVucyBhIHRhYiAoaW4gbW9kZXJuIGJyb3dzZXIpLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtIGxvZ2luT3B0aW9ucyBPcHRpb25zIHBhc3NlZCBhcyBVUkwgcGFyYW1ldGVycyB0byB0aGUgU1NPLlxuICAgICAqL1xuICAgIHB1YmxpYyBvcGVuTG9naW5UYWIob3B0aW9ucz86IExvZ2luT3B0aW9ucykge1xuICAgICAgICBjb25zdCBsb2dpbldpbmRvdyA9IHdpbmRvdy5vcGVuKHRoaXMuZ2V0U1NPVVJMKG9wdGlvbnMpLCAnU2lnbiBpbiB0byBFbGl4aXInKTtcbiAgICAgICAgaWYgKGxvZ2luV2luZG93KSB7XG4gICAgICAgICAgICBsb2dpbldpbmRvdy5mb2N1cygpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQGRlcHJlY2F0ZWQgdXNlIG9wZW5Mb2dpblRhYiBtZXRob2QgaW5zdGVhZFxuICAgICAqIHNpbmNlIHZlcnNpb24gMS4wLjAtYmV0YS41LlxuICAgICAqIHRhYk9wZW4gd2lsbCBiZSBkZWxldGVkIGluIHZlcnNpb24gMS4wLjBcbiAgICAgKi9cbiAgICBwdWJsaWMgdGFiT3BlbihvcHRpb25zPzogTG9naW5PcHRpb25zKSB7XG4gICAgICAgIHRoaXMuX2RlcHJlY2F0aW9uV2FybmluZygndGFiT3BlbicsICdvcGVuTG9naW5UYWInKTtcbiAgICAgICAgdGhpcy5vcGVuTG9naW5UYWIob3B0aW9ucyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUHJvZHVjZXMgYSBVUkwgdGhhdCBhbGxvd3MgbG9nZ2luZyBpbnRvIHRoZSBzaW5nbGUgc2lnbiBvbiAoU1NPKSBwYWdlLlxuICAgICAqIFRoZSBVUkwgY2FucyBiZSBvcGVuZWQgaW4gYSBuZXcgdGFiIHVzaW5nIHRhcmdldD1cIl9ibGFua1wiLFxuICAgICAqIG9yIGluIGEgbmV3IHdpbmRvdyB1c2luZyB3aW5kb3cub3BlbigpLlxuICAgICAqXG4gICAgICogU2VlIG1ldGhvZCBfZmlsdGVyTG9naW5PcHRpb25zIHJlZ2FyZGluZyBzZWN1cml0eSByaXNrcyBvZiBjZXJ0YWluXG4gICAgICogTG9naW5PcHRpb25zLlxuICAgICAqXG4gICAgICogQHBhcmFtICBsb2dpbk9wdGlvbnMgT3B0aW9ucyBwYXNzZWQgYXMgVVJMIHBhcmFtZXRlcnMgdG8gdGhlIFNTTy5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIFRoZSBTU08gVVJMLlxuICAgICAqXG4gICAgICovXG4gICAgcHVibGljIGdldFNTT1VSTChvcHRpb25zPzogTG9naW5PcHRpb25zKTogc3RyaW5nIHtcbiAgICAgICAgY29uc3QgZnJhZ21lbnRzID0gdGhpcy5fZm9ybWF0RnJhZ21lbnRzKHtcbiAgICAgICAgICAgICdmcm9tJzogdGhpcy5fZG9tYWluLFxuICAgICAgICAgICAgLi4ub3B0aW9uc1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGAke3RoaXMuX2FwcFVSTH0vc3NvJHtmcmFnbWVudHN9YDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGdW5jdGlvbnMgdGhhdCBsb2dzIG91dCB0aGUgdXNlci5cbiAgICAgKiBJdCB0cmlnZ2VycyB0aGUgbG9nb3V0IGNhbGxiYWNrcy5cbiAgICAgKiBJdCBpcyBhbiBhcnJvdyBmdW5jdGlvbiAobGFtYmRhKSBiZWNhdXNlIGluIHRoYXQgd2F5IGl0IGhhcyBhIHJlZmVyZW5jZVxuICAgICAqIHRvICd0aGlzJyB3aGVuIHVzZWQgaW4gc2V0VGltZW91dCBjYWxsLlxuICAgICAqL1xuICAgIHB1YmxpYyBsb2dPdXQoKSB7XG4gICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyKCk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZVVzZXIoKTtcblxuICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgIHRoaXMuX2NvbW1LZXlVcGRhdGVyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIEFBUCBhY2NvdW50XG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB1aWQgb2YgdGhlIG5ldyB1c2VyXG4gICAgICovXG4gICAgcHVibGljIGNyZWF0ZUFBUGFjY291bnQobmV3VXNlcjoge1xuICAgICAgICBuYW1lPzogc3RyaW5nLFxuICAgICAgICB1c2VybmFtZTogc3RyaW5nLFxuICAgICAgICBwYXNzd29yZDogc3RyaW5nLFxuICAgICAgICBlbWFpbD86IHN0cmluZyxcbiAgICAgICAgb3JnYW5pemF0aW9uPzogc3RyaW5nXG4gICAgfSk6IE9ic2VydmFibGUgPCBzdHJpbmcgPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9odHRwLnBvc3QodGhpcy5fYXV0aFVSTCwgbmV3VXNlciwge1xuICAgICAgICAgICAgcmVzcG9uc2VUeXBlOiAndGV4dCdcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9naW4gZGlyZWN0bHkgdGhyb3VnaCB0aGUgQUFQXG4gICAgICpcbiAgICAgKiBTZWUgbWV0aG9kIF9maWx0ZXJMb2dpbk9wdGlvbnMgcmVnYXJkaW5nIHNlY3VyaXR5IHJpc2tzIG9mIGNlcnRhaW5cbiAgICAgKiBMb2dpbk9wdGlvbnMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB0cnVlIGlmIHRoZSB1c2VyIHN1Y2Nlc3NmdWxseSBsb2dpbiwgb3RoZXJ3aXNlIGZhbHNlXG4gICAgICovXG4gICAgcHVibGljIGxvZ2luQUFQKFxuICAgICAgICB1c2VyOiB7XG4gICAgICAgICAgICB1c2VybmFtZTogc3RyaW5nLFxuICAgICAgICAgICAgcGFzc3dvcmQ6IHN0cmluZyxcbiAgICAgICAgfSwgb3B0aW9ucz86IExvZ2luT3B0aW9uc1xuICAgICk6IE9ic2VydmFibGUgPCBib29sZWFuID4ge1xuICAgICAgICBjb25zdCBmcmFnbWVudHMgPSB0aGlzLl9mb3JtYXRGcmFnbWVudHMob3B0aW9ucyk7XG4gICAgICAgIHJldHVybiB0aGlzLl9odHRwLmdldChgJHt0aGlzLl9hdXRoVVJMfSR7ZnJhZ21lbnRzfWAsIHtcbiAgICAgICAgICAgIGhlYWRlcnM6IHRoaXMuX2NyZWF0ZUF1dGhIZWFkZXIodXNlciksXG4gICAgICAgICAgICByZXNwb25zZVR5cGU6ICd0ZXh0J1xuICAgICAgICB9KS5waXBlKFxuICAgICAgICAgICAgdGFwKHRva2VuID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLl9zdG9yYWdlUmVtb3ZlcigpO1xuICAgICAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VVcGRhdGVyKHRva2VuKTtcbiAgICAgICAgICAgICAgICB0aGlzLl91cGRhdGVVc2VyKCk7XG5cbiAgICAgICAgICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgICAgICAgICAgdGhpcy5fY29tbUtleVVwZGF0ZXIoKTtcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgbWFwKEJvb2xlYW4pLFxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoYW5nZSBwYXNzd29yZCBBQVAgYWNjb3VudFxuICAgICAqXG4gICAgICogQHJldHVybnMgdHJ1ZSB3aGVuIHBhc3N3b3JkIGlzIHN1Y2Nlc3NmdWxseSBjaGFuZ2VkXG4gICAgICovXG4gICAgcHVibGljIGNoYW5nZVBhc3N3b3JkQUFQKHtcbiAgICAgICAgdXNlcm5hbWUsXG4gICAgICAgIG9sZFBhc3N3b3JkLFxuICAgICAgICBuZXdQYXNzd29yZFxuICAgIH06IHtcbiAgICAgICAgdXNlcm5hbWU6IHN0cmluZyxcbiAgICAgICAgb2xkUGFzc3dvcmQ6IHN0cmluZyxcbiAgICAgICAgbmV3UGFzc3dvcmQ6IHN0cmluZyxcbiAgICB9KTogT2JzZXJ2YWJsZSA8IGJvb2xlYW4gPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9odHRwLnBhdGNoKHRoaXMuX2F1dGhVUkwsIHtcbiAgICAgICAgICAgIHVzZXJuYW1lLFxuICAgICAgICAgICAgcGFzc3dvcmQ6IG5ld1Bhc3N3b3JkXG4gICAgICAgIH0sIHtcbiAgICAgICAgICAgIGhlYWRlcnM6IHRoaXMuX2NyZWF0ZUF1dGhIZWFkZXIoe1xuICAgICAgICAgICAgICAgIHVzZXJuYW1lLFxuICAgICAgICAgICAgICAgIHBhc3N3b3JkOiBvbGRQYXNzd29yZFxuICAgICAgICAgICAgfSlcbiAgICAgICAgfSkucGlwZShcbiAgICAgICAgICAgIG1hcChyZXNwb25zZSA9PiB0cnVlKSAvLyByZXNwb25zZSBpcyBlbXB0eSwgYnV0IGlmIGl0IHJlYWNoZXMgdGhpcyBwb2ludCB0aGUgcmVxdWVzdCBoYXMgc3VjY2Vzc2Z1bGx5IGNvbXBsZXRlZFxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZCBhIGNhbGxiYWNrIHRvIHRoZSBMb2dJbiBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBjYWxsYmFjayBUaGUgRnVuY3Rpb24gY2FsbGVkIHdoZW4gdGhlIGxvZ2luIGV2ZW50IGlzIHRyaWdnZXJlZCBhbmQgdGhlXG4gICAgICogICAgSldUIHRva2VuIGlzIHJlY2VpdmVkIGFuZCBhY2NlcHRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIFRoZSBldmVudCByZWdpc3RyYXRpb24gaWQgKG5lY2Vzc2FyeSB0byB1bnJlZ2lzdGVyIHRoZSBldmVudCkuXG4gICAgICovXG4gICAgcHVibGljIGFkZExvZ0luRXZlbnRMaXN0ZW5lcihjYWxsYmFjazogRnVuY3Rpb24pOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fbG9naW5DYWxsYmFja3MucHVzaChjYWxsYmFjayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGEgY2FsbGJhY2sgZnJvbSB0aGUgTG9nSW4gZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gaWQgVGhlIGlkIGdpdmVuIHdoZW4gZXZlbnQgbGlzdGVuZXIgd2FzIGFkZGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgdHJ1ZSB3aGVuIHJlbW92ZSBzdWNjZXNzZnVsbHksIGZhbHNlIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgcmVtb3ZlTG9nSW5FdmVudExpc3RlbmVyKGlkOiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGRlbGV0ZSB0aGlzLl9sb2dpbkNhbGxiYWNrc1tpZCAtIDFdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZCBhIGNhbGxiYWNrIHRvIHRoZSBMb2dPdXQgZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gY2FsbGJhY2sgVGhlIEZ1bmN0aW9uIGNhbGxlZCB3aGVuIHRoZSBsb2dvdXQgZXZlbnQgaXMgdHJpZ2dlcmVkIGFuZCB0aGVcbiAgICAgKiAgICBKV1QgdG9rZW4gaXMgcmVjZWl2ZWQgYW5kIGFjY2VwdGVkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgVGhlIHJlZ2lzdHJhdGlvbiBpZCAobmVjZXNzYXJ5IHRvIHVucmVnaXN0ZXIgdGhlIGV2ZW50KS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYWRkTG9nT3V0RXZlbnRMaXN0ZW5lcihjYWxsYmFjazogRnVuY3Rpb24pOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fbG9nb3V0Q2FsbGJhY2tzLnB1c2goY2FsbGJhY2spO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIGNhbGxiYWNrIGZyb20gdGhlIExvZ091dCBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBpZCBUaGUgaWQgZ2l2ZW4gd2hlbiBldmVudCBsaXN0ZW5lciB3YXMgYWRkZWQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB0cnVlIHdoZW4gcmVtb3ZlIHN1Y2Nlc3NmdWxseSwgZmFsc2Ugb3RoZXJ3aXNlLlxuICAgICAqL1xuICAgIHB1YmxpYyByZW1vdmVMb2dPdXRFdmVudExpc3RlbmVyKGlkOiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGRlbGV0ZSB0aGlzLl9sb2dvdXRDYWxsYmFja3NbaWQgLSAxXTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWZyZXNoIHRva2VuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB0cnVlIHdoZW4gdG9rZW4gaXMgc3VjY2Vzc2Z1bGx5IHJlZnJlc2hlZFxuICAgICAqL1xuICAgIHB1YmxpYyByZWZyZXNoKCk6IE9ic2VydmFibGUgPCBib29sZWFuID4ge1xuICAgICAgICByZXR1cm4gdGhpcy5faHR0cC5nZXQodGhpcy5fdG9rZW5VUkwsIHtcbiAgICAgICAgICAgIHJlc3BvbnNlVHlwZTogJ3RleHQnXG4gICAgICAgIH0pLnBpcGUoXG4gICAgICAgICAgICB0YXAodG9rZW4gPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuX3N0b3JhZ2VSZW1vdmVyKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5fc3RvcmFnZVVwZGF0ZXIodG9rZW4pO1xuICAgICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVVzZXIoKTtcblxuICAgICAgICAgICAgICAgIC8vIFRyaWdnZXJzIHVwZGF0aW5nIG90aGVyIHdpbmRvd3NcbiAgICAgICAgICAgICAgICB0aGlzLl9jb21tS2V5VXBkYXRlcigpO1xuICAgICAgICAgICAgfSksXG4gICAgICAgICAgICBtYXAoQm9vbGVhbilcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGb3JtYXQgYW5kIGZpbHRlciBmcmFnbWVudCBvcHRpb25zXG4gICAgICpcbiAgICAgKiBAcGFyYW1zIG9wdGlvbnNcbiAgICAgKlxuICAgICAqIEByZXR1cm5zIGZyYWdtZW50IHN0cmluZ1xuICAgICAqL1xuICAgIHByaXZhdGUgX2Zvcm1hdEZyYWdtZW50cyhvcHRpb25zPzogTG9naW5PcHRpb25zKSB7XG4gICAgICAgIGlmIChvcHRpb25zKSB7XG4gICAgICAgICAgICB0aGlzLl9maWx0ZXJMb2dpbk9wdGlvbnMob3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm4gJz8nICsgT2JqZWN0LmVudHJpZXMob3B0aW9ucylcbiAgICAgICAgICAgICAgICAubWFwKChba2V5LCB2YWx1ZV0pID0+IGAke2tleX09JHt2YWx1ZX1gKS5qb2luKCcmJyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gJyc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRmlsdGVycyBvcHRpb25zIHRoYXQgYXJlIHVuc2VjdXJlLlxuICAgICAqXG4gICAgICogU2VlIHRoZSBhZHZhbmNlIG9wdGlvbnMgdGhhdCBjYW4gYmUgcmVxdWVzdGVkIHRocm91Z2ggdGhlIG9wdGlvbnMgcGFyYW1ldGVyOlxuICAgICAqIGh0dHBzOi8vYXBpLmFhaS5lYmkuYWMudWsvZG9jcy9hdXRoZW50aWNhdGlvbi9hdXRoZW50aWNhdGlvbi5pbmRleC5odG1sI19jb21tb25fYXR0cmlidXRlc1xuICAgICAqXG4gICAgICogVGhlIHRpbWUgdG8gbGl2ZSBwYXJhbWVudGVyICh0dGwpIGRlZmF1bHQgdmFsdWUgaXMgNjAgbWludXRlcy4gSXQgaXMgYVxuICAgICAqIGJpZyBzZWN1cml0eSByaXNrIHRvIHJlcXVlc3QgbG9uZ2VyIHR0bC4gSWYgYSB0aGlyZCBwYXJ0eSBnZXRzIGhvbGQgb2ZcbiAgICAgKiBzdWNoIHRva2VuLCBtZWFucyB0aGF0IHRoZXkgY291bGQgdXNlIGl0IGZvciBhIGRheSwgd2VlaywgeWVhclxuICAgICAqIChlc3NlbnRpYWxseSwgbGlrZSBoYXZpbmcgdGhlIHVzZXJuYW1lL3Bhc3N3b3JkKS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSAgbG9naW5PcHRpb25zIE9wdGlvbnMgcGFzc2VkIGFzIFVSTCBwYXJhbWV0ZXJzIHRvIHRoZSBTU08uXG4gICAgICpcbiAgICAgKlxuICAgICAqL1xuICAgIHByaXZhdGUgX2ZpbHRlckxvZ2luT3B0aW9ucyhvcHRpb25zOiBMb2dpbk9wdGlvbnMpIHtcbiAgICAgICAgaWYgKE9iamVjdC5rZXlzKG9wdGlvbnMpLmluZGV4T2YoJ3R0bCcpID4gLTEpIHtcbiAgICAgICAgICAgIGNvbnN0IHR0bDogbnVtYmVyID0gK29wdGlvbnNbJ3R0bCddO1xuICAgICAgICAgICAgY29uc3Qgc29mdExpbWl0ID0gNjA7XG4gICAgICAgICAgICBjb25zdCBoYXJkTGltaXQgPSA2MCAqIDI0O1xuICAgICAgICAgICAgaWYgKHR0bCA+IGhhcmRMaW1pdCkge1xuICAgICAgICAgICAgICAgIHRocm93IChuZXcgRXJyb3IoYExvZ2luIHJlcXVlc3RlZCB3aXRoIGFuIGV4cGlyYXRpb24gbG9uZ2VyIHRoYW4gJHtoYXJkTGltaXR9IG1pbnV0ZXMhIFRoaXMgaXMgbm90IGFsbG93ZWQuYCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHR0bCA+IHNvZnRMaW1pdCkge1xuICAgICAgICAgICAgICAgIHdpbmRvdy5jb25zb2xlLndhcm4oYExvZ2luIHJlcXVlc3RlZCB3aXRoIGFuIGV4cGlyYXRpb24gbG9uZ2VyIHRoYW4gJHtzb2Z0TGltaXR9IG1pbnV0ZXMhYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIEF1dGhvcml6YXRpb24gaGVhZGVyXG4gICAgICpcbiAgICAgKiBAcGFyYW0gb2JqZWN0IHdpdGggdXNlcm5hbWUgYW5kIHBhc3N3b3JkLlxuICAgICAqXG4gICAgICogQHJldHVybnMgTmV3IGF1dGhvcml6YXRpb24gaGVhZGVyXG4gICAgICovXG4gICAgcHJpdmF0ZSBfY3JlYXRlQXV0aEhlYWRlcih7XG4gICAgICAgIHVzZXJuYW1lLFxuICAgICAgICBwYXNzd29yZFxuICAgIH06IHtcbiAgICAgICAgdXNlcm5hbWU6IHN0cmluZyxcbiAgICAgICAgcGFzc3dvcmQ6IHN0cmluZyxcbiAgICB9KSB7XG4gICAgICAgIGNvbnN0IGF1dGhUb2tlbiA9IGJ0b2EoYCR7dXNlcm5hbWV9OiR7cGFzc3dvcmR9YCk7XG4gICAgICAgIHJldHVybiBuZXcgSHR0cEhlYWRlcnMoe1xuICAgICAgICAgICAgJ0F1dGhvcml6YXRpb24nOiBgQmFzaWMgJHthdXRoVG9rZW59YFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMaXN0ZW4gZm9yIGxvZ2luIG1lc3NhZ2VzIGZyb20gb3RoZXIgd2luZG93cy5cbiAgICAgKiBUaGVzZSBtZXNzYWdlcyBjb250YWluIHRoZSB0b2tlbnMgZnJvbSB0aGUgQUFQLlxuICAgICAqIElmIGEgdG9rZW4gaXMgcmVjZWl2ZWQgdGhlbiB0aGUgY2FsbGJhY2tzIGFyZSB0cmlnZ2VyZWQuXG4gICAgICovXG4gICAgcHJpdmF0ZSBfbGlzdGVuTG9naW5NZXNzYWdlKHJlbmRlcmVyOiBSZW5kZXJlcjIpOiBGdW5jdGlvbiB7XG4gICAgICAgIHJldHVybiByZW5kZXJlci5saXN0ZW4oJ3dpbmRvdycsICdtZXNzYWdlJywgKGV2ZW50OiBNZXNzYWdlRXZlbnQpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5fbWVzc2FnZUlzQWNjZXB0YWJsZShldmVudCkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLl9zdG9yYWdlVXBkYXRlcihldmVudC5kYXRhKTtcblxuICAgICAgICAgICAgLy8gSSBkb24ndCBrbm93IGhvdyB0byB0eXBlIGd1YXJkIGV2ZW50LnNvdXJjZVxuICAgICAgICAgICAgLy8gVGhpcyBkb2Vzbid0IHdvcmtcbiAgICAgICAgICAgIC8vIGlmIChldmVudC5zb3VyY2UgaW5zdGFuY2VvZiBXaW5kb3cpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5zb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAoZXZlbnQuc291cmNlIGFzIFdpbmRvdykuY2xvc2UoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5fdXBkYXRlVXNlcigpO1xuXG4gICAgICAgICAgICAvLyBUcmlnZ2VycyB1cGRhdGluZyBvdGhlciB3aW5kb3dzXG4gICAgICAgICAgICB0aGlzLl9jb21tS2V5VXBkYXRlcigpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKiogTGlzdGVuIHRvIGNoYW5nZXMgaW4gdGhlIHRva2VuIGZyb20gKm90aGVyKiB3aW5kb3dzLlxuICAgICAqXG4gICAgICogRm9yIGludGVyLXdpbmRvdyBjb21tdW5pY2F0aW9uIG1lc3NhZ2VzIGFyZSB0cmFuc21pdHRlZCB0cm91Z2ggY2hhbmdlc1xuICAgICAqIG9uIGEgZHVtbXkgc3RvcmFnZ