UNPKG

angular-auth-oidc-client

Version:
405 lines 69.9 kB
import { HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { of } from 'rxjs'; import { map } from 'rxjs/operators'; import { UriEncoder } from './uri-encoder'; import * as i0 from "@angular/core"; import * as i1 from "../../logging/logger.service"; import * as i2 from "../../flows/flows-data.service"; import * as i3 from "../flowHelper/flow-helper.service"; import * as i4 from "../../storage/storage-persistence.service"; import * as i5 from "../../validation/jwt-window-crypto.service"; const CALLBACK_PARAMS_TO_CHECK = ['code', 'state', 'token', 'id_token']; const AUTH0_ENDPOINT = 'auth0.com'; export class UrlService { constructor(loggerService, flowsDataService, flowHelper, storagePersistenceService, jwtWindowCryptoService) { this.loggerService = loggerService; this.flowsDataService = flowsDataService; this.flowHelper = flowHelper; this.storagePersistenceService = storagePersistenceService; this.jwtWindowCryptoService = jwtWindowCryptoService; } getUrlParameter(urlToCheck, name) { if (!urlToCheck) { return ''; } if (!name) { return ''; } name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); const regex = new RegExp('[\\?&#]' + name + '=([^&#]*)'); const results = regex.exec(urlToCheck); return results === null ? '' : decodeURIComponent(results[1]); } isCallbackFromSts(currentUrl) { return CALLBACK_PARAMS_TO_CHECK.some((x) => !!this.getUrlParameter(currentUrl, x)); } getRefreshSessionSilentRenewUrl(config, customParams) { if (this.flowHelper.isCurrentFlowCodeFlow(config)) { return this.createUrlCodeFlowWithSilentRenew(config, customParams); } return of(this.createUrlImplicitFlowWithSilentRenew(config, customParams) || ''); } getAuthorizeParUrl(requestUri, configuration) { const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); if (!authWellKnownEndPoints) { this.loggerService.logError(configuration, 'authWellKnownEndpoints is undefined'); return null; } const authorizationEndpoint = authWellKnownEndPoints.authorizationEndpoint; if (!authorizationEndpoint) { this.loggerService.logError(configuration, `Can not create an authorize URL when authorizationEndpoint is '${authorizationEndpoint}'`); return null; } const { clientId } = configuration; if (!clientId) { this.loggerService.logError(configuration, `getAuthorizeParUrl could not add clientId because it was: `, clientId); return null; } const urlParts = authorizationEndpoint.split('?'); const authorizationUrl = urlParts[0]; const existingParams = urlParts[1]; let params = this.createHttpParams(existingParams); params = params.set('request_uri', requestUri); params = params.append('client_id', clientId); return `${authorizationUrl}?${params}`; } getAuthorizeUrl(config, authOptions) { if (this.flowHelper.isCurrentFlowCodeFlow(config)) { return this.createUrlCodeFlowAuthorize(config, authOptions); } return of(this.createUrlImplicitFlowAuthorize(config, authOptions) || ''); } createEndSessionUrl(idTokenHint, configuration, customParamsEndSession) { // Auth0 needs a special logout url // See https://auth0.com/docs/api/authentication#logout if (this.isAuth0Endpoint(configuration)) { return this.composeAuth0Endpoint(configuration); } const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); const endSessionEndpoint = authWellKnownEndPoints?.endSessionEndpoint; if (!endSessionEndpoint) { return null; } const urlParts = endSessionEndpoint.split('?'); const authorizationEndSessionUrl = urlParts[0]; const existingParams = urlParts[1]; let params = this.createHttpParams(existingParams); if (!!idTokenHint) { params = params.set('id_token_hint', idTokenHint); } const postLogoutRedirectUri = this.getPostLogoutRedirectUrl(configuration); if (postLogoutRedirectUri) { params = params.append('post_logout_redirect_uri', postLogoutRedirectUri); } if (customParamsEndSession) { params = this.appendCustomParams({ ...customParamsEndSession }, params); } return `${authorizationEndSessionUrl}?${params}`; } createRevocationEndpointBodyAccessToken(token, configuration) { const clientId = this.getClientId(configuration); if (!clientId) { return null; } let params = this.createHttpParams(); params = params.set('client_id', clientId); params = params.set('token', token); params = params.set('token_type_hint', 'access_token'); return params.toString(); } createRevocationEndpointBodyRefreshToken(token, configuration) { const clientId = this.getClientId(configuration); if (!clientId) { return null; } let params = this.createHttpParams(); params = params.set('client_id', clientId); params = params.set('token', token); params = params.set('token_type_hint', 'refresh_token'); return params.toString(); } getRevocationEndpointUrl(configuration) { const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); const revocationEndpoint = authWellKnownEndPoints?.revocationEndpoint; if (!revocationEndpoint) { return null; } const urlParts = revocationEndpoint.split('?'); const revocationEndpointUrl = urlParts[0]; return revocationEndpointUrl; } createBodyForCodeFlowCodeRequest(code, configuration, customTokenParams) { const clientId = this.getClientId(configuration); if (!clientId) { return null; } let params = this.createHttpParams(); params = params.set('grant_type', 'authorization_code'); params = params.set('client_id', clientId); if (!configuration.disablePkce) { const codeVerifier = this.flowsDataService.getCodeVerifier(configuration); if (!codeVerifier) { this.loggerService.logError(configuration, `CodeVerifier is not set `, codeVerifier); return null; } params = params.set('code_verifier', codeVerifier); } params = params.set('code', code); if (customTokenParams) { params = this.appendCustomParams({ ...customTokenParams }, params); } const silentRenewUrl = this.getSilentRenewUrl(configuration); if (this.flowsDataService.isSilentRenewRunning(configuration) && silentRenewUrl) { params = params.set('redirect_uri', silentRenewUrl); return params.toString(); } const redirectUrl = this.getRedirectUrl(configuration); if (!redirectUrl) { return null; } params = params.set('redirect_uri', redirectUrl); return params.toString(); } createBodyForCodeFlowRefreshTokensRequest(refreshToken, configuration, customParamsRefresh) { const clientId = this.getClientId(configuration); if (!clientId) { return null; } let params = this.createHttpParams(); params = params.set('grant_type', 'refresh_token'); params = params.set('client_id', clientId); params = params.set('refresh_token', refreshToken); if (customParamsRefresh) { params = this.appendCustomParams({ ...customParamsRefresh }, params); } return params.toString(); } createBodyForParCodeFlowRequest(configuration, customParamsRequest) { const redirectUrl = this.getRedirectUrl(configuration); if (!redirectUrl) { return of(null); } const state = this.flowsDataService.getExistingOrCreateAuthStateControl(configuration); const nonce = this.flowsDataService.createNonce(configuration); this.loggerService.logDebug(configuration, 'Authorize created. adding myautostate: ' + state); // code_challenge with "S256" const codeVerifier = this.flowsDataService.createCodeVerifier(configuration); return this.jwtWindowCryptoService.generateCodeChallenge(codeVerifier).pipe(map((codeChallenge) => { const { clientId, responseType, scope, hdParam, customParamsAuthRequest } = configuration; let params = this.createHttpParams(''); params = params.set('client_id', clientId); params = params.append('redirect_uri', redirectUrl); params = params.append('response_type', responseType); params = params.append('scope', scope); params = params.append('nonce', nonce); params = params.append('state', state); params = params.append('code_challenge', codeChallenge); params = params.append('code_challenge_method', 'S256'); if (hdParam) { params = params.append('hd', hdParam); } if (customParamsAuthRequest) { params = this.appendCustomParams({ ...customParamsAuthRequest }, params); } if (customParamsRequest) { params = this.appendCustomParams({ ...customParamsRequest }, params); } return params.toString(); })); } createAuthorizeUrl(codeChallenge, redirectUrl, nonce, state, configuration, prompt, customRequestParams) { const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); const authorizationEndpoint = authWellKnownEndPoints?.authorizationEndpoint; if (!authorizationEndpoint) { this.loggerService.logError(configuration, `Can not create an authorize URL when authorizationEndpoint is '${authorizationEndpoint}'`); return null; } const { clientId, responseType, scope, hdParam, customParamsAuthRequest } = configuration; if (!clientId) { this.loggerService.logError(configuration, `createAuthorizeUrl could not add clientId because it was: `, clientId); return null; } if (!responseType) { this.loggerService.logError(configuration, `createAuthorizeUrl could not add responseType because it was: `, responseType); return null; } if (!scope) { this.loggerService.logError(configuration, `createAuthorizeUrl could not add scope because it was: `, scope); return null; } const urlParts = authorizationEndpoint.split('?'); const authorizationUrl = urlParts[0]; const existingParams = urlParts[1]; let params = this.createHttpParams(existingParams); params = params.set('client_id', clientId); params = params.append('redirect_uri', redirectUrl); params = params.append('response_type', responseType); params = params.append('scope', scope); params = params.append('nonce', nonce); params = params.append('state', state); if (this.flowHelper.isCurrentFlowCodeFlow(configuration) && codeChallenge !== null) { params = params.append('code_challenge', codeChallenge); params = params.append('code_challenge_method', 'S256'); } const mergedParams = { ...customParamsAuthRequest, ...customRequestParams }; if (Object.keys(mergedParams).length > 0) { params = this.appendCustomParams({ ...mergedParams }, params); } if (prompt) { params = this.overWriteParam(params, 'prompt', prompt); } if (hdParam) { params = params.append('hd', hdParam); } return `${authorizationUrl}?${params}`; } createUrlImplicitFlowWithSilentRenew(configuration, customParams) { const state = this.flowsDataService.getExistingOrCreateAuthStateControl(configuration); const nonce = this.flowsDataService.createNonce(configuration); const silentRenewUrl = this.getSilentRenewUrl(configuration); if (!silentRenewUrl) { return null; } this.loggerService.logDebug(configuration, 'RefreshSession created. adding myautostate: ', state); const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); if (authWellKnownEndPoints) { return this.createAuthorizeUrl('', silentRenewUrl, nonce, state, configuration, 'none', customParams); } this.loggerService.logError(configuration, 'authWellKnownEndpoints is undefined'); return null; } createUrlCodeFlowWithSilentRenew(configuration, customParams) { const state = this.flowsDataService.getExistingOrCreateAuthStateControl(configuration); const nonce = this.flowsDataService.createNonce(configuration); this.loggerService.logDebug(configuration, 'RefreshSession created. adding myautostate: ' + state); // code_challenge with "S256" const codeVerifier = this.flowsDataService.createCodeVerifier(configuration); return this.jwtWindowCryptoService.generateCodeChallenge(codeVerifier).pipe(map((codeChallenge) => { const silentRenewUrl = this.getSilentRenewUrl(configuration); if (!silentRenewUrl) { return ''; } const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); if (authWellKnownEndPoints) { return this.createAuthorizeUrl(codeChallenge, silentRenewUrl, nonce, state, configuration, 'none', customParams); } this.loggerService.logWarning(configuration, 'authWellKnownEndpoints is undefined'); return null; })); } createUrlImplicitFlowAuthorize(configuration, authOptions) { const state = this.flowsDataService.getExistingOrCreateAuthStateControl(configuration); const nonce = this.flowsDataService.createNonce(configuration); this.loggerService.logDebug(configuration, 'Authorize created. adding myautostate: ' + state); const redirectUrl = this.getRedirectUrl(configuration, authOptions); if (!redirectUrl) { return null; } const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', configuration); if (authWellKnownEndPoints) { const { customParams } = authOptions || {}; return this.createAuthorizeUrl('', redirectUrl, nonce, state, configuration, null, customParams); } this.loggerService.logError(configuration, 'authWellKnownEndpoints is undefined'); return null; } createUrlCodeFlowAuthorize(config, authOptions) { const state = this.flowsDataService.getExistingOrCreateAuthStateControl(config); const nonce = this.flowsDataService.createNonce(config); this.loggerService.logDebug(config, 'Authorize created. adding myautostate: ' + state); const redirectUrl = this.getRedirectUrl(config, authOptions); if (!redirectUrl) { return of(null); } return this.getCodeChallenge(config).pipe(map((codeChallenge) => { const authWellKnownEndPoints = this.storagePersistenceService.read('authWellKnownEndPoints', config); if (authWellKnownEndPoints) { const { customParams } = authOptions || {}; return this.createAuthorizeUrl(codeChallenge, redirectUrl, nonce, state, config, null, customParams); } this.loggerService.logError(config, 'authWellKnownEndpoints is undefined'); return ''; })); } getCodeChallenge(config) { if (config.disablePkce) { return of(null); } // code_challenge with "S256" const codeVerifier = this.flowsDataService.createCodeVerifier(config); return this.jwtWindowCryptoService.generateCodeChallenge(codeVerifier); } getRedirectUrl(configuration, authOptions) { let { redirectUrl } = configuration; if (authOptions?.redirectUrl) { // override by redirectUrl from authOptions redirectUrl = authOptions.redirectUrl; } if (!redirectUrl) { this.loggerService.logError(configuration, `could not get redirectUrl, was: `, redirectUrl); return null; } return redirectUrl; } getSilentRenewUrl(configuration) { const { silentRenewUrl } = configuration; if (!silentRenewUrl) { this.loggerService.logError(configuration, `could not get silentRenewUrl, was: `, silentRenewUrl); return null; } return silentRenewUrl; } getPostLogoutRedirectUrl(configuration) { const { postLogoutRedirectUri } = configuration; if (!postLogoutRedirectUri) { this.loggerService.logError(configuration, `could not get postLogoutRedirectUri, was: `, postLogoutRedirectUri); return null; } return postLogoutRedirectUri; } getClientId(configuration) { const { clientId } = configuration; if (!clientId) { this.loggerService.logError(configuration, `could not get clientId, was: `, clientId); return null; } return clientId; } appendCustomParams(customParams, params) { for (const [key, value] of Object.entries({ ...customParams })) { params = params.append(key, value.toString()); } return params; } overWriteParam(params, key, value) { return params.set(key, value); } createHttpParams(existingParams) { existingParams = existingParams ?? ''; const params = new HttpParams({ fromString: existingParams, encoder: new UriEncoder(), }); return params; } isAuth0Endpoint(configuration) { const { authority } = configuration; if (!authority) { return false; } return authority.endsWith(AUTH0_ENDPOINT); } composeAuth0Endpoint(configuration) { // format: https://YOUR_DOMAIN/v2/logout?client_id=YOUR_CLIENT_ID&returnTo=LOGOUT_URL const { authority, clientId } = configuration; const postLogoutRedirectUrl = this.getPostLogoutRedirectUrl(configuration); return `${authority}/v2/logout?client_id=${clientId}&returnTo=${postLogoutRedirectUrl}`; } } UrlService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0", ngImport: i0, type: UrlService, deps: [{ token: i1.LoggerService }, { token: i2.FlowsDataService }, { token: i3.FlowHelper }, { token: i4.StoragePersistenceService }, { token: i5.JwtWindowCryptoService }], target: i0.ɵɵFactoryTarget.Injectable }); UrlService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0", ngImport: i0, type: UrlService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0", ngImport: i0, type: UrlService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.LoggerService }, { type: i2.FlowsDataService }, { type: i3.FlowHelper }, { type: i4.StoragePersistenceService }, { type: i5.JwtWindowCryptoService }]; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXJsLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWF1dGgtb2lkYy1jbGllbnQvc3JjL2xpYi91dGlscy91cmwvdXJsLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFjLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN0QyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFRckMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7Ozs7OztBQUUzQyxNQUFNLHdCQUF3QixHQUFHLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7QUFDeEUsTUFBTSxjQUFjLEdBQUcsV0FBVyxDQUFDO0FBR25DLE1BQU0sT0FBTyxVQUFVO0lBQ3JCLFlBQ21CLGFBQTRCLEVBQzVCLGdCQUFrQyxFQUNsQyxVQUFzQixFQUN0Qix5QkFBb0QsRUFDcEQsc0JBQThDO1FBSjlDLGtCQUFhLEdBQWIsYUFBYSxDQUFlO1FBQzVCLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUFDbEMsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQUN0Qiw4QkFBeUIsR0FBekIseUJBQXlCLENBQTJCO1FBQ3BELDJCQUFzQixHQUF0QixzQkFBc0IsQ0FBd0I7SUFDOUQsQ0FBQztJQUVKLGVBQWUsQ0FBQyxVQUFlLEVBQUUsSUFBUztRQUN4QyxJQUFJLENBQUMsVUFBVSxFQUFFO1lBQ2YsT0FBTyxFQUFFLENBQUM7U0FDWDtRQUVELElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDVCxPQUFPLEVBQUUsQ0FBQztTQUNYO1FBRUQsSUFBSSxHQUFHLElBQUksQ0FBQyxP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U0FBUyxFQUFFLEdBQUcsYUFBYSxDQUFDO1FBRXBDLElBQUksQ0FBQyxTQUFTLEVBQUU7WUFDZCxPQUFPLEtBQUssQ0FBQztTQUNkO1FBRUQsT0FBTyxTQUFTLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFTyxvQkFBb0IsQ0FBQyxhQUFrQztRQUM3RCxxRkFBcUY7UUFDckYsTUFBTSxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsR0FBRyxhQUFhLENBQUM7UUFDOUMsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFM0UsT0FBTyxHQUFHLFNBQVMsd0JBQXdCLFFBQVEsYUFBYSxxQkFBcUIsRUFBRSxDQUFDO0lBQzFGLENBQUM7O3VHQS9rQlUsVUFBVTsyR0FBVixVQUFVOzJGQUFWLFVBQVU7a0JBRHRCLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBIdHRwUGFyYW1zIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xyXG5pbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IE9ic2VydmFibGUsIG9mIH0gZnJvbSAncnhqcyc7XHJcbmltcG9ydCB7IG1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuaW1wb3J0IHsgQXV0aE9wdGlvbnMgfSBmcm9tICcuLi8uLi9hdXRoLW9wdGlvbnMnO1xyXG5pbXBvcnQgeyBPcGVuSWRDb25maWd1cmF0aW9uIH0gZnJvbSAnLi4vLi4vY29uZmlnL29wZW5pZC1jb25maWd1cmF0aW9uJztcclxuaW1wb3J0IHsgRmxvd3NEYXRhU2VydmljZSB9IGZyb20gJy4uLy4uL2Zsb3dzL2Zsb3dzLWRhdGEuc2VydmljZSc7XHJcbmltcG9ydCB7IExvZ2dlclNlcnZpY2UgfSBmcm9tICcuLi8uLi9sb2dnaW5nL2xvZ2dlci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgU3RvcmFnZVBlcnNpc3RlbmNlU2VydmljZSB9IGZyb20gJy4uLy4uL3N0b3JhZ2Uvc3RvcmFnZS1wZXJzaXN0ZW5jZS5zZXJ2aWNlJztcclxuaW1wb3J0IHsgSnd0V2luZG93Q3J5cHRvU2VydmljZSB9IGZyb20gJy4uLy4uL3ZhbGlkYXRpb24vand0LXdpbmRvdy1jcnlwdG8uc2VydmljZSc7XHJcbmltcG9ydCB7IEZsb3dIZWxwZXIgfSBmcm9tICcuLi9mbG93SGVscGVyL2Zsb3ctaGVscGVyLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBVcmlFbmNvZGVyIH0gZnJvbSAnLi91cmktZW5jb2Rlcic7XHJcblxyXG5jb25zdCBDQUxMQkFDS19QQVJBTVNfVE9fQ0hFQ0sgPSBbJ2NvZGUnLCAnc3RhdGUnLCAndG9rZW4nLCAnaWRfdG9rZW4nXTtcclxuY29uc3QgQVVUSDBfRU5EUE9JTlQgPSAnYXV0aDAuY29tJztcclxuXHJcbkBJbmplY3RhYmxlKClcclxuZXhwb3J0IGNsYXNzIFVybFNlcnZpY2Uge1xyXG4gIGNvbnN0cnVjdG9yKFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBsb2dnZXJTZXJ2aWNlOiBMb2dnZXJTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBmbG93c0RhdGFTZXJ2aWNlOiBGbG93c0RhdGFTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBmbG93SGVscGVyOiBGbG93SGVscGVyLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBzdG9yYWdlUGVyc2lzdGVuY2VTZXJ2aWNlOiBTdG9yYWdlUGVyc2lzdGVuY2VTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBqd3RXaW5kb3dDcnlwdG9TZXJ2aWNlOiBKd3RXaW5kb3dDcnlwdG9TZXJ2aWNlXHJcbiAgKSB7fVxyXG5cclxuICBnZXRVcmxQYXJhbWV0ZXIodXJsVG9DaGVjazogYW55LCBuYW1lOiBhbnkpOiBzdHJpbmcge1xyXG4gICAgaWYgKCF1cmxUb0NoZWNrKSB7XHJcbiAgICAgIHJldHVybiAnJztcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIW5hbWUpIHtcclxuICAgICAgcmV0dXJuICcnO1xyXG4gICAgfVxyXG5cclxuICAgIG5hbWUgPSBuYW1lLnJlcGxhY2UoL1tbXS8sICdcXFxcWycpLnJlcGxhY2UoL1tcXF1dLywgJ1xcXFxdJyk7XHJcbiAgICBjb25zdCByZWdleCA9IG5ldyBSZWdFeHAoJ1tcXFxcPyYjXScgKyBuYW1lICsgJz0oW14mI10qKScpO1xyXG4gICAgY29uc3QgcmVzdWx0cyA9IHJlZ2V4LmV4ZWModXJsVG9DaGVjayk7XHJcblxyXG4gICAgcmV0dXJuIHJlc3VsdHMgPT09IG51bGwgPyAnJyA6IGRlY29kZVVSSUNvbXBvbmVudChyZXN1bHRzWzFdKTtcclxuICB9XHJcblxyXG4gIGlzQ2FsbGJhY2tGcm9tU3RzKGN1cnJlbnRVcmw6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIENBTExCQUNLX1BBUkFNU19UT19DSEVDSy5zb21lKCh4KSA9PiAhIXRoaXMuZ2V0VXJsUGFyYW1ldGVyKGN1cnJlbnRVcmwsIHgpKTtcclxuICB9XHJcblxyXG4gIGdldFJlZnJlc2hTZXNzaW9uU2lsZW50UmVuZXdVcmwoXHJcbiAgICBjb25maWc6IE9wZW5JZENvbmZpZ3VyYXRpb24sXHJcbiAgICBjdXN0b21QYXJhbXM/OiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfVxyXG4gICk6IE9ic2VydmFibGU8c3RyaW5nPiB7XHJcbiAgICBpZiAodGhpcy5mbG93SGVscGVyLmlzQ3VycmVudEZsb3dDb2RlRmxvdyhjb25maWcpKSB7XHJcbiAgICAgIHJldHVybiB0aGlzLmNyZWF0ZVVybENvZGVGbG93V2l0aFNpbGVudFJlbmV3KGNvbmZpZywgY3VzdG9tUGFyYW1zKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gb2YodGhpcy5jcmVhdGVVcmxJbXBsaWNpdEZsb3dXaXRoU2lsZW50UmVuZXcoY29uZmlnLCBjdXN0b21QYXJhbXMpIHx8ICcnKTtcclxuICB9XHJcblxyXG4gIGdldEF1dGhvcml6ZVBhclVybChyZXF1ZXN0VXJpOiBzdHJpbmcsIGNvbmZpZ3VyYXRpb246IE9wZW5JZENvbmZpZ3VyYXRpb24pOiBzdHJpbmcge1xyXG4gICAgY29uc3QgYXV0aFdlbGxLbm93bkVuZFBvaW50cyA9IHRoaXMuc3RvcmFnZVBlcnNpc3RlbmNlU2VydmljZS5yZWFkKCdhdXRoV2VsbEtub3duRW5kUG9pbnRzJywgY29uZmlndXJhdGlvbik7XHJcblxyXG4gICAgaWYgKCFhdXRoV2VsbEtub3duRW5kUG9pbnRzKSB7XHJcbiAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dFcnJvcihjb25maWd1cmF0aW9uLCAnYXV0aFdlbGxLbm93bkVuZHBvaW50cyBpcyB1bmRlZmluZWQnKTtcclxuXHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IGF1dGhvcml6YXRpb25FbmRwb2ludCA9IGF1dGhXZWxsS25vd25FbmRQb2ludHMuYXV0aG9yaXphdGlvbkVuZHBvaW50O1xyXG5cclxuICAgIGlmICghYXV0aG9yaXphdGlvbkVuZHBvaW50KSB7XHJcbiAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dFcnJvcihcclxuICAgICAgICBjb25maWd1cmF0aW9uLFxyXG4gICAgICAgIGBDYW4gbm90IGNyZWF0ZSBhbiBhdXRob3JpemUgVVJMIHdoZW4gYXV0aG9yaXphdGlvbkVuZHBvaW50IGlzICcke2F1dGhvcml6YXRpb25FbmRwb2ludH0nYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgeyBjbGllbnRJZCB9ID0gY29uZmlndXJhdGlvbjtcclxuXHJcbiAgICBpZiAoIWNsaWVudElkKSB7XHJcbiAgICAgIHRoaXMubG9nZ2VyU2VydmljZS5sb2dFcnJvcihjb25maWd1cmF0aW9uLCBgZ2V0QXV0aG9yaXplUGFyVXJsIGNvdWxkIG5vdCBhZGQgY2xpZW50SWQgYmVjYXVzZSBpdCB3YXM6IGAsIGNsaWVudElkKTtcclxuXHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHVybFBhcnRzID0gYXV0aG9yaXphdGlvbkVuZHBvaW50LnNwbGl0KCc/Jyk7XHJcbiAgICBjb25zdCBhdXRob3JpemF0aW9uVXJsID0gdXJsUGFydHNbMF07XHJcbiAgICBjb25zdCBleGlzdGluZ1BhcmFtcyA9IHVybFBhcnRzWzFdO1xyXG4gICAgbGV0IHBhcmFtcyA9IHRoaXMuY3JlYXRlSHR0cFBhcmFtcyhleGlzdGluZ1BhcmFtcyk7XHJcblxyXG4gICAgcGFyYW1zID0gcGFyYW1zLnNldCgncmVxdWVzdF91cmknLCByZXF1ZXN0VXJpKTtcclxuICAgIHBhcmFtcyA9IHBhcmFtcy5hcHBlbmQoJ2NsaWVudF9pZCcsIGNsaWVudElkKTtcclxuXHJcbiAgICByZXR1cm4gYCR7YXV0aG9yaXphdGlvblVybH0/JHtwYXJhbXN9YDtcclxuICB9XHJcblxyXG4gIGdldEF1dGhvcml6ZVVybChjb25maWc6IE9wZW5JZENvbmZpZ3VyYXRpb24sIGF1dGhPcHRpb25zPzogQXV0aE9wdGlvbnMpOiBPYnNlcnZhYmxlPHN0cmluZz4ge1xyXG4gICAgaWYgKHRoaXMuZmxvd0hlbHBlci5pc0N1cnJlbnRGbG93Q29kZUZsb3coY29uZmlnKSkge1xyXG4gICAgICByZXR1cm4gdGhpcy5jcmVhdGVVcmxDb2RlRmxvd0F1dGhvcml6ZShjb25maWcsIGF1dGhPcHRpb25zKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gb2YodGhpcy5jcmVhdGVVcmxJbXBsaWNpdEZsb3dBdXRob3JpemUoY29uZmlnLCBhdXRoT3B0aW9ucykgfHwgJycpO1xyXG4gIH1cclxuXHJcbiAgY3JlYXRlRW5kU2Vzc2lvblVybChcclxuICAgIGlkVG9rZW5IaW50OiBzdHJpbmcsXHJcbiAgICBjb25maWd1cmF0aW9uOiBPcGVuSWRDb25maWd1cmF0aW9uLFxyXG4gICAgY3VzdG9tUGFyYW1zRW5kU2Vzc2lvbj86IHsgW3A6IHN0cmluZ106IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfVxyXG4gICk6IHN0cmluZyB7XHJcbiAgICAvLyBBdXRoMCBuZWVkcyBhIHNwZWNpYWwgbG9nb3V0IHVybFxyXG4gICAgLy8gU2VlIGh0dHBzOi8vYXV0aDAuY29tL2RvY3MvYXBpL2F1dGhlbnRpY2F0aW9uI2xvZ291dFxyXG5cclxuICAgIGlmICh0aGlzLmlzQXV0aDBFbmRwb2ludChjb25maWd1cmF0aW9uKSkge1xyXG4gICAgICByZXR1cm4gdGhpcy5jb21wb3NlQXV0aDBFbmRwb2ludChjb25maWd1cmF0aW9uKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBhdXRoV2VsbEtub3duRW5kUG9pbnRzID0gdGhpcy5zdG9yYWdlUGVyc2lzdGVuY2VTZXJ2aWNlLnJlYWQoJ2F1dGhXZWxsS25vd25FbmRQb2ludHMnLCBjb25maWd1cmF0aW9uKTtcclxuICAgIGNvbnN0IGVuZFNlc3Npb25FbmRwb2ludCA9IGF1dGhXZWxsS25vd25FbmRQb2ludHM/LmVuZFNlc3Npb25FbmRwb2ludDtcclxuXHJcbiAgICBpZiAoIWVuZFNlc3Npb25FbmRwb2ludCkge1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB1cmxQYXJ0cyA9IGVuZFNlc3Npb25FbmRwb2ludC5zcGxpdCgnPycpO1xyXG4gICAgY29uc3QgYXV0aG9yaXphdGlvbkVuZFNlc3Npb25VcmwgPSB1cmxQYXJ0c1swXTtcclxuICAgIGNvbnN0IGV4aXN0aW5nUGFyYW1zID0gdXJsUGFydHNbMV07XHJcbiAgICBsZXQgcGFyYW1zID0gdGhpcy5jcmVhdGVIdHRwUGFyYW1zKGV4aXN0aW5nUGFyYW1zKTtcclxuXHJcbiAgICBpZiAoISFpZFRva2VuSGludCkge1xyXG4gICAgICBwYXJhbXMgPSBwYXJhbXMuc2V0KCdpZF90b2tlbl9oaW50JywgaWRUb2tlbkhpbnQpO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHBvc3RMb2dvdXRSZWRpcmVjdFVyaSA9IHRoaXMuZ2V0UG9zdExvZ291dFJlZGlyZWN0VXJsKGNvbmZpZ3VyYXRpb24pO1xyXG5cclxuICAgIGlmIChwb3N0TG9nb3V0UmVkaXJlY3RVcmkpIHtcclxuICAgICAgcGFyYW1zID0gcGFyYW1zLmFwcGVuZCgncG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpJywgcG9zdExvZ291dFJlZGlyZWN0VXJpKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoY3VzdG9tUGFyYW1zRW5kU2Vzc2lvbikge1xyXG4gICAgICBwYXJhbXMgPSB0aGlzLmFwcGVuZEN1c3RvbVBhcmFtcyh7IC4uLmN1c3RvbVBhcmFtc0VuZFNlc3Npb24gfSwgcGFyYW1zKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gYCR7YXV0aG9yaXphdGlvbkVuZFNlc3Npb25Vcmx9PyR7cGFyYW1zfWA7XHJcbiAgfVxyXG5cclxuICBjcmVhdGVSZXZvY2F0aW9uRW5kcG9pbnRCb2R5QWNjZXNzVG9rZW4odG9rZW46IGFueSwgY29uZmlndXJhdGlvbjogT3BlbklkQ29uZmlndXJhdGlvbik6IHN0cmluZyB7XHJcbiAgICBjb25zdCBjbGllbnRJZCA9IHRoaXMuZ2V0Q2xpZW50SWQoY29uZmlndXJhdGlvbik7XHJcblxyXG4gICAgaWYgKCFjbGllbnRJZCkge1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgcGFyYW1zID0gdGhpcy5jcmVhdGVIdHRwUGFyYW1zKCk7XHJcblxyXG4gICAgcGFyYW1zID0gcGFyYW1zLnNldCgnY2xpZW50X2lkJywgY2xpZW50SWQpO1xyXG4gICAgcGFyYW1zID0gcGFyYW1zLnNldCgndG9rZW4nLCB0b2tlbik7XHJcbiAgICBwYXJhbXMgPSBwYXJhbXMuc2V0KCd0b2tlbl90eXBlX2hpbnQnLCAnYWNjZXNzX3Rva2VuJyk7XHJcblxyXG4gICAgcmV0dXJuIHBhcmFtcy50b1N0cmluZygpO1xyXG4gIH1cclxuXHJcbiAgY3JlYXRlUmV2b2NhdGlvbkVuZHBvaW50Qm9keVJlZnJlc2hUb2tlbih0b2tlbjogYW55LCBjb25maWd1cmF0aW9uOiBPcGVuSWRDb25maWd1cmF0aW9uKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IGNsaWVudElkID0gdGhpcy5nZXRDbGllbnRJZChjb25maWd1cmF0aW9uKTtcclxuXHJcbiAgICBpZiAoIWNsaWVudElkKSB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCBwYXJhbXMgPSB0aGlzLmNyZWF0ZUh0dHBQYXJhbXMoKTtcclxuXHJcbiAgICBwYXJhbXMgPSBwYXJhbXMuc2V0KCdjbGllbnRfaWQnLCBjbGllbnRJZCk7XHJcbiAgICBwYXJhbXMgPSBwYXJhbXMuc2V0KCd0b2tlbicsIHRva2VuKTtcclxuICAgIHBhcmFtcyA9IHBhcmFtcy5zZXQoJ3Rva2VuX3R5cGVfaGludCcsICdyZWZyZXNoX3Rva2VuJyk7XHJcblxyXG4gICAgcmV0dXJuIHBhcmFtcy50b1N0cmluZygpO1xyXG4gIH1cclxuXHJcbiAgZ2V0UmV2b2NhdGlvbkVuZHBvaW50VXJsKGNvbmZpZ3VyYXRpb246IE9wZW5JZENvbmZpZ3VyYXRpb24pOiBzdHJpbmcge1xyXG4gICAgY29uc3QgYXV0aFdlbGxLbm93bkVuZFBvaW50cyA9IHRoaXMuc3RvcmFnZVBlcnNpc3RlbmNlU2VydmljZS5yZWFkKCdhdXRoV2VsbEtub3duRW5kUG9pbnRzJywgY29uZmlndXJhdGlvbik7XHJcbiAgICBjb25zdCByZXZvY2F0