UNPKG

rentdynamics

Version:

Package to help facilitate communicating with the Rent Dynamics API

374 lines 50.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decodePayload = exports.encodeSegment = exports.ProofOfPossession = exports.ClientHelpers = exports.ClientOptions = exports.BASE_URL = exports.Client = void 0; /** * Client is a convenience class for interacting with the Rent Dynamics services. * * Note if this class does not suit your needs, it may be wise to roll your own implementation using * the {@linkcode ClientHelpers} class. */ class Client { constructor(options) { this.helpers = new ClientHelpers(options); } /** * get wraps a request library to work with the Rent Dynamics API. * @param endpoint the path following the baseUrl. * @example get('/foo'); */ async get(endpoint) { const fullUrl = this.helpers.baseUrl + endpoint; const headers = await this.helpers.getHeaders(endpoint, undefined, this.authToken); return fetch(fullUrl.replace(/\|/g, '%7C'), { method: 'GET', headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' }) }); } /** * put wraps a request library to work with the Rent Dynamics API. * @param endpoint the path following the baseUrl. * @param payload a JSON serializable object. * @example put('/foo', { bar: 1 }); */ async put(endpoint, payload) { const fullUrl = this.helpers.baseUrl + endpoint; const headers = await this.helpers.getHeaders(endpoint, payload, this.authToken); return fetch(fullUrl, { method: 'PUT', headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' }), body: JSON.stringify(payload) }); } /** * post wraps a request library to work with the Rent Dynamics API. * @param endpoint the path following the baseUrl. * @param payload a JSON serializable object. * @example post('/foo', { bar: 1 }); */ async post(endpoint, payload) { const fullUrl = this.helpers.baseUrl + endpoint; const headers = await this.helpers.getHeaders(endpoint, payload, this.authToken); return fetch(fullUrl, { method: 'POST', headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' }), body: JSON.stringify(payload) }); } /** * delete wraps a request library to work with the Rent Dynamics API. * @param endpoint the path following the baseUrl. * @example delete('/foo/1'); */ async delete(endpoint) { const fullUrl = this.helpers.baseUrl + endpoint; const headers = await this.helpers.getHeaders(endpoint, undefined, this.authToken); return fetch(fullUrl, { method: 'DELETE', headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' }) }); } /** * login enables an instance of {@linkcode Client} to make authenticated requests to the Rent * Dynamics API. */ async login(username, password) { const _password = await this.helpers.encryptPassword(password); const endpoint = '/auth/login'; const result = await this.post(endpoint, { username, password: _password }); if (!result.ok) { return result; } const { token } = await result.json(); this.authToken = token; return result; } /** logout invalidates the users session generated by {@linkcode login}. */ async logout() { const endpoint = '/auth/logout'; const result = await this.post(endpoint, { authToken: this.authToken }); if (!result.ok) { return result; } this.authToken = undefined; return result; } } exports.Client = Client; /** BASE_URL is a collection of base urls for each dev/prod Rent Dynamics service. */ var BASE_URL; (function (BASE_URL) { BASE_URL["DEV_RD"] = "https://api.rentdynamics.dev"; BASE_URL["PROD_RD"] = "https://api.rentdynamics.com"; BASE_URL["DEV_RP"] = "https://api.rentplus.dev"; BASE_URL["PROD_RP"] = "https://api.rentplus.com"; })(BASE_URL = exports.BASE_URL || (exports.BASE_URL = {})); /** ClientOptions is consumed and updated by {@linkcode ClientHelpers}. */ class ClientOptions { constructor() { /** * baseUrl is the base request url. The default is the development rentdynamics api. A custom * string may be provided beyond the {@linkcode BASE_URL} options. */ this.baseUrl = BASE_URL.DEV_RD; /** * getEncoder is used to encode text. The encoder can be overridden as needed. For example in a * node environment. * @example * const options = new ClientOptions(); * options.getEncoder = async () => new (await import('util')).TextEncoder(); */ this.getEncoder = async () => new TextEncoder(); /** * getCryptographer is used for cryptography. The cryptographer can be overridden as needed. For * example in a node environment. * @example * const options = new ClientOptions(); * options.getCryptographer = async () => (await import('crypto')).subtle; */ this.getCryptographer = async () => crypto.subtle; } } exports.ClientOptions = ClientOptions; /** * ClientHelpers is a collection of utilities consumed by {@linkcode Client}. ClientHelpers can be * used to calculate headers in case a consumer wants to build their own API client. */ class ClientHelpers { constructor(options) { this.options = options; } /** * baseUrl is the base url used throughout the {@linkcode ClientHelpers} instance. It is initially * configured through {@linkcode ClientOptions}. */ get baseUrl() { return this.options.baseUrl; } /** * getTimestamp is used to calculate the timestamp header. This method is not likely to be called * on it's own. Instead, it is typically used to mock the current time. */ getTimestamp() { return Date.now(); } /** * getHeaders creates headers for the given params. If an auth token is included, this method will * generate an `Authorization` header. */ async getHeaders(endpoint, payload, authToken) { const headers = {}; if (this.options.apiKey && this.options.apiSecretKey) { if (!payload || !Object.keys(payload).length) { payload = undefined; } if (typeof payload !== 'undefined') { payload = JSON.stringify(this.formatPayload(payload)); } const timestamp = this.getTimestamp(); const nonce = await this.getNonce(timestamp, endpoint, payload); if (authToken) { headers.Authorization = 'TOKEN ' + authToken; } headers['x-rd-api-key'] = this.options.apiKey; headers['x-rd-api-nonce'] = nonce; headers['x-rd-timestamp'] = timestamp.toString(); return headers; } return headers; } /** formatPayload formats the payload for nonce calculation. */ formatPayload(payload) { let formattedPayload = {}; if (payload === undefined || payload === null) { formattedPayload = null; } else if (payload !== Object(payload)) { formattedPayload = payload; } else if (Array.isArray(payload)) { formattedPayload = []; payload.forEach((_, index) => { formattedPayload[index] = this.formatPayload(payload[index]); }); } else { Object.keys(payload) .sort() .forEach(k => { if (typeof payload[k] === 'object') { formattedPayload[k] = this.formatPayload(payload[k]); } else if (typeof payload[k] === 'string') { formattedPayload[k] = payload[k].replace(/ /g, ''); } else { formattedPayload[k] = payload[k]; } }); } return formattedPayload; } /** getNonce calculates the nonce for the given params. */ async getNonce(timestamp, url, payloadStr) { if (!this.options.apiSecretKey) return Promise.resolve(''); const encodedUrl = encodeURI(url) .replace(/%7[Cc]/g, '|') .replace(/%20/g, ' '); const nonceStr = typeof payloadStr !== 'undefined' ? timestamp + encodedUrl + payloadStr : timestamp + encodedUrl; const cryptographer = await this.options.getCryptographer(); const encoder = await this.options.getEncoder(); const key = encoder.encode(this.options.apiSecretKey); const data = encoder.encode(nonceStr); const algorithm = { name: 'HMAC', hash: 'SHA-1' }; const hmac = await cryptographer.importKey('raw', key, algorithm, false, ['sign']); const signed = await cryptographer.sign(algorithm.name, hmac, data); return _hexDigest(signed); } /** encryptPassword encrypts the password for login. */ async encryptPassword(password) { const cryptographer = await this.options.getCryptographer(); const encoder = await this.options.getEncoder(); const encodedPassword = encoder.encode(password); const digestedPassword = await cryptographer.digest('SHA-1', encodedPassword); return _hexDigest(digestedPassword); } } exports.ClientHelpers = ClientHelpers; const _hexDigest = (buf) => Array.from(new Uint8Array(buf)) .map(b => b.toString(16).padStart(2, '0')) .join(''); /** * ProofOfPossession encapsulates generic low level logic related to generating proof of possession * tokens and storing their associated key pair. */ class ProofOfPossession { /** * constructor creates an instance capable of generating proof of possession tokens. * * @param indexedDB window.indexedDB satisfies this interface. If you're not in a browser * environment, proof of possession tokens are likely not necessary. */ constructor(indexedDB) { this._encoder = new TextEncoder(); this._keyStoreName = 'keys'; this._keyKey = 1; this._dbName = 'rdb'; this._dbVersion = 2; this._algorithm = { name: 'RSA-PSS', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: 'SHA-256', saltLength: 32 }; this.indexedDB = indexedDB; } /** * generateProof generates a proof of possession JWT with the key pair. The key pair comes from a * get or create method. Where the key pair will continuously be used until evicted with * evictKeyPair. */ async generateProof() { const key = await this._getOrCreateKey(); const header = this._getHeader(); const payload = this._getPayload(); const encodedHeaderAndPayload = this._encoder.encode(`${header}.${payload}`); const signature = await crypto.subtle.sign(this._algorithm, key.privateKey, encodedHeaderAndPayload); const encodedSignature = (0, exports.encodeSegment)(String.fromCharCode(...new Uint8Array(signature))); return `${header}.${payload}.${encodedSignature}`; } /** * getPublicKey gets or creates a key pair and stores it. The public key component is then * extracted and returned in a base64 url encoded form. */ async getPublicKey() { const keyPair = await this._getOrCreateKey(); const pk = await crypto.subtle.exportKey('jwk', keyPair.publicKey); return (0, exports.encodeSegment)(JSON.stringify(pk)); } /** * evictKeyPair evicts the currently stored key pair. No operation is performed if the key pair * does not exist. */ evictKeyPair() { return new Promise((resolve, reject) => { const db = this.indexedDB.open(this._dbName, this._dbVersion); db.onsuccess = () => { const t = db.result.transaction(this._keyStoreName, 'readwrite'); const req = t.objectStore(this._keyStoreName).delete(this._keyKey); req.onsuccess = resolve; req.onerror = reject; }; db.onupgradeneeded = this._upgradeDb(db); db.onerror = reject; }); } _getHeader() { return (0, exports.encodeSegment)(JSON.stringify({ typ: 'dpop+jwt', alg: 'PS256' })); } _getPayload() { const d = Math.floor(new Date().getTime() / 1000); return (0, exports.encodeSegment)(JSON.stringify({ iat: d })); } _getOrCreateKey() { return new Promise((resolve, reject) => { const db = this.indexedDB.open(this._dbName, this._dbVersion); db.onsuccess = this._onDatabaseOpened(resolve, reject, db); db.onupgradeneeded = this._upgradeDb(db); db.onerror = reject; }); } _onDatabaseOpened(resolve, reject, r) { return () => { const t = r.result.transaction(this._keyStoreName, 'readwrite'); t.onerror = reject; const req = t.objectStore(this._keyStoreName).get(this._keyKey); req.onerror = reject; req.onsuccess = this._onKeyLookup(resolve, reject, r, req); }; } _onKeyLookup(resolve, reject, db, keyRequest) { return async () => { if (keyRequest.result) { return resolve(keyRequest.result); } const key = await this._createKey(); const t2 = db.result.transaction(this._keyStoreName, 'readwrite'); t2.onerror = reject; const req2 = t2.objectStore(this._keyStoreName).put(key, this._keyKey); req2.onsuccess = () => resolve(key); }; } _upgradeDb(r) { return () => { if (!r.result.objectStoreNames.contains(this._keyStoreName)) { r.result.createObjectStore(this._keyStoreName); } }; } _createKey() { return crypto.subtle.generateKey(this._algorithm, false, ['sign', 'verify']); } } exports.ProofOfPossession = ProofOfPossession; /** encodeSegment base64url encodes a string or JWT segment */ const encodeSegment = (segment) => { return btoa(segment).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); }; exports.encodeSegment = encodeSegment; /** decodePayload decodes the payload segment of a JWT to it's object representation */ const decodePayload = (token) => { const [, payload] = token.split('.'); const padding = payload.length % 4 === 0 ? '' : '='.repeat(4 - (payload.length % 4)); return JSON.parse(atob(`${payload.replace(/-/g, '+').replace(/_/g, '/')}${padding}`)); }; exports.decodePayload = decodePayload; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBSUE7Ozs7O0dBS0c7QUFDSCxNQUFhLE1BQU07SUFRakIsWUFBWSxPQUFzQjtRQUNoQyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLEdBQUcsQ0FBQyxRQUFnQjtRQUMvQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUM7UUFDaEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNuRixPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsRUFBRTtZQUMxQyxNQUFNLEVBQUUsS0FBSztZQUNiLE9BQU8sa0NBQ0YsT0FBTyxLQUNWLGNBQWMsRUFBRSxrQkFBa0IsR0FDbkM7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQWdCLEVBQUUsT0FBZ0I7UUFDakQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEdBQUcsUUFBUSxDQUFDO1FBQ2hELE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDakYsT0FBTyxLQUFLLENBQUMsT0FBTyxFQUFFO1lBQ3BCLE1BQU0sRUFBRSxLQUFLO1lBQ2IsT0FBTyxrQ0FDRixPQUFPLEtBQ1YsY0FBYyxFQUFFLGtCQUFrQixH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LENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNyRixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsR0FBRyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDeEYsQ0FBQyxDQUFDO0FBSlcsUUFBQSxhQUFhLGlCQUl4QiIsInNvdXJjZXNDb250ZW50IjpbIi8qKiBQYXlsb2FkIGlzIGtub3duIGFzIHRoZSBcImJvZHlcIiBvZiBhIFJlcXVlc3QuIFRoZSBwYXlsb2FkIG11c3QgYmUgSlNPTiBzZXJpYWxpemFibGUuICovXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxudHlwZSBQYXlsb2FkID0gYW55O1xuXG4vKipcbiAqIENsaWVudCBpcyBhIGNvbnZlbmllbmNlIGNsYXNzIGZvciBpbnRlcmFjdGluZyB3aXRoIHRoZSBSZW50IER5bmFtaWNzIHNlcnZpY2VzLlxuICpcbiAqIE5vdGUgaWYgdGhpcyBjbGFzcyBkb2VzIG5vdCBzdWl0IHlvdXIgbmVlZHMsIGl0IG1heSBiZSB3aXNlIHRvIHJvbGwgeW91ciBvd24gaW1wbGVtZW50YXRpb24gdXNpbmdcbiAqIHRoZSB7QGxpbmtjb2RlIENsaWVudEhlbHBlcnN9IGNsYXNzLlxuICovXG5leHBvcnQgY2xhc3MgQ2xpZW50IHtcbiAgLyoqXG4gICAqIGF1dGhUb2tlbiBpZiBkZWZpbmVkLCBhdXRoVG9rZW4gaXMgdXNlZCB0byBtYWtlIGF1dGhlbnRpY2F0ZWQgcmVxdWVzdHMuIE5vdGUgYXV0aFRva2VuIGlzIG5vdFxuICAgKiB0eXBpY2FsbHkgZ2V0L3NldCBtYW51YWxseSwgYnV0IGluc3RlYWQgaXMgbWFuYWdlZCB0aHJvdWdoIHRoZSBsb2dpbi9sb2dvdXQgbWV0aG9kcy5cbiAgICovXG4gIHB1YmxpYyBhdXRoVG9rZW46IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSByZWFkb25seSBoZWxwZXJzOiBDbGllbnRIZWxwZXJzO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IENsaWVudE9wdGlvbnMpIHtcbiAgICB0aGlzLmhlbHBlcnMgPSBuZXcgQ2xpZW50SGVscGVycyhvcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXQgd3JhcHMgYSByZXF1ZXN0IGxpYnJhcnkgdG8gd29yayB3aXRoIHRoZSBSZW50IER5bmFtaWNzIEFQSS5cbiAgICogQHBhcmFtIGVuZHBvaW50IHRoZSBwYXRoIGZvbGxvd2luZyB0aGUgYmFzZVVybC5cbiAgICogQGV4YW1wbGUgZ2V0KCcvZm9vJyk7XG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0KGVuZHBvaW50OiBzdHJpbmcpIHtcbiAgICBjb25zdCBmdWxsVXJsID0gdGhpcy5oZWxwZXJzLmJhc2VVcmwgKyBlbmRwb2ludDtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy5oZWxwZXJzLmdldEhlYWRlcnMoZW5kcG9pbnQsIHVuZGVmaW5lZCwgdGhpcy5hdXRoVG9rZW4pO1xuICAgIHJldHVybiBmZXRjaChmdWxsVXJsLnJlcGxhY2UoL1xcfC9nLCAnJTdDJyksIHtcbiAgICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIC4uLmhlYWRlcnMsXG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbidcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBwdXQgd3JhcHMgYSByZXF1ZXN0IGxpYnJhcnkgdG8gd29yayB3aXRoIHRoZSBSZW50IER5bmFtaWNzIEFQSS5cbiAgICogQHBhcmFtIGVuZHBvaW50IHRoZSBwYXRoIGZvbGxvd2luZyB0aGUgYmFzZVVybC5cbiAgICogQHBhcmFtIHBheWxvYWQgYSBKU09OIHNlcmlhbGl6YWJsZSBvYmplY3QuXG4gICAqIEBleGFtcGxlIHB1dCgnL2ZvbycsIHsgYmFyOiAxIH0pO1xuICAgKi9cbiAgcHVibGljIGFzeW5jIHB1dChlbmRwb2ludDogc3RyaW5nLCBwYXlsb2FkOiBQYXlsb2FkKSB7XG4gICAgY29uc3QgZnVsbFVybCA9IHRoaXMuaGVscGVycy5iYXNlVXJsICsgZW5kcG9pbnQ7XG4gICAgY29uc3QgaGVhZGVycyA9IGF3YWl0IHRoaXMuaGVscGVycy5nZXRIZWFkZXJzKGVuZHBvaW50LCBwYXlsb2FkLCB0aGlzLmF1dGhUb2tlbik7XG4gICAgcmV0dXJuIGZldGNoKGZ1bGxVcmwsIHtcbiAgICAgIG1ldGhvZDogJ1BVVCcsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIC4uLmhlYWRlcnMsXG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbidcbiAgICAgIH0sXG4gICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIHBvc3Qgd3JhcHMgYSByZXF1ZXN0IGxpYnJhcnkgdG8gd29yayB3aXRoIHRoZSBSZW50IER5bmFtaWNzIEFQSS5cbiAgICogQHBhcmFtIGVuZHBvaW50IHRoZSBwYXRoIGZvbGxvd2luZyB0aGUgYmFzZVVybC5cbiAgICogQHBhcmFtIHBheWxvYWQgYSBKU09OIHNlcmlhbGl6YWJsZSBvYmplY3QuXG4gICAqIEBleGFtcGxlIHBvc3QoJy9mb28nLCB7IGJhcjogMSB9KTtcbiAgICovXG4gIHB1YmxpYyBhc3luYyBwb3N0KGVuZHBvaW50OiBzdHJpbmcsIHBheWxvYWQ6IFBheWxvYWQpIHtcbiAgICBjb25zdCBmdWxsVXJsID0gdGhpcy5oZWxwZXJzLmJhc2VVcmwgKyBlbmRwb2ludDtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy5oZWxwZXJzLmdldEhlYWRlcnMoZW5kcG9pbnQsIHBheWxvYWQsIHRoaXMuYXV0aFRva2VuKTtcbiAgICByZXR1cm4gZmV0Y2goZnVsbFVybCwge1xuICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIC4uLmhlYWRlcnMsXG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbidcbiAgICAgIH0sXG4gICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIGRlbGV0ZSB3cmFwcyBhIHJlcXVlc3QgbGlicmFyeSB0byB3b3JrIHdpdGggdGhlIFJlbnQgRHluYW1pY3MgQVBJLlxuICAgKiBAcGFyYW0gZW5kcG9pbnQgdGhlIHBhdGggZm9sbG93aW5nIHRoZSBiYXNlVXJsLlxuICAgKiBAZXhhbXBsZSBkZWxldGUoJy9mb28vMScpO1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGRlbGV0ZShlbmRwb2ludDogc3RyaW5nKSB7XG4gICAgY29uc3QgZnVsbFVybCA9IHRoaXMuaGVscGVycy5iYXNlVXJsICsgZW5kcG9pbnQ7XG4gICAgY29uc3QgaGVhZGVycyA9IGF3YWl0IHRoaXMuaGVscGVycy5nZXRIZWFkZXJzKGVuZHBvaW50LCB1bmRlZmluZWQsIHRoaXMuYXV0aFRva2VuKTtcbiAgICByZXR1cm4gZmV0Y2goZnVsbFVybCwge1xuICAgICAgbWV0aG9kOiAnREVMRVRFJyxcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4uaGVhZGVycyxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIGxvZ2luIGVuYWJsZXMgYW4gaW5zdGFuY2Ugb2Yge0BsaW5rY29kZSBDbGllbnR9IHRvIG1ha2UgYXV0aGVudGljYXRlZCByZXF1ZXN0cyB0byB0aGUgUmVudFxuICAgKiBEeW5hbWljcyBBUEkuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgbG9naW4odXNlcm5hbWU6IHN0cmluZywgcGFzc3dvcmQ6IHN0cmluZykge1xuICAgIGNvbnN0IF9wYXNzd29yZCA9IGF3YWl0IHRoaXMuaGVscGVycy5lbmNyeXB0UGFzc3dvcmQocGFzc3dvcmQpO1xuICAgIGNvbnN0IGVuZHBvaW50ID0gJy9hdXRoL2xvZ2luJztcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnBvc3QoZW5kcG9pbnQsIHsgdXNlcm5hbWUsIHBhc3N3b3JkOiBfcGFzc3dvcmQgfSk7XG4gICAgaWYgKCFyZXN1bHQub2spIHtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIGNvbnN0IHsgdG9rZW4gfSA9IGF3YWl0IHJlc3VsdC5qc29uKCk7XG4gICAgdGhpcy5hdXRoVG9rZW4gPSB0b2tlbjtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqIGxvZ291dCBpbnZhbGlkYXRlcyB0aGUgdXNlcnMgc2Vzc2lvbiBnZW5lcmF0ZWQgYnkge0BsaW5rY29kZSBsb2dpbn0uICovXG4gIHB1YmxpYyBhc3luYyBsb2dvdXQoKSB7XG4gICAgY29uc3QgZW5kcG9pbnQgPSAnL2F1dGgvbG9nb3V0JztcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnBvc3QoZW5kcG9pbnQsIHsgYXV0aFRva2VuOiB0aGlzLmF1dGhUb2tlbiB9KTtcbiAgICBpZiAoIXJlc3VsdC5vaykge1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgdGhpcy5hdXRoVG9rZW4gPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufVxuXG4vKiogQkFTRV9VUkwgaXMgYSBjb2xsZWN0aW9uIG9mIGJhc2UgdXJscyBmb3IgZWFjaCBkZXYvcHJvZCBSZW50IER5bmFtaWNzIHNlcnZpY2UuICovXG5leHBvcnQgZW51bSBCQVNFX1VSTCB7XG4gIERFVl9SRCA9ICdodHRwczovL2FwaS5yZW50ZHluYW1pY3MuZGV2JyxcbiAgUFJPRF9SRCA9ICdodHRwczovL2FwaS5yZW50ZHluYW1pY3MuY29tJyxcbiAgREVWX1JQID0gJ2h0dHBzOi8vYXBpLnJlbnRwbHVzLmRldicsXG4gIFBST0RfUlAgPSAnaHR0cHM6Ly9hcGkucmVudHBsdXMuY29tJ1xufVxuXG5leHBvcnQgdHlwZSBSZEVuY29kZXIgPSBQaWNrPFRleHRFbmNvZGVyLCAnZW5jb2RlJz47XG5cbmV4cG9ydCB0eXBlIFJkQ3J5cHRvZ3JhcGhlciA9IFBpY2s8U3VidGxlQ3J5cHRvLCAnaW1wb3J0S2V5Jz4gJlxuICBQaWNrPFN1YnRsZUNyeXB0bywgJ3NpZ24nPiAmXG4gIFBpY2s8U3VidGxlQ3J5cHRvLCAnZGlnZXN0Jz47XG5cbi8qKiBDbGllbnRPcHRpb25zIGlzIGNvbnN1bWVkIGFuZCB1cGRhdGVkIGJ5IHtAbGlua2NvZGUgQ2xpZW50SGVscGVyc30uICovXG5leHBvcnQgY2xhc3MgQ2xpZW50T3B0aW9ucyB7XG4gIC8qKiBhcGlLZXkgaWYgZGVmaW5lZCBhcGlLZXkgaXMgdXNlZCB0byBjYWxjdWxhdGUgYXV0aCBoZWFkZXJzLiAqL1xuICBwdWJsaWMgYXBpS2V5OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIC8qKiBhcGlTZWNyZXRLZXkgaWYgZGVmaW5lZCBhcGlTZWNyZXRLZXkgaXMgdXNlZCB0byBjYWxjdWxhdGUgYXV0aCBoZWFkZXJzLiAgKi9cbiAgcHVibGljIGFwaVNlY3JldEtleTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAvKipcbiAgICogYmFzZVVybCBpcyB0aGUgYmFzZSByZXF1ZXN0IHVybC4gVGhlIGRlZmF1bHQgaXMgdGhlIGRldmVsb3BtZW50IHJlbnRkeW5hbWljcyBhcGkuIEEgY3VzdG9tXG4gICAqIHN0cmluZyBtYXkgYmUgcHJvdmlkZWQgYmV5b25kIHRoZSB7QGxpbmtjb2RlIEJBU0VfVVJMfSBvcHRpb25zLlxuICAgKi9cbiAgcHVibGljIGJhc2VVcmw6IEJBU0VfVVJMIHwgc3RyaW5nID0gQkFTRV9VUkwuREVWX1JEO1xuXG4gIC8qKlxuICAgKiBnZXRFbmNvZGVyIGlzIHVzZWQgdG8gZW5jb2RlIHRleHQuIFRoZSBlbmNvZGVyIGNhbiBiZSBvdmVycmlkZGVuIGFzIG5lZWRlZC4gRm9yIGV4YW1wbGUgaW4gYVxuICAgKiBub2RlIGVudmlyb25tZW50LlxuICAgKiBAZXhhbXBsZVxuICAgKiBjb25zdCBvcHRpb25zID0gbmV3IENsaWVudE9wdGlvbnMoKTtcbiAgICogb3B0aW9ucy5nZXRFbmNvZGVyID0gYXN5bmMgKCkgPT4gbmV3IChhd2FpdCBpbXBvcnQoJ3V0aWwnKSkuVGV4dEVuY29kZXIoKTtcbiAgICovXG4gIHB1YmxpYyBnZXRFbmNvZGVyID0gYXN5bmMgKCk6IFByb21pc2U8UmRFbmNvZGVyPiA9PiBuZXcgVGV4dEVuY29kZXIoKTtcblxuICAvKipcbiAgICogZ2V0Q3J5cHRvZ3JhcGhlciBpcyB1c2VkIGZvciBjcnlwdG9ncmFwaHkuIFRoZSBjcnlwdG9ncmFwaGVyIGNhbiBiZSBvdmVycmlkZGVuIGFzIG5lZWRlZC4gRm9yXG4gICAqIGV4YW1wbGUgaW4gYSBub2RlIGVudmlyb25tZW50LlxuICAgKiBAZXhhbXBsZVxuICAgKiBjb25zdCBvcHRpb25zID0gbmV3IENsaWVudE9wdGlvbnMoKTtcbiAgICogb3B0aW9ucy5nZXRDcnlwdG9ncmFwaGVyID0gYXN5bmMgKCkgPT4gKGF3YWl0IGltcG9ydCgnY3J5cHRvJykpLnN1YnRsZTtcbiAgICovXG4gIHB1YmxpYyBnZXRDcnlwdG9ncmFwaGVyID0gYXN5bmMgKCk6IFByb21pc2U8UmRDcnlwdG9ncmFwaGVyPiA9PiBjcnlwdG8uc3VidGxlO1xufVxuXG4vKipcbiAqIENsaWVudEhlbHBlcnMgaXMgYSBjb2xsZWN0aW9uIG9mIHV0aWxpdGllcyBjb25zdW1lZCBieSB7QGxpbmtjb2RlIENsaWVudH0uIENsaWVudEhlbHBlcnMgY2FuIGJlXG4gKiB1c2VkIHRvIGNhbGN1bGF0ZSBoZWFkZXJzIGluIGNhc2UgYSBjb25zdW1lciB3YW50cyB0byBidWlsZCB0aGVpciBvd24gQVBJIGNsaWVudC5cbiAqL1xuZXhwb3J0IGNsYXNzIENsaWVudEhlbHBlcnMge1xuICBwcml2YXRlIG9wdGlvbnM6IENsaWVudE9wdGlvbnM7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogQ2xpZW50T3B0aW9ucykge1xuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnM7XG4gIH1cblxuICAvKipcbiAgICogYmFzZVVybCBpcyB0aGUgYmFzZSB1cmwgdXNlZCB0aHJvdWdob3V0IHRoZSB7QGxpbmtjb2RlIENsaWVudEhlbHBlcnN9IGluc3RhbmNlLiBJdCBpcyBpbml0aWFsbHlcbiAgICogY29uZmlndXJlZCB0aHJvdWdoIHtAbGlua2NvZGUgQ2xpZW50T3B0aW9uc30uXG4gICAqL1xuICBnZXQgYmFzZVVybCgpIHtcbiAgICByZXR1cm4gdGhpcy5vcHRpb25zLmJhc2VVcmw7XG4gIH1cblxuICAvKipcbiAgICogZ2V0VGltZXN0YW1wIGlzIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSB0aW1lc3RhbXAgaGVhZGVyLiBUaGlzIG1ldGhvZCBpcyBub3QgbGlrZWx5IHRvIGJlIGNhbGxlZFxuICAgKiBvbiBpdCdzIG93bi4gSW5zdGVhZCwgaXQgaXMgdHlwaWNhbGx5IHVzZWQgdG8gbW9jayB0aGUgY3VycmVudCB0aW1lLlxuICAgKi9cbiAgcHVibGljIGdldFRpbWVzdGFtcCgpIHtcbiAgICByZXR1cm4gRGF0ZS5ub3coKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXRIZWFkZXJzIGNyZWF0ZXMgaGVhZGVycyBmb3IgdGhlIGdpdmVuIHBhcmFtcy4gSWYgYW4gYXV0aCB0b2tlbiBpcyBpbmNsdWRlZCwgdGhpcyBtZXRob2Qgd2lsbFxuICAgKiBnZW5lcmF0ZSBhbiBgQXV0aG9yaXphdGlvbmAgaGVhZGVyLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGdldEhlYWRlcnMoXG4gICAgZW5kcG9pbnQ6IHN0cmluZyxcbiAgICBwYXlsb2FkPzogUGF5bG9hZCB8IHVuZGVmaW5lZCxcbiAgICBhdXRoVG9rZW4/OiBzdHJpbmcgfCB1bmRlZmluZWRcbiAgKSB7XG4gICAgY29uc3QgaGVhZGVyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuICAgIGlmICh0aGlzLm9wdGlvbnMuYXBpS2V5ICYmIHRoaXMub3B0aW9ucy5hcGlTZWNyZXRLZXkpIHtcbiAgICAgIGlmICghcGF5bG9hZCB8fCAhT2JqZWN0LmtleXMocGF5bG9hZCkubGVuZ3RoKSB7XG4gICAgICAgIHBheWxvYWQgPSB1bmRlZmluZWQ7XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIHBheWxvYWQgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIHBheWxvYWQgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmZvcm1hdFBheWxvYWQocGF5bG9hZCkpO1xuICAgICAgfVxuICAgICAgY29uc3QgdGltZXN0YW1wID0gdGhpcy5nZXRUaW1lc3RhbXAoKTtcbiAgICAgIGNvbnN0IG5vbmNlID0gYXdhaXQgdGhpcy5nZXROb25jZSh0aW1lc3RhbXAsIGVuZHBvaW50LCBwYXlsb2FkKTtcbiAgICAgIGlmIChhdXRoVG9rZW4pIHtcbiAgICAgICAgaGVhZGVycy5BdXRob3JpemF0aW9uID0gJ1RPS0VOICcgKyBhdXRoVG9rZW47XG4gICAgICB9XG4gICAgICBoZWFkZXJzWyd4LXJkLWFwaS1rZXknXSA9IHRoaXMub3B0aW9ucy5hcGlLZXk7XG4gICAgICBoZWFkZXJzWyd4LXJkLWFwaS1ub25jZSddID0gbm9uY2U7XG4gICAgICBoZWFkZXJzWyd4LXJkLXRpbWVzdGFtcCddID0gdGltZXN0YW1wLnRvU3RyaW5nKCk7XG4gICAgICByZXR1cm4gaGVhZGVycztcbiAgICB9XG4gICAgcmV0dXJuIGhlYWRlcnM7XG4gIH1cblxuICAvKiogZm9ybWF0UGF5bG9hZCBmb3JtYXRzIHRoZSBwYXlsb2FkIGZvciBub25jZSBjYWxjdWxhdGlvbi4gKi9cbiAgcHVibGljIGZvcm1hdFBheWxvYWQocGF5bG9hZDogUGF5bG9hZCk6IFBheWxvYWQge1xuICAgIGxldCBmb3JtYXR0ZWRQYXlsb2FkOiBQYXlsb2FkID0ge307XG4gICAgaWYgKHBheWxvYWQgPT09IHVuZGVmaW5lZCB8fCBwYXlsb2FkID09PSBudWxsKSB7XG4gICAgICBmb3JtYXR0ZWRQYXlsb2FkID0gbnVsbDtcbiAgICB9IGVsc2UgaWYgKHBheWxvYWQgIT09IE9iamVjdChwYXlsb2FkKSkge1xuICAgICAgZm9ybWF0dGVkUGF5bG9hZCA9IHBheWxvYWQ7XG4gICAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KHBheWxvYWQpKSB7XG4gICAgICBmb3JtYXR0ZWRQYXlsb2FkID0gW107XG4gICAgICBwYXlsb2FkLmZvckVhY2goKF8sIGluZGV4KSA9PiB7XG4gICAgICAgIGZvcm1hdHRlZFBheWxvYWRbaW5kZXhdID0gdGhpcy5mb3JtYXRQYXlsb2FkKHBheWxvYWRbaW5kZXhdKTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBPYmplY3Qua2V5cyhwYXlsb2FkKVxuICAgICAgICAuc29ydCgpXG4gICAgICAgIC5mb3JFYWNoKGsgPT4ge1xuICAgICAgICAgIGlmICh0eXBlb2YgcGF5bG9hZFtrXSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIGZvcm1hdHRlZFBheWxvYWRba10gPSB0aGlzLmZvcm1hdFBheWxvYWQocGF5bG9hZFtrXSk7XG4gICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgcGF5bG9hZFtrXSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIGZvcm1hdHRlZFBheWxvYWRba10gPSBwYXlsb2FkW2tdLnJlcGxhY2UoLyAvZywgJycpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmb3JtYXR0ZWRQYXlsb2FkW2tdID0gcGF5bG9hZFtrXTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gZm9ybWF0dGVkUGF5bG9hZDtcbiAgfVxuXG4gIC8qKiBnZXROb25jZSBjYWxjdWxhdGVzIHRoZSBub25jZSBmb3IgdGhlIGdpdmVuIHBhcmFtcy4gKi9cbiAgcHVibGljIGFzeW5jIGdldE5vbmNlKHRpbWVzdGFtcDogbnVtYmVyLCB1cmw6IHN0cmluZywgcGF5bG9hZFN0cj86IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgaWYgKCF0aGlzLm9wdGlvbnMuYXBpU2VjcmV0S2V5KSByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCcnKTtcbiAgICBjb25zdCBlbmNvZGVkVXJsID0gZW5jb2RlVVJJKHVybClcbiAgICAgIC5yZXBsYWNlKC8lN1tDY10vZywgJ3wnKVxuICAgICAgLnJlcGxhY2UoLyUyMC9nLCAnICcpO1xuICAgIGNvbnN0IG5vbmNlU3RyID1cbiAgICAgIHR5cGVvZiBwYXlsb2FkU3RyICE9PSAndW5kZWZpbmVkJ1xuICAgICAgICA/IHRpbWVzdGFtcCArIGVuY29kZWRVcmwgKyBwYXlsb2FkU3RyXG4gICAgICAgIDogdGltZXN0YW1wICsgZW5jb2RlZFVybDtcbiAgICBjb25zdCBjcnlwdG9ncmFwaGVyID0gYXdhaXQgdGhpcy5vcHRpb25zLmdldENyeXB0b2dyYXBoZXIoKTtcbiAgICBjb25zdCBlbmNvZGVyID0gYXdhaXQgdGhpcy5vcHRpb25zLmdldEVuY29kZXIoKTtcbiAgICBjb25zdCBrZXkgPSBlbmNvZGVyLmVuY29kZSh0aGlzLm9wdGlvbnMuYXBpU2VjcmV0S2V5KTtcbiAgICBjb25zdCBkYXRhID0gZW5jb2Rlci5lbmNvZGUobm9uY2VTdHIpO1xuICAgIGNvbnN0IGFsZ29yaXRobSA9IHsgbmFtZTogJ0hNQUMnLCBoYXNoOiAnU0hBLTEnIH07XG4gICAgY29uc3QgaG1hYyA9IGF3YWl0IGNyeXB0b2dyYXBoZXIuaW1wb3J0S2V5KCdyYXcnLCBrZXksIGFsZ29yaXRobSwgZmFsc2UsIFsnc2lnbiddKTtcbiAgICBjb25zdCBzaWduZWQgPSBhd2FpdCBjcnlwdG9ncmFwaGVyLnNpZ24oYWxnb3JpdGhtLm5hbWUsIGhtYWMsIGRhdGEpO1xuICAgIHJldHVybiBfaGV4RGlnZXN0KHNpZ25lZCk7XG4gIH1cblxuICAvKiogZW5jcnlwdFBhc3N3b3JkIGVuY3J5cHRzIHRoZSBwYXNzd29yZCBmb3IgbG9naW4uICovXG4gIHB1YmxpYyBhc3luYyBlbmNyeXB0UGFzc3dvcmQocGFzc3dvcmQ6IHN0cmluZykge1xuICAgIGNvbnN0IGNyeXB0b2dyYXBoZXIgPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0Q3J5cHRvZ3JhcGhlcigpO1xuICAgIGNvbnN0IGVuY29kZXIgPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0RW5jb2RlcigpO1xuICAgIGNvbnN0IGVuY29kZWRQYXNzd29yZCA9IGVuY29kZXIuZW5jb2RlKHBhc3N3b3JkKTtcbiAgICBjb25zdCBkaWdlc3RlZFBhc3N3b3JkID0gYXdhaXQgY3J5cHRvZ3JhcGhlci5kaWdlc3QoJ1NIQS0xJywgZW5jb2RlZFBhc3N3b3JkKTtcbiAgICByZXR1cm4gX2hleERpZ2VzdChkaWdlc3RlZFBhc3N3b3JkKTtcbiAgfVxufVxuXG5jb25zdCBfaGV4RGlnZXN0ID0gKGJ1ZjogQXJyYXlCdWZmZXIpOiBzdHJpbmcgPT5cbiAgQXJyYXkuZnJvbShuZXcgVWludDhBcnJheShidWYpKVxuICAgIC5tYXAoYiA9PiBiLnRvU3RyaW5nKDE2KS5wYWRTdGFydCgyLCAnMCcpKVxuICAgIC5qb2luKCcnKTtcblxuLyoqXG4gKiBQcm9vZk9mUG9zc2Vzc2lvbiBlbmNhcHN1bGF0ZXMgZ2VuZXJpYyBsb3cgbGV2ZWwgbG9naWMgcmVsYXRlZCB0byBnZW5lcmF0aW5nIHByb29mIG9mIHBvc3Nlc3Npb25cbiAqIHRva2VucyBhbmQgc3RvcmluZyB0aGVpciBhc3NvY2lhdGVkIGtleSBwYWlyLlxuICovXG5leHBvcnQgY2xhc3MgUHJvb2ZPZlBvc3Nlc3Npb24ge1xuICBwcml2YXRlIHJlYWRvbmx5IF9lbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2tleVN0b3JlTmFtZSA9ICdrZXlzJztcbiAgcHJpdmF0ZSByZWFkb25seSBfa2V5S2V5ID0gMTtcbiAgcHJpdmF0ZSByZWFkb25seSBfZGJOYW1lID0gJ3JkYic7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2RiVmVyc2lvbiA9IDI7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2FsZ29yaXRobSA9IHtcbiAgICBuYW1lOiAnUlNBLVBTUycsXG4gICAgbW9kdWx1c0xlbmd0aDogMjA0OCxcbiAgICBwdWJsaWNFeHBvbmVudDogbmV3IFVpbnQ4QXJyYXkoWzB4MDEsIDB4MDAsIDB4MDFdKSwgLy8gNjU1MzdcbiAgICBoYXNoOiAnU0hBLTI1NicsXG4gICAgc2FsdExlbmd0aDogMzJcbiAgfTtcbiAgcHJpdmF0ZSByZWFkb25seSBpbmRleGVkREI6IElEQkZhY3Rvcnk7XG5cbiAgLyoqXG4gICAqIGNvbnN0cnVjdG9yIGNyZWF0ZXMgYW4gaW5zdGFuY2UgY2FwYWJsZSBvZiBnZW5lcmF0aW5nIHByb29mIG9mIHBvc3Nlc3Npb24gdG9rZW5zLlxuICAgKlxuICAgKiBAcGFyYW0gaW5kZXhlZERCIHdpbmRvdy5pbmRleGVkREIgc2F0aXNmaWVzIHRoaXMgaW50ZXJmYWNlLiBJZiB5b3UncmUgbm90IGluIGEgYnJvd3NlclxuICAgKiBlbnZpcm9ubWVudCwgcHJvb2Ygb2YgcG9zc2Vzc2lvbiB0b2tlbnMgYXJlIGxpa2VseSBub3QgbmVjZXNzYXJ5LlxuICAgKi9cbiAgY29uc3RydWN0b3IoaW5kZXhlZERCOiBJREJGYWN0b3J5KSB7XG4gICAgdGhpcy5pbmRleGVkREIgPSBpbmRleGVkREI7XG4gIH1cblxuICAvKipcbiAgICogZ2VuZXJhdGVQcm9vZiBnZW5lcmF0ZXMgYSBwcm9vZiBvZiBwb3NzZXNzaW9uIEpXVCB3aXRoIHRoZSBrZXkgcGFpci4gVGhlIGtleSBwYWlyIGNvbWVzIGZyb20gYVxuICAgKiBnZXQgb3IgY3JlYXRlIG1ldGhvZC4gV2hlcmUgdGhlIGtleSBwYWlyIHdpbGwgY29udGludW91c2x5IGJlIHVzZWQgdW50aWwgZXZpY3RlZCB3aXRoXG4gICAqIGV2aWN0S2V5UGFpci5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBnZW5lcmF0ZVByb29mKCkge1xuICAgIGNvbnN0IGtleSA9IGF3YWl0IHRoaXMuX2dldE9yQ3JlYXRlS2V5KCk7XG4gICAgY29uc3QgaGVhZGVyID0gdGhpcy5fZ2V0SGVhZGVyKCk7XG4gICAgY29uc3QgcGF5bG9hZCA9IHRoaXMuX2dldFBheWxvYWQoKTtcbiAgICBjb25zdCBlbmNvZGVkSGVhZGVyQW5kUGF5bG9hZCA9IHRoaXMuX2VuY29kZXIuZW5jb2RlKGAke2hlYWRlcn0uJHtwYXlsb2FkfWApO1xuICAgIGNvbnN0IHNpZ25hdHVyZSA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuc2lnbihcbiAgICAgIHRoaXMuX2FsZ29yaXRobSxcbiAgICAgIGtleS5wcml2YXRlS2V5LFxuICAgICAgZW5jb2RlZEhlYWRlckFuZFBheWxvYWRcbiAgICApO1xuICAgIGNvbnN0IGVuY29kZWRTaWduYXR1cmUgPSBlbmNvZGVTZWdtZW50KFN0cmluZy5mcm9tQ2hhckNvZGUoLi4ubmV3IFVpbnQ4QXJyYXkoc2lnbmF0dXJlKSkpO1xuICAgIHJldHVybiBgJHtoZWFkZXJ9LiR7cGF5bG9hZH0uJHtlbmNvZGVkU2lnbmF0dXJlfWA7XG4gIH1cblxuICAvKipcbiAgICogZ2V0UHVibGljS2V5IGdldHMgb3IgY3JlYXRlcyBhIGtleSBwYWlyIGFuZCBzdG9yZXMgaXQuIFRoZSBwdWJsaWMga2V5IGNvbXBvbmVudCBpcyB0aGVuXG4gICAqIGV4dHJhY3RlZCBhbmQgcmV0dXJuZWQgaW4gYSBiYXNlNjQgdXJsIGVuY29kZWQgZm9ybS5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBnZXRQdWJsaWNLZXkoKSB7XG4gICAgY29uc3Qga2V5UGFpciA9IGF3YWl0IHRoaXMuX2dldE9yQ3JlYXRlS2V5KCk7XG4gICAgY29uc3QgcGsgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmV4cG9ydEtleSgnandrJywga2V5UGFpci5wdWJsaWNLZXkpO1xuICAgIHJldHVybiBlbmNvZGVTZWdtZW50KEpTT04uc3RyaW5naWZ5KHBrKSk7XG4gIH1cblxuICAvKipcbiAgICogZXZpY3RLZXlQYWlyIGV2aWN0cyB0aGUgY3VycmVudGx5IHN0b3JlZCBrZXkgcGFpci4gTm8gb3BlcmF0aW9uIGlzIHBlcmZvcm1lZCBpZiB0aGUga2V5IHBhaXJcbiAgICogZG9lcyBub3QgZXhpc3QuXG4gICAqL1xuICBwdWJsaWMgZXZpY3RLZXlQYWlyKCkge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBkYiA9IHRoaXMuaW5kZXhlZERCLm9wZW4odGhpcy5fZGJOYW1lLCB0aGlzLl9kYlZlcnNpb24pO1xuICAgICAgZGIub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICBjb25zdCB0ID0gZGIucmVzdWx0LnRyYW5zYWN0aW9uKHRoaXMuX2tleVN0b3JlTmFtZSwgJ3JlYWR3cml0ZScpO1xuICAgICAgICBjb25zdCByZXEgPSB0Lm9iamVjdFN0b3JlKHRoaXMuX2tleVN0b3JlTmFtZSkuZGVsZXRlKHRoaXMuX2tleUtleSk7XG4gICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSByZXNvbHZlO1xuICAgICAgICByZXEub25lcnJvciA9IHJlamVjdDtcbiAgICAgIH07XG4gICAgICBkYi5vbnVwZ3JhZGVuZWVkZWQgPSB0aGlzLl91cGdyYWRlRGIoZGIpO1xuICAgICAgZGIub25lcnJvciA9IHJlamVjdDtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgX2dldEhlYWRlcigpIHtcbiAgICByZXR1cm4gZW5jb2RlU2VnbWVudChcbiAgICAgIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgdHlwOiAnZHBvcCtqd3QnLFxuICAgICAgICBhbGc6ICdQUzI1NidcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgX2dldFBheWxvYWQoKSB7XG4gICAgY29uc3QgZCA9IE1hdGguZmxvb3IobmV3IERhdGUoKS5nZXRUaW1lKCkgLyAxMDAwKTtcbiAgICByZXR1cm4gZW5jb2RlU2VnbWVudChcbiAgICAgIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgaWF0OiBkXG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIF9nZXRPckNyZWF0ZUtleSgpOiBQcm9taXNlPENyeXB0b0tleVBhaXI+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgZGIgPSB0aGlzLmluZGV4ZWREQi5vcGVuKHRoaXMuX2RiTmFtZSwgdGhpcy5fZGJWZXJzaW9uKTtcbiAgICAgIGRiLm9uc3VjY2VzcyA9IHRoaXMuX29uRGF0YWJhc2VPcGVuZWQocmVzb2x2ZSwgcmVqZWN0LCBkYik7XG4gICAgICBkYi5vbnVwZ3JhZGVuZWVkZWQgPSB0aGlzLl91cGdyYWRlRGIoZGIpO1xuICAgICAgZGIub25lcnJvciA9IHJlamVjdDtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgX29uRGF0YWJhc2VPcGVuZWQoXG4gICAgcmVzb2x2ZTogKHZhbHVlOiBDcnlwdG9LZXlQYWlyKSA9PiB2b2lkLFxuICAgIHJlamVjdDogKHJlYXNvbj86IHVua25vd24pID0+IHZvaWQsXG4gICAgcjogSURCT3BlbkRCUmVxdWVzdFxuICApIHtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgY29uc3QgdCA9IHIucmVzdWx0LnRyYW5zYWN0aW9uKHRoaXMuX2tleVN0b3JlTmFtZSwgJ3JlYWR3cml0ZScpO1xuICAgICAgdC5vbmVycm9yID0gcmVqZWN0O1xuICAgICAgY29uc3QgcmVxID0gdC5vYmplY3RTdG9yZSh0aGlzLl9rZXlTdG9yZU5hbWUpLmdldCh0aGlzLl9rZXlLZXkpO1xuICAgICAgcmVxLm9uZXJyb3IgPSByZWplY3Q7XG4gICAgICByZXEub25zdWNjZXNzID0gdGhpcy5fb25LZXlMb29rdXAocmVzb2x2ZSwgcmVqZWN0LCByLCByZXEpO1xuICAgIH07XG4gIH1cblxuICBwcml2YXRlIF9vbktleUxvb2t1cChcbiAgICByZXNvbHZlOiAodmFsdWU6IENyeXB0b0tleVBhaXIpID0+IHZvaWQsXG4gICAgcmVqZWN0OiAocmVhc29uPzogdW5rbm93bikgPT4gdm9pZCxcbiAgICBkYjogSURCT3BlbkRCUmVxdWVzdCxcbiAgICBrZXlSZXF1ZXN0OiBJREJSZXF1ZXN0XG4gICkge1xuICAgIHJldHVybiBhc3luYyAoKSA9PiB7XG4gICAgICBpZiAoa2V5UmVxdWVzdC5yZXN1bHQpIHtcbiAgICAgICAgcmV0dXJuIHJlc29sdmUoa2V5UmVxdWVzdC5yZXN1bHQpO1xuICAgICAgfVxuICAgICAgY29uc3Qga2V5ID0gYXdhaXQgdGhpcy5fY3JlYXRlS2V5KCk7XG4gICAgICBjb25zdCB0MiA9IGRiLnJlc3VsdC50cmFuc2FjdGlvbih0aGlzLl9rZXlTdG9yZU5hbWUsICdyZWFkd3JpdGUnKTtcbiAgICAgIHQyLm9uZXJyb3IgPSByZWplY3Q7XG4gICAgICBjb25zdCByZXEyID0gdDIub2JqZWN0U3RvcmUodGhpcy5fa2V5U3RvcmVOYW1lKS5wdXQoa2V5LCB0aGlzLl9rZXlLZXkpO1xuICAgICAgcmVxMi5vbnN1Y2Nlc3MgPSAoKSA9PiByZXNvbHZlKGtleSk7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgX3VwZ3JhZGVEYihyOiBJREJPcGVuREJSZXF1ZXN0KSB7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGlmICghci5yZXN1bHQub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucyh0aGlzLl9rZXlTdG9yZU5hbWUpKSB7XG4gICAgICAgIHIucmVzdWx0LmNyZWF0ZU9iamVjdFN0b3JlKHRoaXMuX2tleVN0b3JlTmFtZSk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgX2NyZWF0ZUtleSgpIHtcbiAgICByZXR1cm4gY3J5cHRvLnN1YnRsZS5nZW5lcmF0ZUtleSh0aGlzLl9hbGdvcml0aG0sIGZhbHNlLCBbJ3NpZ24nLCAndmVyaWZ5J10pO1xuICB9XG59XG5cbi8qKiBlbmNvZGVTZWdtZW50IGJhc2U2NHVybCBlbmNvZGVzIGEgc3RyaW5nIG9yIEpXVCBzZWdtZW50ICovXG5leHBvcnQgY29uc3QgZW5jb2RlU2VnbWVudCA9IChzZWdtZW50