minio
Version:
S3 Compatible Cloud Storage client
189 lines (186 loc) • 28.3 kB
JavaScript
import * as fs from "fs/promises";
import * as http from "http";
import * as https from "https";
import { URL, URLSearchParams } from "url";
import { CredentialProvider } from "./CredentialProvider.mjs";
import { Credentials } from "./Credentials.mjs";
import { parseXml } from "./internal/helper.mjs";
import { request } from "./internal/request.mjs";
import { readAsString } from "./internal/response.mjs";
export class IamAwsProvider extends CredentialProvider {
accessExpiresAt = '';
constructor({
customEndpoint = undefined,
transportAgent = undefined
}) {
super({
accessKey: '',
secretKey: ''
});
this.customEndpoint = customEndpoint;
this.transportAgent = transportAgent;
/**
* Internal Tracking variables
*/
this._credentials = null;
}
async getCredentials() {
if (!this._credentials || this.isAboutToExpire()) {
this._credentials = await this.fetchCredentials();
}
return this._credentials;
}
async fetchCredentials() {
try {
// check for IRSA (https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
const tokenFile = process.env.AWS_WEB_IDENTITY_TOKEN_FILE;
if (tokenFile) {
return await this.fetchCredentialsUsingTokenFile(tokenFile);
}
// try with IAM role for EC2 instances (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)
let tokenHeader = 'Authorization';
let token = process.env.AWS_CONTAINER_AUTHORIZATION_TOKEN;
const relativeUri = process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI;
const fullUri = process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI;
let url;
if (relativeUri) {
url = new URL(relativeUri, 'http://169.254.170.2');
} else if (fullUri) {
url = new URL(fullUri);
} else {
token = await this.fetchImdsToken();
tokenHeader = 'X-aws-ec2-metadata-token';
url = await this.getIamRoleNamedUrl(token);
}
return this.requestCredentials(url, tokenHeader, token);
} catch (err) {
throw new Error(`Failed to get Credentials: ${err}`, {
cause: err
});
}
}
async fetchCredentialsUsingTokenFile(tokenFile) {
const token = await fs.readFile(tokenFile, {
encoding: 'utf8'
});
const region = process.env.AWS_REGION;
const stsEndpoint = new URL(region ? `https://sts.${region}.amazonaws.com` : 'https://sts.amazonaws.com');
const hostValue = stsEndpoint.hostname;
const portValue = stsEndpoint.port;
const qryParams = new URLSearchParams({
Action: 'AssumeRoleWithWebIdentity',
Version: '2011-06-15'
});
const roleArn = process.env.AWS_ROLE_ARN;
if (roleArn) {
qryParams.set('RoleArn', roleArn);
const roleSessionName = process.env.AWS_ROLE_SESSION_NAME;
qryParams.set('RoleSessionName', roleSessionName ? roleSessionName : Date.now().toString());
}
qryParams.set('WebIdentityToken', token);
qryParams.sort();
const requestOptions = {
hostname: hostValue,
port: portValue,
path: `${stsEndpoint.pathname}?${qryParams.toString()}`,
protocol: stsEndpoint.protocol,
method: 'POST',
headers: {},
agent: this.transportAgent
};
const transport = stsEndpoint.protocol === 'http:' ? http : https;
const res = await request(transport, requestOptions, null);
const body = await readAsString(res);
const assumeRoleResponse = parseXml(body);
const creds = assumeRoleResponse.AssumeRoleWithWebIdentityResponse.AssumeRoleWithWebIdentityResult.Credentials;
this.accessExpiresAt = creds.Expiration;
return new Credentials({
accessKey: creds.AccessKeyId,
secretKey: creds.SecretAccessKey,
sessionToken: creds.SessionToken
});
}
async fetchImdsToken() {
const endpoint = this.customEndpoint ? this.customEndpoint : 'http://169.254.169.254';
const url = new URL('/latest/api/token', endpoint);
const requestOptions = {
hostname: url.hostname,
port: url.port,
path: `${url.pathname}${url.search}`,
protocol: url.protocol,
method: 'PUT',
headers: {
'X-aws-ec2-metadata-token-ttl-seconds': '21600'
},
agent: this.transportAgent
};
const transport = url.protocol === 'http:' ? http : https;
const res = await request(transport, requestOptions, null);
return await readAsString(res);
}
async getIamRoleNamedUrl(token) {
const endpoint = this.customEndpoint ? this.customEndpoint : 'http://169.254.169.254';
const url = new URL('latest/meta-data/iam/security-credentials/', endpoint);
const roleName = await this.getIamRoleName(url, token);
return new URL(`${url.pathname}/${encodeURIComponent(roleName)}`, url.origin);
}
async getIamRoleName(url, token) {
const requestOptions = {
hostname: url.hostname,
port: url.port,
path: `${url.pathname}${url.search}`,
protocol: url.protocol,
method: 'GET',
headers: {
'X-aws-ec2-metadata-token': token
},
agent: this.transportAgent
};
const transport = url.protocol === 'http:' ? http : https;
const res = await request(transport, requestOptions, null);
const body = await readAsString(res);
const roleNames = body.split(/\r\n|[\n\r\u2028\u2029]/);
if (roleNames.length === 0) {
throw new Error(`No IAM roles attached to EC2 service ${url}`);
}
return roleNames[0];
}
async requestCredentials(url, tokenHeader, token) {
const headers = {};
if (token) {
headers[tokenHeader] = token;
}
const requestOptions = {
hostname: url.hostname,
port: url.port,
path: `${url.pathname}${url.search}`,
protocol: url.protocol,
method: 'GET',
headers: headers,
agent: this.transportAgent
};
const transport = url.protocol === 'http:' ? http : https;
const res = await request(transport, requestOptions, null);
const body = await readAsString(res);
const ecsCredentials = JSON.parse(body);
if (!ecsCredentials.Code || ecsCredentials.Code != 'Success') {
throw new Error(`${url} failed with code ${ecsCredentials.Code} and message ${ecsCredentials.Message}`);
}
this.accessExpiresAt = ecsCredentials.Expiration;
return new Credentials({
accessKey: ecsCredentials.AccessKeyID,
secretKey: ecsCredentials.SecretAccessKey,
sessionToken: ecsCredentials.Token
});
}
isAboutToExpire() {
const expiresAt = new Date(this.accessExpiresAt);
const provisionalExpiry = new Date(Date.now() + 1000 * 10); // 10 seconds leeway
return provisionalExpiry > expiresAt;
}
}
// deprecated default export, please use named exports.
// keep for backward compatibility.
// eslint-disable-next-line import/no-default-export
export default IamAwsProvider;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmcyIsImh0dHAiLCJodHRwcyIsIlVSTCIsIlVSTFNlYXJjaFBhcmFtcyIsIkNyZWRlbnRpYWxQcm92aWRlciIsIkNyZWRlbnRpYWxzIiwicGFyc2VYbWwiLCJyZXF1ZXN0IiwicmVhZEFzU3RyaW5nIiwiSWFtQXdzUHJvdmlkZXIiLCJhY2Nlc3NFeHBpcmVzQXQiLCJjb25zdHJ1Y3RvciIsImN1c3RvbUVuZHBvaW50IiwidW5kZWZpbmVkIiwidHJhbnNwb3J0QWdlbnQiLCJhY2Nlc3NLZXkiLCJzZWNyZXRLZXkiLCJfY3JlZGVudGlhbHMiLCJnZXRDcmVkZW50aWFscyIsImlzQWJvdXRUb0V4cGlyZSIsImZldGNoQ3JlZGVudGlhbHMiLCJ0b2tlbkZpbGUiLCJwcm9jZXNzIiwiZW52IiwiQVdTX1dFQl9JREVOVElUWV9UT0tFTl9GSUxFIiwiZmV0Y2hDcmVkZW50aWFsc1VzaW5nVG9rZW5GaWxlIiwidG9rZW5IZWFkZXIiLCJ0b2tlbiIsIkFXU19DT05UQUlORVJfQVVUSE9SSVpBVElPTl9UT0tFTiIsInJlbGF0aXZlVXJpIiwiQVdTX0NPTlRBSU5FUl9DUkVERU5USUFMU19SRUxBVElWRV9VUkkiLCJmdWxsVXJpIiwiQVdTX0NPTlRBSU5FUl9DUkVERU5USUFMU19GVUxMX1VSSSIsInVybCIsImZldGNoSW1kc1Rva2VuIiwiZ2V0SWFtUm9sZU5hbWVkVXJsIiwicmVxdWVzdENyZWRlbnRpYWxzIiwiZXJyIiwiRXJyb3IiLCJjYXVzZSIsInJlYWRGaWxlIiwiZW5jb2RpbmciLCJyZWdpb24iLCJBV1NfUkVHSU9OIiwic3RzRW5kcG9pbnQiLCJob3N0VmFsdWUiLCJob3N0bmFtZSIsInBvcnRWYWx1ZSIsInBvcnQiLCJxcnlQYXJhbXMiLCJBY3Rpb24iLCJWZXJzaW9uIiwicm9sZUFybiIsIkFXU19ST0xFX0FSTiIsInNldCIsInJvbGVTZXNzaW9uTmFtZSIsIkFXU19ST0xFX1NFU1NJT05fTkFNRSIsIkRhdGUiLCJub3ciLCJ0b1N0cmluZyIsInNvcnQiLCJyZXF1ZXN0T3B0aW9ucyIsInBhdGgiLCJwYXRobmFtZSIsInByb3RvY29sIiwibWV0aG9kIiwiaGVhZGVycyIsImFnZW50IiwidHJhbnNwb3J0IiwicmVzIiwiYm9keSIsImFzc3VtZVJvbGVSZXNwb25zZSIsImNyZWRzIiwiQXNzdW1lUm9sZVdpdGhXZWJJZGVudGl0eVJlc3BvbnNlIiwiQXNzdW1lUm9sZVdpdGhXZWJJZGVudGl0eVJlc3VsdCIsIkV4cGlyYXRpb24iLCJBY2Nlc3NLZXlJZCIsIlNlY3JldEFjY2Vzc0tleSIsInNlc3Npb25Ub2tlbiIsIlNlc3Npb25Ub2tlbiIsImVuZHBvaW50Iiwic2VhcmNoIiwicm9sZU5hbWUiLCJnZXRJYW1Sb2xlTmFtZSIsImVuY29kZVVSSUNvbXBvbmVudCIsIm9yaWdpbiIsInJvbGVOYW1lcyIsInNwbGl0IiwibGVuZ3RoIiwiZWNzQ3JlZGVudGlhbHMiLCJKU09OIiwicGFyc2UiLCJDb2RlIiwiTWVzc2FnZSIsIkFjY2Vzc0tleUlEIiwiVG9rZW4iLCJleHBpcmVzQXQiLCJwcm92aXNpb25hbEV4cGlyeSJdLCJzb3VyY2VzIjpbIklhbUF3c1Byb3ZpZGVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnXG5pbXBvcnQgKiBhcyBodHRwIGZyb20gJ25vZGU6aHR0cCdcbmltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ25vZGU6aHR0cHMnXG5pbXBvcnQgeyBVUkwsIFVSTFNlYXJjaFBhcmFtcyB9IGZyb20gJ25vZGU6dXJsJ1xuXG5pbXBvcnQgeyBDcmVkZW50aWFsUHJvdmlkZXIgfSBmcm9tICcuL0NyZWRlbnRpYWxQcm92aWRlci50cydcbmltcG9ydCB7IENyZWRlbnRpYWxzIH0gZnJvbSAnLi9DcmVkZW50aWFscy50cydcbmltcG9ydCB7IHBhcnNlWG1sIH0gZnJvbSAnLi9pbnRlcm5hbC9oZWxwZXIudHMnXG5pbXBvcnQgeyByZXF1ZXN0IH0gZnJvbSAnLi9pbnRlcm5hbC9yZXF1ZXN0LnRzJ1xuaW1wb3J0IHsgcmVhZEFzU3RyaW5nIH0gZnJvbSAnLi9pbnRlcm5hbC9yZXNwb25zZS50cydcblxuaW50ZXJmYWNlIEFzc3VtZVJvbGVSZXNwb25zZSB7XG4gIEFzc3VtZVJvbGVXaXRoV2ViSWRlbnRpdHlSZXNwb25zZToge1xuICAgIEFzc3VtZVJvbGVXaXRoV2ViSWRlbnRpdHlSZXN1bHQ6IHtcbiAgICAgIENyZWRlbnRpYWxzOiB7XG4gICAgICAgIEFjY2Vzc0tleUlkOiBzdHJpbmdcbiAgICAgICAgU2VjcmV0QWNjZXNzS2V5OiBzdHJpbmdcbiAgICAgICAgU2Vzc2lvblRva2VuOiBzdHJpbmdcbiAgICAgICAgRXhwaXJhdGlvbjogc3RyaW5nXG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbmludGVyZmFjZSBFY3NDcmVkZW50aWFscyB7XG4gIEFjY2Vzc0tleUlEOiBzdHJpbmdcbiAgU2VjcmV0QWNjZXNzS2V5OiBzdHJpbmdcbiAgVG9rZW46IHN0cmluZ1xuICBFeHBpcmF0aW9uOiBzdHJpbmdcbiAgQ29kZTogc3RyaW5nXG4gIE1lc3NhZ2U6IHN0cmluZ1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElhbUF3c1Byb3ZpZGVyT3B0aW9ucyB7XG4gIGN1c3RvbUVuZHBvaW50Pzogc3RyaW5nXG4gIHRyYW5zcG9ydEFnZW50PzogaHR0cC5BZ2VudFxufVxuXG5leHBvcnQgY2xhc3MgSWFtQXdzUHJvdmlkZXIgZXh0ZW5kcyBDcmVkZW50aWFsUHJvdmlkZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IGN1c3RvbUVuZHBvaW50Pzogc3RyaW5nXG5cbiAgcHJpdmF0ZSBfY3JlZGVudGlhbHM6IENyZWRlbnRpYWxzIHwgbnVsbFxuICBwcml2YXRlIHJlYWRvbmx5IHRyYW5zcG9ydEFnZW50PzogaHR0cC5BZ2VudFxuICBwcml2YXRlIGFjY2Vzc0V4cGlyZXNBdCA9ICcnXG5cbiAgY29uc3RydWN0b3IoeyBjdXN0b21FbmRwb2ludCA9IHVuZGVmaW5lZCwgdHJhbnNwb3J0QWdlbnQgPSB1bmRlZmluZWQgfTogSWFtQXdzUHJvdmlkZXJPcHRpb25zKSB7XG4gICAgc3VwZXIoeyBhY2Nlc3NLZXk6ICcnLCBzZWNyZXRLZXk6ICcnIH0pXG5cbiAgICB0aGlzLmN1c3RvbUVuZHBvaW50ID0gY3VzdG9tRW5kcG9pbnRcbiAgICB0aGlzLnRyYW5zcG9ydEFnZW50ID0gdHJhbnNwb3J0QWdlbnRcblxuICAgIC8qKlxuICAgICAqIEludGVybmFsIFRyYWNraW5nIHZhcmlhYmxlc1xuICAgICAqL1xuICAgIHRoaXMuX2NyZWRlbnRpYWxzID0gbnVsbFxuICB9XG5cbiAgYXN5bmMgZ2V0Q3JlZGVudGlhbHMoKTogUHJvbWlzZTxDcmVkZW50aWFscz4ge1xuICAgIGlmICghdGhpcy5fY3JlZGVudGlhbHMgfHwgdGhpcy5pc0Fib3V0VG9FeHBpcmUoKSkge1xuICAgICAgdGhpcy5fY3JlZGVudGlhbHMgPSBhd2FpdCB0aGlzLmZldGNoQ3JlZGVudGlhbHMoKVxuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHNcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hDcmVkZW50aWFscygpOiBQcm9taXNlPENyZWRlbnRpYWxzPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIGNoZWNrIGZvciBJUlNBIChodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vZWtzL2xhdGVzdC91c2VyZ3VpZGUvaWFtLXJvbGVzLWZvci1zZXJ2aWNlLWFjY291bnRzLmh0bWwpXG4gICAgICBjb25zdCB0b2tlbkZpbGUgPSBwcm9jZXNzLmVudi5BV1NfV0VCX0lERU5USVRZX1RPS0VOX0ZJTEVcbiAgICAgIGlmICh0b2tlbkZpbGUpIHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZmV0Y2hDcmVkZW50aWFsc1VzaW5nVG9rZW5GaWxlKHRva2VuRmlsZSlcbiAgICAgIH1cblxuICAgICAgLy8gdHJ5IHdpdGggSUFNIHJvbGUgZm9yIEVDMiBpbnN0YW5jZXMgKGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9BV1NFQzIvbGF0ZXN0L1VzZXJHdWlkZS9pYW0tcm9sZXMtZm9yLWFtYXpvbi1lYzIuaHRtbClcbiAgICAgIGxldCB0b2tlbkhlYWRlciA9ICdBdXRob3JpemF0aW9uJ1xuICAgICAgbGV0IHRva2VuID0gcHJvY2Vzcy5lbnYuQVdTX0NPTlRBSU5FUl9BVVRIT1JJWkFUSU9OX1RPS0VOXG4gICAgICBjb25zdCByZWxhdGl2ZVVyaSA9IHByb2Nlc3MuZW52LkFXU19DT05UQUlORVJfQ1JFREVOVElBTFNfUkVMQVRJVkVfVVJJXG4gICAgICBjb25zdCBmdWxsVXJpID0gcHJvY2Vzcy5lbnYuQVdTX0NPTlRBSU5FUl9DUkVERU5USUFMU19GVUxMX1VSSVxuICAgICAgbGV0IHVybDogVVJMXG4gICAgICBpZiAocmVsYXRpdmVVcmkpIHtcbiAgICAgICAgdXJsID0gbmV3IFVSTChyZWxhdGl2ZVVyaSwgJ2h0dHA6Ly8xNjkuMjU0LjE3MC4yJylcbiAgICAgIH0gZWxzZSBpZiAoZnVsbFVyaSkge1xuICAgICAgICB1cmwgPSBuZXcgVVJMKGZ1bGxVcmkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0b2tlbiA9IGF3YWl0IHRoaXMuZmV0Y2hJbWRzVG9rZW4oKVxuICAgICAgICB0b2tlbkhlYWRlciA9ICdYLWF3cy1lYzItbWV0YWRhdGEtdG9rZW4nXG4gICAgICAgIHVybCA9IGF3YWl0IHRoaXMuZ2V0SWFtUm9sZU5hbWVkVXJsKHRva2VuKVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcy5yZXF1ZXN0Q3JlZGVudGlhbHModXJsLCB0b2tlbkhlYWRlciwgdG9rZW4pXG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgQ3JlZGVudGlhbHM6ICR7ZXJyfWAsIHsgY2F1c2U6IGVyciB9KVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hDcmVkZW50aWFsc1VzaW5nVG9rZW5GaWxlKHRva2VuRmlsZTogc3RyaW5nKTogUHJvbWlzZTxDcmVkZW50aWFscz4ge1xuICAgIGNvbnN0IHRva2VuID0gYXdhaXQgZnMucmVhZEZpbGUodG9rZW5GaWxlLCB7IGVuY29kaW5nOiAndXRmOCcgfSlcbiAgICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OXG4gICAgY29uc3Qgc3RzRW5kcG9pbnQgPSBuZXcgVVJMKHJlZ2lvbiA/IGBodHRwczovL3N0cy4ke3JlZ2lvbn0uYW1hem9uYXdzLmNvbWAgOiAnaHR0cHM6Ly9zdHMuYW1hem9uYXdzLmNvbScpXG5cbiAgICBjb25zdCBob3N0VmFsdWUgPSBzdHNFbmRwb2ludC5ob3N0bmFtZVxuICAgIGNvbnN0IHBvcnRWYWx1ZSA9IHN0c0VuZHBvaW50LnBvcnRcbiAgICBjb25zdCBxcnlQYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHtcbiAgICAgIEFjdGlvbjogJ0Fzc3VtZVJvbGVXaXRoV2ViSWRlbnRpdHknLFxuICAgICAgVmVyc2lvbjogJzIwMTEtMDYtMTUnLFxuICAgIH0pXG5cbiAgICBjb25zdCByb2xlQXJuID0gcHJvY2Vzcy5lbnYuQVdTX1JPTEVfQVJOXG4gICAgaWYgKHJvbGVBcm4pIHtcbiAgICAgIHFyeVBhcmFtcy5zZXQoJ1JvbGVBcm4nLCByb2xlQXJuKVxuICAgICAgY29uc3Qgcm9sZVNlc3Npb25OYW1lID0gcHJvY2Vzcy5lbnYuQVdTX1JPTEVfU0VTU0lPTl9OQU1FXG4gICAgICBxcnlQYXJhbXMuc2V0KCdSb2xlU2Vzc2lvbk5hbWUnLCByb2xlU2Vzc2lvbk5hbWUgPyByb2xlU2Vzc2lvbk5hbWUgOiBEYXRlLm5vdygpLnRvU3RyaW5nKCkpXG4gICAgfVxuXG4gICAgcXJ5UGFyYW1zLnNldCgnV2ViSWRlbnRpdHlUb2tlbicsIHRva2VuKVxuICAgIHFyeVBhcmFtcy5zb3J0KClcblxuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xuICAgICAgaG9zdG5hbWU6IGhvc3RWYWx1ZSxcbiAgICAgIHBvcnQ6IHBvcnRWYWx1ZSxcbiAgICAgIHBhdGg6IGAke3N0c0VuZHBvaW50LnBhdGhuYW1lfT8ke3FyeVBhcmFtcy50b1N0cmluZygpfWAsXG4gICAgICBwcm90b2NvbDogc3RzRW5kcG9pbnQucHJvdG9jb2wsXG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGhlYWRlcnM6IHt9LFxuICAgICAgYWdlbnQ6IHRoaXMudHJhbnNwb3J0QWdlbnQsXG4gICAgfSBzYXRpc2ZpZXMgaHR0cC5SZXF1ZXN0T3B0aW9uc1xuXG4gICAgY29uc3QgdHJhbnNwb3J0ID0gc3RzRW5kcG9pbnQucHJvdG9jb2wgPT09ICdodHRwOicgPyBodHRwIDogaHR0cHNcbiAgICBjb25zdCByZXMgPSBhd2FpdCByZXF1ZXN0KHRyYW5zcG9ydCwgcmVxdWVzdE9wdGlvbnMsIG51bGwpXG4gICAgY29uc3QgYm9keSA9IGF3YWl0IHJlYWRBc1N0cmluZyhyZXMpXG5cbiAgICBjb25zdCBhc3N1bWVSb2xlUmVzcG9uc2U6IEFzc3VtZVJvbGVSZXNwb25zZSA9IHBhcnNlWG1sKGJvZHkpXG4gICAgY29uc3QgY3JlZHMgPSBhc3N1bWVSb2xlUmVzcG9uc2UuQXNzdW1lUm9sZVdpdGhXZWJJZGVudGl0eVJlc3BvbnNlLkFzc3VtZVJvbGVXaXRoV2ViSWRlbnRpdHlSZXN1bHQuQ3JlZGVudGlhbHNcbiAgICB0aGlzLmFjY2Vzc0V4cGlyZXNBdCA9IGNyZWRzLkV4cGlyYXRpb25cbiAgICByZXR1cm4gbmV3IENyZWRlbnRpYWxzKHtcbiAgICAgIGFjY2Vzc0tleTogY3JlZHMuQWNjZXNzS2V5SWQsXG4gICAgICBzZWNyZXRLZXk6IGNyZWRzLlNlY3JldEFjY2Vzc0tleSxcbiAgICAgIHNlc3Npb25Ub2tlbjogY3JlZHMuU2Vzc2lvblRva2VuLFxuICAgIH0pXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGZldGNoSW1kc1Rva2VuKCkge1xuICAgIGNvbnN0IGVuZHBvaW50ID0gdGhpcy5jdXN0b21FbmRwb2ludCA/IHRoaXMuY3VzdG9tRW5kcG9pbnQgOiAnaHR0cDovLzE2OS4yNTQuMTY5LjI1NCdcbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKCcvbGF0ZXN0L2FwaS90b2tlbicsIGVuZHBvaW50KVxuXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSB7XG4gICAgICBob3N0bmFtZTogdXJsLmhvc3RuYW1lLFxuICAgICAgcG9ydDogdXJsLnBvcnQsXG4gICAgICBwYXRoOiBgJHt1cmwucGF0aG5hbWV9JHt1cmwuc2VhcmNofWAsXG4gICAgICBwcm90b2NvbDogdXJsLnByb3RvY29sLFxuICAgICAgbWV0aG9kOiAnUFVUJyxcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgJ1gtYXdzLWVjMi1tZXRhZGF0YS10b2tlbi10dGwtc2Vjb25kcyc6ICcyMTYwMCcsXG4gICAgICB9LFxuICAgICAgYWdlbnQ6IHRoaXMudHJhbnNwb3J0QWdlbnQsXG4gICAgfSBzYXRpc2ZpZXMgaHR0cC5SZXF1ZXN0T3B0aW9uc1xuXG4gICAgY29uc3QgdHJhbnNwb3J0ID0gdXJsLnByb3RvY29sID09PSAnaHR0cDonID8gaHR0cCA6IGh0dHBzXG4gICAgY29uc3QgcmVzID0gYXdhaXQgcmVxdWVzdCh0cmFuc3BvcnQsIHJlcXVlc3RPcHRpb25zLCBudWxsKVxuICAgIHJldHVybiBhd2FpdCByZWFkQXNTdHJpbmcocmVzKVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXRJYW1Sb2xlTmFtZWRVcmwodG9rZW46IHN0cmluZykge1xuICAgIGNvbnN0IGVuZHBvaW50ID0gdGhpcy5jdXN0b21FbmRwb2ludCA/IHRoaXMuY3VzdG9tRW5kcG9pbnQgOiAnaHR0cDovLzE2OS4yNTQuMTY5LjI1NCdcbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKCdsYXRlc3QvbWV0YS1kYXRhL2lhbS9zZWN1cml0eS1jcmVkZW50aWFscy8nLCBlbmRwb2ludClcblxuICAgIGNvbnN0IHJvbGVOYW1lID0gYXdhaXQgdGhpcy5nZXRJYW1Sb2xlTmFtZSh1cmwsIHRva2VuKVxuICAgIHJldHVybiBuZXcgVVJMKGAke3VybC5wYXRobmFtZX0vJHtlbmNvZGVVUklDb21wb25lbnQocm9sZU5hbWUpfWAsIHVybC5vcmlnaW4pXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldElhbVJvbGVOYW1lKHVybDogVVJMLCB0b2tlbjogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcbiAgICAgIGhvc3RuYW1lOiB1cmwuaG9zdG5hbWUsXG4gICAgICBwb3J0OiB1cmwucG9ydCxcbiAgICAgIHBhdGg6IGAke3VybC5wYXRobmFtZX0ke3VybC5zZWFyY2h9YCxcbiAgICAgIHByb3RvY29sOiB1cmwucHJvdG9jb2wsXG4gICAgICBtZXRob2Q6ICdHRVQnLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAnWC1hd3MtZWMyLW1ldGFkYXRhLXRva2VuJzogdG9rZW4sXG4gICAgICB9LFxuICAgICAgYWdlbnQ6IHRoaXMudHJhbnNwb3J0QWdlbnQsXG4gICAgfSBzYXRpc2ZpZXMgaHR0cC5SZXF1ZXN0T3B0aW9uc1xuXG4gICAgY29uc3QgdHJhbnNwb3J0ID0gdXJsLnByb3RvY29sID09PSAnaHR0cDonID8gaHR0cCA6IGh0dHBzXG4gICAgY29uc3QgcmVzID0gYXdhaXQgcmVxdWVzdCh0cmFuc3BvcnQsIHJlcXVlc3RPcHRpb25zLCBudWxsKVxuICAgIGNvbnN0IGJvZHkgPSBhd2FpdCByZWFkQXNTdHJpbmcocmVzKVxuICAgIGNvbnN0IHJvbGVOYW1lcyA9IGJvZHkuc3BsaXQoL1xcclxcbnxbXFxuXFxyXFx1MjAyOFxcdTIwMjldLylcbiAgICBpZiAocm9sZU5hbWVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBJQU0gcm9sZXMgYXR0YWNoZWQgdG8gRUMyIHNlcnZpY2UgJHt1cmx9YClcbiAgICB9XG4gICAgcmV0dXJuIHJvbGVOYW1lc1swXSBhcyBzdHJpbmdcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVxdWVzdENyZWRlbnRpYWxzKHVybDogVVJMLCB0b2tlbkhlYWRlcjogc3RyaW5nLCB0b2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxDcmVkZW50aWFscz4ge1xuICAgIGNvbnN0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fVxuICAgIGlmICh0b2tlbikge1xuICAgICAgaGVhZGVyc1t0b2tlbkhlYWRlcl0gPSB0b2tlblxuICAgIH1cbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcbiAgICAgIGhvc3RuYW1lOiB1cmwuaG9zdG5hbWUsXG4gICAgICBwb3J0OiB1cmwucG9ydCxcbiAgICAgIHBhdGg6IGAke3VybC5wYXRobmFtZX0ke3VybC5zZWFyY2h9YCxcbiAgICAgIHByb3RvY29sOiB1cmwucHJvdG9jb2wsXG4gICAgICBtZXRob2Q6ICdHRVQnLFxuICAgICAgaGVhZGVyczogaGVhZGVycyxcbiAgICAgIGFnZW50OiB0aGlzLnRyYW5zcG9ydEFnZW50LFxuICAgIH0gc2F0aXNmaWVzIGh0dHAuUmVxdWVzdE9wdGlvbnNcblxuICAgIGNvbnN0IHRyYW5zcG9ydCA9IHVybC5wcm90b2NvbCA9PT0gJ2h0dHA6JyA/IGh0dHAgOiBodHRwc1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IHJlcXVlc3QodHJhbnNwb3J0LCByZXF1ZXN0T3B0aW9ucywgbnVsbClcbiAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVhZEFzU3RyaW5nKHJlcylcbiAgICBjb25zdCBlY3NDcmVkZW50aWFscyA9IEpTT04ucGFyc2UoYm9keSkgYXMgRWNzQ3JlZGVudGlhbHNcbiAgICBpZiAoIWVjc0NyZWRlbnRpYWxzLkNvZGUgfHwgZWNzQ3JlZGVudGlhbHMuQ29kZSAhPSAnU3VjY2VzcycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJHt1cmx9IGZhaWxlZCB3aXRoIGNvZGUgJHtlY3NDcmVkZW50aWFscy5Db2RlfSBhbmQgbWVzc2FnZSAke2Vjc0NyZWRlbnRpYWxzLk1lc3NhZ2V9YClcbiAgICB9XG5cbiAgICB0aGlzLmFjY2Vzc0V4cGlyZXNBdCA9IGVjc0NyZWRlbnRpYWxzLkV4cGlyYXRpb25cbiAgICByZXR1cm4gbmV3IENyZWRlbnRpYWxzKHtcbiAgICAgIGFjY2Vzc0tleTogZWNzQ3JlZGVudGlhbHMuQWNjZXNzS2V5SUQsXG4gICAgICBzZWNyZXRLZXk6IGVjc0NyZWRlbnRpYWxzLlNlY3JldEFjY2Vzc0tleSxcbiAgICAgIHNlc3Npb25Ub2tlbjogZWNzQ3JlZGVudGlhbHMuVG9rZW4sXG4gICAgfSlcbiAgfVxuXG4gIHByaXZhdGUgaXNBYm91dFRvRXhwaXJlKCkge1xuICAgIGNvbnN0IGV4cGlyZXNBdCA9IG5ldyBEYXRlKHRoaXMuYWNjZXNzRXhwaXJlc0F0KVxuICAgIGNvbnN0IHByb3Zpc2lvbmFsRXhwaXJ5ID0gbmV3IERhdGUoRGF0ZS5ub3coKSArIDEwMDAgKiAxMCkgLy8gMTAgc2Vjb25kcyBsZWV3YXlcbiAgICByZXR1cm4gcHJvdmlzaW9uYWxFeHBpcnkgPiBleHBpcmVzQXRcbiAgfVxufVxuXG4vLyBkZXByZWNhdGVkIGRlZmF1bHQgZXhwb3J0LCBwbGVhc2UgdXNlIG5hbWVkIGV4cG9ydHMuXG4vLyBrZWVwIGZvciBiYWNrd2FyZCBjb21wYXRpYmlsaXR5LlxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1kZWZhdWx0LWV4cG9ydFxuZXhwb3J0IGRlZmF1bHQgSWFtQXdzUHJvdmlkZXJcbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxFQUFFO0FBQ2QsT0FBTyxLQUFLQyxJQUFJO0FBQ2hCLE9BQU8sS0FBS0MsS0FBSztBQUNqQixTQUFTQyxHQUFHLEVBQUVDLGVBQWU7QUFFN0IsU0FBU0Msa0JBQWtCLFFBQVEsMEJBQXlCO0FBQzVELFNBQVNDLFdBQVcsUUFBUSxtQkFBa0I7QUFDOUMsU0FBU0MsUUFBUSxRQUFRLHVCQUFzQjtBQUMvQyxTQUFTQyxPQUFPLFFBQVEsd0JBQXVCO0FBQy9DLFNBQVNDLFlBQVksUUFBUSx5QkFBd0I7QUE2QnJELE9BQU8sTUFBTUMsY0FBYyxTQUFTTCxrQkFBa0IsQ0FBQztF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