UNPKG

angular-aap-auth

Version:

## This library is deprecated. Please, use [ng-ebi-authorization](https://www.npmjs.com/package/ng-ebi-authorization).

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