angular-auth-oidc-client
Version:
Angular Lib for OpenID Connect & OAuth2
413 lines • 60.7 kB
JavaScript
import { Injectable, inject } from '@angular/core';
import { concatMap, map } from 'rxjs/operators';
import { AuthStateService } from './auth-state/auth-state.service';
import { CheckAuthService } from './auth-state/check-auth.service';
import { CallbackService } from './callback/callback.service';
import { RefreshSessionService } from './callback/refresh-session.service';
import { AuthWellKnownService } from './config/auth-well-known/auth-well-known.service';
import { ConfigurationService } from './config/config.service';
import { FlowsDataService } from './flows/flows-data.service';
import { CheckSessionService } from './iframe/check-session.service';
import { LoginService } from './login/login.service';
import { LogoffRevocationService } from './logoff-revoke/logoff-revocation.service';
import { UserService } from './user-data/user.service';
import { TokenHelperService } from './utils/tokenHelper/token-helper.service';
import { UrlService } from './utils/url/url.service';
import { toSignal } from '@angular/core/rxjs-interop';
import * as i0 from "@angular/core";
export class OidcSecurityService {
constructor() {
this.checkSessionService = inject(CheckSessionService);
this.checkAuthService = inject(CheckAuthService);
this.userService = inject(UserService);
this.tokenHelperService = inject(TokenHelperService);
this.configurationService = inject(ConfigurationService);
this.authStateService = inject(AuthStateService);
this.flowsDataService = inject(FlowsDataService);
this.callbackService = inject(CallbackService);
this.logoffRevocationService = inject(LogoffRevocationService);
this.loginService = inject(LoginService);
this.refreshSessionService = inject(RefreshSessionService);
this.urlService = inject(UrlService);
this.authWellKnownService = inject(AuthWellKnownService);
/**
* Provides information about the user after they have logged in.
*
* @returns Returns an object containing either the user data directly (single config) or
* the user data per config in case you are running with multiple configs
*/
this.userData = toSignal(this.userData$, { requireSync: true });
/**
* Emits each time an authorization event occurs.
*
* @returns Returns an object containing if you are authenticated or not.
* Single Config: true if config is authenticated, false if not.
* Multiple Configs: true is all configs are authenticated, false if only one of them is not
*
* The `allConfigsAuthenticated` property contains the auth information _per config_.
*/
this.authenticated = toSignal(this.isAuthenticated$, { requireSync: true });
}
/**
* Provides information about the user after they have logged in.
*
* @returns Returns an object containing either the user data directly (single config) or
* the user data per config in case you are running with multiple configs
*/
get userData$() {
return this.userService.userData$;
}
/**
* Emits each time an authorization event occurs.
*
* @returns Returns an object containing if you are authenticated or not.
* Single Config: true if config is authenticated, false if not.
* Multiple Configs: true is all configs are authenticated, false if only one of them is not
*
* The `allConfigsAuthenticated` property contains the auth information _per config_.
*/
get isAuthenticated$() {
return this.authStateService.authenticated$;
}
/**
* Emits each time the server sends a CheckSession event and the value changed. This property will always return
* true.
*/
get checkSessionChanged$() {
return this.checkSessionService.checkSessionChanged$;
}
/**
* Emits on a Security Token Service callback. The observable will never contain a value.
*/
get stsCallback$() {
return this.callbackService.stsCallback$;
}
preloadAuthWellKnownDocument(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(concatMap((config) => this.authWellKnownService.queryAndStoreAuthWellKnownEndPoints(config)));
}
/**
* Returns the currently active OpenID configurations.
*
* @returns an array of OpenIdConfigurations.
*/
getConfigurations() {
return this.configurationService.getAllConfigurations();
}
/**
* Returns a single active OpenIdConfiguration.
*
* @param configId The configId to identify the config. If not passed, the first one is being returned
*/
getConfiguration(configId) {
return this.configurationService.getOpenIDConfiguration(configId);
}
/**
* Returns the userData for a configuration
*
* @param configId The configId to identify the config. If not passed, the first one is being used
*/
getUserData(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.userService.getUserDataFromStore(config)));
}
/**
* Starts the complete setup flow for one configuration. Calling will start the entire authentication flow, and the returned observable
* will denote whether the user was successfully authenticated including the user data, the access token, the configId and
* an error message in case an error happened
*
* @param url The URL to perform the authorization on the behalf of.
* @param configId The configId to perform the authorization on the behalf of. If not passed, the first configs will be taken
*
* @returns An object `LoginResponse` containing all information about the login
*/
checkAuth(url, configId) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(concatMap(({ allConfigs, currentConfig }) => this.checkAuthService.checkAuth(currentConfig, allConfigs, url)));
}
/**
* Starts the complete setup flow for multiple configurations.
* Calling will start the entire authentication flow, and the returned observable
* will denote whether the user was successfully authenticated including the user data, the access token, the configId and
* an error message in case an error happened in an array for each config which was provided
*
* @param url The URL to perform the authorization on the behalf of.
*
* @returns An array of `LoginResponse` objects containing all information about the logins
*/
checkAuthMultiple(url) {
return this.configurationService
.getOpenIDConfigurations()
.pipe(concatMap(({ allConfigs }) => this.checkAuthService.checkAuthMultiple(allConfigs, url)));
}
/**
* Provides information about the current authenticated state
*
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns A boolean whether the config is authenticated or not.
*/
isAuthenticated(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.authStateService.isAuthenticated(config)));
}
/**
* Checks the server for an authenticated session using the iframe silent renew if not locally authenticated.
*/
checkAuthIncludingServer(configId) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(concatMap(({ allConfigs, currentConfig }) => this.checkAuthService.checkAuthIncludingServer(currentConfig, allConfigs)));
}
/**
* Returns the access token for the login scenario.
*
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns A string with the access token.
*/
getAccessToken(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.authStateService.getAccessToken(config)));
}
/**
* Returns the ID token for the sign-in.
*
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns A string with the id token.
*/
getIdToken(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.authStateService.getIdToken(config)));
}
/**
* Returns the refresh token, if present, for the sign-in.
*
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns A string with the refresh token.
*/
getRefreshToken(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.authStateService.getRefreshToken(config)));
}
/**
* Returns the authentication result, if present, for the sign-in.
*
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns A object with the authentication result
*/
getAuthenticationResult(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.authStateService.getAuthenticationResult(config)));
}
/**
* Returns the payload from the ID token.
*
* @param encode Set to true if the payload is base64 encoded
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns The payload from the id token.
*/
getPayloadFromIdToken(encode = false, configId) {
return this.configurationService.getOpenIDConfiguration(configId).pipe(map((config) => {
const token = this.authStateService.getIdToken(config);
return this.tokenHelperService.getPayloadFromToken(token, encode, config);
}));
}
/**
* Returns the payload from the access token.
*
* @param encode Set to true if the payload is base64 encoded
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns The payload from the access token.
*/
getPayloadFromAccessToken(encode = false, configId) {
return this.configurationService.getOpenIDConfiguration(configId).pipe(map((config) => {
const token = this.authStateService.getAccessToken(config);
return this.tokenHelperService.getPayloadFromToken(token, encode, config);
}));
}
/**
* Sets a custom state for the authorize request.
*
* @param state The state to set.
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*/
setState(state, configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.flowsDataService.setAuthStateControl(state, config)));
}
/**
* Gets the state value used for the authorize request.
*
* @param configId The configId to check the information for. If not passed, the first configs will be taken
*
* @returns The state value used for the authorize request.
*/
getState(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.flowsDataService.getAuthStateControl(config)));
}
/**
* Redirects the user to the Security Token Service to begin the authentication process.
*
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
* @param authOptions The custom options for the the authentication request.
*/
authorize(configId, authOptions) {
this.configurationService
.getOpenIDConfiguration(configId)
.subscribe((config) => this.loginService.login(config, authOptions));
}
/**
* Opens the Security Token Service in a new window to begin the authentication process.
*
* @param authOptions The custom options for the authentication request.
* @param popupOptions The configuration for the popup window.
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*
* @returns An `Observable<LoginResponse>` containing all information about the login
*/
authorizeWithPopUp(authOptions, popupOptions, configId) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(concatMap(({ allConfigs, currentConfig }) => this.loginService.loginWithPopUp(currentConfig, allConfigs, authOptions, popupOptions)));
}
/**
* Manually refreshes the session.
*
* @param customParams Custom parameters to pass to the refresh request.
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*
* @returns An `Observable<LoginResponse>` containing all information about the login
*/
forceRefreshSession(customParams, configId) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(concatMap(({ allConfigs, currentConfig }) => this.refreshSessionService.userForceRefreshSession(currentConfig, allConfigs, customParams)));
}
/**
* Revokes the refresh token (if present) and the access token on the server and then performs the logoff operation.
* The refresh token and and the access token are revoked on the server. If the refresh token does not exist
* only the access token is revoked. Then the logout run.
*
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
* @param logoutAuthOptions The custom options for the request.
*
* @returns An observable when the action is finished
*/
logoffAndRevokeTokens(configId, logoutAuthOptions) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(concatMap(({ allConfigs, currentConfig }) => this.logoffRevocationService.logoffAndRevokeTokens(currentConfig, allConfigs, logoutAuthOptions)));
}
/**
* Logs out on the server and the local client. If the server state has changed, confirmed via check session,
* then only a local logout is performed.
*
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
* @param logoutAuthOptions with custom parameters and/or an custom url handler
*/
logoff(configId, logoutAuthOptions) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(concatMap(({ allConfigs, currentConfig }) => this.logoffRevocationService.logoff(currentConfig, allConfigs, logoutAuthOptions)));
}
/**
* Logs the user out of the application without logging them out of the server.
* Use this method if you have _one_ config enabled.
*
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*/
logoffLocal(configId) {
this.configurationService
.getOpenIDConfigurations(configId)
.subscribe(({ allConfigs, currentConfig }) => this.logoffRevocationService.logoffLocal(currentConfig, allConfigs));
}
/**
* Logs the user out of the application for all configs without logging them out of the server.
* Use this method if you have _multiple_ configs enabled.
*/
logoffLocalMultiple() {
this.configurationService
.getOpenIDConfigurations()
.subscribe(({ allConfigs }) => this.logoffRevocationService.logoffLocalMultiple(allConfigs));
}
/**
* Revokes an access token on the Security Token Service. This is only required in the code flow with refresh tokens. If no token is
* provided, then the token from the storage is revoked. You can pass any token to revoke.
* https://tools.ietf.org/html/rfc7009
*
* @param accessToken The access token to revoke.
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*
* @returns An observable when the action is finished
*/
revokeAccessToken(accessToken, configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(concatMap((config) => this.logoffRevocationService.revokeAccessToken(config, accessToken)));
}
/**
* Revokes a refresh token on the Security Token Service. This is only required in the code flow with refresh tokens. If no token is
* provided, then the token from the storage is revoked. You can pass any token to revoke.
* https://tools.ietf.org/html/rfc7009
*
* @param refreshToken The access token to revoke.
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*
* @returns An observable when the action is finished
*/
revokeRefreshToken(refreshToken, configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(concatMap((config) => this.logoffRevocationService.revokeRefreshToken(config, refreshToken)));
}
/**
* Creates the end session URL which can be used to implement an alternate server logout.
*
* @param customParams
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*
* @returns A string with the end session url or null
*/
getEndSessionUrl(customParams, configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(map((config) => this.urlService.getEndSessionUrl(config, customParams)));
}
/**
* Creates the authorize URL based on your flow
*
* @param customParams
* @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
*
* @returns A string with the authorize URL or null
*/
getAuthorizeUrl(customParams, configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(concatMap((config) => this.urlService.getAuthorizeUrl(config, customParams ? { customParams } : undefined)));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: OidcSecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: OidcSecurityService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: OidcSecurityService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2lkYy5zZWN1cml0eS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1hdXRoLW9pZGMtY2xpZW50L3NyYy9saWIvb2lkYy5zZWN1cml0eS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRW5ELE9BQU8sRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFHaEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzlELE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBRTNFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQ3hGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRy9ELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzlELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXJFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVyRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUNwRixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFdkQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFDOUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3JELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw0QkFBNEIsQ0FB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QUFDO2FBQ2hDLElBQUksQ0FDSCxTQUFTLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUNuQixJQUFJLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FDN0IsTUFBTSxFQUNOLFlBQVksQ0FBQyxDQUFDLENBQUMsRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUM1QyxDQUNGLENBQ0YsQ0FBQztJQUNOLENBQUM7OEdBbmlCVSxtQkFBbUI7a0hBQW5CLG1CQUFtQixjQUROLE1BQU07OzJGQUNuQixtQkFBbUI7a0JBRC9CLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgY29uY2F0TWFwLCBtYXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XHJcbmltcG9ydCB7IEF1dGhPcHRpb25zLCBMb2dvdXRBdXRoT3B0aW9ucyB9IGZyb20gJy4vYXV0aC1vcHRpb25zJztcclxuaW1wb3J0IHsgQXV0aGVudGljYXRlZFJlc3VsdCB9IGZyb20gJy4vYXV0aC1zdGF0ZS9hdXRoLXJlc3VsdCc7XHJcbmltcG9ydCB7IEF1dGhTdGF0ZVNlcnZpY2UgfSBmcm9tICcuL2F1dGgtc3RhdGUvYXV0aC1zdGF0ZS5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQ2hlY2tBdXRoU2VydmljZSB9IGZyb20gJy4vYXV0aC1zdGF0ZS9jaGVjay1hdXRoLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBDYWxsYmFja1NlcnZpY2UgfSBmcm9tICcuL2NhbGxiYWNrL2NhbGxiYWNrLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBSZWZyZXNoU2Vzc2lvblNlcnZpY2UgfSBmcm9tICcuL2NhbGxiYWNrL3JlZnJlc2gtc2Vzc2lvbi5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQXV0aFdlbGxLbm93bkVuZHBvaW50cyB9IGZyb20gJy4vY29uZmlnL2F1dGgtd2VsbC1rbm93bi9hdXRoLXdlbGwta25vd24tZW5kcG9pbnRzJztcclxuaW1wb3J0IHsgQXV0aFdlbGxLbm93blNlcnZpY2UgfSBmcm9tICcuL2NvbmZpZy9hdXRoLXdlbGwta25vd24vYXV0aC13ZWxsLWtub3duLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBDb25maWd1cmF0aW9uU2VydmljZSB9IGZyb20gJy4vY29uZmlnL2NvbmZpZy5zZXJ2aWNlJztcclxuaW1wb3J0IHsgT3BlbklkQ29uZmlndXJhdGlvbiB9IGZyb20gJy4vY29uZmlnL29wZW5pZC1jb25maWd1cmF0aW9uJztcclxuaW1wb3J0IHsgQXV0aFJlc3VsdCB9IGZyb20gJy4vZmxvd3MvY2FsbGJhY2stY29udGV4dCc7XHJcbmltcG9ydCB7IEZsb3dzRGF0YVNlcnZpY2UgfSBmcm9tICcuL2Zsb3dzL2Zsb3dzLWRhdGEuc2VydmljZSc7XHJcbmltcG9ydCB7IENoZWNrU2Vzc2lvblNlcnZpY2UgfSBmcm9tICcuL2lmcmFtZS9jaGVjay1zZXNzaW9uLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBMb2dpblJlc3BvbnNlIH0gZnJvbSAnLi9sb2dpbi9sb2dpbi1yZXNwb25zZSc7XHJcbmltcG9ydCB7IExvZ2luU2VydmljZSB9IGZyb20gJy4vbG9naW4vbG9naW4uc2VydmljZSc7XHJcbmltcG9ydCB7IFBvcHVwT3B0aW9ucyB9IGZyb20gJy4vbG9naW4vcG9wdXAvcG9wdXAtb3B0aW9ucyc7XHJcbmltcG9ydCB7IExvZ29mZlJldm9jYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9sb2dvZmYtcmV2b2tlL2xvZ29mZi1yZXZvY2F0aW9uLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBVc2VyU2VydmljZSB9IGZyb20gJy4vdXNlci1kYXRhL3VzZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IFVzZXJEYXRhUmVzdWx0IH0gZnJvbSAnLi91c2VyLWRhdGEvdXNlcmRhdGEtcmVzdWx0JztcclxuaW1wb3J0IHsgVG9rZW5IZWxwZXJTZXJ2aWNlIH0gZnJvbSAnLi91dGlscy90b2tlbkhlbHBlci90b2tlbi1oZWxwZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IFVybFNlcnZpY2UgfSBmcm9tICcuL3V0aWxzL3VybC91cmwuc2VydmljZSc7XHJcbmltcG9ydCB7IHRvU2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZS9yeGpzLWludGVyb3AnO1xyXG5cclxuQEluamVjdGFibGUoeyBwcm92aWRlZEluOiAncm9vdCcgfSlcclxuZXhwb3J0IGNsYXNzIE9pZGNTZWN1cml0eVNlcnZpY2Uge1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgY2hlY2tTZXNzaW9uU2VydmljZSA9IGluamVjdChDaGVja1Nlc3Npb25TZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSBjaGVja0F1dGhTZXJ2aWNlID0gaW5qZWN0KENoZWNrQXV0aFNlcnZpY2UpO1xyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IHVzZXJTZXJ2aWNlID0gaW5qZWN0KFVzZXJTZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSB0b2tlbkhlbHBlclNlcnZpY2UgPSBpbmplY3QoVG9rZW5IZWxwZXJTZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSBjb25maWd1cmF0aW9uU2VydmljZSA9IGluamVjdChDb25maWd1cmF0aW9uU2VydmljZSk7XHJcblxyXG4gIHByaXZhdGUgcmVhZG9ubHkgYXV0aFN0YXRlU2VydmljZSA9IGluamVjdChBdXRoU3RhdGVTZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSBmbG93c0RhdGFTZXJ2aWNlID0gaW5qZWN0KEZsb3dzRGF0YVNlcnZpY2UpO1xyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IGNhbGxiYWNrU2VydmljZSA9IGluamVjdChDYWxsYmFja1NlcnZpY2UpO1xyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IGxvZ29mZlJldm9jYXRpb25TZXJ2aWNlID0gaW5qZWN0KExvZ29mZlJldm9jYXRpb25TZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSBsb2dpblNlcnZpY2UgPSBpbmplY3QoTG9naW5TZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSByZWZyZXNoU2Vzc2lvblNlcnZpY2UgPSBpbmplY3QoUmVmcmVzaFNlc3Npb25TZXJ2aWNlKTtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSB1cmxTZXJ2aWNlID0gaW5qZWN0KFVybFNlcnZpY2UpO1xyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IGF1dGhXZWxsS25vd25TZXJ2aWNlID0gaW5qZWN0KEF1dGhXZWxsS25vd25TZXJ2aWNlKTtcclxuXHJcbiAgLyoqXHJcbiAgICogUHJvdmlkZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHVzZXIgYWZ0ZXIgdGhleSBoYXZlIGxvZ2dlZCBpbi5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIFJldHVybnMgYW4gb2JqZWN0IGNvbnRhaW5pbmcgZWl0aGVyIHRoZSB1c2VyIGRhdGEgZGlyZWN0bHkgKHNpbmdsZSBjb25maWcpIG9yXHJcbiAgICogdGhlIHVzZXIgZGF0YSBwZXIgY29uZmlnIGluIGNhc2UgeW91IGFyZSBydW5uaW5nIHdpdGggbXVsdGlwbGUgY29uZmlnc1xyXG4gICAqL1xyXG4gIGdldCB1c2VyRGF0YSQoKTogT2JzZXJ2YWJsZTxVc2VyRGF0YVJlc3VsdD4ge1xyXG4gICAgcmV0dXJuIHRoaXMudXNlclNlcnZpY2UudXNlckRhdGEkO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUHJvdmlkZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHVzZXIgYWZ0ZXIgdGhleSBoYXZlIGxvZ2dlZCBpbi5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIFJldHVybnMgYW4gb2JqZWN0IGNvbnRhaW5pbmcgZWl0aGVyIHRoZSB1c2VyIGRhdGEgZGlyZWN0bHkgKHNpbmdsZSBjb25maWcpIG9yXHJcbiAgICogdGhlIHVzZXIgZGF0YSBwZXIgY29uZmlnIGluIGNhc2UgeW91IGFyZSBydW5uaW5nIHdpdGggbXVsdGlwbGUgY29uZmlnc1xyXG4gICAqL1xyXG4gIHVzZXJEYXRhID0gdG9TaWduYWwodGhpcy51c2VyRGF0YSQsIHsgcmVxdWlyZVN5bmM6IHRydWUgfSk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEVtaXRzIGVhY2ggdGltZSBhbiBhdXRob3JpemF0aW9uIGV2ZW50IG9jY3Vycy5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIFJldHVybnMgYW4gb2JqZWN0IGNvbnRhaW5pbmcgaWYgeW91IGFyZSBhdXRoZW50aWNhdGVkIG9yIG5vdC5cclxuICAgKiBTaW5nbGUgQ29uZmlnOiB0cnVlIGlmIGNvbmZpZyBpcyBhdXRoZW50aWNhdGVkLCBmYWxzZSBpZiBub3QuXHJcbiAgICogTXVsdGlwbGUgQ29uZmlnczogdHJ1ZSBpcyBhbGwgY29uZmlncyBhcmUgYXV0aGVudGljYXRlZCwgZmFsc2UgaWYgb25seSBvbmUgb2YgdGhlbSBpcyBub3RcclxuICAgKlxyXG4gICAqIFRoZSBgYWxsQ29uZmlnc0F1dGhlbnRpY2F0ZWRgIHByb3BlcnR5IGNvbnRhaW5zIHRoZSBhdXRoIGluZm9ybWF0aW9uIF9wZXIgY29uZmlnXy5cclxuICAgKi9cclxuICBnZXQgaXNBdXRoZW50aWNhdGVkJCgpOiBPYnNlcnZhYmxlPEF1dGhlbnRpY2F0ZWRSZXN1bHQ+IHtcclxuICAgIHJldHVybiB0aGlzLmF1dGhTdGF0ZVNlcnZpY2UuYXV0aGVudGljYXRlZCQ7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBFbWl0cyBlYWNoIHRpbWUgYW4gYXV0aG9yaXphdGlvbiBldmVudCBvY2N1cnMuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBSZXR1cm5zIGFuIG9iamVjdCBjb250YWluaW5nIGlmIHlvdSBhcmUgYXV0aGVudGljYXRlZCBvciBub3QuXHJcbiAgICogU2luZ2xlIENvbmZpZzogdHJ1ZSBpZiBjb25maWcgaXMgYXV0aGVudGljYXRlZCwgZmFsc2UgaWYgbm90LlxyXG4gICAqIE11bHRpcGxlIENvbmZpZ3M6IHRydWUgaXMgYWxsIGNvbmZpZ3MgYXJlIGF1dGhlbnRpY2F0ZWQsIGZhbHNlIGlmIG9ubHkgb25lIG9mIHRoZW0gaXMgbm90XHJcbiAgICpcclxuICAgKiBUaGUgYGFsbENvbmZpZ3NBdXRoZW50aWNhdGVkYCBwcm9wZXJ0eSBjb250YWlucyB0aGUgYXV0aCBpbmZvcm1hdGlvbiBfcGVyIGNvbmZpZ18uXHJcbiAgICovXHJcbiAgYXV0aGVudGljYXRlZCA9IHRvU2lnbmFsKHRoaXMuaXNBdXRoZW50aWNhdGVkJCwgeyByZXF1aXJlU3luYzogdHJ1ZSB9KTtcclxuXHJcbiAgLyoqXHJcbiAgICogRW1pdHMgZWFjaCB0aW1lIHRoZSBzZXJ2ZXIgc2VuZHMgYSBDaGVja1Nlc3Npb24gZXZlbnQgYW5kIHRoZSB2YWx1ZSBjaGFuZ2VkLiBUaGlzIHByb3BlcnR5IHdpbGwgYWx3YXlzIHJldHVyblxyXG4gICAqIHRydWUuXHJcbiAgICovXHJcbiAgZ2V0IGNoZWNrU2Vzc2lvbkNoYW5nZWQkKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY2hlY2tTZXNzaW9uU2VydmljZS5jaGVja1Nlc3Npb25DaGFuZ2VkJDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEVtaXRzIG9uIGEgU2VjdXJpdHkgVG9rZW4gU2VydmljZSBjYWxsYmFjay4gVGhlIG9ic2VydmFibGUgd2lsbCBuZXZlciBjb250YWluIGEgdmFsdWUuXHJcbiAgICovXHJcbiAgZ2V0IHN0c0NhbGxiYWNrJCgpOiBPYnNlcnZhYmxlPHZvaWQ+IHtcclxuICAgIHJldHVybiB0aGlzLmNhbGxiYWNrU2VydmljZS5zdHNDYWxsYmFjayQ7XHJcbiAgfVxyXG5cclxuICBwcmVsb2FkQXV0aFdlbGxLbm93bkRvY3VtZW50KFxyXG4gICAgY29uZmlnSWQ/OiBzdHJpbmdcclxuICApOiBPYnNlcnZhYmxlPEF1dGhXZWxsS25vd25FbmRwb2ludHM+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKVxyXG4gICAgICAucGlwZShcclxuICAgICAgICBjb25jYXRNYXAoKGNvbmZpZykgPT5cclxuICAgICAgICAgIHRoaXMuYXV0aFdlbGxLbm93blNlcnZpY2UucXVlcnlBbmRTdG9yZUF1dGhXZWxsS25vd25FbmRQb2ludHMoY29uZmlnKVxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdGhlIGN1cnJlbnRseSBhY3RpdmUgT3BlbklEIGNvbmZpZ3VyYXRpb25zLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMgYW4gYXJyYXkgb2YgT3BlbklkQ29uZmlndXJhdGlvbnMuXHJcbiAgICovXHJcbiAgZ2V0Q29uZmlndXJhdGlvbnMoKTogT3BlbklkQ29uZmlndXJhdGlvbltdIHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlLmdldEFsbENvbmZpZ3VyYXRpb25zKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIGEgc2luZ2xlIGFjdGl2ZSBPcGVuSWRDb25maWd1cmF0aW9uLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBpZGVudGlmeSB0aGUgY29uZmlnLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3Qgb25lIGlzIGJlaW5nIHJldHVybmVkXHJcbiAgICovXHJcbiAgZ2V0Q29uZmlndXJhdGlvbihjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8T3BlbklkQ29uZmlndXJhdGlvbiB8IG51bGw+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlLmdldE9wZW5JRENvbmZpZ3VyYXRpb24oY29uZmlnSWQpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV0dXJucyB0aGUgdXNlckRhdGEgZm9yIGEgY29uZmlndXJhdGlvblxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBpZGVudGlmeSB0aGUgY29uZmlnLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3Qgb25lIGlzIGJlaW5nIHVzZWRcclxuICAgKi9cclxuICBnZXRVc2VyRGF0YShjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8YW55PiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZVxyXG4gICAgICAuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZClcclxuICAgICAgLnBpcGUobWFwKChjb25maWcpID0+IHRoaXMudXNlclNlcnZpY2UuZ2V0VXNlckRhdGFGcm9tU3RvcmUoY29uZmlnKSkpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU3RhcnRzIHRoZSBjb21wbGV0ZSBzZXR1cCBmbG93IGZvciBvbmUgY29uZmlndXJhdGlvbi4gQ2FsbGluZyB3aWxsIHN0YXJ0IHRoZSBlbnRpcmUgYXV0aGVudGljYXRpb24gZmxvdywgYW5kIHRoZSByZXR1cm5lZCBvYnNlcnZhYmxlXHJcbiAgICogd2lsbCBkZW5vdGUgd2hldGhlciB0aGUgdXNlciB3YXMgc3VjY2Vzc2Z1bGx5IGF1dGhlbnRpY2F0ZWQgaW5jbHVkaW5nIHRoZSB1c2VyIGRhdGEsIHRoZSBhY2Nlc3MgdG9rZW4sIHRoZSBjb25maWdJZCBhbmRcclxuICAgKiBhbiBlcnJvciBtZXNzYWdlIGluIGNhc2UgYW4gZXJyb3IgaGFwcGVuZWRcclxuICAgKlxyXG4gICAqIEBwYXJhbSB1cmwgVGhlIFVSTCB0byBwZXJmb3JtIHRoZSBhdXRob3JpemF0aW9uIG9uIHRoZSBiZWhhbGYgb2YuXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBwZXJmb3JtIHRoZSBhdXRob3JpemF0aW9uIG9uIHRoZSBiZWhhbGYgb2YuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIEFuIG9iamVjdCBgTG9naW5SZXNwb25zZWAgY29udGFpbmluZyBhbGwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGxvZ2luXHJcbiAgICovXHJcbiAgY2hlY2tBdXRoKHVybD86IHN0cmluZywgY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPExvZ2luUmVzcG9uc2U+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9ucyhjb25maWdJZClcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgY29uY2F0TWFwKCh7IGFsbENvbmZpZ3MsIGN1cnJlbnRDb25maWcgfSkgPT5cclxuICAgICAgICAgIHRoaXMuY2hlY2tBdXRoU2VydmljZS5jaGVja0F1dGgoY3VycmVudENvbmZpZywgYWxsQ29uZmlncywgdXJsKVxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFN0YXJ0cyB0aGUgY29tcGxldGUgc2V0dXAgZmxvdyBmb3IgbXVsdGlwbGUgY29uZmlndXJhdGlvbnMuXHJcbiAgICogQ2FsbGluZyB3aWxsIHN0YXJ0IHRoZSBlbnRpcmUgYXV0aGVudGljYXRpb24gZmxvdywgYW5kIHRoZSByZXR1cm5lZCBvYnNlcnZhYmxlXHJcbiAgICogd2lsbCBkZW5vdGUgd2hldGhlciB0aGUgdXNlciB3YXMgc3VjY2Vzc2Z1bGx5IGF1dGhlbnRpY2F0ZWQgaW5jbHVkaW5nIHRoZSB1c2VyIGRhdGEsIHRoZSBhY2Nlc3MgdG9rZW4sIHRoZSBjb25maWdJZCBhbmRcclxuICAgKiBhbiBlcnJvciBtZXNzYWdlIGluIGNhc2UgYW4gZXJyb3IgaGFwcGVuZWQgaW4gYW4gYXJyYXkgZm9yIGVhY2ggY29uZmlnIHdoaWNoIHdhcyBwcm92aWRlZFxyXG4gICAqXHJcbiAgICogQHBhcmFtIHVybCBUaGUgVVJMIHRvIHBlcmZvcm0gdGhlIGF1dGhvcml6YXRpb24gb24gdGhlIGJlaGFsZiBvZi5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIEFuIGFycmF5IG9mIGBMb2dpblJlc3BvbnNlYCBvYmplY3RzIGNvbnRhaW5pbmcgYWxsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBsb2dpbnNcclxuICAgKi9cclxuICBjaGVja0F1dGhNdWx0aXBsZSh1cmw/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPExvZ2luUmVzcG9uc2VbXT4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2VcclxuICAgICAgLmdldE9wZW5JRENvbmZpZ3VyYXRpb25zKClcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgY29uY2F0TWFwKCh7IGFsbENvbmZpZ3MgfSkgPT5cclxuICAgICAgICAgIHRoaXMuY2hlY2tBdXRoU2VydmljZS5jaGVja0F1dGhNdWx0aXBsZShhbGxDb25maWdzLCB1cmwpXHJcbiAgICAgICAgKVxyXG4gICAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUHJvdmlkZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgYXV0aGVudGljYXRlZCBzdGF0ZVxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBjaGVjayB0aGUgaW5mb3JtYXRpb24gZm9yLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3QgY29uZmlncyB3aWxsIGJlIHRha2VuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBBIGJvb2xlYW4gd2hldGhlciB0aGUgY29uZmlnIGlzIGF1dGhlbnRpY2F0ZWQgb3Igbm90LlxyXG4gICAqL1xyXG4gIGlzQXV0aGVudGljYXRlZChjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2VcclxuICAgICAgLmdldE9wZW5JRENvbmZpZ3VyYXRpb24oY29uZmlnSWQpXHJcbiAgICAgIC5waXBlKG1hcCgoY29uZmlnKSA9PiB0aGlzLmF1dGhTdGF0ZVNlcnZpY2UuaXNBdXRoZW50aWNhdGVkKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrcyB0aGUgc2VydmVyIGZvciBhbiBhdXRoZW50aWNhdGVkIHNlc3Npb24gdXNpbmcgdGhlIGlmcmFtZSBzaWxlbnQgcmVuZXcgaWYgbm90IGxvY2FsbHkgYXV0aGVudGljYXRlZC5cclxuICAgKi9cclxuICBjaGVja0F1dGhJbmNsdWRpbmdTZXJ2ZXIoY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPExvZ2luUmVzcG9uc2U+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9ucyhjb25maWdJZClcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgY29uY2F0TWFwKCh7IGFsbENvbmZpZ3MsIGN1cnJlbnRDb25maWcgfSkgPT5cclxuICAgICAgICAgIHRoaXMuY2hlY2tBdXRoU2VydmljZS5jaGVja0F1dGhJbmNsdWRpbmdTZXJ2ZXIoXHJcbiAgICAgICAgICAgIGN1cnJlbnRDb25maWcsXHJcbiAgICAgICAgICAgIGFsbENvbmZpZ3NcclxuICAgICAgICAgIClcclxuICAgICAgICApXHJcbiAgICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBhY2Nlc3MgdG9rZW4gZm9yIHRoZSBsb2dpbiBzY2VuYXJpby5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgQSBzdHJpbmcgd2l0aCB0aGUgYWNjZXNzIHRva2VuLlxyXG4gICAqL1xyXG4gIGdldEFjY2Vzc1Rva2VuKGNvbmZpZ0lkPzogc3RyaW5nKTogT2JzZXJ2YWJsZTxzdHJpbmc+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKVxyXG4gICAgICAucGlwZShtYXAoKGNvbmZpZykgPT4gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmdldEFjY2Vzc1Rva2VuKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdGhlIElEIHRva2VuIGZvciB0aGUgc2lnbi1pbi5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgQSBzdHJpbmcgd2l0aCB0aGUgaWQgdG9rZW4uXHJcbiAgICovXHJcbiAgZ2V0SWRUb2tlbihjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8c3RyaW5nPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZVxyXG4gICAgICAuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZClcclxuICAgICAgLnBpcGUobWFwKChjb25maWcpID0+IHRoaXMuYXV0aFN0YXRlU2VydmljZS5nZXRJZFRva2VuKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdGhlIHJlZnJlc2ggdG9rZW4sIGlmIHByZXNlbnQsIGZvciB0aGUgc2lnbi1pbi5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgQSBzdHJpbmcgd2l0aCB0aGUgcmVmcmVzaCB0b2tlbi5cclxuICAgKi9cclxuICBnZXRSZWZyZXNoVG9rZW4oY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPHN0cmluZz4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2VcclxuICAgICAgLmdldE9wZW5JRENvbmZpZ3VyYXRpb24oY29uZmlnSWQpXHJcbiAgICAgIC5waXBlKG1hcCgoY29uZmlnKSA9PiB0aGlzLmF1dGhTdGF0ZVNlcnZpY2UuZ2V0UmVmcmVzaFRva2VuKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdGhlIGF1dGhlbnRpY2F0aW9uIHJlc3VsdCwgaWYgcHJlc2VudCwgZm9yIHRoZSBzaWduLWluLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBjaGVjayB0aGUgaW5mb3JtYXRpb24gZm9yLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3QgY29uZmlncyB3aWxsIGJlIHRha2VuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBBIG9iamVjdCB3aXRoIHRoZSBhdXRoZW50aWNhdGlvbiByZXN1bHRcclxuICAgKi9cclxuICBnZXRBdXRoZW50aWNhdGlvblJlc3VsdChjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8QXV0aFJlc3VsdCB8IG51bGw+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKVxyXG4gICAgICAucGlwZShcclxuICAgICAgICBtYXAoKGNvbmZpZykgPT4gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmdldEF1dGhlbnRpY2F0aW9uUmVzdWx0KGNvbmZpZykpXHJcbiAgICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBwYXlsb2FkIGZyb20gdGhlIElEIHRva2VuLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGVuY29kZSBTZXQgdG8gdHJ1ZSBpZiB0aGUgcGF5bG9hZCBpcyBiYXNlNjQgZW5jb2RlZFxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgVGhlIHBheWxvYWQgZnJvbSB0aGUgaWQgdG9rZW4uXHJcbiAgICovXHJcbiAgZ2V0UGF5bG9hZEZyb21JZFRva2VuKGVuY29kZSA9IGZhbHNlLCBjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8YW55PiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKS5waXBlKFxyXG4gICAgICBtYXAoKGNvbmZpZykgPT4ge1xyXG4gICAgICAgIGNvbnN0IHRva2VuID0gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmdldElkVG9rZW4oY29uZmlnKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMudG9rZW5IZWxwZXJTZXJ2aWNlLmdldFBheWxvYWRGcm9tVG9rZW4oXHJcbiAgICAgICAgICB0b2tlbixcclxuICAgICAgICAgIGVuY29kZSxcclxuICAgICAgICAgIGNvbmZpZ1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV0dXJucyB0aGUgcGF5bG9hZCBmcm9tIHRoZSBhY2Nlc3MgdG9rZW4uXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZW5jb2RlIFNldCB0byB0cnVlIGlmIHRoZSBwYXlsb2FkIGlzIGJhc2U2NCBlbmNvZGVkXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBjaGVjayB0aGUgaW5mb3JtYXRpb24gZm9yLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3QgY29uZmlncyB3aWxsIGJlIHRha2VuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBUaGUgcGF5bG9hZCBmcm9tIHRoZSBhY2Nlc3MgdG9rZW4uXHJcbiAgICovXHJcbiAgZ2V0UGF5bG9hZEZyb21BY2Nlc3NUb2tlbihcclxuICAgIGVuY29kZSA9IGZhbHNlLFxyXG4gICAgY29uZmlnSWQ/OiBzdHJpbmdcclxuICApOiBPYnNlcnZhYmxlPGFueT4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2UuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZCkucGlwZShcclxuICAgICAgbWFwKChjb25maWcpID0+IHtcclxuICAgICAgICBjb25zdCB0b2tlbiA9IHRoaXMuYXV0aFN0YXRlU2VydmljZS5nZXRBY2Nlc3NUb2tlbihjb25maWcpO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy50b2tlbkhlbHBlclNlcnZpY2UuZ2V0UGF5bG9hZEZyb21Ub2tlbihcclxuICAgICAgICAgIHRva2VuLFxyXG4gICAgICAgICAgZW5jb2RlLFxyXG4gICAgICAgICAgY29uZmlnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSlcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIGEgY3VzdG9tIHN0YXRlIGZvciB0aGUgYXV0aG9yaXplIHJlcXVlc3QuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gc3RhdGUgVGhlIHN0YXRlIHRvIHNldC5cclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIGNoZWNrIHRoZSBpbmZvcm1hdGlvbiBmb3IuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKi9cclxuICBzZXRTdGF0ZShzdGF0ZTogc3RyaW5nLCBjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2VcclxuICAgICAgLmdldE9wZW5JRENvbmZpZ3VyYXRpb24oY29uZmlnSWQpXHJcbiAgICAgIC5waXBlKFxyXG4gICAgICAgIG1hcCgoY29uZmlnKSA9PlxyXG4gICAgICAgICAgdGhpcy5mbG93c0RhdGFTZXJ2aWNlLnNldEF1dGhTdGF0ZUNvbnRyb2woc3RhdGUsIGNvbmZpZylcclxuICAgICAgICApXHJcbiAgICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBHZXRzIHRoZSBzdGF0ZSB2YWx1ZSB1c2VkIGZvciB0aGUgYXV0aG9yaXplIHJlcXVlc3QuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIGNoZWNrIHRoZSBpbmZvcm1hdGlvbiBmb3IuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIFRoZSBzdGF0ZSB2YWx1ZSB1c2VkIGZvciB0aGUgYXV0aG9yaXplIHJlcXVlc3QuXHJcbiAgICovXHJcbiAgZ2V0U3RhdGUoY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPHN0cmluZz4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2VcclxuICAgICAgLmdldE9wZW5JRENvbmZpZ3VyYXRpb24oY29uZmlnSWQpXHJcbiAgICAgIC5waXBlKG1hcCgoY29uZmlnKSA9PiB0aGlzLmZsb3dzRGF0YVNlcnZpY2UuZ2V0QXV0aFN0YXRlQ29udHJvbChjb25maWcpKSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZWRpcmVjdHMgdGhlIHVzZXIgdG8gdGhlIFNlY3VyaXR5IFRva2VuIFNlcnZpY2UgdG8gYmVnaW4gdGhlIGF1dGhlbnRpY2F0aW9uIHByb2Nlc3MuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIHBlcmZvcm0gdGhlIGFjdGlvbiBpbiBiZWhhbGYgb2YuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKiBAcGFyYW0gYXV0aE9wdGlvbnMgVGhlIGN1c3RvbSBvcHRpb25zIGZvciB0aGUgdGhlIGF1dGhlbnRpY2F0aW9uIHJlcXVlc3QuXHJcbiAgICovXHJcbiAgYXV0aG9yaXplKGNvbmZpZ0lkPzogc3RyaW5nLCBhdXRoT3B0aW9ucz86IEF1dGhPcHRpb25zKTogdm9pZCB7XHJcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKVxyXG4gICAgICAuc3Vic2NyaWJlKChjb25maWcpID0+IHRoaXMubG9naW5TZXJ2aWNlLmxvZ2luKGNvbmZpZywgYXV0aE9w