UNPKG

angular-auth-oidc-client

Version:
413 lines 60.7 kB
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