rentdynamics
Version:
Package to help facilitate communicating with the Rent Dynamics API
365 lines • 50.1 kB
JavaScript
/**
* 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