UNPKG

angular-auth-oidc-client

Version:
259 lines 55.3 kB
import { Injectable, inject } from '@angular/core'; import { of } from 'rxjs'; import { map, mergeMap } from 'rxjs/operators'; import { LoggerService } from '../logging/logger.service'; import { StoragePersistenceService } from '../storage/storage-persistence.service'; import { EqualityService } from '../utils/equality/equality.service'; import { FlowHelper } from '../utils/flowHelper/flow-helper.service'; import { TokenHelperService } from '../utils/tokenHelper/token-helper.service'; import { StateValidationResult } from './state-validation-result'; import { TokenValidationService } from './token-validation.service'; import { ValidationResult } from './validation-result'; import * as i0 from "@angular/core"; export class StateValidationService { constructor() { this.storagePersistenceService = inject(StoragePersistenceService); this.tokenValidationService = inject(TokenValidationService); this.tokenHelperService = inject(TokenHelperService); this.loggerService = inject(LoggerService); this.equalityService = inject(EqualityService); this.flowHelper = inject(FlowHelper); } getValidatedStateResult(callbackContext, configuration) { const hasError = Boolean(callbackContext.authResult?.error); const hasCallbackContext = Boolean(callbackContext); if (!hasCallbackContext || hasError) { return of(new StateValidationResult('', '', false, {})); } return this.validateState(callbackContext, configuration); } validateState(callbackContext, configuration) { const toReturn = new StateValidationResult(); const authStateControl = this.storagePersistenceService.read('authStateControl', configuration); if (!this.tokenValidationService.validateStateFromHashCallback(callbackContext.authResult?.state, authStateControl, configuration)) { this.loggerService.logWarning(configuration, 'authCallback incorrect state'); toReturn.state = ValidationResult.StatesDoNotMatch; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } const isCurrentFlowImplicitFlowWithAccessToken = this.flowHelper.isCurrentFlowImplicitFlowWithAccessToken(configuration); const isCurrentFlowCodeFlow = this.flowHelper.isCurrentFlowCodeFlow(configuration); if (isCurrentFlowImplicitFlowWithAccessToken || isCurrentFlowCodeFlow) { toReturn.accessToken = callbackContext.authResult?.access_token ?? ''; } const disableIdTokenValidation = configuration.disableIdTokenValidation; if (disableIdTokenValidation) { toReturn.state = ValidationResult.Ok; toReturn.authResponseIsValid = true; return of(toReturn); } const isInRefreshTokenFlow = callbackContext.isRenewProcess && !!callbackContext.refreshToken; const hasIdToken = Boolean(callbackContext.authResult?.id_token); if (isInRefreshTokenFlow && !hasIdToken) { toReturn.state = ValidationResult.Ok; toReturn.authResponseIsValid = true; return of(toReturn); } if (hasIdToken) { const { clientId, issValidationOff, maxIdTokenIatOffsetAllowedInSeconds, disableIatOffsetValidation, ignoreNonceAfterRefresh, renewTimeBeforeTokenExpiresInSeconds, } = configuration; toReturn.idToken = callbackContext.authResult?.id_token ?? ''; toReturn.decodedIdToken = this.tokenHelperService.getPayloadFromToken(toReturn.idToken, false, configuration); return this.tokenValidationService .validateSignatureIdToken(toReturn.idToken, callbackContext.jwtKeys, configuration) .pipe(mergeMap((isSignatureIdTokenValid) => { if (!isSignatureIdTokenValid) { this.loggerService.logDebug(configuration, 'authCallback Signature validation failed id_token'); toReturn.state = ValidationResult.SignatureFailed; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } const authNonce = this.storagePersistenceService.read('authNonce', configuration); if (!this.tokenValidationService.validateIdTokenNonce(toReturn.decodedIdToken, authNonce, Boolean(ignoreNonceAfterRefresh), configuration)) { this.loggerService.logWarning(configuration, 'authCallback incorrect nonce, did you call the checkAuth() method multiple times?'); toReturn.state = ValidationResult.IncorrectNonce; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!this.tokenValidationService.validateRequiredIdToken(toReturn.decodedIdToken, configuration)) { this.loggerService.logDebug(configuration, 'authCallback Validation, one of the REQUIRED properties missing from id_token'); toReturn.state = ValidationResult.RequiredPropertyMissing; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!isInRefreshTokenFlow && !this.tokenValidationService.validateIdTokenIatMaxOffset(toReturn.decodedIdToken, maxIdTokenIatOffsetAllowedInSeconds ?? 120, Boolean(disableIatOffsetValidation), configuration)) { this.loggerService.logWarning(configuration, 'authCallback Validation, iat rejected id_token was issued too far away from the current time'); toReturn.state = ValidationResult.MaxOffsetExpired; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); if (authWellKnownEndPoints) { if (issValidationOff) { this.loggerService.logDebug(configuration, 'iss validation is turned off, this is not recommended!'); } else if (!issValidationOff && !this.tokenValidationService.validateIdTokenIss(toReturn.decodedIdToken, authWellKnownEndPoints.issuer, configuration)) { this.loggerService.logWarning(configuration, 'authCallback incorrect iss does not match authWellKnownEndpoints issuer'); toReturn.state = ValidationResult.IssDoesNotMatchIssuer; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } } else { this.loggerService.logWarning(configuration, 'authWellKnownEndpoints is undefined'); toReturn.state = ValidationResult.NoAuthWellKnownEndPoints; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!this.tokenValidationService.validateIdTokenAud(toReturn.decodedIdToken, clientId, configuration)) { this.loggerService.logWarning(configuration, 'authCallback incorrect aud'); toReturn.state = ValidationResult.IncorrectAud; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!this.tokenValidationService.validateIdTokenAzpExistsIfMoreThanOneAud(toReturn.decodedIdToken)) { this.loggerService.logWarning(configuration, 'authCallback missing azp'); toReturn.state = ValidationResult.IncorrectAzp; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!this.tokenValidationService.validateIdTokenAzpValid(toReturn.decodedIdToken, clientId)) { this.loggerService.logWarning(configuration, 'authCallback incorrect azp'); toReturn.state = ValidationResult.IncorrectAzp; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!this.isIdTokenAfterRefreshTokenRequestValid(callbackContext, toReturn.decodedIdToken, configuration)) { this.loggerService.logWarning(configuration, 'authCallback pre, post id_token claims do not match in refresh'); toReturn.state = ValidationResult.IncorrectIdTokenClaimsAfterRefresh; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } if (!isInRefreshTokenFlow && !this.tokenValidationService.validateIdTokenExpNotExpired(toReturn.decodedIdToken, configuration, renewTimeBeforeTokenExpiresInSeconds)) { this.loggerService.logWarning(configuration, 'authCallback id token expired'); toReturn.state = ValidationResult.TokenExpired; this.handleUnsuccessfulValidation(configuration); return of(toReturn); } return this.validateDefault(isCurrentFlowImplicitFlowWithAccessToken, isCurrentFlowCodeFlow, toReturn, configuration, callbackContext); })); } else { this.loggerService.logDebug(configuration, 'No id_token found, skipping id_token validation'); } return this.validateDefault(isCurrentFlowImplicitFlowWithAccessToken, isCurrentFlowCodeFlow, toReturn, configuration, callbackContext); } validateDefault(isCurrentFlowImplicitFlowWithAccessToken, isCurrentFlowCodeFlow, toReturn, configuration, callbackContext) { // flow id_token if (!isCurrentFlowImplicitFlowWithAccessToken && !isCurrentFlowCodeFlow) { toReturn.authResponseIsValid = true; toReturn.state = ValidationResult.Ok; this.handleSuccessfulValidation(configuration); this.handleUnsuccessfulValidation(configuration); return of(toReturn); } // only do check if id_token returned, no always the case when using refresh tokens if (callbackContext.authResult?.id_token) { const idTokenHeader = this.tokenHelperService.getHeaderFromToken(toReturn.idToken, false, configuration); if (isCurrentFlowCodeFlow && !toReturn.decodedIdToken.at_hash) { this.loggerService.logDebug(configuration, 'Code Flow active, and no at_hash in the id_token, skipping check!'); } else { return this.tokenValidationService .validateIdTokenAtHash(toReturn.accessToken, toReturn.decodedIdToken.at_hash, idTokenHeader.alg, // 'RS256' configuration) .pipe(map((valid) => { if (!valid || !toReturn.accessToken) { this.loggerService.logWarning(configuration, 'authCallback incorrect at_hash'); toReturn.state = ValidationResult.IncorrectAtHash; this.handleUnsuccessfulValidation(configuration); return toReturn; } else { toReturn.authResponseIsValid = true; toReturn.state = ValidationResult.Ok; this.handleSuccessfulValidation(configuration); return toReturn; } })); } } toReturn.authResponseIsValid = true; toReturn.state = ValidationResult.Ok; this.handleSuccessfulValidation(configuration); return of(toReturn); } isIdTokenAfterRefreshTokenRequestValid(callbackContext, newIdToken, configuration) { const { useRefreshToken, disableRefreshIdTokenAuthTimeValidation } = configuration; if (!useRefreshToken) { return true; } if (!callbackContext.existingIdToken) { return true; } const decodedIdToken = this.tokenHelperService.getPayloadFromToken(callbackContext.existingIdToken, false, configuration); // Upon successful validation of the Refresh Token, the response body is the Token Response of Section 3.1.3.3 // except that it might not contain an id_token. // If an ID Token is returned as a result of a token refresh request, the following requirements apply: // its iss Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, if (decodedIdToken.iss !== newIdToken.iss) { this.loggerService.logDebug(configuration, `iss do not match: ${decodedIdToken.iss} ${newIdToken.iss}`); return false; } // its azp Claim Value MUST be the same as in the ID Token issued when the original authentication occurred; // if no azp Claim was present in the original ID Token, one MUST NOT be present in the new ID Token, and // otherwise, the same rules apply as apply when issuing an ID Token at the time of the original authentication. if (decodedIdToken.azp !== newIdToken.azp) { this.loggerService.logDebug(configuration, `azp do not match: ${decodedIdToken.azp} ${newIdToken.azp}`); return false; } // its sub Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, if (decodedIdToken.sub !== newIdToken.sub) { this.loggerService.logDebug(configuration, `sub do not match: ${decodedIdToken.sub} ${newIdToken.sub}`); return false; } // its aud Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, if (!this.equalityService.isStringEqualOrNonOrderedArrayEqual(decodedIdToken?.aud, newIdToken?.aud)) { this.loggerService.logDebug(configuration, `aud in new id_token is not valid: '${decodedIdToken?.aud}' '${newIdToken.aud}'`); return false; } if (disableRefreshIdTokenAuthTimeValidation) { return true; } // its iat Claim MUST represent the time that the new ID Token is issued, // if the ID Token contains an auth_time Claim, its value MUST represent the time of the original authentication // - not the time that the new ID token is issued, if (decodedIdToken.auth_time !== newIdToken.auth_time) { this.loggerService.logDebug(configuration, `auth_time do not match: ${decodedIdToken.auth_time} ${newIdToken.auth_time}`); return false; } return true; } handleSuccessfulValidation(configuration) { const { autoCleanStateAfterAuthentication } = configuration; this.storagePersistenceService.write('authNonce', null, configuration); if (autoCleanStateAfterAuthentication) { this.storagePersistenceService.write('authStateControl', '', configuration); } this.loggerService.logDebug(configuration, 'authCallback token(s) validated, continue'); } handleUnsuccessfulValidation(configuration) { const { autoCleanStateAfterAuthentication } = configuration; this.storagePersistenceService.write('authNonce', null, configuration); if (autoCleanStateAfterAuthentication) { this.storagePersistenceService.write('authStateControl', '', configuration); } this.loggerService.logDebug(configuration, 'authCallback token(s) invalid'); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: StateValidationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: StateValidationService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: StateValidationService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGUtdmFsaWRhdGlvbi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1hdXRoLW9pZGMtY2xpZW50L3NyYy9saWIvdmFsaWRhdGlvbi9zdGF0ZS12YWxpZGF0aW9uLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUFjLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN0QyxPQUFPLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRy9DLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMxRCxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUNuRixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDckUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQy9FLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2xFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDOztBQUd2RCxNQUFNLE9BQU8sc0JBQXNCO0lBRG5DO1FBRW1CLDhCQUF5QixHQUFHLE1BQU0sQ0FDakQseUJBQXlCLENBQzFCLENBQUM7UUFFZSwyQkFBc0IsR0FBRyxNQUFNLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUV4RCx1QkFBa0IsR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUVoRCxrQkFBYSxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMs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RGxDLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBvZiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgbWFwLCBtZXJnZU1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IE9wZW5JZENvbmZpZ3VyYXRpb24gfSBmcm9tICcuLi9jb25maWcvb3BlbmlkLWNvbmZpZ3VyYXRpb24nO1xuaW1wb3J0IHsgQ2FsbGJhY2tDb250ZXh0IH0gZnJvbSAnLi4vZmxvd3MvY2FsbGJhY2stY29udGV4dCc7XG5pbXBvcnQgeyBMb2dnZXJTZXJ2aWNlIH0gZnJvbSAnLi4vbG9nZ2luZy9sb2dnZXIuc2VydmljZSc7XG5pbXBvcnQgeyBTdG9yYWdlUGVyc2lzdGVuY2VTZXJ2aWNlIH0gZnJvbSAnLi4vc3RvcmFnZS9zdG9yYWdlLXBlcnNpc3RlbmNlLnNlcnZpY2UnO1xuaW1wb3J0IHsgRXF1YWxpdHlTZXJ2aWNlIH0gZnJvbSAnLi4vdXRpbHMvZXF1YWxpdHkvZXF1YWxpdHkuc2VydmljZSc7XG5pbXBvcnQgeyBGbG93SGVscGVyIH0gZnJvbSAnLi4vdXRpbHMvZmxvd0hlbHBlci9mbG93LWhlbHBlci5zZXJ2aWNlJztcbmltcG9ydCB7IFRva2VuSGVscGVyU2VydmljZSB9IGZyb20gJy4uL3V0aWxzL3Rva2VuSGVscGVyL3Rva2VuLWhlbHBlci5zZXJ2aWNlJztcbmltcG9ydCB7IFN0YXRlVmFsaWRhdGlvblJlc3VsdCB9IGZyb20gJy4vc3RhdGUtdmFsaWRhdGlvbi1yZXN1bHQnO1xuaW1wb3J0IHsgVG9rZW5WYWxpZGF0aW9uU2VydmljZSB9IGZyb20gJy4vdG9rZW4tdmFsaWRhdGlvbi5zZXJ2aWNlJztcbmltcG9ydCB7IFZhbGlkYXRpb25SZXN1bHQgfSBmcm9tICcuL3ZhbGlkYXRpb24tcmVzdWx0JztcblxuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcbmV4cG9ydCBjbGFzcyBTdGF0ZVZhbGlkYXRpb25TZXJ2aWNlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBzdG9yYWdlUGVyc2lzdGVuY2VTZXJ2aWNlID0gaW5qZWN0KFxuICAgIFN0b3JhZ2VQZXJzaXN0ZW5jZVNlcnZpY2VcbiAgKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IHRva2VuVmFsaWRhdGlvblNlcnZpY2UgPSBpbmplY3QoVG9rZW5WYWxpZGF0aW9uU2VydmljZSk7XG5cbiAgcHJpdmF0ZSByZWFkb25seSB0b2tlbkhlbHBlclNlcnZpY2UgPSBpbmplY3QoVG9rZW5IZWxwZXJTZXJ2aWNlKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlclNlcnZpY2UgPSBpbmplY3QoTG9nZ2VyU2VydmljZSk7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBlcXVhbGl0eVNlcnZpY2UgPSBpbmplY3QoRXF1YWxpdHlTZXJ2aWNlKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGZsb3dIZWxwZXIgPSBpbmplY3QoRmxvd0hlbHBlcik7XG5cbiAgZ2V0VmFsaWRhdGVkU3RhdGVSZXN1bHQoXG4gICAgY2FsbGJhY2tDb250ZXh0OiBDYWxsYmFja0NvbnRleHQsXG4gICAgY29uZmlndXJhdGlvbjogT3BlbklkQ29uZmlndXJhdGlvblxuICApOiBPYnNlcnZhYmxlPFN0YXRlVmFsaWRhdGlvblJlc3VsdD4ge1xuICAgIGNvbnN0IGhhc0Vycm9yID0gQm9vbGVhbihjYWxsYmFja0NvbnRleHQuYXV0aFJlc3VsdD8uZXJyb3IpO1xuICAgIGNvbnN0IGhhc0NhbGxiYWNrQ29udGV4dCA9IEJvb2xlYW4oY2FsbGJhY2tDb250ZXh0KTtcblxuICAgIGlmICghaGFzQ2FsbGJhY2tDb250ZXh0IHx8IGhhc0Vycm9yKSB7XG4gICAgICByZXR1cm4gb2YobmV3IFN0YXRlVmFsaWRhdGlvblJlc3VsdCgnJywgJycsIGZhbHNlLCB7fSkpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnZhbGlkYXRlU3RhdGUoY2FsbGJhY2tDb250ZXh0LCBjb25maWd1cmF0aW9uKTtcbiAgfVxuXG4gIHByaXZhdGUgdmFsaWRhdGVTdGF0ZShcbiAgICBjYWxsYmFja0NvbnRleHQ6IENhbGxiYWNrQ29udGV4dCxcbiAgICBjb25maWd1cmF0aW9uOiBPcGVuSWRDb25maWd1cmF0aW9uXG4gICk6IE9ic2VydmFibGU8U3RhdGVWYWxpZGF0aW9uUmVzdWx0PiB7XG4gICAgY29uc3QgdG9SZXR1cm4gPSBuZXcgU3RhdGVWYWxpZGF0aW9uUmVzdWx0KCk7XG4gICAgY29uc3QgYXV0aFN0YXRlQ29udHJvbCA9IHRoaXMuc3RvcmFnZVBlcnNpc3RlbmNlU2VydmljZS5yZWFkKFxuICAgICAgJ2F1dGhTdGF0ZUNvbnRyb2wnLFxuICAgICAgY29uZmlndXJhdGlvblxuICAgICk7XG5cbiAgICBpZiAoXG4gICAgICAhdGhpcy50b2tlblZhbGlkYXRpb25TZXJ2aWNlLnZhbGlkYXRlU3RhdGVGcm9tSGFzaENhbGxiYWNrKFxuICAgICAgICBjYWxsYmFja0NvbnRleHQuYXV0aFJlc3VsdD8uc3RhdGUsXG4gICAgICAgIGF1dGhTdGF0ZUNvbnRyb2wsXG4gICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgIClcbiAgICApIHtcbiAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dXYXJuaW5nKFxuICAgICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgICAnYXV0aENhbGxiYWNrIGluY29ycmVjdCBzdGF0ZSdcbiAgICAgICk7XG4gICAgICB0b1JldHVybi5zdGF0ZSA9IFZhbGlkYXRpb25SZXN1bHQuU3RhdGVzRG9Ob3RNYXRjaDtcbiAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgcmV0dXJuIG9mKHRvUmV0dXJuKTtcbiAgICB9XG5cbiAgICBjb25zdCBpc0N1cnJlbnRGbG93SW1wbGljaXRGbG93V2l0aEFjY2Vzc1Rva2VuID1cbiAgICAgIHRoaXMuZmxvd0hlbHBlci5pc0N1cnJlbnRGbG93SW1wbGljaXRGbG93V2l0aEFjY2Vzc1Rva2VuKGNvbmZpZ3VyYXRpb24pO1xuICAgIGNvbnN0IGlzQ3VycmVudEZsb3dDb2RlRmxvdyA9XG4gICAgICB0aGlzLmZsb3dIZWxwZXIuaXNDdXJyZW50Rmxvd0NvZGVGbG93KGNvbmZpZ3VyYXRpb24pO1xuXG4gICAgaWYgKGlzQ3VycmVudEZsb3dJbXBsaWNpdEZsb3dXaXRoQWNjZXNzVG9rZW4gfHwgaXNDdXJyZW50Rmxvd0NvZGVGbG93KSB7XG4gICAgICB0b1JldHVybi5hY2Nlc3NUb2tlbiA9IGNhbGxiYWNrQ29udGV4dC5hdXRoUmVzdWx0Py5hY2Nlc3NfdG9rZW4gPz8gJyc7XG4gICAgfVxuXG4gICAgY29uc3QgZGlzYWJsZUlkVG9rZW5WYWxpZGF0aW9uID0gY29uZmlndXJhdGlvbi5kaXNhYmxlSWRUb2tlblZhbGlkYXRpb247XG5cbiAgICBpZiAoZGlzYWJsZUlkVG9rZW5WYWxpZGF0aW9uKSB7XG4gICAgICB0b1JldHVybi5zdGF0ZSA9IFZhbGlkYXRpb25SZXN1bHQuT2s7XG4gICAgICB0b1JldHVybi5hdXRoUmVzcG9uc2VJc1ZhbGlkID0gdHJ1ZTtcblxuICAgICAgcmV0dXJuIG9mKHRvUmV0dXJuKTtcbiAgICB9XG5cbiAgICBjb25zdCBpc0luUmVmcmVzaFRva2VuRmxvdyA9XG4gICAgICBjYWxsYmFja0NvbnRleHQuaXNSZW5ld1Byb2Nlc3MgJiYgISFjYWxsYmFja0NvbnRleHQucmVmcmVzaFRva2VuO1xuICAgIGNvbnN0IGhhc0lkVG9rZW4gPSBCb29sZWFuKGNhbGxiYWNrQ29udGV4dC5hdXRoUmVzdWx0Py5pZF90b2tlbik7XG5cbiAgICBpZiAoaXNJblJlZnJlc2hUb2tlbkZsb3cgJiYgIWhhc0lkVG9rZW4pIHtcbiAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5PaztcbiAgICAgIHRvUmV0dXJuLmF1dGhSZXNwb25zZUlzVmFsaWQgPSB0cnVlO1xuXG4gICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgIH1cblxuICAgIGlmIChoYXNJZFRva2VuKSB7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGNsaWVudElkLFxuICAgICAgICBpc3NWYWxpZGF0aW9uT2ZmLFxuICAgICAgICBtYXhJZFRva2VuSWF0T2Zmc2V0QWxsb3dlZEluU2Vjb25kcyxcbiAgICAgICAgZGlzYWJsZUlhdE9mZnNldFZhbGlkYXRpb24sXG4gICAgICAgIGlnbm9yZU5vbmNlQWZ0ZXJSZWZyZXNoLFxuICAgICAgICByZW5ld1RpbWVCZWZvcmVUb2tlbkV4cGlyZXNJblNlY29uZHMsXG4gICAgICB9ID0gY29uZmlndXJhdGlvbjtcblxuICAgICAgdG9SZXR1cm4uaWRUb2tlbiA9IGNhbGxiYWNrQ29udGV4dC5hdXRoUmVzdWx0Py5pZF90b2tlbiA/PyAnJztcbiAgICAgIHRvUmV0dXJuLmRlY29kZWRJZFRva2VuID0gdGhpcy50b2tlbkhlbHBlclNlcnZpY2UuZ2V0UGF5bG9hZEZyb21Ub2tlbihcbiAgICAgICAgdG9SZXR1cm4uaWRUb2tlbixcbiAgICAgICAgZmFsc2UsXG4gICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICk7XG5cbiAgICAgIHJldHVybiB0aGlzLnRva2VuVmFsaWRhdGlvblNlcnZpY2VcbiAgICAgICAgLnZhbGlkYXRlU2lnbmF0dXJlSWRUb2tlbihcbiAgICAgICAgICB0b1JldHVybi5pZFRva2VuLFxuICAgICAgICAgIGNhbGxiYWNrQ29udGV4dC5qd3RLZXlzLFxuICAgICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgKVxuICAgICAgICAucGlwZShcbiAgICAgICAgICBtZXJnZU1hcCgoaXNTaWduYXR1cmVJZFRva2VuVmFsaWQ6IGJvb2xlYW4pID0+IHtcbiAgICAgICAgICAgIGlmICghaXNTaWduYXR1cmVJZFRva2VuVmFsaWQpIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgICAgICAgJ2F1dGhDYWxsYmFjayBTaWduYXR1cmUgdmFsaWRhdGlvbiBmYWlsZWQgaWRfdG9rZW4nXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5TaWduYXR1cmVGYWlsZWQ7XG4gICAgICAgICAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgICAgICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBhdXRoTm9uY2UgPSB0aGlzLnN0b3JhZ2VQZXJzaXN0ZW5jZVNlcnZpY2UucmVhZChcbiAgICAgICAgICAgICAgJ2F1dGhOb25jZScsXG4gICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgIXRoaXMudG9rZW5WYWxpZGF0aW9uU2VydmljZS52YWxpZGF0ZUlkVG9rZW5Ob25jZShcbiAgICAgICAgICAgICAgICB0b1JldHVybi5kZWNvZGVkSWRUb2tlbixcbiAgICAgICAgICAgICAgICBhdXRoTm9uY2UsXG4gICAgICAgICAgICAgICAgQm9vbGVhbihpZ25vcmVOb25jZUFmdGVyUmVmcmVzaCksXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvblxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ1dhcm5pbmcoXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAnYXV0aENhbGxiYWNrIGluY29ycmVjdCBub25jZSwgZGlkIHlvdSBjYWxsIHRoZSBjaGVja0F1dGgoKSBtZXRob2QgbXVsdGlwbGUgdGltZXM/J1xuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICB0b1JldHVybi5zdGF0ZSA9IFZhbGlkYXRpb25SZXN1bHQuSW5jb3JyZWN0Tm9uY2U7XG4gICAgICAgICAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgICAgICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICF0aGlzLnRva2VuVmFsaWRhdGlvblNlcnZpY2UudmFsaWRhdGVSZXF1aXJlZElkVG9rZW4oXG4gICAgICAgICAgICAgICAgdG9SZXR1cm4uZGVjb2RlZElkVG9rZW4sXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvblxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgICAgICAgJ2F1dGhDYWxsYmFjayBWYWxpZGF0aW9uLCBvbmUgb2YgdGhlIFJFUVVJUkVEIHByb3BlcnRpZXMgbWlzc2luZyBmcm9tIGlkX3Rva2VuJ1xuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICB0b1JldHVybi5zdGF0ZSA9IFZhbGlkYXRpb25SZXN1bHQuUmVxdWlyZWRQcm9wZXJ0eU1pc3Npbmc7XG4gICAgICAgICAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgICAgICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICFpc0luUmVmcmVzaFRva2VuRmxvdyAmJlxuICAgICAgICAgICAgICAhdGhpcy50b2tlblZhbGlkYXRpb25TZXJ2aWNlLnZhbGlkYXRlSWRUb2tlbklhdE1heE9mZnNldChcbiAgICAgICAgICAgICAgICB0b1JldHVybi5kZWNvZGVkSWRUb2tlbixcbiAgICAgICAgICAgICAgICBtYXhJZFRva2VuSWF0T2Zmc2V0QWxsb3dlZEluU2Vjb25kcyA/PyAxMjAsXG4gICAgICAgICAgICAgICAgQm9vbGVhbihkaXNhYmxlSWF0T2Zmc2V0VmFsaWRhdGlvbiksXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvblxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ1dhcm5pbmcoXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAnYXV0aENhbGxiYWNrIFZhbGlkYXRpb24sIGlhdCByZWplY3RlZCBpZF90b2tlbiB3YXMgaXNzdWVkIHRvbyBmYXIgYXdheSBmcm9tIHRoZSBjdXJyZW50IHRpbWUnXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5NYXhPZmZzZXRFeHBpcmVkO1xuICAgICAgICAgICAgICB0aGlzLmhhbmRsZVVuc3VjY2Vzc2Z1bFZhbGlkYXRpb24oY29uZmlndXJhdGlvbik7XG5cbiAgICAgICAgICAgICAgcmV0dXJuIG9mKHRvUmV0dXJuKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgYXV0aFdlbGxLbm93bkVuZFBvaW50cyA9IHRoaXMuc3RvcmFnZVBlcnNpc3RlbmNlU2VydmljZS5yZWFkKFxuICAgICAgICAgICAgICAnYXV0aFdlbGxLbm93bkVuZFBvaW50cycsXG4gICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGlmIChhdXRoV2VsbEtub3duRW5kUG9pbnRzKSB7XG4gICAgICAgICAgICAgIGlmIChpc3NWYWxpZGF0aW9uT2ZmKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ0RlYnVnKFxuICAgICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAgICdpc3MgdmFsaWRhdGlvbiBpcyB0dXJuZWQgb2ZmLCB0aGlzIGlzIG5vdCByZWNvbW1lbmRlZCEnXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICAgICAgICAhaXNzVmFsaWRhdGlvbk9mZiAmJlxuICAgICAgICAgICAgICAgICF0aGlzLnRva2VuVmFsaWRhdGlvblNlcnZpY2UudmFsaWRhdGVJZFRva2VuSXNzKFxuICAgICAgICAgICAgICAgICAgdG9SZXR1cm4uZGVjb2RlZElkVG9rZW4sXG4gICAgICAgICAgICAgICAgICBhdXRoV2VsbEtub3duRW5kUG9pbnRzLmlzc3VlcixcbiAgICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dXYXJuaW5nKFxuICAgICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAgICdhdXRoQ2FsbGJhY2sgaW5jb3JyZWN0IGlzcyBkb2VzIG5vdCBtYXRjaCBhdXRoV2VsbEtub3duRW5kcG9pbnRzIGlzc3VlcidcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5Jc3NEb2VzTm90TWF0Y2hJc3N1ZXI7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVVbnN1Y2Nlc3NmdWxWYWxpZGF0aW9uKGNvbmZpZ3VyYXRpb24pO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIG9mKHRvUmV0dXJuKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ1dhcm5pbmcoXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAnYXV0aFdlbGxLbm93bkVuZHBvaW50cyBpcyB1bmRlZmluZWQnXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5Ob0F1dGhXZWxsS25vd25FbmRQb2ludHM7XG4gICAgICAgICAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgICAgICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICF0aGlzLnRva2VuVmFsaWRhdGlvblNlcnZpY2UudmFsaWRhdGVJZFRva2VuQXVkKFxuICAgICAgICAgICAgICAgIHRvUmV0dXJuLmRlY29kZWRJZFRva2VuLFxuICAgICAgICAgICAgICAgIGNsaWVudElkLFxuICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dXYXJuaW5nKFxuICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgICAgICAgJ2F1dGhDYWxsYmFjayBpbmNvcnJlY3QgYXVkJ1xuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICB0b1JldHVybi5zdGF0ZSA9IFZhbGlkYXRpb25SZXN1bHQuSW5jb3JyZWN0QXVkO1xuICAgICAgICAgICAgICB0aGlzLmhhbmRsZVVuc3VjY2Vzc2Z1bFZhbGlkYXRpb24oY29uZmlndXJhdGlvbik7XG5cbiAgICAgICAgICAgICAgcmV0dXJuIG9mKHRvUmV0dXJuKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAhdGhpcy50b2tlblZhbGlkYXRpb25TZXJ2aWNlLnZhbGlkYXRlSWRUb2tlbkF6cEV4aXN0c0lmTW9yZVRoYW5PbmVBdWQoXG4gICAgICAgICAgICAgICAgdG9SZXR1cm4uZGVjb2RlZElkVG9rZW5cbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dXYXJuaW5nKFxuICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgICAgICAgJ2F1dGhDYWxsYmFjayBtaXNzaW5nIGF6cCdcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgdG9SZXR1cm4uc3RhdGUgPSBWYWxpZGF0aW9uUmVzdWx0LkluY29ycmVjdEF6cDtcbiAgICAgICAgICAgICAgdGhpcy5oYW5kbGVVbnN1Y2Nlc3NmdWxWYWxpZGF0aW9uKGNvbmZpZ3VyYXRpb24pO1xuXG4gICAgICAgICAgICAgIHJldHVybiBvZih0b1JldHVybik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgIXRoaXMudG9rZW5WYWxpZGF0aW9uU2VydmljZS52YWxpZGF0ZUlkVG9rZW5BenBWYWxpZChcbiAgICAgICAgICAgICAgICB0b1JldHVybi5kZWNvZGVkSWRUb2tlbixcbiAgICAgICAgICAgICAgICBjbGllbnRJZFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ1dhcm5pbmcoXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAnYXV0aENhbGxiYWNrIGluY29ycmVjdCBhenAnXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5JbmNvcnJlY3RBenA7XG4gICAgICAgICAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgICAgICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICF0aGlzLmlzSWRUb2tlbkFmdGVyUmVmcmVzaFRva2VuUmVxdWVzdFZhbGlkKFxuICAgICAgICAgICAgICAgIGNhbGxiYWNrQ29udGV4dCxcbiAgICAgICAgICAgICAgICB0b1JldHVybi5kZWNvZGVkSWRUb2tlbixcbiAgICAgICAgICAgICAgICBjb25maWd1cmF0aW9uXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICB0aGlzLmxvZ2dlclNlcnZpY2UubG9nV2FybmluZyhcbiAgICAgICAgICAgICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgICAgICAgICAgICdhdXRoQ2FsbGJhY2sgcHJlLCBwb3N0IGlkX3Rva2VuIGNsYWltcyBkbyBub3QgbWF0Y2ggaW4gcmVmcmVzaCdcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgdG9SZXR1cm4uc3RhdGUgPVxuICAgICAgICAgICAgICAgIFZhbGlkYXRpb25SZXN1bHQuSW5jb3JyZWN0SWRUb2tlbkNsYWltc0FmdGVyUmVmcmVzaDtcbiAgICAgICAgICAgICAgdGhpcy5oYW5kbGVVbnN1Y2Nlc3NmdWxWYWxpZGF0aW9uKGNvbmZpZ3VyYXRpb24pO1xuXG4gICAgICAgICAgICAgIHJldHVybiBvZih0b1JldHVybik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgIWlzSW5SZWZyZXNoVG9rZW5GbG93ICYmXG4gICAgICAgICAgICAgICF0aGlzLnRva2VuVmFsaWRhdGlvblNlcnZpY2UudmFsaWRhdGVJZFRva2VuRXhwTm90RXhwaXJlZChcbiAgICAgICAgICAgICAgICB0b1JldHVybi5kZWNvZGVkSWRUb2tlbixcbiAgICAgICAgICAgICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgICAgICAgICAgIHJlbmV3VGltZUJlZm9yZVRva2VuRXhwaXJlc0luU2Vjb25kc1xuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ1dhcm5pbmcoXG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgICAgICAgICAgICAnYXV0aENhbGxiYWNrIGlkIHRva2VuIGV4cGlyZWQnXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5Ub2tlbkV4cGlyZWQ7XG4gICAgICAgICAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgICAgICAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdGhpcy52YWxpZGF0ZURlZmF1bHQoXG4gICAgICAgICAgICAgIGlzQ3VycmVudEZsb3dJbXBsaWNpdEZsb3dXaXRoQWNjZXNzVG9rZW4sXG4gICAgICAgICAgICAgIGlzQ3VycmVudEZsb3dDb2RlRmxvdyxcbiAgICAgICAgICAgICAgdG9SZXR1cm4sXG4gICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgICAgIGNhbGxiYWNrQ29udGV4dFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmxvZ2dlclNlcnZpY2UubG9nRGVidWcoXG4gICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgICdObyBpZF90b2tlbiBmb3VuZCwgc2tpcHBpbmcgaWRfdG9rZW4gdmFsaWRhdGlvbidcbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMudmFsaWRhdGVEZWZhdWx0KFxuICAgICAgaXNDdXJyZW50Rmxvd0ltcGxpY2l0Rmxvd1dpdGhBY2Nlc3NUb2tlbixcbiAgICAgIGlzQ3VycmVudEZsb3dDb2RlRmxvdyxcbiAgICAgIHRvUmV0dXJuLFxuICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgIGNhbGxiYWNrQ29udGV4dFxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIHZhbGlkYXRlRGVmYXVsdChcbiAgICBpc0N1cnJlbnRGbG93SW1wbGljaXRGbG93V2l0aEFjY2Vzc1Rva2VuOiBib29sZWFuLFxuICAgIGlzQ3VycmVudEZsb3dDb2RlRmxvdzogYm9vbGVhbixcbiAgICB0b1JldHVybjogU3RhdGVWYWxpZGF0aW9uUmVzdWx0LFxuICAgIGNvbmZpZ3VyYXRpb246IE9wZW5JZENvbmZpZ3VyYXRpb24sXG4gICAgY2FsbGJhY2tDb250ZXh0OiBDYWxsYmFja0NvbnRleHRcbiAgKTogT2JzZXJ2YWJsZTxTdGF0ZVZhbGlkYXRpb25SZXN1bHQ+IHtcbiAgICAvLyBmbG93IGlkX3Rva2VuXG4gICAgaWYgKCFpc0N1cnJlbnRGbG93SW1wbGljaXRGbG93V2l0aEFjY2Vzc1Rva2VuICYmICFpc0N1cnJlbnRGbG93Q29kZUZsb3cpIHtcbiAgICAgIHRvUmV0dXJuLmF1dGhSZXNwb25zZUlzVmFsaWQgPSB0cnVlO1xuICAgICAgdG9SZXR1cm4uc3RhdGUgPSBWYWxpZGF0aW9uUmVzdWx0Lk9rO1xuICAgICAgdGhpcy5oYW5kbGVTdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcbiAgICAgIHRoaXMuaGFuZGxlVW5zdWNjZXNzZnVsVmFsaWRhdGlvbihjb25maWd1cmF0aW9uKTtcblxuICAgICAgcmV0dXJuIG9mKHRvUmV0dXJuKTtcbiAgICB9XG5cbiAgICAvLyBvbmx5IGRvIGNoZWNrIGlmIGlkX3Rva2VuIHJldHVybmVkLCBubyBhbHdheXMgdGhlIGNhc2Ugd2hlbiB1c2luZyByZWZyZXNoIHRva2Vuc1xuICAgIGlmIChjYWxsYmFja0NvbnRleHQuYXV0aFJlc3VsdD8uaWRfdG9rZW4pIHtcbiAgICAgIGNvbnN0IGlkVG9rZW5IZWFkZXIgPSB0aGlzLnRva2VuSGVscGVyU2VydmljZS5nZXRIZWFkZXJGcm9tVG9rZW4oXG4gICAgICAgIHRvUmV0dXJuLmlkVG9rZW4sXG4gICAgICAgIGZhbHNlLFxuICAgICAgICBjb25maWd1cmF0aW9uXG4gICAgICApO1xuXG4gICAgICBpZiAoXG4gICAgICAgIGlzQ3VycmVudEZsb3dDb2RlRmxvdyAmJlxuICAgICAgICAhKHRvUmV0dXJuLmRlY29kZWRJZFRva2VuLmF0X2hhc2ggYXMgc3RyaW5nKVxuICAgICAgKSB7XG4gICAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dEZWJ1ZyhcbiAgICAgICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgICAgICdDb2RlIEZsb3cgYWN0aXZlLCBhbmQgbm8gYXRfaGFzaCBpbiB0aGUgaWRfdG9rZW4sIHNraXBwaW5nIGNoZWNrISdcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRva2VuVmFsaWRhdGlvblNlcnZpY2VcbiAgICAgICAgICAudmFsaWRhdGVJZFRva2VuQXRIYXNoKFxuICAgICAgICAgICAgdG9SZXR1cm4uYWNjZXNzVG9rZW4sXG4gICAgICAgICAgICB0b1JldHVybi5kZWNvZGVkSWRUb2tlbi5hdF9oYXNoLFxuICAgICAgICAgICAgaWRUb2tlbkhlYWRlci5hbGcsIC8vICdSUzI1NidcbiAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgICApXG4gICAgICAgICAgLnBpcGUoXG4gICAgICAgICAgICBtYXAoKHZhbGlkOiBib29sZWFuKSA9PiB7XG4gICAgICAgICAgICAgIGlmICghdmFsaWQgfHwgIXRvUmV0dXJuLmFjY2Vzc1Rva2VuKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLmxvZ1dhcm5pbmcoXG4gICAgICAgICAgICAgICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgICAgICAgICAgICAgJ2F1dGhDYWxsYmFjayBpbmNvcnJlY3QgYXRfaGFzaCdcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5JbmNvcnJlY3RBdEhhc2g7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVVbnN1Y2Nlc3NmdWxWYWxpZGF0aW9uKGNvbmZpZ3VyYXRpb24pO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRvUmV0dXJuO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRvUmV0dXJuLmF1dGhSZXNwb25zZUlzVmFsaWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHRvUmV0dXJuLnN0YXRlID0gVmFsaWRhdGlvblJlc3VsdC5PaztcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZVN1Y2Nlc3NmdWxWYWxpZGF0aW9uKGNvbmZpZ3VyYXRpb24pO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRvUmV0dXJuO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdG9SZXR1cm4uYXV0aFJlc3BvbnNlSXNWYWxpZCA9IHRydWU7XG4gICAgdG9SZXR1cm4uc3RhdGUgPSBWYWxpZGF0aW9uUmVzdWx0Lk9rO1xuICAgIHRoaXMuaGFuZGxlU3VjY2Vzc2Z1bFZhbGlkYXRpb24oY29uZmlndXJhdGlvbik7XG5cbiAgICByZXR1cm4gb2YodG9SZXR1cm4pO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0lkVG9rZW5BZnRlclJlZnJlc2hUb2tlblJlcXVlc3RWYWxpZChcbiAgICBjYWxsYmFja0NvbnRleHQ6IENhbGxiYWNrQ29udGV4dCxcbiAgICBuZXdJZFRva2VuOiBhbnksXG4gICAgY29uZmlndXJhdGlvbjogT3BlbklkQ29uZmlndXJhdGlvblxuICApOiBib29sZWFuIHtcbiAgICBjb25zdCB7IHVzZVJlZnJlc2hUb2tlbiwgZGlzYWJsZV