AssumeRoleProvider.mjs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import * as http from "http";
  2. import * as https from "https";
  3. import { URL, URLSearchParams } from "url";
  4. import { CredentialProvider } from "./CredentialProvider.mjs";
  5. import { Credentials } from "./Credentials.mjs";
  6. import { makeDateLong, parseXml, toSha256 } from "./internal/helper.mjs";
  7. import { request } from "./internal/request.mjs";
  8. import { readAsString } from "./internal/response.mjs";
  9. import { signV4ByServiceName } from "./signing.mjs";
  10. /**
  11. * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
  12. */
  13. const defaultExpirySeconds = 900;
  14. export class AssumeRoleProvider extends CredentialProvider {
  15. accessExpiresAt = '';
  16. constructor({
  17. stsEndpoint,
  18. accessKey,
  19. secretKey,
  20. durationSeconds = defaultExpirySeconds,
  21. sessionToken,
  22. policy,
  23. region = '',
  24. roleArn,
  25. roleSessionName,
  26. externalId,
  27. token,
  28. webIdentityToken,
  29. action = 'AssumeRole',
  30. transportAgent = undefined
  31. }) {
  32. super({
  33. accessKey,
  34. secretKey,
  35. sessionToken
  36. });
  37. this.stsEndpoint = new URL(stsEndpoint);
  38. this.accessKey = accessKey;
  39. this.secretKey = secretKey;
  40. this.policy = policy;
  41. this.region = region;
  42. this.roleArn = roleArn;
  43. this.roleSessionName = roleSessionName;
  44. this.externalId = externalId;
  45. this.token = token;
  46. this.webIdentityToken = webIdentityToken;
  47. this.action = action;
  48. this.durationSeconds = parseInt(durationSeconds);
  49. let expirySeconds = this.durationSeconds;
  50. if (this.durationSeconds < defaultExpirySeconds) {
  51. expirySeconds = defaultExpirySeconds;
  52. }
  53. this.expirySeconds = expirySeconds; // for calculating refresh of credentials.
  54. // By default, nodejs uses a global agent if the 'agent' property
  55. // is set to undefined. Otherwise, it's okay to assume the users
  56. // know what they're doing if they specify a custom transport agent.
  57. this.transportAgent = transportAgent;
  58. const isHttp = this.stsEndpoint.protocol === 'http:';
  59. this.transport = isHttp ? http : https;
  60. /**
  61. * Internal Tracking variables
  62. */
  63. this._credentials = null;
  64. }
  65. getRequestConfig() {
  66. const hostValue = this.stsEndpoint.hostname;
  67. const portValue = this.stsEndpoint.port;
  68. const qryParams = new URLSearchParams({
  69. Action: this.action,
  70. Version: '2011-06-15'
  71. });
  72. qryParams.set('DurationSeconds', this.expirySeconds.toString());
  73. if (this.policy) {
  74. qryParams.set('Policy', this.policy);
  75. }
  76. if (this.roleArn) {
  77. qryParams.set('RoleArn', this.roleArn);
  78. }
  79. if (this.roleSessionName != null) {
  80. qryParams.set('RoleSessionName', this.roleSessionName);
  81. }
  82. if (this.token != null) {
  83. qryParams.set('Token', this.token);
  84. }
  85. if (this.webIdentityToken) {
  86. qryParams.set('WebIdentityToken', this.webIdentityToken);
  87. }
  88. if (this.externalId) {
  89. qryParams.set('ExternalId', this.externalId);
  90. }
  91. const urlParams = qryParams.toString();
  92. const contentSha256 = toSha256(urlParams);
  93. const date = new Date();
  94. const requestOptions = {
  95. hostname: hostValue,
  96. port: portValue,
  97. path: '/',
  98. protocol: this.stsEndpoint.protocol,
  99. method: 'POST',
  100. headers: {
  101. 'Content-Type': 'application/x-www-form-urlencoded',
  102. 'content-length': urlParams.length.toString(),
  103. host: hostValue,
  104. 'x-amz-date': makeDateLong(date),
  105. 'x-amz-content-sha256': contentSha256
  106. },
  107. agent: this.transportAgent
  108. };
  109. requestOptions.headers.authorization = signV4ByServiceName(requestOptions, this.accessKey, this.secretKey, this.region, date, contentSha256, 'sts');
  110. return {
  111. requestOptions,
  112. requestData: urlParams
  113. };
  114. }
  115. async performRequest() {
  116. const {
  117. requestOptions,
  118. requestData
  119. } = this.getRequestConfig();
  120. const res = await request(this.transport, requestOptions, requestData);
  121. const body = await readAsString(res);
  122. return parseXml(body);
  123. }
  124. parseCredentials(respObj) {
  125. if (respObj.ErrorResponse) {
  126. var _respObj$ErrorRespons, _respObj$ErrorRespons2, _respObj$ErrorRespons3, _respObj$ErrorRespons4;
  127. throw new Error(`Unable to obtain credentials: ${(_respObj$ErrorRespons = respObj.ErrorResponse) === null || _respObj$ErrorRespons === void 0 ? void 0 : (_respObj$ErrorRespons2 = _respObj$ErrorRespons.Error) === null || _respObj$ErrorRespons2 === void 0 ? void 0 : _respObj$ErrorRespons2.Code} ${(_respObj$ErrorRespons3 = respObj.ErrorResponse) === null || _respObj$ErrorRespons3 === void 0 ? void 0 : (_respObj$ErrorRespons4 = _respObj$ErrorRespons3.Error) === null || _respObj$ErrorRespons4 === void 0 ? void 0 : _respObj$ErrorRespons4.Message}`, {
  128. cause: respObj
  129. });
  130. }
  131. const {
  132. AssumeRoleResponse: {
  133. AssumeRoleResult: {
  134. Credentials: {
  135. AccessKeyId: accessKey,
  136. SecretAccessKey: secretKey,
  137. SessionToken: sessionToken,
  138. Expiration: expiresAt
  139. }
  140. }
  141. }
  142. } = respObj;
  143. this.accessExpiresAt = expiresAt;
  144. return new Credentials({
  145. accessKey,
  146. secretKey,
  147. sessionToken
  148. });
  149. }
  150. async refreshCredentials() {
  151. try {
  152. const assumeRoleCredentials = await this.performRequest();
  153. this._credentials = this.parseCredentials(assumeRoleCredentials);
  154. } catch (err) {
  155. throw new Error(`Failed to get Credentials: ${err}`, {
  156. cause: err
  157. });
  158. }
  159. return this._credentials;
  160. }
  161. async getCredentials() {
  162. if (this._credentials && !this.isAboutToExpire()) {
  163. return this._credentials;
  164. }
  165. this._credentials = await this.refreshCredentials();
  166. return this._credentials;
  167. }
  168. isAboutToExpire() {
  169. const expiresAt = new Date(this.accessExpiresAt);
  170. const provisionalExpiry = new Date(Date.now() + 1000 * 10); // check before 10 seconds.
  171. return provisionalExpiry > expiresAt;
  172. }
  173. }
  174. // deprecated default export, please use named exports.
  175. // keep for backward compatibility.
  176. // eslint-disable-next-line import/no-default-export
  177. export default AssumeRoleProvider;
  178. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJodHRwIiwiaHR0cHMiLCJVUkwiLCJVUkxTZWFyY2hQYXJhbXMiLCJDcmVkZW50aWFsUHJvdmlkZXIiLCJDcmVkZW50aWFscyIsIm1ha2VEYXRlTG9uZyIsInBhcnNlWG1sIiwidG9TaGEyNTYiLCJyZXF1ZXN0IiwicmVhZEFzU3RyaW5nIiwic2lnblY0QnlTZXJ2aWNlTmFtZSIsImRlZmF1bHRFeHBpcnlTZWNvbmRzIiwiQXNzdW1lUm9sZVByb3ZpZGVyIiwiYWNjZXNzRXhwaXJlc0F0IiwiY29uc3RydWN0b3IiLCJzdHNFbmRwb2ludCIsImFjY2Vzc0tleSIsInNlY3JldEtleSIsImR1cmF0aW9uU2Vjb25kcyIsInNlc3Npb25Ub2tlbiIsInBvbGljeSIsInJlZ2lvbiIsInJvbGVBcm4iLCJyb2xlU2Vzc2lvbk5hbWUiLCJleHRlcm5hbElkIiwidG9rZW4iLCJ3ZWJJZGVudGl0eVRva2VuIiwiYWN0aW9uIiwidHJhbnNwb3J0QWdlbnQiLCJ1bmRlZmluZWQiLCJwYXJzZUludCIsImV4cGlyeVNlY29uZHMiLCJpc0h0dHAiLCJwcm90b2NvbCIsInRyYW5zcG9ydCIsIl9jcmVkZW50aWFscyIsImdldFJlcXVlc3RDb25maWciLCJob3N0VmFsdWUiLCJob3N0bmFtZSIsInBvcnRWYWx1ZSIsInBvcnQiLCJxcnlQYXJhbXMiLCJBY3Rpb24iLCJWZXJzaW9uIiwic2V0IiwidG9TdHJpbmciLCJ1cmxQYXJhbXMiLCJjb250ZW50U2hhMjU2IiwiZGF0ZSIsIkRhdGUiLCJyZXF1ZXN0T3B0aW9ucyIsInBhdGgiLCJtZXRob2QiLCJoZWFkZXJzIiwibGVuZ3RoIiwiaG9zdCIsImFnZW50IiwiYXV0aG9yaXphdGlvbiIsInJlcXVlc3REYXRhIiwicGVyZm9ybVJlcXVlc3QiLCJyZXMiLCJib2R5IiwicGFyc2VDcmVkZW50aWFscyIsInJlc3BPYmoiLCJFcnJvclJlc3BvbnNlIiwiX3Jlc3BPYmokRXJyb3JSZXNwb25zIiwiX3Jlc3BPYmokRXJyb3JSZXNwb25zMiIsIl9yZXNwT2JqJEVycm9yUmVzcG9uczMiLCJfcmVzcE9iaiRFcnJvclJlc3BvbnM0IiwiRXJyb3IiLCJDb2RlIiwiTWVzc2FnZSIsImNhdXNlIiwiQXNzdW1lUm9sZVJlc3BvbnNlIiwiQXNzdW1lUm9sZVJlc3VsdCIsIkFjY2Vzc0tleUlkIiwiU2VjcmV0QWNjZXNzS2V5IiwiU2Vzc2lvblRva2VuIiwiRXhwaXJhdGlvbiIsImV4cGlyZXNBdCIsInJlZnJlc2hDcmVkZW50aWFscyIsImFzc3VtZVJvbGVDcmVkZW50aWFscyIsImVyciIsImdldENyZWRlbnRpYWxzIiwiaXNBYm91dFRvRXhwaXJlIiwicHJvdmlzaW9uYWxFeHBpcnkiLCJub3ciXSwic291cmNlcyI6WyJBc3N1bWVSb2xlUHJvdmlkZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgaHR0cCBmcm9tICdub2RlOmh0dHAnXG5pbXBvcnQgKiBhcyBodHRwcyBmcm9tICdub2RlOmh0dHBzJ1xuaW1wb3J0IHsgVVJMLCBVUkxTZWFyY2hQYXJhbXMgfSBmcm9tICdub2RlOnVybCdcblxuaW1wb3J0IHsgQ3JlZGVudGlhbFByb3ZpZGVyIH0gZnJvbSAnLi9DcmVkZW50aWFsUHJvdmlkZXIudHMnXG5pbXBvcnQgeyBDcmVkZW50aWFscyB9IGZyb20gJy4vQ3JlZGVudGlhbHMudHMnXG5pbXBvcnQgeyBtYWtlRGF0ZUxvbmcsIHBhcnNlWG1sLCB0b1NoYTI1NiB9IGZyb20gJy4vaW50ZXJuYWwvaGVscGVyLnRzJ1xuaW1wb3J0IHsgcmVxdWVzdCB9IGZyb20gJy4vaW50ZXJuYWwvcmVxdWVzdC50cydcbmltcG9ydCB7IHJlYWRBc1N0cmluZyB9IGZyb20gJy4vaW50ZXJuYWwvcmVzcG9uc2UudHMnXG5pbXBvcnQgdHlwZSB7IFRyYW5zcG9ydCB9IGZyb20gJy4vaW50ZXJuYWwvdHlwZS50cydcbmltcG9ydCB7IHNpZ25WNEJ5U2VydmljZU5hbWUgfSBmcm9tICcuL3NpZ25pbmcudHMnXG5cbi8qKlxuICogQHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vU1RTL2xhdGVzdC9BUElSZWZlcmVuY2UvQVBJX0Fzc3VtZVJvbGUuaHRtbFxuICovXG50eXBlIENyZWRlbnRpYWxSZXNwb25zZSA9IHtcbiAgRXJyb3JSZXNwb25zZT86IHtcbiAgICBFcnJvcj86IHtcbiAgICAgIENvZGU/OiBzdHJpbmdcbiAgICAgIE1lc3NhZ2U/OiBzdHJpbmdcbiAgICB9XG4gIH1cblxuICBBc3N1bWVSb2xlUmVzcG9uc2U6IHtcbiAgICBBc3N1bWVSb2xlUmVzdWx0OiB7XG4gICAgICBDcmVkZW50aWFsczoge1xuICAgICAgICBBY2Nlc3NLZXlJZDogc3RyaW5nXG4gICAgICAgIFNlY3JldEFjY2Vzc0tleTogc3RyaW5nXG4gICAgICAgIFNlc3Npb25Ub2tlbjogc3RyaW5nXG4gICAgICAgIEV4cGlyYXRpb246IHN0cmluZ1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIEFzc3VtZVJvbGVQcm92aWRlck9wdGlvbnMge1xuICBzdHNFbmRwb2ludDogc3RyaW5nXG4gIGFjY2Vzc0tleTogc3RyaW5nXG4gIHNlY3JldEtleTogc3RyaW5nXG4gIGR1cmF0aW9uU2Vjb25kcz86IG51bWJlclxuICBzZXNzaW9uVG9rZW4/OiBzdHJpbmdcbiAgcG9saWN5Pzogc3RyaW5nXG4gIHJlZ2lvbj86IHN0cmluZ1xuICByb2xlQXJuPzogc3RyaW5nXG4gIHJvbGVTZXNzaW9uTmFtZT86IHN0cmluZ1xuICBleHRlcm5hbElkPzogc3RyaW5nXG4gIHRva2VuPzogc3RyaW5nXG4gIHdlYklkZW50aXR5VG9rZW4/OiBzdHJpbmdcbiAgYWN0aW9uPzogc3RyaW5nXG4gIHRyYW5zcG9ydEFnZW50PzogaHR0cC5BZ2VudFxufVxuXG5jb25zdCBkZWZhdWx0RXhwaXJ5U2Vjb25kcyA9IDkwMFxuXG5leHBvcnQgY2xhc3MgQXNzdW1lUm9sZVByb3ZpZGVyIGV4dGVuZHMgQ3JlZGVudGlhbFByb3ZpZGVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBzdHNFbmRwb2ludDogVVJMXG4gIHByaXZhdGUgcmVhZG9ubHkgYWNjZXNzS2V5OiBzdHJpbmdcbiAgcHJpdmF0ZSByZWFkb25seSBzZWNyZXRLZXk6IHN0cmluZ1xuICBwcml2YXRlIHJlYWRvbmx5IGR1cmF0aW9uU2Vjb25kczogbnVtYmVyXG4gIHByaXZhdGUgcmVhZG9ubHkgcG9saWN5Pzogc3RyaW5nXG4gIHByaXZhdGUgcmVhZG9ubHkgcmVnaW9uOiBzdHJpbmdcbiAgcHJpdmF0ZSByZWFkb25seSByb2xlQXJuPzogc3RyaW5nXG4gIHByaXZhdGUgcmVhZG9ubHkgcm9sZVNlc3Npb25OYW1lPzogc3RyaW5nXG4gIHByaXZhdGUgcmVhZG9ubHkgZXh0ZXJuYWxJZD86IHN0cmluZ1xuICBwcml2YXRlIHJlYWRvbmx5IHRva2VuPzogc3RyaW5nXG4gIHByaXZhdGUgcmVhZG9ubHkgd2ViSWRlbnRpdHlUb2tlbj86IHN0cmluZ1xuICBwcml2YXRlIHJlYWRvbmx5IGFjdGlvbjogc3RyaW5nXG5cbiAgcHJpdmF0ZSBfY3JlZGVudGlhbHM6IENyZWRlbnRpYWxzIHwgbnVsbFxuICBwcml2YXRlIHJlYWRvbmx5IGV4cGlyeVNlY29uZHM6IG51bWJlclxuICBwcml2YXRlIGFjY2Vzc0V4cGlyZXNBdCA9ICcnXG4gIHByaXZhdGUgcmVhZG9ubHkgdHJhbnNwb3J0QWdlbnQ/OiBodHRwLkFnZW50XG5cbiAgcHJpdmF0ZSByZWFkb25seSB0cmFuc3BvcnQ6IFRyYW5zcG9ydFxuXG4gIGNvbnN0cnVjdG9yKHtcbiAgICBzdHNFbmRwb2ludCxcbiAgICBhY2Nlc3NLZXksXG4gICAgc2VjcmV0S2V5LFxuICAgIGR1cmF0aW9uU2Vjb25kcyA9IGRlZmF1bHRFeHBpcnlTZWNvbmRzLFxuICAgIHNlc3Npb25Ub2tlbixcbiAgICBwb2xpY3ksXG4gICAgcmVnaW9uID0gJycsXG4gICAgcm9sZUFybixcbiAgICByb2xlU2Vzc2lvbk5hbWUsXG4gICAgZXh0ZXJuYWxJZCxcbiAgICB0b2tlbixcbiAgICB3ZWJJZGVudGl0eVRva2VuLFxuICAgIGFjdGlvbiA9ICdBc3N1bWVSb2xlJyxcbiAgICB0cmFuc3BvcnRBZ2VudCA9IHVuZGVmaW5lZCxcbiAgfTogQXNzdW1lUm9sZVByb3ZpZGVyT3B0aW9ucykge1xuICAgIHN1cGVyKHsgYWNjZXNzS2V5LCBzZWNyZXRLZXksIHNlc3Npb25Ub2tlbiB9KVxuXG4gICAgdGhpcy5zdHNFbmRwb2ludCA9IG5ldyBVUkwoc3RzRW5kcG9pbnQpXG4gICAgdGhpcy5hY2Nlc3NLZXkgPSBhY2Nlc3NLZXlcbiAgICB0aGlzLnNlY3JldEtleSA9IHNlY3JldEtleVxuICAgIHRoaXMucG9saWN5ID0gcG9saWN5XG4gICAgdGhpcy5yZWdpb24gPSByZWdpb25cbiAgICB0aGlzLnJvbGVBcm4gPSByb2xlQXJuXG4gICAgdGhpcy5yb2xlU2Vzc2lvbk5hbWUgPSByb2xlU2Vzc2lvbk5hbWVcbiAgICB0aGlzLmV4dGVybmFsSWQgPSBleHRlcm5hbElkXG4gICAgdGhpcy50b2tlbiA9IHRva2VuXG4gICAgdGhpcy53ZWJJZGVudGl0eVRva2VuID0gd2ViSWRlbnRpdHlUb2tlblxuICAgIHRoaXMuYWN0aW9uID0gYWN0aW9uXG5cbiAgICB0aGlzLmR1cmF0aW9uU2Vjb25kcyA9IHBhcnNlSW50KGR1cmF0aW9uU2Vjb25kcyBhcyB1bmtub3duIGFzIHN0cmluZylcblxuICAgIGxldCBleHBpcnlTZWNvbmRzID0gdGhpcy5kdXJhdGlvblNlY29uZHNcbiAgICBpZiAodGhpcy5kdXJhdGlvblNlY29uZHMgPCBkZWZhdWx0RXhwaXJ5U2Vjb25kcykge1xuICAgICAgZXhwaXJ5U2Vjb25kcyA9IGRlZmF1bHRFeHBpcnlTZWNvbmRzXG4gICAgfVxuICAgIHRoaXMuZXhwaXJ5U2Vjb25kcyA9IGV4cGlyeVNlY29uZHMgLy8gZm9yIGNhbGN1bGF0aW5nIHJlZnJlc2ggb2YgY3JlZGVudGlhbHMuXG5cbiAgICAvLyBCeSBkZWZhdWx0LCBub2RlanMgdXNlcyBhIGdsb2JhbCBhZ2VudCBpZiB0aGUgJ2FnZW50JyBwcm9wZXJ0eVxuICAgIC8vIGlzIHNldCB0byB1bmRlZmluZWQuIE90aGVyd2lzZSwgaXQncyBva2F5IHRvIGFzc3VtZSB0aGUgdXNlcnNcbiAgICAvLyBrbm93IHdoYXQgdGhleSdyZSBkb2luZyBpZiB0aGV5IHNwZWNpZnkgYSBjdXN0b20gdHJhbnNwb3J0IGFnZW50LlxuICAgIHRoaXMudHJhbnNwb3J0QWdlbnQgPSB0cmFuc3BvcnRBZ2VudFxuICAgIGNvbnN0IGlzSHR0cDogYm9vbGVhbiA9IHRoaXMuc3RzRW5kcG9pbnQucHJvdG9jb2wgPT09ICdodHRwOidcbiAgICB0aGlzLnRyYW5zcG9ydCA9IGlzSHR0cCA/IGh0dHAgOiBodHRwc1xuXG4gICAgLyoqXG4gICAgICogSW50ZXJuYWwgVHJhY2tpbmcgdmFyaWFibGVzXG4gICAgICovXG4gICAgdGhpcy5fY3JlZGVudGlhbHMgPSBudWxsXG4gIH1cblxuICBnZXRSZXF1ZXN0Q29uZmlnKCk6IHtcbiAgICByZXF1ZXN0T3B0aW9uczogaHR0cC5SZXF1ZXN0T3B0aW9uc1xuICAgIHJlcXVlc3REYXRhOiBzdHJpbmdcbiAgfSB7XG4gICAgY29uc3QgaG9zdFZhbHVlID0gdGhpcy5zdHNFbmRwb2ludC5ob3N0bmFtZVxuICAgIGNvbnN0IHBvcnRWYWx1ZSA9IHRoaXMuc3RzRW5kcG9pbnQucG9ydFxuICAgIGNvbnN0IHFyeVBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoeyBBY3Rpb246IHRoaXMuYWN0aW9uLCBWZXJzaW9uOiAnMjAxMS0wNi0xNScgfSlcblxuICAgIHFyeVBhcmFtcy5zZXQoJ0R1cmF0aW9uU2Vjb25kcycsIHRoaXMuZXhwaXJ5U2Vjb25kcy50b1N0cmluZygpKVxuXG4gICAgaWYgKHRoaXMucG9saWN5KSB7XG4gICAgICBxcnlQYXJhbXMuc2V0KCdQb2xpY3knLCB0aGlzLnBvbGljeSlcbiAgICB9XG4gICAgaWYgKHRoaXMucm9sZUFybikge1xuICAgICAgcXJ5UGFyYW1zLnNldCgnUm9sZUFybicsIHRoaXMucm9sZUFybilcbiAgICB9XG5cbiAgICBpZiAodGhpcy5yb2xlU2Vzc2lvbk5hbWUgIT0gbnVsbCkge1xuICAgICAgcXJ5UGFyYW1zLnNldCgnUm9sZVNlc3Npb25OYW1lJywgdGhpcy5yb2xlU2Vzc2lvbk5hbWUpXG4gICAgfVxuICAgIGlmICh0aGlzLnRva2VuICE9IG51bGwpIHtcbiAgICAgIHFyeVBhcmFtcy5zZXQoJ1Rva2VuJywgdGhpcy50b2tlbilcbiAgICB9XG5cbiAgICBpZiAodGhpcy53ZWJJZGVudGl0eVRva2VuKSB7XG4gICAgICBxcnlQYXJhbXMuc2V0KCdXZWJJZGVudGl0eVRva2VuJywgdGhpcy53ZWJJZGVudGl0eVRva2VuKVxuICAgIH1cblxuICAgIGlmICh0aGlzLmV4dGVybmFsSWQpIHtcbiAgICAgIHFyeVBhcmFtcy5zZXQoJ0V4dGVybmFsSWQnLCB0aGlzLmV4dGVybmFsSWQpXG4gICAgfVxuXG4gICAgY29uc3QgdXJsUGFyYW1zID0gcXJ5UGFyYW1zLnRvU3RyaW5nKClcbiAgICBjb25zdCBjb250ZW50U2hhMjU2ID0gdG9TaGEyNTYodXJsUGFyYW1zKVxuXG4gICAgY29uc3QgZGF0ZSA9IG5ldyBEYXRlKClcblxuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xuICAgICAgaG9zdG5hbWU6IGhvc3RWYWx1ZSxcbiAgICAgIHBvcnQ6IHBvcnRWYWx1ZSxcbiAgICAgIHBhdGg6ICcvJyxcbiAgICAgIHByb3RvY29sOiB0aGlzLnN0c0VuZHBvaW50LnByb3RvY29sLFxuICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyxcbiAgICAgICAgJ2NvbnRlbnQtbGVuZ3RoJzogdXJsUGFyYW1zLmxlbmd0aC50b1N0cmluZygpLFxuICAgICAgICBob3N0OiBob3N0VmFsdWUsXG4gICAgICAgICd4LWFtei1kYXRlJzogbWFrZURhdGVMb25nKGRhdGUpLFxuICAgICAgICAneC1hbXotY29udGVudC1zaGEyNTYnOiBjb250ZW50U2hhMjU2LFxuICAgICAgfSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuICAgICAgYWdlbnQ6IHRoaXMudHJhbnNwb3J0QWdlbnQsXG4gICAgfSBzYXRpc2ZpZXMgaHR0cC5SZXF1ZXN0T3B0aW9uc1xuXG4gICAgcmVxdWVzdE9wdGlvbnMuaGVhZGVycy5hdXRob3JpemF0aW9uID0gc2lnblY0QnlTZXJ2aWNlTmFtZShcbiAgICAgIHJlcXVlc3RPcHRpb25zLFxuICAgICAgdGhpcy5hY2Nlc3NLZXksXG4gICAgICB0aGlzLnNlY3JldEtleSxcbiAgICAgIHRoaXMucmVnaW9uLFxuICAgICAgZGF0ZSxcbiAgICAgIGNvbnRlbnRTaGEyNTYsXG4gICAgICAnc3RzJyxcbiAgICApXG5cbiAgICByZXR1cm4ge1xuICAgICAgcmVxdWVzdE9wdGlvbnMsXG4gICAgICByZXF1ZXN0RGF0YTogdXJsUGFyYW1zLFxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHBlcmZvcm1SZXF1ZXN0KCk6IFByb21pc2U8Q3JlZGVudGlhbFJlc3BvbnNlPiB7XG4gICAgY29uc3QgeyByZXF1ZXN0T3B0aW9ucywgcmVxdWVzdERhdGEgfSA9IHRoaXMuZ2V0UmVxdWVzdENvbmZpZygpXG5cbiAgICBjb25zdCByZXMgPSBhd2FpdCByZXF1ZXN0KHRoaXMudHJhbnNwb3J0LCByZXF1ZXN0T3B0aW9ucywgcmVxdWVzdERhdGEpXG5cbiAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVhZEFzU3RyaW5nKHJlcylcblxuICAgIHJldHVybiBwYXJzZVhtbChib2R5KVxuICB9XG5cbiAgcGFyc2VDcmVkZW50aWFscyhyZXNwT2JqOiBDcmVkZW50aWFsUmVzcG9uc2UpOiBDcmVkZW50aWFscyB7XG4gICAgaWYgKHJlc3BPYmouRXJyb3JSZXNwb25zZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgVW5hYmxlIHRvIG9idGFpbiBjcmVkZW50aWFsczogJHtyZXNwT2JqLkVycm9yUmVzcG9uc2U/LkVycm9yPy5Db2RlfSAke3Jlc3BPYmouRXJyb3JSZXNwb25zZT8uRXJyb3I/Lk1lc3NhZ2V9YCxcbiAgICAgICAgeyBjYXVzZTogcmVzcE9iaiB9LFxuICAgICAgKVxuICAgIH1cblxuICAgIGNvbnN0IHtcbiAgICAgIEFzc3VtZVJvbGVSZXNwb25zZToge1xuICAgICAgICBBc3N1bWVSb2xlUmVzdWx0OiB7XG4gICAgICAgICAgQ3JlZGVudGlhbHM6IHtcbiAgICAgICAgICAgIEFjY2Vzc0tleUlkOiBhY2Nlc3NLZXksXG4gICAgICAgICAgICBTZWNyZXRBY2Nlc3NLZXk6IHNlY3JldEtleSxcbiAgICAgICAgICAgIFNlc3Npb25Ub2tlbjogc2Vzc2lvblRva2VuLFxuICAgICAgICAgICAgRXhwaXJhdGlvbjogZXhwaXJlc0F0LFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0gPSByZXNwT2JqXG5cbiAgICB0aGlzLmFjY2Vzc0V4cGlyZXNBdCA9IGV4cGlyZXNBdFxuXG4gICAgcmV0dXJuIG5ldyBDcmVkZW50aWFscyh7IGFjY2Vzc0tleSwgc2VjcmV0S2V5LCBzZXNzaW9uVG9rZW4gfSlcbiAgfVxuXG4gIGFzeW5jIHJlZnJlc2hDcmVkZW50aWFscygpOiBQcm9taXNlPENyZWRlbnRpYWxzPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGFzc3VtZVJvbGVDcmVkZW50aWFscyA9IGF3YWl0IHRoaXMucGVyZm9ybVJlcXVlc3QoKVxuICAgICAgdGhpcy5fY3JlZGVudGlhbHMgPSB0aGlzLnBhcnNlQ3JlZGVudGlhbHMoYXNzdW1lUm9sZUNyZWRlbnRpYWxzKVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IENyZWRlbnRpYWxzOiAke2Vycn1gLCB7IGNhdXNlOiBlcnIgfSlcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5fY3JlZGVudGlhbHNcbiAgfVxuXG4gIGFzeW5jIGdldENyZWRlbnRpYWxzKCk6IFByb21pc2U8Q3JlZGVudGlhbHM+IHtcbiAgICBpZiAodGhpcy5fY3JlZGVudGlhbHMgJiYgIXRoaXMuaXNBYm91dFRvRXhwaXJlKCkpIHtcbiAgICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFsc1xuICAgIH1cblxuICAgIHRoaXMuX2NyZWRlbnRpYWxzID0gYXdhaXQgdGhpcy5yZWZyZXNoQ3JlZGVudGlhbHMoKVxuICAgIHJldHVybiB0aGlzLl9jcmVkZW50aWFsc1xuICB9XG5cbiAgaXNBYm91dFRvRXhwaXJlKCkge1xuICAgIGNvbnN0IGV4cGlyZXNBdCA9IG5ldyBEYXRlKHRoaXMuYWNjZXNzRXhwaXJlc0F0KVxuICAgIGNvbnN0IHByb3Zpc2lvbmFsRXhwaXJ5ID0gbmV3IERhdGUoRGF0ZS5ub3coKSArIDEwMDAgKiAxMCkgLy8gY2hlY2sgYmVmb3JlIDEwIHNlY29uZHMuXG4gICAgcmV0dXJuIHByb3Zpc2lvbmFsRXhwaXJ5ID4gZXhwaXJlc0F0XG4gIH1cbn1cblxuLy8gZGVwcmVjYXRlZCBkZWZhdWx0IGV4cG9ydCwgcGxlYXNlIHVzZSBuYW1lZCBleHBvcnRzLlxuLy8ga2VlcCBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eS5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZGVmYXVsdC1leHBvcnRcbmV4cG9ydCBkZWZhdWx0IEFzc3VtZVJvbGVQcm92aWRlclxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLElBQUk7QUFDaEIsT0FBTyxLQUFLQyxLQUFLO0FBQ2pCLFNBQVNDLEdBQUcsRUFBRUMsZUFBZTtBQUU3QixTQUFTQyxrQkFBa0IsUUFBUSwwQkFBeUI7QUFDNUQsU0FBU0MsV0FBVyxRQUFRLG1CQUFrQjtBQUM5QyxTQUFTQyxZQUFZLEVBQUVDLFFBQVEsRUFBRUMsUUFBUSxRQUFRLHVCQUFzQjtBQUN2RSxTQUFTQyxPQUFPLFFBQVEsd0JBQXVCO0FBQy9DLFNBQVNDLFlBQVksUUFBUSx5QkFBd0I7QUFFckQsU0FB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