angular-auth-oidc-client
Version:
Angular Lib for OpenID Connect & OAuth2
394 lines • 59.7 kB
JavaScript
import { Injectable } from '@angular/core';
import { BehaviorSubject, throwError } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import * as i0 from "@angular/core";
import * as i1 from "./iframe/check-session.service";
import * as i2 from "./auth-state/check-auth.service";
import * as i3 from "./user-data/user.service";
import * as i4 from "./utils/tokenHelper/token-helper.service";
import * as i5 from "./config/config.service";
import * as i6 from "./auth-state/auth-state.service";
import * as i7 from "./flows/flows-data.service";
import * as i8 from "./callback/callback.service";
import * as i9 from "./logoff-revoke/logoff-revocation.service";
import * as i10 from "./login/login.service";
import * as i11 from "./callback/refresh-session.service";
import * as i12 from "./utils/url/url.service";
import * as i13 from "./config/auth-well-known/auth-well-known.service";
export class OidcSecurityService {
constructor(checkSessionService, checkAuthService, userService, tokenHelperService, configurationService, authStateService, flowsDataService, callbackService, logoffRevocationService, loginService, refreshSessionService, urlService, authWellKnownService) {
this.checkSessionService = checkSessionService;
this.checkAuthService = checkAuthService;
this.userService = userService;
this.tokenHelperService = tokenHelperService;
this.configurationService = configurationService;
this.authStateService = authStateService;
this.flowsDataService = flowsDataService;
this.callbackService = callbackService;
this.logoffRevocationService = logoffRevocationService;
this.loginService = loginService;
this.refreshSessionService = refreshSessionService;
this.urlService = urlService;
this.authWellKnownService = authWellKnownService;
this.isLoading = new BehaviorSubject(true);
this.finishLoading = () => {
this.isLoading.next(false);
};
this.finishLoadingOnError = (err) => {
this.isLoading.next(false);
return throwError(() => err);
};
}
/**
* 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$;
}
/**
* @deprecated This property should not be used. Please use the `PublicEventsService` instead. This property is removed in future versions
* Emits false when the observable, returned by one of the checkAuth() methods, emits a value, or errors. Initial value: true.
*/
get isLoading$() {
return this.isLoading.asObservable();
}
preloadAuthWellKnownDocument(configId) {
return this.configurationService
.getOpenIDConfiguration(configId)
.pipe(switchMap((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(switchMap(({ allConfigs, currentConfig }) => this.checkAuthService.checkAuth(currentConfig, allConfigs, url)), tap(this.finishLoading), catchError(this.finishLoadingOnError));
}
/**
* 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.
* @param configId The configId to perform the authorization on the behalf of. If not passed, all of the current
* configured ones will be used to check.
*
* @returns An array of `LoginResponse` objects containing all information about the logins
*/
checkAuthMultiple(url) {
return this.configurationService.getOpenIDConfigurations().pipe(switchMap(({ allConfigs }) => this.checkAuthService.checkAuthMultiple(allConfigs, url)), tap(this.finishLoading), catchError(this.finishLoadingOnError));
}
/**
* 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(switchMap(({ allConfigs, currentConfig }) => this.checkAuthService.checkAuthIncludingServer(currentConfig, allConfigs)), tap(this.finishLoading), catchError(this.finishLoadingOnError));
}
/**
* 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(switchMap(({ 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(switchMap(({ 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 authOptions The custom options for the request.
*
* @returns An observable when the action is finished
*/
logoffAndRevokeTokens(configId, authOptions) {
return this.configurationService
.getOpenIDConfigurations(configId)
.pipe(switchMap(({ allConfigs, currentConfig }) => this.logoffRevocationService.logoffAndRevokeTokens(currentConfig, allConfigs, authOptions)));
}
/**
* 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 authOptions with custom parameters and/or an custom url handler
*/
logoff(configId, authOptions) {
this.configurationService
.getOpenIDConfigurations(configId)
.subscribe(({ allConfigs, currentConfig }) => this.logoffRevocationService.logoff(currentConfig, allConfigs, authOptions));
}
/**
* 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(switchMap((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(switchMap((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.logoffRevocationService.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(switchMap((config) => this.urlService.getAuthorizeUrl(config, customParams ? { customParams } : undefined)));
}
}
OidcSecurityService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0", ngImport: i0, type: OidcSecurityService, deps: [{ token: i1.CheckSessionService }, { token: i2.CheckAuthService }, { token: i3.UserService }, { token: i4.TokenHelperService }, { token: i5.ConfigurationService }, { token: i6.AuthStateService }, { token: i7.FlowsDataService }, { token: i8.CallbackService }, { token: i9.LogoffRevocationService }, { token: i10.LoginService }, { token: i11.RefreshSessionService }, { token: i12.UrlService }, { token: i13.AuthWellKnownService }], target: i0.ɵɵFactoryTarget.Injectable });
OidcSecurityService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0", ngImport: i0, type: OidcSecurityService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0", ngImport: i0, type: OidcSecurityService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i1.CheckSessionService }, { type: i2.CheckAuthService }, { type: i3.UserService }, { type: i4.TokenHelperService }, { type: i5.ConfigurationService }, { type: i6.AuthStateService }, { type: i7.FlowsDataService }, { type: i8.CallbackService }, { type: i9.LogoffRevocationService }, { type: i10.LoginService }, { type: i11.RefreshSessionService }, { type: i12.UrlService }, { type: i13.AuthWellKnownService }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2lkYy5zZWN1cml0eS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1hdXRoLW9pZGMtY2xpZW50L3NyYy9saWIvb2lkYy5zZWN1cml0eS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFFLGVBQWUsRUFBYyxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDL0QsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDOzs7Ozs7Ozs7Ozs7Ozs7QUF1QmpFLE1BQU0sT0FBTyxtQkFBbUI7SUFpRDlCLFlBQ21CLG1CQUF3QyxFQUN4QyxnQkFBa0MsRUFDbEMsV0FBd0IsRUFDeEIsa0JBQXNDLEVBQ3RDLG9CQUEwQyxFQUMxQyxnQkFBa0MsRUFDbEMsZ0JBQWtDLEVBQ2xDLGVBQWdDLEVBQ2hDLHVCQUFnRCxFQUNoRCxZQUEwQixFQUMxQixxQkFBNEMsRUFDNUMsVUFBc0IsRUFDdEIsb0JBQTBDO1FBWjFDLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBcUI7UUFDeEMscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUNsQyxnQkFBVyxHQUFYLFdBQVcsQ0FBYTtRQUN4Qix1QkFBa0IsR0FBbEIsa0JBQWtCLENBQW9CO1FBQ3RDLHlCQUFvQixHQUFwQixvQkFBb0IsQ0FBc0I7UUFDMUMscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUNsQyxxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQWtCO1FBQ2xDLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQUNoQyw0QkFBdUIsR0FBdkIsdUJBQXVCLENBQXlCO1FBQ2hELGlCQUFZLEdBQVosWUFBWSxDQUFjO1FBQzFCLDBCQUFxQixHQUFyQixxQkFBcUIsQ0FBdUI7UUFDNUMsZUFB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QUFtQjsyRkFBbkIsbUJBQW1CO2tCQUQvQixVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIHRocm93RXJyb3IgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgY2F0Y2hFcnJvciwgbWFwLCBzd2l0Y2hNYXAsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuaW1wb3J0IHsgQXV0aE9wdGlvbnMgfSBmcm9tICcuL2F1dGgtb3B0aW9ucyc7XHJcbmltcG9ydCB7IEF1dGhlbnRpY2F0ZWRSZXN1bHQgfSBmcm9tICcuL2F1dGgtc3RhdGUvYXV0aC1yZXN1bHQnO1xyXG5pbXBvcnQgeyBBdXRoU3RhdGVTZXJ2aWNlIH0gZnJvbSAnLi9hdXRoLXN0YXRlL2F1dGgtc3RhdGUuc2VydmljZSc7XHJcbmltcG9ydCB7IENoZWNrQXV0aFNlcnZpY2UgfSBmcm9tICcuL2F1dGgtc3RhdGUvY2hlY2stYXV0aC5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQ2FsbGJhY2tTZXJ2aWNlIH0gZnJvbSAnLi9jYWxsYmFjay9jYWxsYmFjay5zZXJ2aWNlJztcclxuaW1wb3J0IHsgUmVmcmVzaFNlc3Npb25TZXJ2aWNlIH0gZnJvbSAnLi9jYWxsYmFjay9yZWZyZXNoLXNlc3Npb24uc2VydmljZSc7XHJcbmltcG9ydCB7IEF1dGhXZWxsS25vd25FbmRwb2ludHMgfSBmcm9tICcuL2NvbmZpZy9hdXRoLXdlbGwta25vd24vYXV0aC13ZWxsLWtub3duLWVuZHBvaW50cyc7XHJcbmltcG9ydCB7IEF1dGhXZWxsS25vd25TZXJ2aWNlIH0gZnJvbSAnLi9jb25maWcvYXV0aC13ZWxsLWtub3duL2F1dGgtd2VsbC1rbm93bi5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQ29uZmlndXJhdGlvblNlcnZpY2UgfSBmcm9tICcuL2NvbmZpZy9jb25maWcuc2VydmljZSc7XHJcbmltcG9ydCB7IE9wZW5JZENvbmZpZ3VyYXRpb24gfSBmcm9tICcuL2NvbmZpZy9vcGVuaWQtY29uZmlndXJhdGlvbic7XHJcbmltcG9ydCB7IEZsb3dzRGF0YVNlcnZpY2UgfSBmcm9tICcuL2Zsb3dzL2Zsb3dzLWRhdGEuc2VydmljZSc7XHJcbmltcG9ydCB7IENoZWNrU2Vzc2lvblNlcnZpY2UgfSBmcm9tICcuL2lmcmFtZS9jaGVjay1zZXNzaW9uLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBMb2dpblJlc3BvbnNlIH0gZnJvbSAnLi9sb2dpbi9sb2dpbi1yZXNwb25zZSc7XHJcbmltcG9ydCB7IExvZ2luU2VydmljZSB9IGZyb20gJy4vbG9naW4vbG9naW4uc2VydmljZSc7XHJcbmltcG9ydCB7IFBvcHVwT3B0aW9ucyB9IGZyb20gJy4vbG9naW4vcG9wdXAvcG9wdXAtb3B0aW9ucyc7XHJcbmltcG9ydCB7IExvZ29mZlJldm9jYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9sb2dvZmYtcmV2b2tlL2xvZ29mZi1yZXZvY2F0aW9uLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBVc2VyU2VydmljZSB9IGZyb20gJy4vdXNlci1kYXRhL3VzZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IFVzZXJEYXRhUmVzdWx0IH0gZnJvbSAnLi91c2VyLWRhdGEvdXNlcmRhdGEtcmVzdWx0JztcclxuaW1wb3J0IHsgVG9rZW5IZWxwZXJTZXJ2aWNlIH0gZnJvbSAnLi91dGlscy90b2tlbkhlbHBlci90b2tlbi1oZWxwZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IFVybFNlcnZpY2UgfSBmcm9tICcuL3V0aWxzL3VybC91cmwuc2VydmljZSc7XHJcblxyXG5ASW5qZWN0YWJsZSgpXHJcbmV4cG9ydCBjbGFzcyBPaWRjU2VjdXJpdHlTZXJ2aWNlIHtcclxuICAvKipcclxuICAgKiBQcm92aWRlcyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdXNlciBhZnRlciB0aGV5IGhhdmUgbG9nZ2VkIGluLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMgUmV0dXJucyBhbiBvYmplY3QgY29udGFpbmluZyBlaXRoZXIgdGhlIHVzZXIgZGF0YSBkaXJlY3RseSAoc2luZ2xlIGNvbmZpZykgb3JcclxuICAgKiB0aGUgdXNlciBkYXRhIHBlciBjb25maWcgaW4gY2FzZSB5b3UgYXJlIHJ1bm5pbmcgd2l0aCBtdWx0aXBsZSBjb25maWdzXHJcbiAgICovXHJcbiAgZ2V0IHVzZXJEYXRhJCgpOiBPYnNlcnZhYmxlPFVzZXJEYXRhUmVzdWx0PiB7XHJcbiAgICByZXR1cm4gdGhpcy51c2VyU2VydmljZS51c2VyRGF0YSQ7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBFbWl0cyBlYWNoIHRpbWUgYW4gYXV0aG9yaXphdGlvbiBldmVudCBvY2N1cnMuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBSZXR1cm5zIGFuIG9iamVjdCBjb250YWluaW5nIGlmIHlvdSBhcmUgYXV0aGVudGljYXRlZCBvciBub3QuXHJcbiAgICogU2luZ2xlIENvbmZpZzogdHJ1ZSBpZiBjb25maWcgaXMgYXV0aGVudGljYXRlZCwgZmFsc2UgaWYgbm90LlxyXG4gICAqIE11bHRpcGxlIENvbmZpZ3M6IHRydWUgaXMgYWxsIGNvbmZpZ3MgYXJlIGF1dGhlbnRpY2F0ZWQsIGZhbHNlIGlmIG9ubHkgb25lIG9mIHRoZW0gaXMgbm90XHJcbiAgICpcclxuICAgKiBUaGUgYGFsbENvbmZpZ3NBdXRoZW50aWNhdGVkYCBwcm9wZXJ0eSBjb250YWlucyB0aGUgYXV0aCBpbmZvcm1hdGlvbiBfcGVyIGNvbmZpZ18uXHJcbiAgICovXHJcbiAgZ2V0IGlzQXV0aGVudGljYXRlZCQoKTogT2JzZXJ2YWJsZTxBdXRoZW50aWNhdGVkUmVzdWx0PiB7XHJcbiAgICByZXR1cm4gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmF1dGhlbnRpY2F0ZWQkO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRW1pdHMgZWFjaCB0aW1lIHRoZSBzZXJ2ZXIgc2VuZHMgYSBDaGVja1Nlc3Npb24gZXZlbnQgYW5kIHRoZSB2YWx1ZSBjaGFuZ2VkLiBUaGlzIHByb3BlcnR5IHdpbGwgYWx3YXlzIHJldHVyblxyXG4gICAqIHRydWUuXHJcbiAgICovXHJcbiAgZ2V0IGNoZWNrU2Vzc2lvbkNoYW5nZWQkKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY2hlY2tTZXNzaW9uU2VydmljZS5jaGVja1Nlc3Npb25DaGFuZ2VkJDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEVtaXRzIG9uIGEgU2VjdXJpdHkgVG9rZW4gU2VydmljZSBjYWxsYmFjay4gVGhlIG9ic2VydmFibGUgd2lsbCBuZXZlciBjb250YWluIGEgdmFsdWUuXHJcbiAgICovXHJcbiAgZ2V0IHN0c0NhbGxiYWNrJCgpOiBPYnNlcnZhYmxlPGFueT4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY2FsbGJhY2tTZXJ2aWNlLnN0c0NhbGxiYWNrJDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEBkZXByZWNhdGVkIFRoaXMgcHJvcGVydHkgc2hvdWxkIG5vdCBiZSB1c2VkLiBQbGVhc2UgdXNlIHRoZSBgUHVibGljRXZlbnRzU2VydmljZWAgaW5zdGVhZC4gVGhpcyBwcm9wZXJ0eSBpcyByZW1vdmVkIGluIGZ1dHVyZSB2ZXJzaW9uc1xyXG4gICAqIEVtaXRzIGZhbHNlIHdoZW4gdGhlIG9ic2VydmFibGUsIHJldHVybmVkIGJ5IG9uZSBvZiB0aGUgY2hlY2tBdXRoKCkgbWV0aG9kcywgZW1pdHMgYSB2YWx1ZSwgb3IgZXJyb3JzLiBJbml0aWFsIHZhbHVlOiB0cnVlLlxyXG4gICAqL1xyXG4gIGdldCBpc0xvYWRpbmckKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xyXG4gICAgcmV0dXJuIHRoaXMuaXNMb2FkaW5nLmFzT2JzZXJ2YWJsZSgpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSBpc0xvYWRpbmc6IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPiA9IG5ldyBCZWhhdmlvclN1YmplY3QodHJ1ZSk7XHJcblxyXG4gIGNvbnN0cnVjdG9yKFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBjaGVja1Nlc3Npb25TZXJ2aWNlOiBDaGVja1Nlc3Npb25TZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBjaGVja0F1dGhTZXJ2aWNlOiBDaGVja0F1dGhTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSB1c2VyU2VydmljZTogVXNlclNlcnZpY2UsXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IHRva2VuSGVscGVyU2VydmljZTogVG9rZW5IZWxwZXJTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25maWd1cmF0aW9uU2VydmljZTogQ29uZmlndXJhdGlvblNlcnZpY2UsXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGF1dGhTdGF0ZVNlcnZpY2U6IEF1dGhTdGF0ZVNlcnZpY2UsXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGZsb3dzRGF0YVNlcnZpY2U6IEZsb3dzRGF0YVNlcnZpY2UsXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNhbGxiYWNrU2VydmljZTogQ2FsbGJhY2tTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBsb2dvZmZSZXZvY2F0aW9uU2VydmljZTogTG9nb2ZmUmV2b2NhdGlvblNlcnZpY2UsXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2luU2VydmljZTogTG9naW5TZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSByZWFkb25seSByZWZyZXNoU2Vzc2lvblNlcnZpY2U6IFJlZnJlc2hTZXNzaW9uU2VydmljZSxcclxuICAgIHByaXZhdGUgcmVhZG9ubHkgdXJsU2VydmljZTogVXJsU2VydmljZSxcclxuICAgIHByaXZhdGUgcmVhZG9ubHkgYXV0aFdlbGxLbm93blNlcnZpY2U6IEF1dGhXZWxsS25vd25TZXJ2aWNlXHJcbiAgKSB7fVxyXG5cclxuICBwcmVsb2FkQXV0aFdlbGxLbm93bkRvY3VtZW50KGNvbmZpZ0lkPzogc3RyaW5nKTogT2JzZXJ2YWJsZTxBdXRoV2VsbEtub3duRW5kcG9pbnRzPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZVxyXG4gICAgICAuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZClcclxuICAgICAgLnBpcGUoc3dpdGNoTWFwKChjb25maWcpID0+IHRoaXMuYXV0aFdlbGxLbm93blNlcnZpY2UucXVlcnlBbmRTdG9yZUF1dGhXZWxsS25vd25FbmRQb2ludHMoY29uZmlnKSkpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV0dXJucyB0aGUgY3VycmVudGx5IGFjdGl2ZSBPcGVuSUQgY29uZmlndXJhdGlvbnMuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBhbiBhcnJheSBvZiBPcGVuSWRDb25maWd1cmF0aW9ucy5cclxuICAgKi9cclxuICBnZXRDb25maWd1cmF0aW9ucygpOiBPcGVuSWRDb25maWd1cmF0aW9uW10ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2UuZ2V0QWxsQ29uZmlndXJhdGlvbnMoKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgYSBzaW5nbGUgYWN0aXZlIE9wZW5JZENvbmZpZ3VyYXRpb24uXHJcbiAgICpcclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIGlkZW50aWZ5IHRoZSBjb25maWcuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBvbmUgaXMgYmVpbmcgcmV0dXJuZWRcclxuICAgKi9cclxuICBnZXRDb25maWd1cmF0aW9uKGNvbmZpZ0lkPzogc3RyaW5nKTogT2JzZXJ2YWJsZTxPcGVuSWRDb25maWd1cmF0aW9uPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdGhlIHVzZXJEYXRhIGZvciBhIGNvbmZpZ3VyYXRpb25cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gaWRlbnRpZnkgdGhlIGNvbmZpZy4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IG9uZSBpcyBiZWluZyB1c2VkXHJcbiAgICovXHJcbiAgZ2V0VXNlckRhdGEoY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPGFueT4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2UuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZCkucGlwZShtYXAoKGNvbmZpZykgPT4gdGhpcy51c2VyU2VydmljZS5nZXRVc2VyRGF0YUZyb21TdG9yZShjb25maWcpKSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTdGFydHMgdGhlIGNvbXBsZXRlIHNldHVwIGZsb3cgZm9yIG9uZSBjb25maWd1cmF0aW9uLiBDYWxsaW5nIHdpbGwgc3RhcnQgdGhlIGVudGlyZSBhdXRoZW50aWNhdGlvbiBmbG93LCBhbmQgdGhlIHJldHVybmVkIG9ic2VydmFibGVcclxuICAgKiB3aWxsIGRlbm90ZSB3aGV0aGVyIHRoZSB1c2VyIHdhcyBzdWNjZXNzZnVsbHkgYXV0aGVudGljYXRlZCBpbmNsdWRpbmcgdGhlIHVzZXIgZGF0YSwgdGhlIGFjY2VzcyB0b2tlbiwgdGhlIGNvbmZpZ0lkIGFuZFxyXG4gICAqIGFuIGVycm9yIG1lc3NhZ2UgaW4gY2FzZSBhbiBlcnJvciBoYXBwZW5lZFxyXG4gICAqXHJcbiAgICogQHBhcmFtIHVybCBUaGUgVVJMIHRvIHBlcmZvcm0gdGhlIGF1dGhvcml6YXRpb24gb24gdGhlIGJlaGFsZiBvZi5cclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIHBlcmZvcm0gdGhlIGF1dGhvcml6YXRpb24gb24gdGhlIGJlaGFsZiBvZi4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgQW4gb2JqZWN0IGBMb2dpblJlc3BvbnNlYCBjb250YWluaW5nIGFsbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbG9naW5cclxuICAgKi9cclxuICBjaGVja0F1dGgodXJsPzogc3RyaW5nLCBjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8TG9naW5SZXNwb25zZT4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2UuZ2V0T3BlbklEQ29uZmlndXJhdGlvbnMoY29uZmlnSWQpLnBpcGUoXHJcbiAgICAgIHN3aXRjaE1hcCgoeyBhbGxDb25maWdzLCBjdXJyZW50Q29uZmlnIH0pID0+IHRoaXMuY2hlY2tBdXRoU2VydmljZS5jaGVja0F1dGgoY3VycmVudENvbmZpZywgYWxsQ29uZmlncywgdXJsKSksXHJcbiAgICAgIHRhcCh0aGlzLmZpbmlzaExvYWRpbmcpLFxyXG4gICAgICBjYXRjaEVycm9yKHRoaXMuZmluaXNoTG9hZGluZ09uRXJyb3IpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU3RhcnRzIHRoZSBjb21wbGV0ZSBzZXR1cCBmbG93IGZvciBtdWx0aXBsZSBjb25maWd1cmF0aW9ucy5cclxuICAgKiBDYWxsaW5nIHdpbGwgc3RhcnQgdGhlIGVudGlyZSBhdXRoZW50aWNhdGlvbiBmbG93LCBhbmQgdGhlIHJldHVybmVkIG9ic2VydmFibGVcclxuICAgKiB3aWxsIGRlbm90ZSB3aGV0aGVyIHRoZSB1c2VyIHdhcyBzdWNjZXNzZnVsbHkgYXV0aGVudGljYXRlZCBpbmNsdWRpbmcgdGhlIHVzZXIgZGF0YSwgdGhlIGFjY2VzcyB0b2tlbiwgdGhlIGNvbmZpZ0lkIGFuZFxyXG4gICAqIGFuIGVycm9yIG1lc3NhZ2UgaW4gY2FzZSBhbiBlcnJvciBoYXBwZW5lZCBpbiBhbiBhcnJheSBmb3IgZWFjaCBjb25maWcgd2hpY2ggd2FzIHByb3ZpZGVkXHJcbiAgICpcclxuICAgKiBAcGFyYW0gdXJsIFRoZSBVUkwgdG8gcGVyZm9ybSB0aGUgYXV0aG9yaXphdGlvbiBvbiB0aGUgYmVoYWxmIG9mLlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gcGVyZm9ybSB0aGUgYXV0aG9yaXphdGlvbiBvbiB0aGUgYmVoYWxmIG9mLiBJZiBub3QgcGFzc2VkLCBhbGwgb2YgdGhlIGN1cnJlbnRcclxuICAgKiBjb25maWd1cmVkIG9uZXMgd2lsbCBiZSB1c2VkIHRvIGNoZWNrLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMgQW4gYXJyYXkgb2YgYExvZ2luUmVzcG9uc2VgIG9iamVjdHMgY29udGFpbmluZyBhbGwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGxvZ2luc1xyXG4gICAqL1xyXG4gIGNoZWNrQXV0aE11bHRpcGxlKHVybD86IHN0cmluZyk6IE9ic2VydmFibGU8TG9naW5SZXNwb25zZVtdPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9ucygpLnBpcGUoXHJcbiAgICAgIHN3aXRjaE1hcCgoeyBhbGxDb25maWdzIH0pID0+IHRoaXMuY2hlY2tBdXRoU2VydmljZS5jaGVja0F1dGhNdWx0aXBsZShhbGxDb25maWdzLCB1cmwpKSxcclxuICAgICAgdGFwKHRoaXMuZmluaXNoTG9hZGluZyksXHJcbiAgICAgIGNhdGNoRXJyb3IodGhpcy5maW5pc2hMb2FkaW5nT25FcnJvcilcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBQcm92aWRlcyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY3VycmVudCBhdXRoZW50aWNhdGVkIHN0YXRlXHJcbiAgICpcclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIGNoZWNrIHRoZSBpbmZvcm1hdGlvbiBmb3IuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIEEgYm9vbGVhbiB3aGV0aGVyIHRoZSBjb25maWcgaXMgYXV0aGVudGljYXRlZCBvciBub3QuXHJcbiAgICovXHJcbiAgaXNBdXRoZW50aWNhdGVkKGNvbmZpZ0lkPzogc3RyaW5nKTogT2JzZXJ2YWJsZTxib29sZWFuPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKS5waXBlKG1hcCgoY29uZmlnKSA9PiB0aGlzLmF1dGhTdGF0ZVNlcnZpY2UuaXNBdXRoZW50aWNhdGVkKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrcyB0aGUgc2VydmVyIGZvciBhbiBhdXRoZW50aWNhdGVkIHNlc3Npb24gdXNpbmcgdGhlIGlmcmFtZSBzaWxlbnQgcmVuZXcgaWYgbm90IGxvY2FsbHkgYXV0aGVudGljYXRlZC5cclxuICAgKi9cclxuICBjaGVja0F1dGhJbmNsdWRpbmdTZXJ2ZXIoY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPExvZ2luUmVzcG9uc2U+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlLmdldE9wZW5JRENvbmZpZ3VyYXRpb25zKGNvbmZpZ0lkKS5waXBlKFxyXG4gICAgICBzd2l0Y2hNYXAoKHsgYWxsQ29uZmlncywgY3VycmVudENvbmZpZyB9KSA9PiB0aGlzLmNoZWNrQXV0aFNlcnZpY2UuY2hlY2tBdXRoSW5jbHVkaW5nU2VydmVyKGN1cnJlbnRDb25maWcsIGFsbENvbmZpZ3MpKSxcclxuICAgICAgdGFwKHRoaXMuZmluaXNoTG9hZGluZyksXHJcbiAgICAgIGNhdGNoRXJyb3IodGhpcy5maW5pc2hMb2FkaW5nT25FcnJvcilcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBhY2Nlc3MgdG9rZW4gZm9yIHRoZSBsb2dpbiBzY2VuYXJpby5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgQSBzdHJpbmcgd2l0aCB0aGUgYWNjZXNzIHRva2VuLlxyXG4gICAqL1xyXG4gIGdldEFjY2Vzc1Rva2VuKGNvbmZpZ0lkPzogc3RyaW5nKTogT2JzZXJ2YWJsZTxzdHJpbmc+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlLmdldE9wZW5JRENvbmZpZ3VyYXRpb24oY29uZmlnSWQpLnBpcGUobWFwKChjb25maWcpID0+IHRoaXMuYXV0aFN0YXRlU2VydmljZS5nZXRBY2Nlc3NUb2tlbihjb25maWcpKSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBJRCB0b2tlbiBmb3IgdGhlIHNpZ24taW4uXHJcbiAgICpcclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIGNoZWNrIHRoZSBpbmZvcm1hdGlvbiBmb3IuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIEEgc3RyaW5nIHdpdGggdGhlIGlkIHRva2VuLlxyXG4gICAqL1xyXG4gIGdldElkVG9rZW4oY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPHN0cmluZz4ge1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2UuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZCkucGlwZShtYXAoKGNvbmZpZykgPT4gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmdldElkVG9rZW4oY29uZmlnKSkpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV0dXJucyB0aGUgcmVmcmVzaCB0b2tlbiwgaWYgcHJlc2VudCwgZm9yIHRoZSBzaWduLWluLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBjaGVjayB0aGUgaW5mb3JtYXRpb24gZm9yLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3QgY29uZmlncyB3aWxsIGJlIHRha2VuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBBIHN0cmluZyB3aXRoIHRoZSByZWZyZXNoIHRva2VuLlxyXG4gICAqL1xyXG4gIGdldFJlZnJlc2hUb2tlbihjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8c3RyaW5nPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKS5waXBlKG1hcCgoY29uZmlnKSA9PiB0aGlzLmF1dGhTdGF0ZVNlcnZpY2UuZ2V0UmVmcmVzaFRva2VuKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdGhlIGF1dGhlbnRpY2F0aW9uIHJlc3VsdCwgaWYgcHJlc2VudCwgZm9yIHRoZSBzaWduLWluLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBjaGVjayB0aGUgaW5mb3JtYXRpb24gZm9yLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3QgY29uZmlncyB3aWxsIGJlIHRha2VuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyBBIG9iamVjdCB3aXRoIHRoZSBhdXRoZW50aWNhdGlvbiByZXN1bHRcclxuICAgKi9cclxuICBnZXRBdXRoZW50aWNhdGlvblJlc3VsdChjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8YW55PiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZVxyXG4gICAgICAuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZClcclxuICAgICAgLnBpcGUobWFwKChjb25maWcpID0+IHRoaXMuYXV0aFN0YXRlU2VydmljZS5nZXRBdXRoZW50aWNhdGlvblJlc3VsdChjb25maWcpKSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBwYXlsb2FkIGZyb20gdGhlIElEIHRva2VuLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGVuY29kZSBTZXQgdG8gdHJ1ZSBpZiB0aGUgcGF5bG9hZCBpcyBiYXNlNjQgZW5jb2RlZFxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgVGhlIHBheWxvYWQgZnJvbSB0aGUgaWQgdG9rZW4uXHJcbiAgICovXHJcbiAgZ2V0UGF5bG9hZEZyb21JZFRva2VuKGVuY29kZSA9IGZhbHNlLCBjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8YW55PiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKS5waXBlKFxyXG4gICAgICBtYXAoKGNvbmZpZykgPT4ge1xyXG4gICAgICAgIGNvbnN0IHRva2VuID0gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmdldElkVG9rZW4oY29uZmlnKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMudG9rZW5IZWxwZXJTZXJ2aWNlLmdldFBheWxvYWRGcm9tVG9rZW4odG9rZW4sIGVuY29kZSwgY29uZmlnKTtcclxuICAgICAgfSlcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBwYXlsb2FkIGZyb20gdGhlIGFjY2VzcyB0b2tlbi5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBlbmNvZGUgU2V0IHRvIHRydWUgaWYgdGhlIHBheWxvYWQgaXMgYmFzZTY0IGVuY29kZWRcclxuICAgKiBAcGFyYW0gY29uZmlnSWQgVGhlIGNvbmZpZ0lkIHRvIGNoZWNrIHRoZSBpbmZvcm1hdGlvbiBmb3IuIElmIG5vdCBwYXNzZWQsIHRoZSBmaXJzdCBjb25maWdzIHdpbGwgYmUgdGFrZW5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIFRoZSBwYXlsb2FkIGZyb20gdGhlIGFjY2VzcyB0b2tlbi5cclxuICAgKi9cclxuICBnZXRQYXlsb2FkRnJvbUFjY2Vzc1Rva2VuKGVuY29kZSA9IGZhbHNlLCBjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8YW55PiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZS5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKS5waXBlKFxyXG4gICAgICBtYXAoKGNvbmZpZykgPT4ge1xyXG4gICAgICAgIGNvbnN0IHRva2VuID0gdGhpcy5hdXRoU3RhdGVTZXJ2aWNlLmdldEFjY2Vzc1Rva2VuKGNvbmZpZyk7XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLnRva2VuSGVscGVyU2VydmljZS5nZXRQYXlsb2FkRnJvbVRva2VuKHRva2VuLCBlbmNvZGUsIGNvbmZpZyk7XHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyBhIGN1c3RvbSBzdGF0ZSBmb3IgdGhlIGF1dGhvcml6ZSByZXF1ZXN0LlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHN0YXRlIFRoZSBzdGF0ZSB0byBzZXQuXHJcbiAgICogQHBhcmFtIGNvbmZpZ0lkIFRoZSBjb25maWdJZCB0byBjaGVjayB0aGUgaW5mb3JtYXRpb24gZm9yLiBJZiBub3QgcGFzc2VkLCB0aGUgZmlyc3QgY29uZmlncyB3aWxsIGJlIHRha2VuXHJcbiAgICovXHJcbiAgc2V0U3RhdGUoc3RhdGU6IHN0cmluZywgY29uZmlnSWQ/OiBzdHJpbmcpOiBPYnNlcnZhYmxlPGJvb2xlYW4+IHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb25TZXJ2aWNlXHJcbiAgICAgIC5nZXRPcGVuSURDb25maWd1cmF0aW9uKGNvbmZpZ0lkKVxyXG4gICAgICAucGlwZShtYXAoKGNvbmZpZykgPT4gdGhpcy5mbG93c0RhdGFTZXJ2aWNlLnNldEF1dGhTdGF0ZUNvbnRyb2woc3RhdGUsIGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldHMgdGhlIHN0YXRlIHZhbHVlIHVzZWQgZm9yIHRoZSBhdXRob3JpemUgcmVxdWVzdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gY2hlY2sgdGhlIGluZm9ybWF0aW9uIGZvci4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqXHJcbiAgICogQHJldHVybnMgVGhlIHN0YXRlIHZhbHVlIHVzZWQgZm9yIHRoZSBhdXRob3JpemUgcmVxdWVzdC5cclxuICAgKi9cclxuICBnZXRTdGF0ZShjb25maWdJZD86IHN0cmluZyk6IE9ic2VydmFibGU8c3RyaW5nPiB7XHJcbiAgICByZXR1cm4gdGhpcy5jb25maWd1cmF0aW9uU2VydmljZVxyXG4gICAgICAuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZClcclxuICAgICAgLnBpcGUobWFwKChjb25maWcpID0+IHRoaXMuZmxvd3NEYXRhU2VydmljZS5nZXRBdXRoU3RhdGVDb250cm9sKGNvbmZpZykpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlZGlyZWN0cyB0aGUgdXNlciB0byB0aGUgU2VjdXJpdHkgVG9rZW4gU2VydmljZSB0byBiZWdpbiB0aGUgYXV0aGVudGljYXRpb24gcHJvY2Vzcy5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjb25maWdJZCBUaGUgY29uZmlnSWQgdG8gcGVyZm9ybSB0aGUgYWN0aW9uIGluIGJlaGFsZiBvZi4gSWYgbm90IHBhc3NlZCwgdGhlIGZpcnN0IGNvbmZpZ3Mgd2lsbCBiZSB0YWtlblxyXG4gICAqIEBwYXJhbSBhdXRoT3B0aW9ucyBUaGUgY3VzdG9tIG9wdGlvbnMgZm9yIHRoZSB0aGUgYXV0aGVudGljYXRpb24gcmVxdWVzdC5cclxuICAgKi9cclxuICBhdXRob3JpemUoY29uZmlnSWQ/OiBzdHJpbmcsIGF1dGhPcHRpb25zPzogQXV0aE9wdGlvbnMpOiB2b2lkIHtcclxuICAgIHRoaXMuY29uZmlndXJhdGlvblNlcnZpY2UuZ2V0T3BlbklEQ29uZmlndXJhdGlvbihjb25maWdJZCkuc3Vic2NyaWJlKChjb25maWcpID0+IHRoaXMubG9naW5TZXJ2aWNlLmxvZ2luKGNvbmZpZywgYXV0aE9wdGlvbnMpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIE9wZW5zIHRoZSBTZWN1cml0eSBUb2tlbiBTZXJ2aWNlIGluIGEgbmV3IHdpbmRvdyB0byBiZWdpbiB0aGUgYXV0aGVudGljYXRpb24gcHJvY2Vzcy5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBhdXRoT3B0aW9ucyBUaGUgY3VzdG9tIG9wdGlvbnMgZm9yIHRoZSBhdXRoZW50aWNhdGlvbiByZXF1ZXN0LlxyXG4gICAqIEBwYXJhbSBwb3B1cE9wdGlvbnMgVGhlIGNvbmZpZ3VyYXRpb24gZm9yIHRo