aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
210 lines • 26.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FakeSts = void 0;
/* eslint-disable import/order */
const nock = require("nock");
const uuid = require("uuid");
const xmlJs = require("xml-js");
/**
* Class for mocking AWS HTTP Requests and pretending to be STS
*
* This is necessary for testing our authentication layer. Most other mocking
* libraries don't consider as they mock functional methods which happen BEFORE
* the SDK's HTTP/Authentication layer.
*
* Instead, we want to validate how we're setting up credentials for the
* SDK, so we pretend to be the STS server and have an in-memory database
* of users and roles.
*/
class FakeSts {
constructor() {
this.assumedRoles = new Array();
this.identities = {};
this.roles = {};
}
/**
* Begin mocking
*/
begin() {
const self = this;
nock.disableNetConnect();
if (!nock.isActive()) {
nock.activate();
}
nock(/.*/).persist().post(/.*/).reply(function (uri, body, cb) {
const parsedBody = typeof body === 'string' ? urldecode(body) : body;
try {
const response = self.handleRequest({
uri,
host: this.req.headers.host,
parsedBody,
headers: this.req.headers,
});
cb(null, [200, xmlJs.js2xml(response, { compact: true })]);
}
catch (e) {
cb(null, [400, xmlJs.js2xml({
ErrorResponse: {
_attributes: { xmlns: 'https://sts.amazonaws.com/doc/2011-06-15/' },
Error: {
Type: 'Sender',
Code: e.code ?? 'Error',
Message: e.message,
},
RequestId: '1',
},
}, { compact: true })]);
}
});
// Scrub some environment variables that might be set if we're running on CodeBuild which will interfere with the tests.
delete process.env.AWS_PROFILE;
delete process.env.AWS_REGION;
delete process.env.AWS_DEFAULT_REGION;
delete process.env.AWS_ACCESS_KEY_ID;
delete process.env.AWS_SECRET_ACCESS_KEY;
delete process.env.AWS_SESSION_TOKEN;
}
/**
* Restore everything to normal
*/
restore() {
nock.restore(); // https://github.com/nock/nock/issues/1817
nock.cleanAll();
nock.enableNetConnect();
}
/**
* Register a user
*/
registerUser(account, accessKey, options = {}) {
const userName = options.name ?? `User${Object.keys(this.identities).length + 1}`;
this.identities[accessKey] = {
account: account,
arn: `arn:${options.partition ?? 'aws'}:sts::${account}:user/${userName}`,
userId: `${accessKey}:${userName}`,
};
}
/**
* Register an assumable role
*/
registerRole(account, roleArn, options = {}) {
const roleName = options.name ?? `Role${Object.keys(this.roles).length + 1}`;
this.roles[roleArn] = {
allowedAccounts: options.allowedAccounts ?? [account],
arn: roleArn,
roleName,
account,
};
}
handleRequest(mockRequest) {
const response = (() => {
const identity = this.identity(mockRequest);
switch (mockRequest.parsedBody.Action) {
case 'GetCallerIdentity':
return this.handleGetCallerIdentity(identity);
case 'AssumeRole':
return this.handleAssumeRole(identity, mockRequest);
}
throw new Error(`Unrecognized Action in MockAwsHttp: ${mockRequest.parsedBody.Action}`);
})();
// console.log(mockRequest.parsedBody, '->', response);
return response;
}
handleGetCallerIdentity(identity) {
return {
GetCallerIdentityResponse: {
_attributes: { xmlns: 'https://sts.amazonaws.com/doc/2011-06-15/' },
GetCallerIdentityResult: {
Arn: identity.arn,
UserId: identity.userId,
Account: identity.account,
},
ResponseMetadata: {
RequestId: '1',
},
},
};
}
handleAssumeRole(identity, mockRequest) {
this.checkForFailure(mockRequest.parsedBody.RoleArn);
this.assumedRoles.push({
roleArn: mockRequest.parsedBody.RoleArn,
roleSessionName: mockRequest.parsedBody.RoleSessionName,
serialNumber: mockRequest.parsedBody.SerialNumber,
tokenCode: mockRequest.parsedBody.TokenCode,
});
const roleArn = mockRequest.parsedBody.RoleArn;
const targetRole = this.roles[roleArn];
if (!targetRole) {
throw new Error(`No such role: ${roleArn}`);
}
if (!targetRole.allowedAccounts.includes(identity.account)) {
throw new Error(`Identity from account: ${identity.account} not allowed to assume ${roleArn}, must be one of: ${targetRole.allowedAccounts}`);
}
const freshAccessKey = uuid.v4();
// Register a new "user" (identity) for this access key
this.registerUser(targetRole.account, freshAccessKey, {
name: `AssumedRole-${targetRole.roleName}-${identity.userId}`,
});
return {
AssumeRoleResponse: {
_attributes: { xmlns: 'https://sts.amazonaws.com/doc/2011-06-15/' },
AssumeRoleResult: {
AssumedRoleUser: {
Arn: roleArn,
AssumedRoleId: `${freshAccessKey}:${targetRole.roleName}`,
},
Credentials: {
AccessKeyId: freshAccessKey,
SecretAccessKey: 'Secret',
SessionToken: 'Token',
Expiration: new Date(Date.now() + 3600 * 1000).toISOString(),
},
PackedPolicySize: 6,
},
},
ResponseMetadata: {
RequestId: '1',
},
};
}
checkForFailure(s) {
const failureRequested = s.match(/<FAIL:([^>]+)>/);
if (failureRequested) {
const err = new Error(`STS failing by user request: ${failureRequested[1]}`);
err.code = failureRequested[1];
throw err;
}
}
identity(mockRequest) {
const keyId = this.accessKeyId(mockRequest);
this.checkForFailure(keyId);
const ret = this.identities[keyId];
if (!ret) {
throw new Error(`Unrecognized access key used: ${keyId}`);
}
return ret;
}
/**
* Return the access key from a signed request
*/
accessKeyId(mockRequest) {
// "AWS4-HMAC-SHA256 Credential=(ab1a5e4c-ff41-4811-ac5f-6d1230f7aa90)access/20201210/eu-bla-5/sts/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=9b31011173a7842fa372d4ef7c431c08f0b1514fdaf54145560a4db7ecd24529"
const auth = mockRequest.headers.authorization;
const m = auth?.match(/Credential=([^\/]+)/);
if (!m) {
throw new Error(`No correct authorization header: ${auth}`);
}
return m[1];
}
}
exports.FakeSts = FakeSts;
function urldecode(body) {
const parts = body.split('&');
const ret = {};
for (const part of parts) {
const [k, v] = part.split('=');
ret[decodeURIComponent(k)] = decodeURIComponent(v);
}
return ret;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFrZS1zdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJmYWtlLXN0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpQ0FBaUM7QUFDakMsNkJBQTZCO0FBQzdCLDZCQUE2QjtBQUM3QixnQ0FBZ0M7QUFzQmhDOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFhLE9BQU87SUFNbEI7UUFMZ0IsaUJBQVksR0FBRyxJQUFJLEtBQUssRUFBZSxDQUFDO1FBRWhELGVBQVUsR0FBdUMsRUFBRSxDQUFDO1FBQ3BELFVBQUssR0FBbUMsRUFBRSxDQUFDO0lBR25ELENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUs7UUFDVixNQUFNLElBQUksR0FBRyxJQUFJLENBQUM7UUFFbEIsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRTtZQUNwQixJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7U0FDakI7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssQ0FBQyxVQUFnQixHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDakUsTUFBTSxVQUFVLEdBQUcsT0FBTyxJQUFJLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUVyRSxJQUFJO2dCQUNGLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUM7b0JBQ2xDLEdBQUc7b0JBQ0gsSUFBSSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUk7b0JBQzNCLFVBQVU7b0JBQ1YsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTztpQkFDMUIsQ0FBQyxDQUFDO2dCQUNILEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsS0FB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LElBQVk7SUFDN0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM5QixNQUFNLEdBQUcsR0FBMkIsRUFBRSxDQUFDO0lBQ3ZDLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFO1FBQ3hCLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMvQixHQUFHLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUNwRDtJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9vcmRlciAqL1xuaW1wb3J0ICogYXMgbm9jayBmcm9tICdub2NrJztcbmltcG9ydCAqIGFzIHV1aWQgZnJvbSAndXVpZCc7XG5pbXBvcnQgKiBhcyB4bWxKcyBmcm9tICd4bWwtanMnO1xuXG5pbnRlcmZhY2UgUmVnaXN0ZXJlZElkZW50aXR5IHtcbiAgcmVhZG9ubHkgYWNjb3VudDogc3RyaW5nO1xuICByZWFkb25seSBhcm46IHN0cmluZztcbiAgcmVhZG9ubHkgdXNlcklkOiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBSZWdpc3RlcmVkUm9sZSB7XG4gIHJlYWRvbmx5IGFjY291bnQ6IHN0cmluZztcbiAgcmVhZG9ubHkgYWxsb3dlZEFjY291bnRzOiBzdHJpbmdbXTtcbiAgcmVhZG9ubHkgYXJuOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHJvbGVOYW1lOiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBBc3N1bWVkUm9sZSB7XG4gIHJlYWRvbmx5IHJvbGVBcm46IHN0cmluZztcbiAgcmVhZG9ubHkgc2VyaWFsTnVtYmVyOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHRva2VuQ29kZTogc3RyaW5nO1xuICByZWFkb25seSByb2xlU2Vzc2lvbk5hbWU6IHN0cmluZztcbn1cblxuLyoqXG4gKiBDbGFzcyBmb3IgbW9ja2luZyBBV1MgSFRUUCBSZXF1ZXN0cyBhbmQgcHJldGVuZGluZyB0byBiZSBTVFNcbiAqXG4gKiBUaGlzIGlzIG5lY2Vzc2FyeSBmb3IgdGVzdGluZyBvdXIgYXV0aGVudGljYXRpb24gbGF5ZXIuIE1vc3Qgb3RoZXIgbW9ja2luZ1xuICogbGlicmFyaWVzIGRvbid0IGNvbnNpZGVyIGFzIHRoZXkgbW9jayBmdW5jdGlvbmFsIG1ldGhvZHMgd2hpY2ggaGFwcGVuIEJFRk9SRVxuICogdGhlIFNESydzIEhUVFAvQXV0aGVudGljYXRpb24gbGF5ZXIuXG4gKlxuICogSW5zdGVhZCwgd2Ugd2FudCB0byB2YWxpZGF0ZSBob3cgd2UncmUgc2V0dGluZyB1cCBjcmVkZW50aWFscyBmb3IgdGhlXG4gKiBTREssIHNvIHdlIHByZXRlbmQgdG8gYmUgdGhlIFNUUyBzZXJ2ZXIgYW5kIGhhdmUgYW4gaW4tbWVtb3J5IGRhdGFiYXNlXG4gKiBvZiB1c2VycyBhbmQgcm9sZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBGYWtlU3RzIHtcbiAgcHVibGljIHJlYWRvbmx5IGFzc3VtZWRSb2xlcyA9IG5ldyBBcnJheTxBc3N1bWVkUm9sZT4oKTtcblxuICBwcml2YXRlIGlkZW50aXRpZXM6IFJlY29yZDxzdHJpbmcsIFJlZ2lzdGVyZWRJZGVudGl0eT4gPSB7fTtcbiAgcHJpdmF0ZSByb2xlczogUmVjb3JkPHN0cmluZywgUmVnaXN0ZXJlZFJvbGU+ID0ge307XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gIH1cblxuICAvKipcbiAgICogQmVnaW4gbW9ja2luZ1xuICAgKi9cbiAgcHVibGljIGJlZ2luKCkge1xuICAgIGNvbnN0IHNlbGYgPSB0aGlzO1xuXG4gICAgbm9jay5kaXNhYmxlTmV0Q29ubmVjdCgpO1xuICAgIGlmICghbm9jay5pc0FjdGl2ZSgpKSB7XG4gICAgICBub2NrLmFjdGl2YXRlKCk7XG4gICAgfVxuICAgIG5vY2soLy4qLykucGVyc2lzdCgpLnBvc3QoLy4qLykucmVwbHkoZnVuY3Rpb24gKHRoaXMsIHVyaSwgYm9keSwgY2IpIHtcbiAgICAgIGNvbnN0IHBhcnNlZEJvZHkgPSB0eXBlb2YgYm9keSA9PT0gJ3N0cmluZycgPyB1cmxkZWNvZGUoYm9keSkgOiBib2R5O1xuXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXNwb25zZSA9IHNlbGYuaGFuZGxlUmVxdWVzdCh7XG4gICAgICAgICAgdXJpLFxuICAgICAgICAgIGhvc3Q6IHRoaXMucmVxLmhlYWRlcnMuaG9zdCxcbiAgICAgICAgICBwYXJzZWRCb2R5LFxuICAgICAgICAgIGhlYWRlcnM6IHRoaXMucmVxLmhlYWRlcnMsXG4gICAgICAgIH0pO1xuICAgICAgICBjYihudWxsLCBbMjAwLCB4bWxKcy5qczJ4bWwocmVzcG9uc2UsIHsgY29tcGFjdDogdHJ1ZSB9KV0pO1xuICAgICAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgICAgIGNiKG51bGwsIFs0MDAsIHhtbEpzLmpzMnhtbCh7XG4gICAgICAgICAgRXJyb3JSZXNwb25zZToge1xuICAgICAgICAgICAgX2F0dHJpYnV0ZXM6IHsgeG1sbnM6ICdodHRwczovL3N0cy5hbWF6b25hd3MuY29tL2RvYy8yMDExLTA2LTE1LycgfSxcbiAgICAgICAgICAgIEVycm9yOiB7XG4gICAgICAgICAgICAgIFR5cGU6ICdTZW5kZXInLFxuICAgICAgICAgICAgICBDb2RlOiBlLmNvZGUgPz8gJ0Vycm9yJyxcbiAgICAgICAgICAgICAgTWVzc2FnZTogZS5tZXNzYWdlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIFJlcXVlc3RJZDogJzEnLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sIHsgY29tcGFjdDogdHJ1ZSB9KV0pO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gU2NydWIgc29tZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdGhhdCBtaWdodCBiZSBzZXQgaWYgd2UncmUgcnVubmluZyBvbiBDb2RlQnVpbGQgd2hpY2ggd2lsbCBpbnRlcmZlcmUgd2l0aCB0aGUgdGVzdHMuXG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkFXU19QUk9GSUxFO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5BV1NfREVGQVVMVF9SRUdJT047XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkFXU19BQ0NFU1NfS0VZX0lEO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5BV1NfU0VDUkVUX0FDQ0VTU19LRVk7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkFXU19TRVNTSU9OX1RPS0VOO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc3RvcmUgZXZlcnl0aGluZyB0byBub3JtYWxcbiAgICovXG4gIHB1YmxpYyByZXN0b3JlKCkge1xuICAgIG5vY2sucmVzdG9yZSgpOyAvLyBodHRwczovL2dpdGh1Yi5jb20vbm9jay9ub2NrL2lzc3Vlcy8xODE3XG4gICAgbm9jay5jbGVhbkFsbCgpO1xuICAgIG5vY2suZW5hYmxlTmV0Q29ubmVjdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZ2lzdGVyIGEgdXNlclxuICAgKi9cbiAgcHVibGljIHJlZ2lzdGVyVXNlcihhY2NvdW50OiBzdHJpbmcsIGFjY2Vzc0tleTogc3RyaW5nLCBvcHRpb25zOiBSZWdpc3RlclVzZXJPcHRpb25zID0ge30pIHtcbiAgICBjb25zdCB1c2VyTmFtZSA9IG9wdGlvbnMubmFtZSA/PyBgVXNlciR7T2JqZWN0LmtleXModGhpcy5pZGVudGl0aWVzKS5sZW5ndGggKyAxIH1gO1xuXG4gICAgdGhpcy5pZGVudGl0aWVzW2FjY2Vzc0tleV0gPSB7XG4gICAgICBhY2NvdW50OiBhY2NvdW50LFxuICAgICAgYXJuOiBgYXJuOiR7b3B0aW9ucy5wYXJ0aXRpb24gPz8gJ2F3cyd9OnN0czo6JHthY2NvdW50fTp1c2VyLyR7dXNlck5hbWV9YCxcbiAgICAgIHVzZXJJZDogYCR7YWNjZXNzS2V5fToke3VzZXJOYW1lfWAsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlciBhbiBhc3N1bWFibGUgcm9sZVxuICAgKi9cbiAgcHVibGljIHJlZ2lzdGVyUm9sZShhY2NvdW50OiBzdHJpbmcsIHJvbGVBcm46IHN0cmluZywgb3B0aW9uczogUmVnaXN0ZXJSb2xlT3B0aW9ucyA9IHt9KSB7XG4gICAgY29uc3Qgcm9sZU5hbWUgPSBvcHRpb25zLm5hbWUgPz8gYFJvbGUke09iamVjdC5rZXlzKHRoaXMucm9sZXMpLmxlbmd0aCArIDEgfWA7XG5cbiAgICB0aGlzLnJvbGVzW3JvbGVBcm5dID0ge1xuICAgICAgYWxsb3dlZEFjY291bnRzOiBvcHRpb25zLmFsbG93ZWRBY2NvdW50cyA/PyBbYWNjb3VudF0sXG4gICAgICBhcm46IHJvbGVBcm4sXG4gICAgICByb2xlTmFtZSxcbiAgICAgIGFjY291bnQsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgaGFuZGxlUmVxdWVzdChtb2NrUmVxdWVzdDogTW9ja1JlcXVlc3QpOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICBjb25zdCByZXNwb25zZSA9ICgoKSA9PiB7XG4gICAgICBjb25zdCBpZGVudGl0eSA9IHRoaXMuaWRlbnRpdHkobW9ja1JlcXVlc3QpO1xuXG4gICAgICBzd2l0Y2ggKG1vY2tSZXF1ZXN0LnBhcnNlZEJvZHkuQWN0aW9uKSB7XG4gICAgICAgIGNhc2UgJ0dldENhbGxlcklkZW50aXR5JzpcbiAgICAgICAgICByZXR1cm4gdGhpcy5oYW5kbGVHZXRDYWxsZXJJZGVudGl0eShpZGVudGl0eSk7XG5cbiAgICAgICAgY2FzZSAnQXNzdW1lUm9sZSc6XG4gICAgICAgICAgcmV0dXJuIHRoaXMuaGFuZGxlQXNzdW1lUm9sZShpZGVudGl0eSwgbW9ja1JlcXVlc3QpO1xuICAgICAgfVxuXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFVucmVjb2duaXplZCBBY3Rpb24gaW4gTW9ja0F3c0h0dHA6ICR7bW9ja1JlcXVlc3QucGFyc2VkQm9keS5BY3Rpb259YCk7XG4gICAgfSkoKTtcbiAgICAvLyBjb25zb2xlLmxvZyhtb2NrUmVxdWVzdC5wYXJzZWRCb2R5LCAnLT4nLCByZXNwb25zZSk7XG4gICAgcmV0dXJuIHJlc3BvbnNlO1xuICB9XG5cbiAgcHJpdmF0ZSBoYW5kbGVHZXRDYWxsZXJJZGVudGl0eShpZGVudGl0eTogUmVnaXN0ZXJlZElkZW50aXR5KTogUmVjb3JkPHN0cmluZywgYW55PiB7XG4gICAgcmV0dXJuIHtcbiAgICAgIEdldENhbGxlcklkZW50aXR5UmVzcG9uc2U6IHtcbiAgICAgICAgX2F0dHJpYnV0ZXM6IHsgeG1sbnM6ICdodHRwczovL3N0cy5hbWF6b25hd3MuY29tL2RvYy8yMDExLTA2LTE1LycgfSxcbiAgICAgICAgR2V0Q2FsbGVySWRlbnRpdHlSZXN1bHQ6IHtcbiAgICAgICAgICBBcm46IGlkZW50aXR5LmFybixcbiAgICAgICAgICBVc2VySWQ6IGlkZW50aXR5LnVzZXJJZCxcbiAgICAgICAgICBBY2NvdW50OiBpZGVudGl0eS5hY2NvdW50LFxuICAgICAgICB9LFxuICAgICAgICBSZXNwb25zZU1ldGFkYXRhOiB7XG4gICAgICAgICAgUmVxdWVzdElkOiAnMScsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGhhbmRsZUFzc3VtZVJvbGUoaWRlbnRpdHk6IFJlZ2lzdGVyZWRJZGVudGl0eSwgbW9ja1JlcXVlc3Q6IE1vY2tSZXF1ZXN0KTogUmVjb3JkPHN0cmluZywgYW55PiB7XG4gICAgdGhpcy5jaGVja0ZvckZhaWx1cmUobW9ja1JlcXVlc3QucGFyc2VkQm9keS5Sb2xlQXJuKTtcblxuICAgIHRoaXMuYXNzdW1lZFJvbGVzLnB1c2goe1xuICAgICAgcm9sZUFybjogbW9ja1JlcXVlc3QucGFyc2VkQm9keS5Sb2xlQXJuLFxuICAgICAgcm9sZVNlc3Npb25OYW1lOiBtb2NrUmVxdWVzdC5wYXJzZWRCb2R5LlJvbGVTZXNzaW9uTmFtZSxcbiAgICAgIHNlcmlhbE51bWJlcjogbW9ja1JlcXVlc3QucGFyc2VkQm9keS5TZXJpYWxOdW1iZXIsXG4gICAgICB0b2tlbkNvZGU6IG1vY2tSZXF1ZXN0LnBhcnNlZEJvZHkuVG9rZW5Db2RlLFxuICAgIH0pO1xuXG4gICAgY29uc3Qgcm9sZUFybiA9IG1vY2tSZXF1ZXN0LnBhcnNlZEJvZHkuUm9sZUFybjtcbiAgICBjb25zdCB0YXJnZXRSb2xlID0gdGhpcy5yb2xlc1tyb2xlQXJuXTtcbiAgICBpZiAoIXRhcmdldFJvbGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gc3VjaCByb2xlOiAke3JvbGVBcm59YCk7XG4gICAgfVxuXG4gICAgaWYgKCF0YXJnZXRSb2xlLmFsbG93ZWRBY2NvdW50cy5pbmNsdWRlcyhpZGVudGl0eS5hY2NvdW50KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJZGVudGl0eSBmcm9tIGFjY291bnQ6ICR7aWRlbnRpdHkuYWNjb3VudH0gbm90IGFsbG93ZWQgdG8gYXNzdW1lICR7cm9sZUFybn0sIG11c3QgYmUgb25lIG9mOiAke3RhcmdldFJvbGUuYWxsb3dlZEFjY291bnRzfWApO1xuICAgIH1cblxuICAgIGNvbnN0IGZyZXNoQWNjZXNzS2V5ID0gdXVpZC52NCgpO1xuXG4gICAgLy8gUmVnaXN0ZXIgYSBuZXcgXCJ1c2VyXCIgKGlkZW50aXR5KSBmb3IgdGhpcyBhY2Nlc3Mga2V5XG4gICAgdGhpcy5yZWdpc3RlclVzZXIodGFyZ2V0Um9sZS5hY2NvdW50LCBmcmVzaEFjY2Vzc0tleSwge1xuICAgICAgbmFtZTogYEFzc3VtZWRSb2xlLSR7dGFyZ2V0Um9sZS5yb2xlTmFtZX0tJHtpZGVudGl0eS51c2VySWR9YCxcbiAgICB9KTtcblxuICAgIHJldHVybiB7XG4gICAgICBBc3N1bWVSb2xlUmVzcG9uc2U6IHtcbiAgICAgICAgX2F0dHJpYnV0ZXM6IHsgeG1sbnM6ICdodHRwczovL3N0cy5hbWF6b25hd3MuY29tL2RvYy8yMDExLTA2LTE1LycgfSxcbiAgICAgICAgQXNzdW1lUm9sZVJlc3VsdDoge1xuICAgICAgICAgIEFzc3VtZWRSb2xlVXNlcjoge1xuICAgICAgICAgICAgQXJuOiByb2xlQXJuLFxuICAgICAgICAgICAgQXNzdW1lZFJvbGVJZDogYCR7ZnJlc2hBY2Nlc3NLZXl9OiR7dGFyZ2V0Um9sZS5yb2xlTmFtZX1gLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgQ3JlZGVudGlhbHM6IHtcbiAgICAgICAgICAgIEFjY2Vzc0tleUlkOiBmcmVzaEFjY2Vzc0tleSxcbiAgICAgICAgICAgIFNlY3JldEFjY2Vzc0tleTogJ1NlY3JldCcsXG4gICAgICAgICAgICBTZXNzaW9uVG9rZW46ICdUb2tlbicsXG4gICAgICAgICAgICBFeHBpcmF0aW9uOiBuZXcgRGF0ZShEYXRlLm5vdygpICsgMzYwMCAqIDEwMDApLnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgfSxcbiAgICAgICAgICBQYWNrZWRQb2xpY3lTaXplOiA2LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIFJlc3BvbnNlTWV0YWRhdGE6IHtcbiAgICAgICAgUmVxdWVzdElkOiAnMScsXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGNoZWNrRm9yRmFpbHVyZShzOiBzdHJpbmcpIHtcbiAgICBjb25zdCBmYWlsdXJlUmVxdWVzdGVkID0gcy5tYXRjaCgvPEZBSUw6KFtePl0rKT4vKTtcbiAgICBpZiAoZmFpbHVyZVJlcXVlc3RlZCkge1xuICAgICAgY29uc3QgZXJyID0gbmV3IEVycm9yKGBTVFMgZmFpbGluZyBieSB1c2VyIHJlcXVlc3Q6ICR7ZmFpbHVyZVJlcXVlc3RlZFsxXX1gKTtcbiAgICAgIChlcnIgYXMgYW55KS5jb2RlID0gZmFpbHVyZVJlcXVlc3RlZFsxXTtcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGlkZW50aXR5KG1vY2tSZXF1ZXN0OiBNb2NrUmVxdWVzdCkge1xuICAgIGNvbnN0IGtleUlkID0gdGhpcy5hY2Nlc3NLZXlJZChtb2NrUmVxdWVzdCk7XG4gICAgdGhpcy5jaGVja0ZvckZhaWx1cmUoa2V5SWQpO1xuXG4gICAgY29uc3QgcmV0ID0gdGhpcy5pZGVudGl0aWVzW2tleUlkXTtcbiAgICBpZiAoIXJldCkgeyB0aHJvdyBuZXcgRXJyb3IoYFVucmVjb2duaXplZCBhY2Nlc3Mga2V5IHVzZWQ6ICR7a2V5SWR9YCk7IH1cbiAgICByZXR1cm4gcmV0O1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgYWNjZXNzIGtleSBmcm9tIGEgc2lnbmVkIHJlcXVlc3RcbiAgICovXG4gIHByaXZhdGUgYWNjZXNzS2V5SWQobW9ja1JlcXVlc3Q6IE1vY2tSZXF1ZXN0KTogc3RyaW5nIHtcbiAgICAvLyBcIkFXUzQtSE1BQy1TSEEyNTYgQ3JlZGVudGlhbD0oYWIxYTVlNGMtZmY0MS00ODExLWFjNWYtNmQxMjMwZjdhYTkwKWFjY2Vzcy8yMDIwMTIxMC9ldS1ibGEtNS9zdHMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWhvc3Q7eC1hbXotY29udGVudC1zaGEyNTY7eC1hbXotZGF0ZSwgU2lnbmF0dXJlPTliMzEwMTExNzNhNzg0MmZhMzcyZDRlZjdjNDMxYzA4ZjBiMTUxNGZkYWY1NDE0NTU2MGE0ZGI3ZWNkMjQ1MjlcIlxuICAgIGNvbnN0IGF1dGggPSBtb2NrUmVxdWVzdC5oZWFkZXJzLmF1dGhvcml6YXRpb247XG5cbiAgICBjb25zdCBtID0gYXV0aD8ubWF0Y2goL0NyZWRlbnRpYWw9KFteXFwvXSspLyk7XG4gICAgaWYgKCFtKSB7IHRocm93IG5ldyBFcnJvcihgTm8gY29ycmVjdCBhdXRob3JpemF0aW9uIGhlYWRlcjogJHthdXRofWApOyB9XG4gICAgcmV0dXJuIG1bMV07XG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZWdpc3RlclVzZXJPcHRpb25zIHtcbiAgcmVhZG9ubHkgbmFtZT86IHN0cmluZztcbiAgcmVhZG9ubHkgcGFydGl0aW9uPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlZ2lzdGVyUm9sZU9wdGlvbnMge1xuICByZWFkb25seSBhbGxvd2VkQWNjb3VudHM/OiBzdHJpbmdbXTtcbiAgcmVhZG9ubHkgbmFtZT86IHN0cmluZztcbiAgcmVhZG9ubHkgcGFydGl0aW9uPzogc3RyaW5nO1xufVxuXG5pbnRlcmZhY2UgTW9ja1JlcXVlc3Qge1xuICByZWFkb25seSBob3N0OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHVyaTogc3RyaW5nO1xuICByZWFkb25seSBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICByZWFkb25seSBwYXJzZWRCb2R5OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xufVxuXG5mdW5jdGlvbiB1cmxkZWNvZGUoYm9keTogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gIGNvbnN0IHBhcnRzID0gYm9keS5zcGxpdCgnJicpO1xuICBjb25zdCByZXQ6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcbiAgZm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgY29uc3QgW2ssIHZdID0gcGFydC5zcGxpdCgnPScpO1xuICAgIHJldFtkZWNvZGVVUklDb21wb25lbnQoayldID0gZGVjb2RlVVJJQ29tcG9uZW50KHYpO1xuICB9XG4gIHJldHVybiByZXQ7XG59XG4iXX0=