@scloud/cdk-patterns
Version:
Serverless CDK patterns for common infrastructure needs
434 lines • 62.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cognito = void 0;
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_certificatemanager_1 = require("aws-cdk-lib/aws-certificatemanager");
const cognito = __importStar(require("aws-cdk-lib/aws-cognito"));
const aws_cognito_1 = require("aws-cdk-lib/aws-cognito");
const aws_route53_1 = require("aws-cdk-lib/aws-route53");
const aws_route53_targets_1 = require("aws-cdk-lib/aws-route53-targets");
const constructs_1 = require("constructs");
/**
* Authentication setup with Cognito.
*
* This construct offers a couple convenience static methods for typical use cases:
* - Cognito.withSSO()
* - Cognito.withSocialLogins()
*
* To customise this construct, you'll need to call these methods in the following oprder:
* - new Cognito()
* - addGoogleIdp() (optional)
* - addFacebookIdp() (optional)
* - addSamlIdp() (optional, can be called more than once)
* - createUserPoolClient()
* - addCustomDomain() / addDomainPrefix()
*
* Once set up, you can call signInUrl() to get a URL for the hosted UI sign-in page.
*
* NB: IF you want to use a custom domain, there's an unexpected error where the CDK deployment
* will fail unless there's an A record at the zone apex (at the time of writing) so you need to
* add a record at the apex before you attempt to create a custom domain.
*
* @returns Information about the created UserPool
*/
class Cognito extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, `${id}Cognito`);
this.samlIdps = [];
/** All callback URLs, including any alternative URL will be visible in this properly */
this.callbackUrls = [];
// Store the ID so we can it in methods:
this.id = id;
// Cognito user pool
this.userPool = new aws_cognito_1.UserPool(scope, `${id}UserPool`, {
userPoolName: id,
selfSignUpEnabled: true,
accountRecovery: aws_cognito_1.AccountRecovery.EMAIL_ONLY,
signInAliases: { username: false, email: true },
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
...props,
});
}
addGoogleIdp(googleClientId, googleClientSecret) {
if (this.googleIdp)
throw new Error(`Google identity provider has already been created for ${this.id}. You'll need to call addGoogleIdp before creating the client.`);
if (this.userPoolClient)
throw new Error(`User pool client has already been created for ${this.id}. You'll need to call addGoogleIdp before creating the client.`);
// Google identity provider
this.googleIdp = new aws_cognito_1.UserPoolIdentityProviderGoogle(this, `${this.id}GoogleIDP`, {
userPool: this.userPool,
clientId: googleClientId,
clientSecret: googleClientSecret,
scopes: ['profile', 'email', 'openid'],
attributeMapping: {
email: cognito.ProviderAttribute.GOOGLE_EMAIL,
givenName: cognito.ProviderAttribute.GOOGLE_GIVEN_NAME,
familyName: cognito.ProviderAttribute.GOOGLE_FAMILY_NAME,
fullname: cognito.ProviderAttribute.GOOGLE_NAME,
profilePicture: cognito.ProviderAttribute.GOOGLE_PICTURE,
},
// scopes: [
// 'https://www.googleapis.com/auth/userinfo.email',
// 'https://www.googleapis.com/auth/userinfo.profile'],
});
return this.googleIdp;
}
addFacebookIdp(facebookAppId, facebookAppSecret) {
if (this.googleIdp)
throw new Error(`Facebook identity provider has already been created for ${this.id}. You'll need to call addGoogleIdp before creating the client.`);
if (this.userPoolClient)
throw new Error(`User pool client has already been created for ${this.id}. You'll need to call addFacebookIdp before creating the client.`);
this.facebookIdp = new aws_cognito_1.UserPoolIdentityProviderFacebook(this, `${this.id}FacebookIDP`, {
userPool: this.userPool,
clientId: facebookAppId,
clientSecret: facebookAppSecret,
scopes: ['public_profile', 'email'],
attributeMapping: {
email: cognito.ProviderAttribute.FACEBOOK_EMAIL,
givenName: cognito.ProviderAttribute.FACEBOOK_FIRST_NAME,
familyName: cognito.ProviderAttribute.FACEBOOK_LAST_NAME,
fullname: cognito.ProviderAttribute.FACEBOOK_NAME,
},
});
return this.facebookIdp;
}
/**
* Add a SAML sso identity provider.
*
* You can call this method more than once to add multiple SAML providers.
*
* @param SamlProviderName Name in the Cognito hosted UI under "Sign in with your corporate ID"
* @param FederationMetadataUrl SAML XML URL (e.g. Azure)
* @param FederationMetadataXml SAML metadata XML (e.g. Google Workspace)
*/
addSamlIdp(SamlProviderName, FederationMetadataUrl, FederationMetadataXml) {
// https://docs.aws.amazon.com/cdk/api/latest/docs/aws-cdk-lib_aws-cognito.CfnUserPoolIdentityProvider.html
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpoolidentityprovider.html
if (this.userPoolClient)
throw new Error(`User pool client has already been created for ${this.id}. You'll need to call addSamlIdp before creating the client.`);
const providerDetails = {};
if (FederationMetadataUrl) {
providerDetails.MetadataURL = FederationMetadataUrl;
}
if (FederationMetadataXml) {
providerDetails.MetadataFile = FederationMetadataXml;
}
const samlIdp = new aws_cognito_1.CfnUserPoolIdentityProvider(this, `${this.id}SamlIDP${SamlProviderName}`, {
userPoolId: this.userPool.userPoolId,
providerName: SamlProviderName || this.id,
providerType: 'SAML',
attributeMapping: {
// https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
given_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
family_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
},
providerDetails,
});
this.samlIdps.push(samlIdp);
return samlIdp;
}
/**
* Create a Cognito User Pool Client.
*
* If you want to add identity providers such as Google, Facebook or saml sso you'll need to call addGoogleIdp(), addFacebookIdp() and/or addSamlIdp() first.
*
* @param enableEmail Whether to enable email as a sign-up/sign-in method.
* @param callbackUrl Allowed callback URL on your app to receive an authentication code (?code=...)
* @param alternativeCallbackUrls Zero or more additonal authorized callback URL, for example if you wneed to allow localhost in a development environment.
* @returns cognito.UserPoolClient
*/
createUserPoolClient(callbackUrl, enableEmail, logoutUrl, ...alternativeCallbackUrls) {
if (this.userPoolClient)
throw new Error(`User pool client has already been created for ${this.id}`);
const identityProviders = [];
if (enableEmail)
identityProviders.push(aws_cognito_1.UserPoolClientIdentityProvider.COGNITO);
if (this.googleIdp)
identityProviders.push(aws_cognito_1.UserPoolClientIdentityProvider.GOOGLE);
if (this.facebookIdp)
identityProviders.push(aws_cognito_1.UserPoolClientIdentityProvider.FACEBOOK);
this.samlIdps.forEach((saml) => {
identityProviders.push(aws_cognito_1.UserPoolClientIdentityProvider.custom(saml.providerName));
});
this.callbackUrl = callbackUrl;
this.callbackUrls = [callbackUrl];
this.callbackUrls.push(...alternativeCallbackUrls);
this.logoutUrl = logoutUrl;
const userPoolClient = new aws_cognito_1.UserPoolClient(this, `${this.id}UserPoolClient`, {
userPool: this.userPool,
userPoolClientName: this.id,
generateSecret: false,
preventUserExistenceErrors: true,
supportedIdentityProviders: identityProviders,
oAuth: {
callbackUrls: this.callbackUrls,
logoutUrls: this.logoutUrl ? [this.logoutUrl] : undefined,
flows: {
authorizationCodeGrant: true,
},
scopes: [
cognito.OAuthScope.EMAIL,
cognito.OAuthScope.OPENID,
cognito.OAuthScope.PROFILE,
],
},
});
// These dependencies seemed to be needed at the time of writing:
if (this.googleIdp)
userPoolClient.node.addDependency(this.googleIdp);
if (this.facebookIdp)
userPoolClient.node.addDependency(this.facebookIdp);
if (this.samlIdps) {
this.samlIdps.forEach((samlIdp) => userPoolClient.node.addDependency(samlIdp));
}
this.userPoolClient = userPoolClient;
return this.userPoolClient;
}
/**
* Add a custom domain name to the Cognito User Pool.
*
* AWS recommends auth.<domain> for custom domains, which is the default if you don't pass a value for domainName.
*
* NB at the time of writing there's a hard limit of 4 custom Cognito domains per AWS account.
*
* You can either add a custom domain or a domain prefix, but not both.
*
* @param zone The HostedZone in which to create an alias record for the user pool.
* @param domainName Leave this out to use the recommended `auth.<domain>`, or pass a fully qualified domain name.
*/
addCustomDomain(zone, domainName) {
if (this.domain)
throw new Error(`A domain has already been created for ${this.id}`);
// NB at the time of writing there's a hard limit of 4 custom Cognito domains.
const authDomainName = domainName || `auth.${zone.zoneName}`;
// Custom domain can only be set up after the initial pass has created an A record at the apex
this.domain = new cognito.UserPoolDomain(this, `${this.id}UserPoolDomain`, {
userPool: this.userPool,
customDomain: {
domainName: authDomainName,
certificate: new aws_certificatemanager_1.DnsValidatedCertificate(this, `${this.id}UserPoolCertificate`, {
domainName: authDomainName,
hostedZone: zone,
region: 'us-east-1', // Cloudfront requires this
}),
},
});
// https://stackoverflow.com/a/62075314/723506
new aws_route53_1.ARecord(this, `${this.id}CognitoCustomDomainARecord`, {
zone,
recordName: authDomainName,
target: aws_route53_1.RecordTarget.fromAlias(new aws_route53_targets_1.UserPoolDomainTarget(this.domain)),
});
}
/**
* Set a domain prefix for the URL of the Cognito User Pool.
*
* This will set the user pool URL to https://<domainPrefix>.auth.<region>.amazoncognito.com
*
* You don't have to set a custom domain prefix. If you don't, the prefix will be generated by AWS.
*
* If can set a custom domain prefix, or a custom domain, but not both.
*
* @param domainPrefix Leave this out to use the recommended `auth.<domain>`, or pass a fully qualified domain name.
*/
addDomainPrefix(domainPrefix) {
if (this.domain)
throw new Error(`A domain has already been created for ${this.id}`);
this.domain = new cognito.UserPoolDomain(this, `${this.id}UserPoolDomain`, {
userPool: this.userPool,
cognitoDomain: {
domainPrefix,
},
});
}
/**
* Constructs a URL for the hosted UI sign-in page.
*
* You'll need to call either addCustomDomain() or addDomainPrefix() first.
*
* @param callbackUrl Optional: defaults to the this.callbackUrl.
*/
signInUrl(callbackUrl) {
var _a;
if (!this.domain)
throw new Error(`You must call addCustomDomain() or addDomainPrefix() before calling signInUrl() for ${this.id}`);
if (!this.userPoolClient)
throw new Error(`You must call createUserPoolClient() before calling signInUrl() for ${this.id}`);
return (_a = this.domain) === null || _a === void 0 ? void 0 : _a.signInUrl(this.userPoolClient, { redirectUri: callbackUrl || this.callbackUrl });
}
/**
* @deprecated Use withSSOMetadataUrl() or withSSOMetadataXml() instead.
*
* Creates a Cognito instance configured for SAML sso (e.g. Azure or Google Workspace).
*
* You'll need to pass either a federationMetadataUrl or a federationMetadataXml.
*
* You'll want to pass either a domain prefix (creates https://<prefix>.auth.<region>.amazoncognito.com) or a
* zone (and optionally domainName) if you don't pass a domainName the user pool url will be https://auth.<zoneName>
*
* NB at the time of writing AWS has a hard limit of 4 custom Cognito domains so if you're running multiple user pools
* in a single AWS account you may need to use domain prefixes.
*/
static withSSO(scope, id, callbackUrl, samlProviderName, federationMetadataUrl, federationMetadataXml, zone, domainName, domainPrefix, logoutUrl, ...alternativeCallbackUrls) {
const sso = new Cognito(scope, id);
sso.addSamlIdp(samlProviderName, federationMetadataUrl, federationMetadataXml);
sso.createUserPoolClient(callbackUrl, false, logoutUrl, ...alternativeCallbackUrls);
if (domainPrefix)
sso.addDomainPrefix(domainPrefix);
else if (zone)
sso.addCustomDomain(zone, domainName || `auth.${zone.zoneName}`);
return sso;
}
/**
* Creates a Cognito instance configured for email login.
*
* You'll want to pass either a domain prefix (creates https://<prefix>.auth.<region>.amazoncognito.com) or a
* zone (and optionally domainName) if you don't pass a domainName the user pool url will be https://auth.<zoneName>
*
* NB at the time of writing AWS has a hard limit of 4 custom Cognito domains so if you're running multiple user pools
* in a single AWS account you may need to use domain prefixes.
*/
static withEmailLogin(scope, id, callbackUrl, zone, domainName, domainPrefix, logoutUrl, ...alternativeCallbackUrls) {
const email = new Cognito(scope, id);
email.createUserPoolClient(callbackUrl, true, logoutUrl, ...alternativeCallbackUrls);
if (domainPrefix)
email.addDomainPrefix(domainPrefix);
else if (zone)
email.addCustomDomain(zone, domainName || `auth.${zone.zoneName}`);
return email;
}
/**
* Creates a Cognito instance configured for Social logins (Google and Facebook) and optionally email.
*
* You'll want to pass either a domain prefix (creates https://<prefix>.auth.<region>.amazoncognito.com) or a
* zone (and optionally domainName) if you don't pass a domainName the user pool url will be https://auth.<zoneName>
*
* NB at the time of writing AWS has a hard limit of 4 custom Cognito domains so if you're running multiple user pools
* in a single AWS account you may need to use domain prefixes.
*/
static withSocialLogins(scope, id, callbackUrl, googleClientId, googleClientSecret, facebookAppId, facebookAppSecret, enableEmailLogin, zone, domainName, domainPrefix, logoutUrl, ...alternativeCallbackUrls) {
const social = new Cognito(scope, id);
if (googleClientId && googleClientSecret)
social.addGoogleIdp(googleClientId, googleClientSecret);
if (facebookAppId && facebookAppSecret)
social.addFacebookIdp(facebookAppId, facebookAppSecret);
social.createUserPoolClient(callbackUrl, enableEmailLogin, logoutUrl, ...alternativeCallbackUrls);
if (domainPrefix)
social.addDomainPrefix(domainPrefix);
else if (zone)
social.addCustomDomain(zone, domainName || `auth.${zone.zoneName}`);
return social;
}
/**
* Creates a Cognito instance configured for SAML sso where you have a metadata URL (e.g. Azure).
*
* You'll need to pass a federationMetadataUrl (e.g. provided by Azure).
*
* If configuring an 'Enerprise Application' in Azure, the "Identifier (Entity ID)" will be:
*
* urn:amazon:cognito:sp:<user pool id> (e.g. <region>_XyZaBcD1E)
*
* The "Reply URL (Assertion Consumer Service URL)" will be:
*
* https://<Your user pool domain>/saml2/idpresponse
*
* With an Amazon Cognito domain prefix:
* https://<yourDomainPrefix>.auth.<region>.amazoncognito.com/saml2/idpresponse
* With a custom domain:
* https://<Your custom domain>/saml2/idpresponse
*
* see: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-saml-idp.html
*
* You'll want to pass either a domain prefix (creates https://<prefix>.auth.<region>.amazoncognito.com) or a
* zone (and optionally domainName) if you don't pass a domainName the user pool url will be https://auth.<zoneName>
*
* NB at the time of writing AWS has a hard limit of 4 custom Cognito domains so if you're running multiple user pools
* in a single AWS account you may need to use domain prefixes.
*/
static withSSOMetadataUrl(scope, id, callbackUrl, samlProviderName, federationMetadataUrl, zone, domainName, domainPrefix, logoutUrl, ...alternativeCallbackUrls) {
const sso = new Cognito(scope, id);
sso.addSamlIdp(samlProviderName, federationMetadataUrl, undefined);
sso.createUserPoolClient(callbackUrl, false, logoutUrl, ...alternativeCallbackUrls);
if (domainPrefix)
sso.addDomainPrefix(domainPrefix);
else if (zone)
sso.addCustomDomain(zone, domainName || `auth.${zone.zoneName}`);
return sso;
}
/**
* Creates a Cognito instance configured for SAML sso where you have a metadata XML file (e.g. Google Workspace).
*
* You'll need to pass federationMetadataXml data as a string (e.g. downloaded from your Google Workspace).
*
* If configuring an 'App' in Google Workspace (under "Apps/Web and mobile apps" in the admin console) the "ACS URL" will be:
*
* https://<Your user pool domain>/saml2/idpresponse
*
* With an Amazon Cognito domain prefix:
* https://<yourDomainPrefix>.auth.<region>.amazoncognito.com/saml2/idpresponse
* With a custom domain:
* https://<Your custom domain>/saml2/idpresponse
*
* The "Enitiy ID" will be:
*
* urn:amazon:cognito:sp:<user pool id> (e.g. <region>_XyZaBcD1E)
*
* see: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-saml-idp.html
*
* In Google Workspace you'll need to set "Use access" to e.g. "ON for everyone" (oe select an organisational unit).
* NB it's usually best to test Google Worspace sso in incognito mode as if you're already signed in you may get a 403 error.
* This is possibly because your local cached credentials haven't yet updated with access to the App.
*
* You'll want to pass either a domain prefix (creates https://<prefix>.auth.<region>.amazoncognito.com) or a
* zone (and optionally domainName) if you don't pass a domainName the user pool url will be https://auth.<zoneName>
*
* NB at the time of writing AWS has a hard limit of 4 custom Cognito domains so if you're running multiple user pools
* in a single AWS account you may need to use domain prefixes.
*/
static withSSOMetadataXml(scope, id, callbackUrl, samlProviderName, federationMetadataXml, zone, domainName, domainPrefix, logoutUrl, ...alternativeCallbackUrls) {
const sso = new Cognito(scope, id);
sso.addSamlIdp(samlProviderName, undefined, federationMetadataXml);
sso.createUserPoolClient(callbackUrl, false, logoutUrl, ...alternativeCallbackUrls);
if (domainPrefix)
sso.addDomainPrefix(domainPrefix);
else if (zone)
sso.addCustomDomain(zone, domainName || `auth.${zone.zoneName}`);
return sso;
}
}
exports.Cognito = Cognito;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29nbml0by5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Db2duaXRvLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDZDQUE0QztBQUM1QywrRUFBNkU7QUFDN0UsaUVBQW1EO0FBQ25ELHlEQU1pQztBQUNqQyx5REFFaUM7QUFDakMseUVBQXVFO0FBQ3ZFLDJDQUF1QztBQUV2Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUNILE1BQWEsT0FBUSxTQUFRLHNCQUFTO0lBd0JwQyxZQUNFLEtBQWdCLEVBQ2hCLEVBQVUsRUFDVixLQUFzQztRQUV0QyxLQUFLLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQztRQWhCL0IsYUFBUSxHQUEwQyxFQUFFLENBQUM7UUFLckQsd0ZBQXdGO1FBQ3hGLGlCQUFZLEdBQWEsRUFBRSxDQUFDO1FBWTFCLHdDQUF3QztRQUN4QyxJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQztRQUViLG9CQUFvQjtRQUNwQixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksc0JBQVEsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRTtZQUNuRCxZQUFZLEVBQUUsRUFBRTtZQUNoQixpQkFBaUIsRUFBRSxJQUFJO1lBQ3ZCLGVBQWUsRUFBRSw2QkFBZSxDQUFDLFVBQVU7WUFDM0MsYUFBYSxFQUFFLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFO1lBQy9DLGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87WUFDcEMsR0FBRyxLQUFLO1NBQ1QsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFlBQVksQ0FDVixjQUFzQixFQUN0QixrQkFBMEI7UUFFMUIsSUFBSSxJQUFJLENBQUMsU0FBUztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMseURBQXlELElBQUksQ0FBQyxFQUFFLGdF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USxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNoRixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUM7Q0FDRjtBQWhkRCwwQkFnZEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSZW1vdmFsUG9saWN5IH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgRG5zVmFsaWRhdGVkQ2VydGlmaWNhdGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2VydGlmaWNhdGVtYW5hZ2VyJztcbmltcG9ydCAqIGFzIGNvZ25pdG8gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNvZ25pdG8nO1xuaW1wb3J0IHtcbiAgQWNjb3VudFJlY292ZXJ5LCBDZm5Vc2VyUG9vbElkZW50aXR5UHJvdmlkZXIsIFVzZXJQb29sLCBVc2VyUG9vbENsaWVudCxcbiAgVXNlclBvb2xDbGllbnRJZGVudGl0eVByb3ZpZGVyLFxuICBVc2VyUG9vbERvbWFpbixcbiAgVXNlclBvb2xJZGVudGl0eVByb3ZpZGVyRmFjZWJvb2ssXG4gIFVzZXJQb29sSWRlbnRpdHlQcm92aWRlckdvb2dsZSxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNvZ25pdG8nO1xuaW1wb3J0IHtcbiAgQVJlY29yZCwgSUhvc3RlZFpvbmUsIFJlY29yZFRhcmdldCxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXJvdXRlNTMnO1xuaW1wb3J0IHsgVXNlclBvb2xEb21haW5UYXJnZXQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtcm91dGU1My10YXJnZXRzJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuXG4vKipcbiAqIEF1dGhlbnRpY2F0aW9uIHNldHVwIHdpdGggQ29nbml0by5cbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBvZmZlcnMgYSBjb3VwbGUgY29udmVuaWVuY2Ugc3RhdGljIG1ldGhvZHMgZm9yIHR5cGljYWwgdXNlIGNhc2VzOlxuICogIC0gQ29nbml0by53aXRoU1NPKClcbiAqICAtIENvZ25pdG8ud2l0aFNvY2lhbExvZ2lucygpXG4gKlxuICogVG8gY3VzdG9taXNlIHRoaXMgY29uc3RydWN0LCB5b3UnbGwgbmVlZCB0byBjYWxsIHRoZXNlIG1ldGhvZHMgaW4gdGhlIGZvbGxvd2luZyBvcHJkZXI6XG4gKiAtIG5ldyBDb2duaXRvKClcbiAqIC0gYWRkR29vZ2xlSWRwKCkgKG9wdGlvbmFsKVxuICogLSBhZGRGYWNlYm9va0lkcCgpIChvcHRpb25hbClcbiAqIC0gYWRkU2FtbElkcCgpIChvcHRpb25hbCwgY2FuIGJlIGNhbGxlZCBtb3JlIHRoYW4gb25jZSlcbiAqIC0gY3JlYXRlVXNlclBvb2xDbGllbnQoKVxuICogLSBhZGRDdXN0b21Eb21haW4oKSAvIGFkZERvbWFpblByZWZpeCgpXG4gKlxuICogT25jZSBzZXQgdXAsIHlvdSBjYW4gY2FsbCBzaWduSW5VcmwoKSB0byBnZXQgYSBVUkwgZm9yIHRoZSBob3N0ZWQgVUkgc2lnbi1pbiBwYWdlLlxuICpcbiAqIE5COiBJRiB5b3Ugd2FudCB0byB1c2UgYSBjdXN0b20gZG9tYWluLCB0aGVyZSdzIGFuIHVuZXhwZWN0ZWQgZXJyb3Igd2hlcmUgdGhlIENESyBkZXBsb3ltZW50XG4gKiB3aWxsIGZhaWwgdW5sZXNzIHRoZXJlJ3MgYW4gQSByZWNvcmQgYXQgdGhlIHpvbmUgYXBleCAoYXQgdGhlIHRpbWUgb2Ygd3JpdGluZykgc28geW91IG5lZWQgdG9cbiAqIGFkZCBhIHJlY29yZCBhdCB0aGUgYXBleCBiZWZvcmUgeW91IGF0dGVtcHQgdG8gY3JlYXRlIGEgY3VzdG9tIGRvbWFpbi5cbiAqXG4gKiBAcmV0dXJucyBJbmZvcm1hdGlvbiBhYm91dCB0aGUgY3JlYXRlZCBVc2VyUG9vbFxuICovXG5leHBvcnQgY2xhc3MgQ29nbml0byBleHRlbmRzIENvbnN0cnVjdCB7XG4gIGlkOiBzdHJpbmc7XG5cbiAgdXNlclBvb2w6IFVzZXJQb29sO1xuXG4gIGRvbWFpbjogVXNlclBvb2xEb21haW4gfCB1bmRlZmluZWQ7XG5cbiAgdXNlclBvb2xDbGllbnQ6IFVzZXJQb29sQ2xpZW50O1xuXG4gIGdvb2dsZUlkcDogY29nbml0by5Vc2VyUG9vbElkZW50aXR5UHJvdmlkZXJHb29nbGUgfCB1bmRlZmluZWQ7XG5cbiAgZmFjZWJvb2tJZHA6IGNvZ25pdG8uVXNlclBvb2xJZGVudGl0eVByb3ZpZGVyRmFjZWJvb2sgfCB1bmRlZmluZWQ7XG5cbiAgc2FtbElkcHM6IGNvZ25pdG8uQ2ZuVXNlclBvb2xJZGVudGl0eVByb3ZpZGVyW10gPSBbXTtcblxuICAvKiogVHlwaWNhbGx5IHRoZXJlJ3Mgb25seSBvbmUgY2FsbGJhY2sgVVJMICovXG4gIGNhbGxiYWNrVXJsOiBzdHJpbmc7XG5cbiAgLyoqIEFsbCBjYWxsYmFjayBVUkxzLCBpbmNsdWRpbmcgYW55IGFsdGVybmF0aXZlIFVSTCB3aWxsIGJlIHZpc2libGUgaW4gdGhpcyBwcm9wZXJseSAqL1xuICBjYWxsYmFja1VybHM6IHN0cmluZ1tdID0gW107XG5cbiAgLyoqIE9wdGlvbmFsIGxvZ291dCBVUkwgKi9cbiAgbG9nb3V0VXJsOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgc2NvcGU6IENvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzPzogUGFydGlhbDxjb2duaXRvLlVzZXJQb29sUHJvcHM+LFxuICApIHtcbiAgICBzdXBlcihzY29wZSwgYCR7aWR9Q29nbml0b2ApO1xuXG4gICAgLy8gU3RvcmUgdGhlIElEIHNvIHdlIGNhbiBpdCBpbiBtZXRob2RzOlxuICAgIHRoaXMuaWQgPSBpZDtcblxuICAgIC8vIENvZ25pdG8gdXNlciBwb29sXG4gICAgdGhpcy51c2VyUG9vbCA9IG5ldyBVc2VyUG9vbChzY29wZSwgYCR7aWR9VXNlclBvb2xgLCB7XG4gICAgICB1c2VyUG9vbE5hbWU6IGlkLFxuICAgICAgc2VsZlNpZ25VcEVuYWJsZWQ6IHRydWUsXG4gICAgICBhY2NvdW50UmVjb3Zlcnk6IEFjY291bnRSZWNvdmVyeS5FTUFJTF9PTkxZLFxuICAgICAgc2lnbkluQWxpYXNlczogeyB1c2VybmFtZTogZmFsc2UsIGVtYWlsOiB0cnVlIH0sXG4gICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgICAuLi5wcm9wcyxcbiAgICB9KTtcbiAgfVxuXG4gIGFkZEdvb2dsZUlkcChcbiAgICBnb29nbGVDbGllbnRJZDogc3RyaW5nLFxuICAgIGdvb2dsZUNsaWVudFNlY3JldDogc3RyaW5nLFxuICApOiBVc2VyUG9vbElkZW50aXR5UHJvdmlkZXJHb29nbGUge1xuICAgIGlmICh0aGlzLmdvb2dsZUlkcCkgdGhyb3cgbmV3IEVycm9yKGBHb29nbGUgaWRlbnRpdHkgcHJvdmlkZXIgaGFzIGFscmVhZHkgYmVlbiBjcmVhdGVkIGZvciAke3RoaXMuaWR9LiBZb3UnbGwgbmVlZCB0byBjYWxsIGFkZEdvb2dsZUlkcCBiZWZvcmUgY3JlYXRpbmcgdGhlIGNsaWVudC5gKTtcbiAgICBpZiAodGhpcy51c2VyUG9vbENsaWVudCkgdGhyb3cgbmV3IEVycm9yKGBVc2VyIHBvb2wgY2xpZW50IGhhcyBhbHJlYWR5IGJlZW4gY3JlYXRlZCBmb3IgJHt0aGlzLmlkfS4gWW91J2xsIG5lZWQgdG8gY2FsbCBhZGRHb29nbGVJZHAgYmVmb3JlIGNyZWF0aW5nIHRoZSBjbGllbnQuYCk7XG5cbiAgICAvLyBHb29nbGUgaWRlbnRpdHkgcHJvdmlkZXJcbiAgICB0aGlzLmdvb2dsZUlkcCA9IG5ldyBVc2VyUG9vbElkZW50aXR5UHJvdmlkZXJHb29nbGUodGhpcywgYCR7dGhpcy5pZH1Hb29nbGVJRFBgLCB7XG4gICAgICB1c2VyUG9vbDogdGhpcy51c2VyUG9vbCxcbiAgICAgIGNsaWVudElkOiBnb29nbGVDbGllbnRJZCxcbiAgICAgIGNsaWVudFNlY3JldDogZ29vZ2xlQ2xpZW50U2VjcmV0LFxuICAgICAgc2NvcGVzOiBbJ3Byb2ZpbGUnLCAnZW1haWwnLCAnb3BlbmlkJ10sXG4gICAgICBhdHRyaWJ1dGVNYXBwaW5nOiB7XG4gICAgICAgIGVtYWlsOiBjb2duaXRvLlByb3ZpZGVyQXR0cmlidXRlLkdPT0dMRV9FTUFJTCxcbiAgICAgICAgZ2l2ZW5OYW1lOiBjb2duaXRvLlByb3ZpZGVyQXR0cmlidXRlLkdPT0dMRV9HSVZFTl9OQU1FLFxuICAgICAgICBmYW1pbHlOYW1lOiBjb2duaXRvLlByb3ZpZGVyQXR0cmlidXRlLkdPT0dMRV9GQU1JTFlfTkFNRSxcbiAgICAgICAgZnVsbG5hbWU6IGNvZ25pdG8uUHJvdmlkZXJBdHRyaWJ1dGUuR09PR0xFX05BTUUsXG4gICAgICAgIHByb2ZpbGVQaWN0dXJlOiBjb2duaXRvLlByb3ZpZGVyQXR0cmlidXRlLkdPT0dMRV9QSUNUVVJFLFxuICAgICAgfSxcbiAgICAgIC8vIHNjb3BlczogW1xuICAgICAgLy8gICAnaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC91c2VyaW5mby5lbWFpbCcsXG4gICAgICAvLyAgICdodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL3VzZXJpbmZvLnByb2ZpbGUnXSxcbiAgICB9KTtcblxuICAgIHJldHVybiB0aGlzLmdvb2dsZUlkcDtcbiAgfVxuXG4gIGFkZEZhY2Vib29rSWRwKFxuICAgIGZhY2Vib29rQXBwSWQ6IHN0cmluZyxcbiAgICBmYWNlYm9va0FwcFNlY3JldDogc3RyaW5nLFxuICApOiBVc2VyUG9vbElkZW50aXR5UHJvdmlkZXJGYWNlYm9vayB7XG4gICAgaWYgKHRoaXMuZ29vZ2xlSWRwKSB0aHJvdyBuZXcgRXJyb3IoYEZhY2Vib29rIGlkZW50aXR5IHByb3ZpZGVyIGhhcyBhbHJlYWR5IGJlZW4gY3JlYXRlZCBmb3IgJHt0aGlzLmlkfS4gWW91J2xsIG5lZWQgdG8gY2FsbCBhZGRHb29nbGVJZHAgYmVmb3JlIGNyZWF0aW5nIHRoZSBjbGllbnQuYCk7XG4gICAgaWYgKHRoaXMudXNlclBvb2xDbGllbnQpIHRocm93IG5ldyBFcnJvcihgVXNlciBwb29sIGNsaWVudCBoYXMgYWxyZWFkeSBiZWVuIGNyZWF0ZWQgZm9yICR7dGhpcy5pZH0uIFlvdSdsbCBuZWVkIHRvIGNhbGwgYWRkRmFjZWJvb2tJZHAgYmVmb3JlIGNyZWF0aW5nIHRoZSBjbGllbnQuYCk7XG4gICAgdGhpcy5mYWNlYm9va0lkcCA9IG5ldyBVc2VyUG9vbElkZW50aXR5UHJvdmlkZXJGYWNlYm9vayh0aGlzLCBgJHt0aGlzLmlkfUZhY2Vib29rSURQYCwge1xuICAgICAgdXNlclBvb2w6IHRoaXMudXNlclBvb2wsXG4gICAgICBjbGllbnRJZDogZmFjZWJvb2tBcHBJZCxcbiAgICAgIGNsaWVudFNlY3JldDogZmFjZWJvb2tBcHBTZWNyZXQsXG4gICAgICBzY29wZXM6IFsncHVibGljX3Byb2ZpbGUnLCAnZW1haWwnXSxcbiAgICAgIGF0dHJpYnV0ZU1hcHBpbmc6IHtcbiAgICAgICAgZW1haWw6IGNvZ25pdG8uUHJvdmlkZXJBdHRyaWJ1dGUuRkFDRUJPT0tfRU1BSUwsXG4gICAgICAgIGdpdmVuTmFtZTogY29nbml0by5Qcm92aWRlckF0dHJpYnV0ZS5GQUNFQk9PS19GSVJTVF9OQU1FLFxuICAgICAgICBmYW1pbHlOYW1lOiBjb2duaXRvLlByb3ZpZGVyQXR0cmlidXRlLkZBQ0VCT09LX0xBU1RfTkFNRSxcbiAgICAgICAgZnVsbG5hbWU6IGNvZ25pdG8uUHJvdmlkZXJBdHRyaWJ1dGUuRkFDRUJPT0tfTkFNRSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcy5mYWNlYm9va0lkcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgYSBTQU1MIHNzbyBpZGVudGl0eSBwcm92aWRlci5cbiAgICpcbiAgICogWW91IGNhbiBjYWxsIHRoaXMgbWV0aG9kIG1vcmUgdGhhbiBvbmNlIHRvIGFkZCBtdWx0aXBsZSBTQU1MIHByb3ZpZGVycy5cbiAgICpcbiAgICogQHBhcmFtIFNhbWxQcm92aWRlck5hbWUgTmFtZSBpbiB0aGUgQ29nbml0byBob3N0ZWQgVUkgdW5kZXIgXCJTaWduIGluIHdpdGggeW91ciBjb3Jwb3JhdGUgSURcIlxuICAgKiBAcGFyYW0gRmVkZXJhdGlvbk1ldGFkYXRhVXJsIFNBTUwgWE1MIFVSTCAoZS5nLiBBenVyZSlcbiAgICogQHBhcmFtIEZlZGVyYXRpb25NZXRhZGF0YVhtbCBTQU1MIG1ldGFkYXRhIFhNTCAoZS5nLiBHb29nbGUgV29ya3NwYWNlKVxuICAgKi9cbiAgYWRkU2FtbElkcChcbiAgICBTYW1sUHJvdmlkZXJOYW1lOiBzdHJpbmcsXG4gICAgRmVkZXJhdGlvbk1ldGFkYXRhVXJsPzogc3RyaW5nLFxuICAgIEZlZGVyYXRpb25NZXRhZGF0YVhtbD86IHN0cmluZyxcbiAgKTogQ2ZuVXNlclBvb2xJZGVudGl0eVByb3ZpZGVyIHtcbiAgICAvLyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2FwaS9sYXRlc3QvZG9jcy9hd3MtY2RrLWxpYl9hd3MtY29nbml0by5DZm5Vc2VyUG9vbElkZW50aXR5UHJvdmlkZXIuaHRtbFxuICAgIC8vIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9BV1NDbG91ZEZvcm1hdGlvbi9sYXRlc3QvVXNlckd1aWRlL2F3cy1yZXNvdXJjZS1jb2duaXRvLXVzZXJwb29saWRlbnRpdHlwcm92aWRlci5odG1sXG5cbiAgICBpZiAodGhpcy51c2VyUG9vbENsaWVudCkgdGhyb3cgbmV3IEVycm9yKGBVc2VyIHBvb2wgY2xpZW50IGhhcyBhbHJlYWR5IGJlZW4gY3JlYXRlZCBmb3IgJHt0aGlzLmlkfS4gWW91J2xsIG5lZWQgdG8gY2FsbCBhZGRTYW1sSWRwIGJlZm9yZSBjcmVhdGluZyB0aGUgY2xpZW50LmApO1xuXG4gICAgY29uc3QgcHJvdmlkZXJEZXRhaWxzOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZzsgfSA9IHt9O1xuICAgIGlmIChGZWRlcmF0aW9uTWV0YWRhdGFVcmwpIHtcbiAgICAgIHByb3ZpZGVyRGV0YWlscy5NZXRhZGF0YVVSTCA9IEZlZGVyYXRpb25NZXRhZGF0YVVybDtcbiAgICB9XG4gICAgaWYgKEZlZGVyYXRpb25NZXRhZGF0YVhtbCkge1xuICAgICAgcHJvdmlkZXJEZXRhaWxzLk1ldGFkYXRhRmlsZSA9IEZlZGVyYXRpb25NZXRhZGF0YVhtbDtcbiAgICB9XG5cbiAgICBjb25zdCBzYW1sSWRwID0gbmV3IENmblVzZXJQb29sSWRlbnRpdHlQcm92aWRlcih0aGlzLCBgJHt0aGlzLmlkfVNhbWxJRFAke1NhbWxQcm92aWRlck5hbWV9YCwge1xuICAgICAgdXNlclBvb2xJZDogdGhpcy51c2VyUG9vbC51c2VyUG9vbElkLFxuICAgICAgcHJvdmlkZXJOYW1lOiBTYW1sUHJvdmlkZXJOYW1lIHx8IHRoaXMuaWQsXG4gICAgICBwcm92aWRlclR5cGU6ICdTQU1MJyxcbiAgICAgIGF0dHJpYnV0ZU1hcHBpbmc6IHtcbiAgICAgICAgLy8gaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2NvZ25pdG8vbGF0ZXN0L2RldmVsb3Blcmd1aWRlL3VzZXItcG9vbC1zZXR0aW5ncy1hdHRyaWJ1dGVzLmh0bWxcbiAgICAgICAgZ2l2ZW5fbmFtZTogJ2h0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2dpdmVubmFtZScsXG4gICAgICAgIGZhbWlseV9uYW1lOiAnaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc3VybmFtZScsXG4gICAgICAgIGVtYWlsOiAnaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzJyxcbiAgICAgIH0sXG4gICAgICBwcm92aWRlckRldGFpbHMsXG4gICAgfSk7XG4gICAgdGhpcy5zYW1sSWRwcy5wdXNoKHNhbWxJZHApO1xuXG4gICAgcmV0dXJuIHNhbWxJZHA7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGEgQ29nbml0byBVc2VyIFBvb2wgQ2xpZW50LlxuICAgKlxuICAgKiBJZiB5b3Ugd2FudCB0byBhZGQgaWRlbnRpdHkgcHJvdmlkZXJzIHN1Y2ggYXMgR29vZ2xlLCBGYWNlYm9vayBvciBzYW1sIHNzbyB5b3UnbGwgbmVlZCB0byBjYWxsIGFkZEdvb2dsZUlkcCgpLCBhZGRGYWNlYm9va0lkcCgpIGFuZC9vciBhZGRTYW1sSWRwKCkgZmlyc3QuXG4gICAqXG4gICAqIEBwYXJhbSBlbmFibGVFbWFpbCBXaGV0aGVyIHRvIGVuYWJsZSBlbWFpbCBhcyBhIHNpZ24tdXAvc2lnbi1pbiBtZXRob2QuXG4gICAqIEBwYXJhbSBjYWxsYmFja1VybCBBbGxvd2VkIGNhbGxiYWNrIFVSTCBvbiB5b3VyIGFwcCB0byByZWNlaXZlIGFuIGF1dGhlbnRpY2F0aW9uIGNvZGUgKD9jb2RlPS4uLilcbiAgICogQHBhcmFtIGFsdGVybmF0aXZlQ2FsbGJhY2tVcmxzIFplcm8gb3IgbW9yZSBhZGRpdG9uYWwgYXV0aG9yaXplZCBjYWxsYmFjayBVUkwsIGZvciBleGFtcGxlIGlmIHlvdSB3bmVlZCB0byBhbGxvdyBsb2NhbGhvc3QgaW4gYSBkZXZlbG9wbWVudCBlbnZpcm9ubWVudC5cbiAgICogQHJldHVybnMgY29nbml0by5Vc2VyUG9vbENsaWVudFxuICAgKi9cbiAgY3JlYXRlVXNlclBvb2xDbGllbnQoXG4gICAgY2FsbGJhY2tVcmw6IHN0cmluZyxcbiAgICBlbmFibGVFbWFpbD86IGJvb2xlYW4sXG4gICAgbG9nb3V0VXJsPzogc3RyaW5nLFxuICAgIC4uLmFsdGVybmF0aXZlQ2FsbGJhY2tVcmxzOiBzdHJpbmdbXVxuICApOiBVc2VyUG9vbENsaWVudCB7XG4gICAgaWYgKHRoaXMudXNlclBvb2xDbGllbnQpIHRocm93IG5ldyBFcnJvcihgVXNlciBwb29sIGNsaWVudCBoYXMgYWxyZWFkeSBiZWVuIGNyZWF0ZWQgZm9yICR7dGhpcy5pZH1gKTtcblxuICAgIGNvbnN0IGlkZW50aXR5UHJvdmlkZXJzOiBjb2duaXRvLlVzZXJQb29sQ2xpZW50SWRlbnRpdHlQcm92aWRlcltdID0gW107XG4gICAgaWYgKGVuYWJsZUVtYWlsKSBpZGVudGl0eVByb3ZpZGVycy5wdXNoKFVzZXJQb29sQ2xpZW50SWRlbnRpdHlQcm92aWRlci5DT0dOSVRPKTtcbiAgICBpZiAodGhpcy5nb29nbGVJZHApIGlkZW50aXR5UHJvdmlkZXJzLnB1c2goVXNlclBvb2xDbGllbnRJZGVudGl0eVByb3ZpZGVyLkdPT0dMRSk7XG4gICAgaWYgKHRoaXMuZmFjZWJvb2tJZHApIGlkZW50aXR5UHJvdmlkZXJzLnB1c2goVXNlclBvb2xDbGllbnRJZGVudGl0eVByb3ZpZGVyLkZBQ0VCT09LKTtcbiAgICB0aGlzLnNhbWxJZHBzLmZvckVhY2goKHNhbWwpID0+IHtcbiAgICAgIGlkZW50aXR5UHJvdmlkZXJzLnB1c2goVXNlclBvb2xDbGllbnRJZGVudGl0eVByb3ZpZGVyLmN1c3RvbShzYW1sLnByb3ZpZGVyTmFtZSkpO1xuICAgIH0pO1xuXG4gICAgdGhpcy5jYWxsYmFja1VybCA9IGNhbGxiYWNrVXJsO1xuICAgIHRoaXMuY2FsbGJhY2tVcmxzID0gW2NhbGxiYWNrVXJsXTtcbiAgICB0aGlzLmNhbGxiYWNrVXJscy5wdXNoKC4uLmFsdGVybmF0aXZlQ2FsbGJhY2tVcmxzKTtcbiAgICB0aGlzLmxvZ291dFVybCA9IGxvZ291dFVybDtcbiAgICBjb25zdCB1c2VyUG9vbENsaWVudCA9IG5ldyBVc2VyUG9vbENsaWVudCh0aGlzLCBgJHt0aGlzLmlkfVVzZXJQb29sQ2xpZW50YCwge1xuICAgICAgdXNlclBvb2w6IHRoaXMudXNlclBvb2wsXG4gICAgICB1c2VyUG9vbENsaWVudE5hbWU6IHRoaXMuaWQsXG4gICAgICBnZW5lcmF0ZVNlY3JldDogZmFsc2UsXG4gICAgICBwcmV2ZW50VXNlckV4aXN0ZW5jZUVycm9yczogdHJ1ZSxcbiAgICAgIHN1cHBvcnRlZElkZW50aXR5UHJvdmlkZXJzOiBpZGVudGl0eVByb3ZpZGVycyxcbiAgICAgIG9BdXRoOiB7XG4gICAgICAgIGNhbGxiYWNrVXJsczogdGhpcy5jYWxsYmFja1VybHMsXG4gICAgICAgIGxvZ291dFVybHM6IHRoaXMubG9nb3V0VXJsID8gW3RoaXMubG9nb3V0VXJsXSA6IHVuZGVmaW5lZCxcbiAgICAgICAgZmxvd3M6IHtcbiAgICAgICAgICBhdXRob3JpemF0aW9uQ29kZUdyYW50OiB0cnVlLFxuICAgICAgICB9LFxuICAgICAgICBzY29wZXM6IFtcbiAgICAgICAgICBjb2duaXRvLk9BdXRoU2NvcGUuRU1BSUwsXG4gICAgICAgICAgY29nbml0by5PQXV0aFNjb3BlLk9QRU5JRCxcbiAgICAgICAgICBjb2duaXRvLk9BdXRoU2NvcGUuUFJPRklMRSxcbiAgICAgICAgXSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyBUaGVzZSBkZXBlbmRlbmNpZXMgc2VlbWVkIHRvIGJlIG5lZWRlZCBhdCB0aGUgdGltZSBvZiB3cml0aW5nOlxuICAgIGlmICh0aGlzLmdvb2dsZUlkcCkgdXNlclBvb2xDbGllbnQubm9kZS5hZGREZXBlbmRlbmN5KHRoaXMuZ29vZ2xlSWRwKTtcbiAgICBpZiAodGhpcy5mYWNlYm9va0lkcCkgdXNlclBvb2xDbGllbnQubm9kZS5hZGREZXBlbmRlbmN5KHRoaXMuZmFjZWJvb2tJZHApO1xuICAgIGlmICh0aGlzLnNhbWxJZHBzKSB7XG4gICAgICB0aGlzLnNhbWxJZHBzLmZvckVhY2goKHNhbWxJZHApID0+IHVzZXJQb29sQ2xpZW50Lm5vZGUuYWRkRGVwZW5kZW5jeShzYW1sSWRwKSk7XG4gICAgfVxuXG4gICAgdGhpcy51c2VyUG9vbENsaWVudCA9IHVzZXJQb29sQ2xpZW50O1xuICAgIHJldHVybiB0aGlzLnVzZXJQb29sQ2xpZW50O1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIGN1c3RvbSBkb21haW4gbmFtZSB0byB0aGUgQ29nbml0byBVc2VyIFBvb2wuXG4gICAqXG4gICAqIEFXUyByZWNvbW1lbmRzIGF1dGguPGRvbWFpbj4gZm9yIGN1c3RvbSBkb21haW5zLCB3aGljaCBpcyB0aGUgZGVmYXVsdCBpZiB5b3UgZG9uJ3QgcGFzcyBhIHZhbHVlIGZvciBkb21haW5OYW1lLlxuICAgKlxuICAgKiBOQiBhdCB0aGUgdGltZSBvZiB3cml0aW5nIHRoZXJlJ3MgYSBoYXJkIGxpbWl0IG9mIDQgY3VzdG9tIENvZ25pdG8gZG9tYWlucyBwZXIgQVdTIGFjY291bnQuXG4gICAqXG4gICAqIFlvdSBjYW4gZWl0aGVyIGFkZCBhIGN1c3RvbSBkb21haW4gb3IgYSBkb21haW4gcHJlZml4LCBidXQgbm90IGJvdGguXG4gICAqXG4gICAqIEBwYXJhbSB6b25lIFRoZSBIb3N0ZWRab25lIGluIHdoaWNoIHRvIGNyZWF0ZSBhbiBhbGlhcyByZWNvcmQgZm9yIHRoZSB1c2VyIHBvb2wuXG4gICAqIEBwYXJhbSBkb21haW5OYW1lIExlYXZlIHRoaXMgb3V0IHRvIHVzZSB0aGUgcmVjb21tZW5kZWQgYGF1dGguPGRvbWFpbj5gLCBvciBwYXNzIGEgZnVsbHkgcXVhbGlmaWVkIGRvbWFpbiBuYW1lLlxuICAgKi9cbiAgYWRkQ3VzdG9tRG9tYWluKHpvbmU6IElIb3N0ZWRab25lLCBkb21haW5OYW1lPzogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMuZG9tYWluKSB0aHJvdyBuZXcgRXJyb3IoYEEgZG9tYWluIGhhcyBhbHJlYWR5IGJlZW4gY3JlYXRlZCBmb3IgJHt0aGlzLmlkfWApO1xuXG4gICAgLy8gTkIgYXQgdGhlIHRpbWUgb2Ygd3JpdGluZyB0aGVyZSdzIGEgaGFyZCBsaW1pdCBvZiA0IGN1c3RvbSBDb2duaXRvIGRvbWFpbnMuXG4gICAgY29uc3QgYXV0aERvbWFpbk5hbWUgPSBkb21haW5OYW1lIHx8IGBhdXRoLiR7em9uZS56b25lTmFtZX1gO1xuXG4gICAgLy8gQ3VzdG9tIGRvbWFpbiBjYW4gb25seSBiZSBzZXQgdXAgYWZ0ZXIgdGhlIGluaXRpYWwgcGFzcyBoYXMgY3JlYXRlZCBhbiBBIHJlY29yZCBhdCB0aGUgYXBleFxuICAgIHRoaXMuZG9tYWluID0gbmV3IGNvZ25pdG8uVXNlclBvb2xEb21haW4odGhpcywgYCR7dGhpcy5pZH1Vc2VyUG9vbERvbWFpbmAsIHtcbiAgICAgIHVzZXJQb29sOiB0aGlzLnVzZXJQb29sLFxuICAgICAgY3VzdG9tRG9tYWluOiB7XG4gICAgICAgIGRvbWFpbk5hbWU6IGF1dGhEb21haW5OYW1lLFxuICAgICAgICBjZXJ0aWZpY2F0ZTogbmV3IERuc1ZhbGlkYXRlZENlcnRpZmljYXRlKHRoaXMsIGAke3RoaXMuaWR9VXNlclBvb2xDZXJ0aWZpY2F0ZWAsIHtcbiAgICAgICAgICBkb21haW5OYW1lOiBhdXRoRG9tYWluTmFtZSxcbiAgICAgICAgICBob3N0ZWRab25lOiB6b25lLFxuICAgICAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIENsb3VkZnJvbnQgcmVxdWlyZXMgdGhpc1xuICAgICAgICB9KSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNjIwNzUzMTQvNzIzNTA2XG4gICAgbmV3IEFSZWNvcmQodGhpcywgYCR7dGhpcy5pZH1Db2duaXRvQ3VzdG9tRG9tYWluQVJlY29yZGAsIHtcbiAgICAgIHpvbmUsXG4gICAgICByZWNvcmROYW1lOiBhdXRoRG9tYWluTmFtZSxcbiAgICAgIHRhcmdldDogUmVjb3JkVGFyZ2V0LmZyb21BbGlhcyhcbiAgICAgICAgbmV3IFVzZXJQb29sRG9tYWluVGFyZ2V0KHRoaXMuZG9tYWluKSxcbiAgICAgICksXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU2V0IGEgZG9tYWluIHByZWZpeCBmb3IgdGhlIFVSTCBvZiB0aGUgQ29nbml0byBVc2VyIFBvb2wuXG4gICAqXG4gICAqIFRoaXMgd2lsbCBzZXQgdGhlIHVzZXIgcG9vbCBVUkwgdG8gaHR0cHM6Ly88ZG9tYWluUHJlZml4Pi5hdXRoLjxyZWdpb24+LmFtYXpvbmNvZ25pdG8uY29tXG4gICAqXG4gICAqIFlvdSBkb24ndCBoYXZlIHRvIHNldCBhIGN1c3RvbSBkb21haW4gcHJlZml4LiBJZiB5b3UgZG9uJ3QsIHRoZSBwcmVmaXggd2lsbCBiZSBnZW5lcmF0ZWQgYnkgQVdTLlxuICAgKlxuICAgKiBJZiBjYW4gc2V0IGEgY3VzdG9tIGRvbWFpbiBwcmVmaXgsIG9yIGEgY3VzdG9tIGRvbWFpbiwgYnV0IG5vdCBib3RoLlxuICAgKlxuICAgKiBAcGFyYW0gZG9tYWluUHJlZml4IExlYXZlIHRoaXMgb3V0IHRvIHVzZSB0aGUgcmVjb21tZW5kZWQgYGF1dGguPGRvbWFpbj5gLCBvciBwYXNzIGEgZnVsbHkgcXVhbGlmaWVkIGRvbWFpbiBuYW1lLlxu