UNPKG

rentdynamics

Version:

Package to help facilitate communicating with the Rent Dynamics API

365 lines 50.1 kB
/** * 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. */ export 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; } } /** BASE_URL is a collection of base urls for each dev/prod Rent Dynamics service. */ export 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 || (BASE_URL = {})); /** ClientOptions is consumed and updated by {@linkcode ClientHelpers}. */ export 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; } } /** * 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. */ export 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); } } 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. */ export 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 = 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 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 encodeSegment(JSON.stringify({ typ: 'dpop+jwt', alg: 'PS256' })); } _getPayload() { const d = Math.floor(new Date().getTime() / 1000); return 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']); } } /** encodeSegment base64url encodes a string or JWT segment */ export const encodeSegment = (segment) => { return btoa(segment).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); }; /** decodePayload decodes the payload segment of a JWT to it's object representation */ export 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}`)); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBSUE7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sTUFBTTtJQVFqQixZQUFZLE9BQXNCO1FBQ2hDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQWdCO1FBQy9CLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQztRQUNoRCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ25GLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxFQUFFO1lBQzFDLE1BQU0sRUFBRSxLQUFLO1lBQ2IsT0FBTyxrQ0FDRixPQUFPLEtBQ1YsY0FBYyxFQUFFLGtCQUFrQixHQUNuQztTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBZ0IsRUFBRSxPQUFnQjtRQUNqRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUM7UUFDaEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNqRixPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUU7WUFDcEIsTUFBTSxFQUFFLEtBQUs7WUFDYixPQUFPLGtDQUNGLE9BQU8sS0FDVixjQUFjLEVBQUUsa0JBQWtC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QUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNyRixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsR0FBRyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDeEYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqIFBheWxvYWQgaXMga25vd24gYXMgdGhlIFwiYm9keVwiIG9mIGEgUmVxdWVzdC4gVGhlIHBheWxvYWQgbXVzdCBiZSBKU09OIHNlcmlhbGl6YWJsZS4gKi9cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG50eXBlIFBheWxvYWQgPSBhbnk7XG5cbi8qKlxuICogQ2xpZW50IGlzIGEgY29udmVuaWVuY2UgY2xhc3MgZm9yIGludGVyYWN0aW5nIHdpdGggdGhlIFJlbnQgRHluYW1pY3Mgc2VydmljZXMuXG4gKlxuICogTm90ZSBpZiB0aGlzIGNsYXNzIGRvZXMgbm90IHN1aXQgeW91ciBuZWVkcywgaXQgbWF5IGJlIHdpc2UgdG8gcm9sbCB5b3VyIG93biBpbXBsZW1lbnRhdGlvbiB1c2luZ1xuICogdGhlIHtAbGlua2NvZGUgQ2xpZW50SGVscGVyc30gY2xhc3MuXG4gKi9cbmV4cG9ydCBjbGFzcyBDbGllbnQge1xuICAvKipcbiAgICogYXV0aFRva2VuIGlmIGRlZmluZWQsIGF1dGhUb2tlbiBpcyB1c2VkIHRvIG1ha2UgYXV0aGVudGljYXRlZCByZXF1ZXN0cy4gTm90ZSBhdXRoVG9rZW4gaXMgbm90XG4gICAqIHR5cGljYWxseSBnZXQvc2V0IG1hbnVhbGx5LCBidXQgaW5zdGVhZCBpcyBtYW5hZ2VkIHRocm91Z2ggdGhlIGxvZ2luL2xvZ291dCBtZXRob2RzLlxuICAgKi9cbiAgcHVibGljIGF1dGhUb2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBwcml2YXRlIHJlYWRvbmx5IGhlbHBlcnM6IENsaWVudEhlbHBlcnM7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogQ2xpZW50T3B0aW9ucykge1xuICAgIHRoaXMuaGVscGVycyA9IG5ldyBDbGllbnRIZWxwZXJzKG9wdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldCB3cmFwcyBhIHJlcXVlc3QgbGlicmFyeSB0byB3b3JrIHdpdGggdGhlIFJlbnQgRHluYW1pY3MgQVBJLlxuICAgKiBAcGFyYW0gZW5kcG9pbnQgdGhlIHBhdGggZm9sbG93aW5nIHRoZSBiYXNlVXJsLlxuICAgKiBAZXhhbXBsZSBnZXQoJy9mb28nKTtcbiAgICovXG4gIHB1YmxpYyBhc3luYyBnZXQoZW5kcG9pbnQ6IHN0cmluZykge1xuICAgIGNvbnN0IGZ1bGxVcmwgPSB0aGlzLmhlbHBlcnMuYmFzZVVybCArIGVuZHBvaW50O1xuICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLmhlbHBlcnMuZ2V0SGVhZGVycyhlbmRwb2ludCwgdW5kZWZpbmVkLCB0aGlzLmF1dGhUb2tlbik7XG4gICAgcmV0dXJuIGZldGNoKGZ1bGxVcmwucmVwbGFjZSgvXFx8L2csICclN0MnKSwge1xuICAgICAgbWV0aG9kOiAnR0VUJyxcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4uaGVhZGVycyxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIHB1dCB3cmFwcyBhIHJlcXVlc3QgbGlicmFyeSB0byB3b3JrIHdpdGggdGhlIFJlbnQgRHluYW1pY3MgQVBJLlxuICAgKiBAcGFyYW0gZW5kcG9pbnQgdGhlIHBhdGggZm9sbG93aW5nIHRoZSBiYXNlVXJsLlxuICAgKiBAcGFyYW0gcGF5bG9hZCBhIEpTT04gc2VyaWFsaXphYmxlIG9iamVjdC5cbiAgICogQGV4YW1wbGUgcHV0KCcvZm9vJywgeyBiYXI6IDEgfSk7XG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcHV0KGVuZHBvaW50OiBzdHJpbmcsIHBheWxvYWQ6IFBheWxvYWQpIHtcbiAgICBjb25zdCBmdWxsVXJsID0gdGhpcy5oZWxwZXJzLmJhc2VVcmwgKyBlbmRwb2ludDtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy5oZWxwZXJzLmdldEhlYWRlcnMoZW5kcG9pbnQsIHBheWxvYWQsIHRoaXMuYXV0aFRva2VuKTtcbiAgICByZXR1cm4gZmV0Y2goZnVsbFVybCwge1xuICAgICAgbWV0aG9kOiAnUFVUJyxcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4uaGVhZGVycyxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgfSxcbiAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogcG9zdCB3cmFwcyBhIHJlcXVlc3QgbGlicmFyeSB0byB3b3JrIHdpdGggdGhlIFJlbnQgRHluYW1pY3MgQVBJLlxuICAgKiBAcGFyYW0gZW5kcG9pbnQgdGhlIHBhdGggZm9sbG93aW5nIHRoZSBiYXNlVXJsLlxuICAgKiBAcGFyYW0gcGF5bG9hZCBhIEpTT04gc2VyaWFsaXphYmxlIG9iamVjdC5cbiAgICogQGV4YW1wbGUgcG9zdCgnL2ZvbycsIHsgYmFyOiAxIH0pO1xuICAgKi9cbiAgcHVibGljIGFzeW5jIHBvc3QoZW5kcG9pbnQ6IHN0cmluZywgcGF5bG9hZDogUGF5bG9hZCkge1xuICAgIGNvbnN0IGZ1bGxVcmwgPSB0aGlzLmhlbHBlcnMuYmFzZVVybCArIGVuZHBvaW50O1xuICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLmhlbHBlcnMuZ2V0SGVhZGVycyhlbmRwb2ludCwgcGF5bG9hZCwgdGhpcy5hdXRoVG9rZW4pO1xuICAgIHJldHVybiBmZXRjaChmdWxsVXJsLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4uaGVhZGVycyxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgfSxcbiAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogZGVsZXRlIHdyYXBzIGEgcmVxdWVzdCBsaWJyYXJ5IHRvIHdvcmsgd2l0aCB0aGUgUmVudCBEeW5hbWljcyBBUEkuXG4gICAqIEBwYXJhbSBlbmRwb2ludCB0aGUgcGF0aCBmb2xsb3dpbmcgdGhlIGJhc2VVcmwuXG4gICAqIEBleGFtcGxlIGRlbGV0ZSgnL2Zvby8xJyk7XG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZGVsZXRlKGVuZHBvaW50OiBzdHJpbmcpIHtcbiAgICBjb25zdCBmdWxsVXJsID0gdGhpcy5oZWxwZXJzLmJhc2VVcmwgKyBlbmRwb2ludDtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy5oZWxwZXJzLmdldEhlYWRlcnMoZW5kcG9pbnQsIHVuZGVmaW5lZCwgdGhpcy5hdXRoVG9rZW4pO1xuICAgIHJldHVybiBmZXRjaChmdWxsVXJsLCB7XG4gICAgICBtZXRob2Q6ICdERUxFVEUnLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAuLi5oZWFkZXJzLFxuICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogbG9naW4gZW5hYmxlcyBhbiBpbnN0YW5jZSBvZiB7QGxpbmtjb2RlIENsaWVudH0gdG8gbWFrZSBhdXRoZW50aWNhdGVkIHJlcXVlc3RzIHRvIHRoZSBSZW50XG4gICAqIER5bmFtaWNzIEFQSS5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBsb2dpbih1c2VybmFtZTogc3RyaW5nLCBwYXNzd29yZDogc3RyaW5nKSB7XG4gICAgY29uc3QgX3Bhc3N3b3JkID0gYXdhaXQgdGhpcy5oZWxwZXJzLmVuY3J5cHRQYXNzd29yZChwYXNzd29yZCk7XG4gICAgY29uc3QgZW5kcG9pbnQgPSAnL2F1dGgvbG9naW4nO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucG9zdChlbmRwb2ludCwgeyB1c2VybmFtZSwgcGFzc3dvcmQ6IF9wYXNzd29yZCB9KTtcbiAgICBpZiAoIXJlc3VsdC5vaykge1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgY29uc3QgeyB0b2tlbiB9ID0gYXdhaXQgcmVzdWx0Lmpzb24oKTtcbiAgICB0aGlzLmF1dGhUb2tlbiA9IHRva2VuO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKiogbG9nb3V0IGludmFsaWRhdGVzIHRoZSB1c2VycyBzZXNzaW9uIGdlbmVyYXRlZCBieSB7QGxpbmtjb2RlIGxvZ2lufS4gKi9cbiAgcHVibGljIGFzeW5jIGxvZ291dCgpIHtcbiAgICBjb25zdCBlbmRwb2ludCA9ICcvYXV0aC9sb2dvdXQnO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucG9zdChlbmRwb2ludCwgeyBhdXRoVG9rZW46IHRoaXMuYXV0aFRva2VuIH0pO1xuICAgIGlmICghcmVzdWx0Lm9rKSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICB0aGlzLmF1dGhUb2tlbiA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG59XG5cbi8qKiBCQVNFX1VSTCBpcyBhIGNvbGxlY3Rpb24gb2YgYmFzZSB1cmxzIGZvciBlYWNoIGRldi9wcm9kIFJlbnQgRHluYW1pY3Mgc2VydmljZS4gKi9cbmV4cG9ydCBlbnVtIEJBU0VfVVJMIHtcbiAgREVWX1JEID0gJ2h0dHBzOi8vYXBpLnJlbnRkeW5hbWljcy5kZXYnLFxuICBQUk9EX1JEID0gJ2h0dHBzOi8vYXBpLnJlbnRkeW5hbWljcy5jb20nLFxuICBERVZfUlAgPSAnaHR0cHM6Ly9hcGkucmVudHBsdXMuZGV2JyxcbiAgUFJPRF9SUCA9ICdodHRwczovL2FwaS5yZW50cGx1cy5jb20nXG59XG5cbmV4cG9ydCB0eXBlIFJkRW5jb2RlciA9IFBpY2s8VGV4dEVuY29kZXIsICdlbmNvZGUnPjtcblxuZXhwb3J0IHR5cGUgUmRDcnlwdG9ncmFwaGVyID0gUGljazxTdWJ0bGVDcnlwdG8sICdpbXBvcnRLZXknPiAmXG4gIFBpY2s8U3VidGxlQ3J5cHRvLCAnc2lnbic+ICZcbiAgUGljazxTdWJ0bGVDcnlwdG8sICdkaWdlc3QnPjtcblxuLyoqIENsaWVudE9wdGlvbnMgaXMgY29uc3VtZWQgYW5kIHVwZGF0ZWQgYnkge0BsaW5rY29kZSBDbGllbnRIZWxwZXJzfS4gKi9cbmV4cG9ydCBjbGFzcyBDbGllbnRPcHRpb25zIHtcbiAgLyoqIGFwaUtleSBpZiBkZWZpbmVkIGFwaUtleSBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBhdXRoIGhlYWRlcnMuICovXG4gIHB1YmxpYyBhcGlLZXk6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgLyoqIGFwaVNlY3JldEtleSBpZiBkZWZpbmVkIGFwaVNlY3JldEtleSBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBhdXRoIGhlYWRlcnMuICAqL1xuICBwdWJsaWMgYXBpU2VjcmV0S2V5OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIC8qKlxuICAgKiBiYXNlVXJsIGlzIHRoZSBiYXNlIHJlcXVlc3QgdXJsLiBUaGUgZGVmYXVsdCBpcyB0aGUgZGV2ZWxvcG1lbnQgcmVudGR5bmFtaWNzIGFwaS4gQSBjdXN0b21cbiAgICogc3RyaW5nIG1heSBiZSBwcm92aWRlZCBiZXlvbmQgdGhlIHtAbGlua2NvZGUgQkFTRV9VUkx9IG9wdGlvbnMuXG4gICAqL1xuICBwdWJsaWMgYmFzZVVybDogQkFTRV9VUkwgfCBzdHJpbmcgPSBCQVNFX1VSTC5ERVZfUkQ7XG5cbiAgLyoqXG4gICAqIGdldEVuY29kZXIgaXMgdXNlZCB0byBlbmNvZGUgdGV4dC4gVGhlIGVuY29kZXIgY2FuIGJlIG92ZXJyaWRkZW4gYXMgbmVlZGVkLiBGb3IgZXhhbXBsZSBpbiBhXG4gICAqIG5vZGUgZW52aXJvbm1lbnQuXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IG9wdGlvbnMgPSBuZXcgQ2xpZW50T3B0aW9ucygpO1xuICAgKiBvcHRpb25zLmdldEVuY29kZXIgPSBhc3luYyAoKSA9PiBuZXcgKGF3YWl0IGltcG9ydCgndXRpbCcpKS5UZXh0RW5jb2RlcigpO1xuICAgKi9cbiAgcHVibGljIGdldEVuY29kZXIgPSBhc3luYyAoKTogUHJvbWlzZTxSZEVuY29kZXI+ID0+IG5ldyBUZXh0RW5jb2RlcigpO1xuXG4gIC8qKlxuICAgKiBnZXRDcnlwdG9ncmFwaGVyIGlzIHVzZWQgZm9yIGNyeXB0b2dyYXBoeS4gVGhlIGNyeXB0b2dyYXBoZXIgY2FuIGJlIG92ZXJyaWRkZW4gYXMgbmVlZGVkLiBGb3JcbiAgICogZXhhbXBsZSBpbiBhIG5vZGUgZW52aXJvbm1lbnQuXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IG9wdGlvbnMgPSBuZXcgQ2xpZW50T3B0aW9ucygpO1xuICAgKiBvcHRpb25zLmdldENyeXB0b2dyYXBoZXIgPSBhc3luYyAoKSA9PiAoYXdhaXQgaW1wb3J0KCdjcnlwdG8nKSkuc3VidGxlO1xuICAgKi9cbiAgcHVibGljIGdldENyeXB0b2dyYXBoZXIgPSBhc3luYyAoKTogUHJvbWlzZTxSZENyeXB0b2dyYXBoZXI+ID0+IGNyeXB0by5zdWJ0bGU7XG59XG5cbi8qKlxuICogQ2xpZW50SGVscGVycyBpcyBhIGNvbGxlY3Rpb24gb2YgdXRpbGl0aWVzIGNvbnN1bWVkIGJ5IHtAbGlua2NvZGUgQ2xpZW50fS4gQ2xpZW50SGVscGVycyBjYW4gYmVcbiAqIHVzZWQgdG8gY2FsY3VsYXRlIGhlYWRlcnMgaW4gY2FzZSBhIGNvbnN1bWVyIHdhbnRzIHRvIGJ1aWxkIHRoZWlyIG93biBBUEkgY2xpZW50LlxuICovXG5leHBvcnQgY2xhc3MgQ2xpZW50SGVscGVycyB7XG4gIHByaXZhdGUgb3B0aW9uczogQ2xpZW50T3B0aW9ucztcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBDbGllbnRPcHRpb25zKSB7XG4gICAgdGhpcy5vcHRpb25zID0gb3B0aW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBiYXNlVXJsIGlzIHRoZSBiYXNlIHVybCB1c2VkIHRocm91Z2hvdXQgdGhlIHtAbGlua2NvZGUgQ2xpZW50SGVscGVyc30gaW5zdGFuY2UuIEl0IGlzIGluaXRpYWxseVxuICAgKiBjb25maWd1cmVkIHRocm91Z2gge0BsaW5rY29kZSBDbGllbnRPcHRpb25zfS5cbiAgICovXG4gIGdldCBiYXNlVXJsKCkge1xuICAgIHJldHVybiB0aGlzLm9wdGlvbnMuYmFzZVVybDtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXRUaW1lc3RhbXAgaXMgdXNlZCB0byBjYWxjdWxhdGUgdGhlIHRpbWVzdGFtcCBoZWFkZXIuIFRoaXMgbWV0aG9kIGlzIG5vdCBsaWtlbHkgdG8gYmUgY2FsbGVkXG4gICAqIG9uIGl0J3Mgb3duLiBJbnN0ZWFkLCBpdCBpcyB0eXBpY2FsbHkgdXNlZCB0byBtb2NrIHRoZSBjdXJyZW50IHRpbWUuXG4gICAqL1xuICBwdWJsaWMgZ2V0VGltZXN0YW1wKCkge1xuICAgIHJldHVybiBEYXRlLm5vdygpO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldEhlYWRlcnMgY3JlYXRlcyBoZWFkZXJzIGZvciB0aGUgZ2l2ZW4gcGFyYW1zLiBJZiBhbiBhdXRoIHRva2VuIGlzIGluY2x1ZGVkLCB0aGlzIG1ldGhvZCB3aWxsXG4gICAqIGdlbmVyYXRlIGFuIGBBdXRob3JpemF0aW9uYCBoZWFkZXIuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0SGVhZGVycyhcbiAgICBlbmRwb2ludDogc3RyaW5nLFxuICAgIHBheWxvYWQ/OiBQYXlsb2FkIHwgdW5kZWZpbmVkLFxuICAgIGF1dGhUb2tlbj86IHN0cmluZyB8IHVuZGVmaW5lZFxuICApIHtcbiAgICBjb25zdCBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgaWYgKHRoaXMub3B0aW9ucy5hcGlLZXkgJiYgdGhpcy5vcHRpb25zLmFwaVNlY3JldEtleSkge1xuICAgICAgaWYgKCFwYXlsb2FkIHx8ICFPYmplY3Qua2V5cyhwYXlsb2FkKS5sZW5ndGgpIHtcbiAgICAgICAgcGF5bG9hZCA9IHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcGF5bG9hZCAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgcGF5bG9hZCA9IEpTT04uc3RyaW5naWZ5KHRoaXMuZm9ybWF0UGF5bG9hZChwYXlsb2FkKSk7XG4gICAgICB9XG4gICAgICBjb25zdCB0aW1lc3RhbXAgPSB0aGlzLmdldFRpbWVzdGFtcCgpO1xuICAgICAgY29uc3Qgbm9uY2UgPSBhd2FpdCB0aGlzLmdldE5vbmNlKHRpbWVzdGFtcCwgZW5kcG9pbnQsIHBheWxvYWQpO1xuICAgICAgaWYgKGF1dGhUb2tlbikge1xuICAgICAgICBoZWFkZXJzLkF1dGhvcml6YXRpb24gPSAnVE9LRU4gJyArIGF1dGhUb2tlbjtcbiAgICAgIH1cbiAgICAgIGhlYWRlcnNbJ3gtcmQtYXBpLWtleSddID0gdGhpcy5vcHRpb25zLmFwaUtleTtcbiAgICAgIGhlYWRlcnNbJ3gtcmQtYXBpLW5vbmNlJ10gPSBub25jZTtcbiAgICAgIGhlYWRlcnNbJ3gtcmQtdGltZXN0YW1wJ10gPSB0aW1lc3RhbXAudG9TdHJpbmcoKTtcbiAgICAgIHJldHVybiBoZWFkZXJzO1xuICAgIH1cbiAgICByZXR1cm4gaGVhZGVycztcbiAgfVxuXG4gIC8qKiBmb3JtYXRQYXlsb2FkIGZvcm1hdHMgdGhlIHBheWxvYWQgZm9yIG5vbmNlIGNhbGN1bGF0aW9uLiAqL1xuICBwdWJsaWMgZm9ybWF0UGF5bG9hZChwYXlsb2FkOiBQYXlsb2FkKTogUGF5bG9hZCB7XG4gICAgbGV0IGZvcm1hdHRlZFBheWxvYWQ6IFBheWxvYWQgPSB7fTtcbiAgICBpZiAocGF5bG9hZCA9PT0gdW5kZWZpbmVkIHx8IHBheWxvYWQgPT09IG51bGwpIHtcbiAgICAgIGZvcm1hdHRlZFBheWxvYWQgPSBudWxsO1xuICAgIH0gZWxzZSBpZiAocGF5bG9hZCAhPT0gT2JqZWN0KHBheWxvYWQpKSB7XG4gICAgICBmb3JtYXR0ZWRQYXlsb2FkID0gcGF5bG9hZDtcbiAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkocGF5bG9hZCkpIHtcbiAgICAgIGZvcm1hdHRlZFBheWxvYWQgPSBbXTtcbiAgICAgIHBheWxvYWQuZm9yRWFjaCgoXywgaW5kZXgpID0+IHtcbiAgICAgICAgZm9ybWF0dGVkUGF5bG9hZFtpbmRleF0gPSB0aGlzLmZvcm1hdFBheWxvYWQocGF5bG9hZFtpbmRleF0pO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIE9iamVjdC5rZXlzKHBheWxvYWQpXG4gICAgICAgIC5zb3J0KClcbiAgICAgICAgLmZvckVhY2goayA9PiB7XG4gICAgICAgICAgaWYgKHR5cGVvZiBwYXlsb2FkW2tdID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgZm9ybWF0dGVkUGF5bG9hZFtrXSA9IHRoaXMuZm9ybWF0UGF5bG9hZChwYXlsb2FkW2tdKTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBwYXlsb2FkW2tdID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgZm9ybWF0dGVkUGF5bG9hZFtrXSA9IHBheWxvYWRba10ucmVwbGFjZSgvIC9nLCAnJyk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZvcm1hdHRlZFBheWxvYWRba10gPSBwYXlsb2FkW2tdO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBmb3JtYXR0ZWRQYXlsb2FkO1xuICB9XG5cbiAgLyoqIGdldE5vbmNlIGNhbGN1bGF0ZXMgdGhlIG5vbmNlIGZvciB0aGUgZ2l2ZW4gcGFyYW1zLiAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0Tm9uY2UodGltZXN0YW1wOiBudW1iZXIsIHVybDogc3RyaW5nLCBwYXlsb2FkU3RyPzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMub3B0aW9ucy5hcGlTZWNyZXRLZXkpIHJldHVybiBQcm9taXNlLnJlc29sdmUoJycpO1xuICAgIGNvbnN0IGVuY29kZWRVcmwgPSBlbmNvZGVVUkkodXJsKVxuICAgICAgLnJlcGxhY2UoLyU3W0NjXS9nLCAnfCcpXG4gICAgICAucmVwbGFjZSgvJTIwL2csICcgJyk7XG4gICAgY29uc3Qgbm9uY2VTdHIgPVxuICAgICAgdHlwZW9mIHBheWxvYWRTdHIgIT09ICd1bmRlZmluZWQnXG4gICAgICAgID8gdGltZXN0YW1wICsgZW5jb2RlZFVybCArIHBheWxvYWRTdHJcbiAgICAgICAgOiB0aW1lc3RhbXAgKyBlbmNvZGVkVXJsO1xuICAgIGNvbnN0IGNyeXB0b2dyYXBoZXIgPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0Q3J5cHRvZ3JhcGhlcigpO1xuICAgIGNvbnN0IGVuY29kZXIgPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0RW5jb2RlcigpO1xuICAgIGNvbnN0IGtleSA9IGVuY29kZXIuZW5jb2RlKHRoaXMub3B0aW9ucy5hcGlTZWNyZXRLZXkpO1xuICAgIGNvbnN0IGRhdGEgPSBlbmNvZGVyLmVuY29kZShub25jZVN0cik7XG4gICAgY29uc3QgYWxnb3JpdGhtID0geyBuYW1lOiAnSE1BQycsIGhhc2g6ICdTSEEtMScgfTtcbiAgICBjb25zdCBobWFjID0gYXdhaXQgY3J5cHRvZ3JhcGhlci5pbXBvcnRLZXkoJ3JhdycsIGtleSwgYWxnb3JpdGhtLCBmYWxzZSwgWydzaWduJ10pO1xuICAgIGNvbnN0IHNpZ25lZCA9IGF3YWl0IGNyeXB0b2dyYXBoZXIuc2lnbihhbGdvcml0aG0ubmFtZSwgaG1hYywgZGF0YSk7XG4gICAgcmV0dXJuIF9oZXhEaWdlc3Qoc2lnbmVkKTtcbiAgfVxuXG4gIC8qKiBlbmNyeXB0UGFzc3dvcmQgZW5jcnlwdHMgdGhlIHBhc3N3b3JkIGZvciBsb2dpbi4gKi9cbiAgcHVibGljIGFzeW5jIGVuY3J5cHRQYXNzd29yZChwYXNzd29yZDogc3RyaW5nKSB7XG4gICAgY29uc3QgY3J5cHRvZ3JhcGhlciA9IGF3YWl0IHRoaXMub3B0aW9ucy5nZXRDcnlwdG9ncmFwaGVyKCk7XG4gICAgY29uc3QgZW5jb2RlciA9IGF3YWl0IHRoaXMub3B0aW9ucy5nZXRFbmNvZGVyKCk7XG4gICAgY29uc3QgZW5jb2RlZFBhc3N3b3JkID0gZW5jb2Rlci5lbmNvZGUocGFzc3dvcmQpO1xuICAgIGNvbnN0IGRpZ2VzdGVkUGFzc3dvcmQgPSBhd2FpdCBjcnlwdG9ncmFwaGVyLmRpZ2VzdCgnU0hBLTEnLCBlbmNvZGVkUGFzc3dvcmQpO1xuICAgIHJldHVybiBfaGV4RGlnZXN0KGRpZ2VzdGVkUGFzc3dvcmQpO1xuICB9XG59XG5cbmNvbnN0IF9oZXhEaWdlc3QgPSAoYnVmOiBBcnJheUJ1ZmZlcik6IHN0cmluZyA9PlxuICBBcnJheS5mcm9tKG5ldyBVaW50OEFycmF5KGJ1ZikpXG4gICAgLm1hcChiID0+IGIudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsICcwJykpXG4gICAgLmpvaW4oJycpO1xuXG4vKipcbiAqIFByb29mT2ZQb3NzZXNzaW9uIGVuY2Fwc3VsYXRlcyBnZW5lcmljIGxvdyBsZXZlbCBsb2dpYyByZWxhdGVkIHRvIGdlbmVyYXRpbmcgcHJvb2Ygb2YgcG9zc2Vzc2lvblxuICogdG9rZW5zIGFuZCBzdG9yaW5nIHRoZWlyIGFzc29jaWF0ZWQga2V5IHBhaXIuXG4gKi9cbmV4cG9ydCBjbGFzcyBQcm9vZk9mUG9zc2Vzc2lvbiB7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2VuY29kZXIgPSBuZXcgVGV4dEVuY29kZXIoKTtcbiAgcHJpdmF0ZSByZWFkb25seSBfa2V5U3RvcmVOYW1lID0gJ2tleXMnO1xuICBwcml2YXRlIHJlYWRvbmx5IF9rZXlLZXkgPSAxO1xuICBwcml2YXRlIHJlYWRvbmx5IF9kYk5hbWUgPSAncmRiJztcbiAgcHJpdmF0ZSByZWFkb25seSBfZGJWZXJzaW9uID0gMjtcbiAgcHJpdmF0ZSByZWFkb25seSBfYWxnb3JpdGhtID0ge1xuICAgIG5hbWU6ICdSU0EtUFNTJyxcbiAgICBtb2R1bHVzTGVuZ3RoOiAyMDQ4LFxuICAgIHB1YmxpY0V4cG9uZW50OiBuZXcgVWludDhBcnJheShbMHgwMSwgMHgwMCwgMHgwMV0pLCAvLyA2NTUzN1xuICAgIGhhc2g6ICdTSEEtMjU2JyxcbiAgICBzYWx0TGVuZ3RoOiAzMlxuICB9O1xuICBwcml2YXRlIHJlYWRvbmx5IGluZGV4ZWREQjogSURCRmFjdG9yeTtcblxuICAvKipcbiAgICogY29uc3RydWN0b3IgY3JlYXRlcyBhbiBpbnN0YW5jZSBjYXBhYmxlIG9mIGdlbmVyYXRpbmcgcHJvb2Ygb2YgcG9zc2Vzc2lvbiB0b2tlbnMuXG4gICAqXG4gICAqIEBwYXJhbSBpbmRleGVkREIgd2luZG93LmluZGV4ZWREQiBzYXRpc2ZpZXMgdGhpcyBpbnRlcmZhY2UuIElmIHlvdSdyZSBub3QgaW4gYSBicm93c2VyXG4gICAqIGVudmlyb25tZW50LCBwcm9vZiBvZiBwb3NzZXNzaW9uIHRva2VucyBhcmUgbGlrZWx5IG5vdCBuZWNlc3NhcnkuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihpbmRleGVkREI6IElEQkZhY3RvcnkpIHtcbiAgICB0aGlzLmluZGV4ZWREQiA9IGluZGV4ZWREQjtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZW5lcmF0ZVByb29mIGdlbmVyYXRlcyBhIHByb29mIG9mIHBvc3Nlc3Npb24gSldUIHdpdGggdGhlIGtleSBwYWlyLiBUaGUga2V5IHBhaXIgY29tZXMgZnJvbSBhXG4gICAqIGdldCBvciBjcmVhdGUgbWV0aG9kLiBXaGVyZSB0aGUga2V5IHBhaXIgd2lsbCBjb250aW51b3VzbHkgYmUgdXNlZCB1bnRpbCBldmljdGVkIHdpdGhcbiAgICogZXZpY3RLZXlQYWlyLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGdlbmVyYXRlUHJvb2YoKSB7XG4gICAgY29uc3Qga2V5ID0gYXdhaXQgdGhpcy5fZ2V0T3JDcmVhdGVLZXkoKTtcbiAgICBjb25zdCBoZWFkZXIgPSB0aGlzLl9nZXRIZWFkZXIoKTtcbiAgICBjb25zdCBwYXlsb2FkID0gdGhpcy5fZ2V0UGF5bG9hZCgpO1xuICAgIGNvbnN0IGVuY29kZWRIZWFkZXJBbmRQYXlsb2FkID0gdGhpcy5fZW5jb2Rlci5lbmNvZGUoYCR7aGVhZGVyfS4ke3BheWxvYWR9YCk7XG4gICAgY29uc3Qgc2lnbmF0dXJlID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5zaWduKFxuICAgICAgdGhpcy5fYWxnb3JpdGhtLFxuICAgICAga2V5LnByaXZhdGVLZXksXG4gICAgICBlbmNvZGVkSGVhZGVyQW5kUGF5bG9hZFxuICAgICk7XG4gICAgY29uc3QgZW5jb2RlZFNpZ25hdHVyZSA9IGVuY29kZVNlZ21lbnQoU3RyaW5nLmZyb21DaGFyQ29kZSguLi5uZXcgVWludDhBcnJheShzaWduYXR1cmUpKSk7XG4gICAgcmV0dXJuIGAke2hlYWRlcn0uJHtwYXlsb2FkfS4ke2VuY29kZWRTaWduYXR1cmV9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXRQdWJsaWNLZXkgZ2V0cyBvciBjcmVhdGVzIGEga2V5IHBhaXIgYW5kIHN0b3JlcyBpdC4gVGhlIHB1YmxpYyBrZXkgY29tcG9uZW50IGlzIHRoZW5cbiAgICogZXh0cmFjdGVkIGFuZCByZXR1cm5lZCBpbiBhIGJhc2U2NCB1cmwgZW5jb2RlZCBmb3JtLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGdldFB1YmxpY0tleSgpIHtcbiAgICBjb25zdCBrZXlQYWlyID0gYXdhaXQgdGhpcy5fZ2V0T3JDcmVhdGVLZXkoKTtcbiAgICBjb25zdCBwayA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuZXhwb3J0S2V5KCdqd2snLCBrZXlQYWlyLnB1YmxpY0tleSk7XG4gICAgcmV0dXJuIGVuY29kZVNlZ21lbnQoSlNPTi5zdHJpbmdpZnkocGspKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBldmljdEtleVBhaXIgZXZpY3RzIHRoZSBjdXJyZW50bHkgc3RvcmVkIGtleSBwYWlyLiBObyBvcGVyYXRpb24gaXMgcGVyZm9ybWVkIGlmIHRoZSBrZXkgcGFpclxuICAgKiBkb2VzIG5vdCBleGlzdC5cbiAgICovXG4gIHB1YmxpYyBldmljdEtleVBhaXIoKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGNvbnN0IGRiID0gdGhpcy5pbmRleGVkREIub3Blbih0aGlzLl9kYk5hbWUsIHRoaXMuX2RiVmVyc2lvbik7XG4gICAgICBkYi5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHQgPSBkYi5yZXN1bHQudHJhbnNhY3Rpb24odGhpcy5fa2V5U3RvcmVOYW1lLCAncmVhZHdyaXRlJyk7XG4gICAgICAgIGNvbnN0IHJlcSA9IHQub2JqZWN0U3RvcmUodGhpcy5fa2V5U3RvcmVOYW1lKS5kZWxldGUodGhpcy5fa2V5S2V5KTtcbiAgICAgICAgcmVxLm9uc3VjY2VzcyA9IHJlc29sdmU7XG4gICAgICAgIHJlcS5vbmVycm9yID0gcmVqZWN0O1xuICAgICAgfTtcbiAgICAgIGRiLm9udXBncmFkZW5lZWRlZCA9IHRoaXMuX3VwZ3JhZGVEYihkYik7XG4gICAgICBkYi5vbmVycm9yID0gcmVqZWN0O1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfZ2V0SGVhZGVyKCkge1xuICAgIHJldHVybiBlbmNvZGVTZWdtZW50KFxuICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICB0eXA6ICdkcG9wK2p3dCcsXG4gICAgICAgIGFsZzogJ1BTMjU2J1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBfZ2V0UGF5bG9hZCgpIHtcbiAgICBjb25zdCBkID0gTWF0aC5mbG9vcihuZXcgRGF0ZSgpLmdldFRpbWUoKSAvIDEwMDApO1xuICAgIHJldHVybiBlbmNvZGVTZWdtZW50KFxuICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICBpYXQ6IGRcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgX2dldE9yQ3JlYXRlS2V5KCk6IFByb21pc2U8Q3J5cHRvS2V5UGFpcj4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBkYiA9IHRoaXMuaW5kZXhlZERCLm9wZW4odGhpcy5fZGJOYW1lLCB0aGlzLl9kYlZlcnNpb24pO1xuICAgICAgZGIub25zdWNjZXNzID0gdGhpcy5fb25EYXRhYmFzZU9wZW5lZChyZXNvbHZlLCByZWplY3QsIGRiKTtcbiAgICAgIGRiLm9udXBncmFkZW5lZWRlZCA9IHRoaXMuX3VwZ3JhZGVEYihkYik7XG4gICAgICBkYi5vbmVycm9yID0gcmVqZWN0O1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfb25EYXRhYmFzZU9wZW5lZChcbiAgICByZXNvbHZlOiAodmFsdWU6IENyeXB0b0tleVBhaXIpID0+IHZvaWQsXG4gICAgcmVqZWN0OiAocmVhc29uPzogdW5rbm93bikgPT4gdm9pZCxcbiAgICByOiBJREJPcGVuREJSZXF1ZXN0XG4gICkge1xuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBjb25zdCB0ID0gci5yZXN1bHQudHJhbnNhY3Rpb24odGhpcy5fa2V5U3RvcmVOYW1lLCAncmVhZHdyaXRlJyk7XG4gICAgICB0Lm9uZXJyb3IgPSByZWplY3Q7XG4gICAgICBjb25zdCByZXEgPSB0Lm9iamVjdFN0b3JlKHRoaXMuX2tleVN0b3JlTmFtZSkuZ2V0KHRoaXMuX2tleUtleSk7XG4gICAgICByZXEub25lcnJvciA9IHJlamVjdDtcbiAgICAgIHJlcS5vbnN1Y2Nlc3MgPSB0aGlzLl9vbktleUxvb2t1cChyZXNvbHZlLCByZWplY3QsIHIsIHJlcSk7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgX29uS2V5TG9va3VwKFxuICAgIHJlc29sdmU6ICh2YWx1ZTogQ3J5cHRvS2V5UGFpcikgPT4gdm9pZCxcbiAgICByZWplY3Q6IChyZWFzb24/OiB1bmtub3duKSA9PiB2b2lkLFxuICAgIGRiOiBJREJPcGVuREJSZXF1ZXN0LFxuICAgIGtleVJlcXVlc3Q6IElEQlJlcXVlc3RcbiAgKSB7XG4gICAgcmV0dXJuIGFzeW5jICgpID0+IHtcbiAgICAgIGlmIChrZXlSZXF1ZXN0LnJlc3VsdCkge1xuICAgICAgICByZXR1cm4gcmVzb2x2ZShrZXlSZXF1ZXN0LnJlc3VsdCk7XG4gICAgICB9XG4gICAgICBjb25zdCBrZXkgPSBhd2FpdCB0aGlzLl9jcmVhdGVLZXkoKTtcbiAgICAgIGNvbnN0IHQyID0gZGIucmVzdWx0LnRyYW5zYWN0aW9uKHRoaXMuX2tleVN0b3JlTmFtZSwgJ3JlYWR3cml0ZScpO1xuICAgICAgdDIub25lcnJvciA9IHJlamVjdDtcbiAgICAgIGNvbnN0IHJlcTIgPSB0Mi5vYmplY3RTdG9yZSh0aGlzLl9rZXlTdG9yZU5hbWUpLnB1dChrZXksIHRoaXMuX2tleUtleSk7XG4gICAgICByZXEyLm9uc3VjY2VzcyA9ICgpID0+IHJlc29sdmUoa2V5KTtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBncmFkZURiKHI6IElEQk9wZW5EQlJlcXVlc3QpIHtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgaWYgKCFyLnJlc3VsdC5vYmplY3RTdG9yZU5hbWVzLmNvbnRhaW5zKHRoaXMuX2tleVN0b3JlTmFtZSkpIHtcbiAgICAgICAgci5yZXN1bHQuY3JlYXRlT2JqZWN0U3RvcmUodGhpcy5fa2V5U3RvcmVOYW1lKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBfY3JlYXRlS2V5KCkge1xuICAgIHJldHVybiBjcnlwdG8uc3VidGxlLmdlbmVyYXRlS2V5KHRoaXMuX2FsZ29yaXRobSwgZmFsc2UsIFsnc2lnbicsICd2ZXJpZnknXSk7XG4gIH1cbn1cblxuLyoqIGVuY29kZVNlZ21lbnQgYmFzZTY0dXJsIGVuY29kZXMgYSBzdHJpbmcgb3IgSldUIHNlZ21lbnQgKi9cbmV4cG9ydCBjb25zdCBlbmNvZGVTZWdtZW50ID0gKHNlZ21lbnQ6IHN0cmluZyk6IHN0cmluZyA9PiB7XG4gIHJldHVybiBidG9hKHNlZ21lbnQpLnJlcGxhY2UoL1xcKy9nLCAnLScpLnJlcGxhY2UoL1xcLy9nLCAnXycpLnJlcGxhY2UoLz0rJC9nLCAnJyk7XG59O1xuXG4vKiogZGVjb2RlUGF5bG9hZCBkZWNvZGVzIHRoZSBwYXlsb2FkIHNlZ21lbnQgb2YgYSBKV1QgdG8gaXQncyBvYmplY3QgcmVwcmVzZW50YXRpb24gKi9cbmV4cG9ydCBjb25zdCBkZWNvZGVQYXlsb2FkID0gKHRva2VuOiBzdHJpbmcpOiB7IGV4cDogbnVtYmVyOyBpYXQ6IG51bWJlciB9ID0+IHtcbiAgY29uc3QgWywgcGF5bG9hZF0gPSB0b2tlbi5zcGxpdCgnLicpO1xuICBjb25zdCBwYWRkaW5nID0gcGF5bG9hZC5sZW5ndGggJSA0ID09PSAwID8gJycgOiAnPScucmVwZWF0KDQgLSAocGF5bG9hZC5sZW5ndGggJSA0KSk7XG4gIHJldHVybiBKU09OLnBhcnNlKGF0b2IoYCR7cGF5bG9hZC5yZXBsYWNlKC8