@financialforcedev/orizuru-auth
Version:
Salesforce authentication for Heroku Node applications.
102 lines • 14.2 kB
JavaScript
;
/*
* Copyright (c) 2019, FinancialForce.com, inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the FinancialForce.com, inc nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
const __1 = require("../..");
const identity_1 = require("../client/salesforce/identity");
const introspect_1 = require("../introspection/introspect");
const accessToken_1 = require("./common/accessToken");
const fail_1 = require("./common/fail");
/**
* Returns an express middleware that introspects the OpenID Connect access token
* passed in an HTTP Authorization header.
*
* If successful, the token information and the user object are set on the request object.
*
* @fires EVENT_TOKEN_INTROSPECTED, EVENT_DENIED
* @param app The Orizuru server instance.
* @param [provider] The name of the auth provider. Defaults to 'salesforce'.
* @param [params] The token introspection middleware parameters.
* @param [opts] The optional parameters used when introspecting tokens.
* @returns An express middleware that introspects an access token.
*/
function createMiddleware(app, provider, params, opts) {
const internalProvider = provider || 'salesforce';
const internalParams = params || app.options.openid[internalProvider];
const _a = opts || accessToken_1.DEFAULT_MIDDLEWARE_OPTIONS, { setTokenOnContext } = _a, grantOptions = __rest(_a, ["setTokenOnContext"]);
const introspectAccessToken = introspect_1.createTokenIntrospector(app.options.authProvider[internalProvider]);
return async function introspectToken(req, res, next) {
try {
const accessToken = accessToken_1.extractAccessToken(req);
const tokenInformation = await introspectAccessToken(accessToken, internalParams, grantOptions);
setTokenInformationOnRequest(app, req, tokenInformation);
accessToken_1.setAccessTokenOnRequest(req, accessToken, setTokenOnContext);
next();
}
catch (error) {
fail_1.fail(app, error, req, res, next);
}
};
}
exports.createMiddleware = createMiddleware;
/**
* Sets the token information on the Orizuru context.
*
* It also sets the user on the request if the username is contained in the introspection
* response.
*
* @fires EVENT_TOKEN_INTROSPECTED, EVENT_TOKEN_VALIDATED
* @param app The Orizuru server instance.
* @param req The HTTP request.
* @param tokenInformation The token information to set on the request.
*/
function setTokenInformationOnRequest(app, req, tokenInformation) {
const orizuru = req.orizuru || {};
app.emit(__1.EVENT_TOKEN_INTROSPECTED, `Token introspected for user (${tokenInformation.username || 'unknown'}) [${req.ip}].`);
if (tokenInformation.username) {
orizuru.user = {
username: tokenInformation.username
};
if (identity_1.isSalesforceIntrospectionResponse(tokenInformation)) {
orizuru.user.organizationId = tokenInformation.userInfo.organizationId;
}
app.emit(__1.EVENT_TOKEN_VALIDATED, `Token validated for user (${tokenInformation.username}) [${req.ip}].`);
}
orizuru.tokenInformation = tokenInformation;
req.orizuru = orizuru;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW5JbnRyb3NwZWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2luZGV4L21pZGRsZXdhcmUvdG9rZW5JbnRyb3NwZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHOzs7Ozs7Ozs7Ozs7O0FBUUgsNkJBQXdFO0FBRXhFLDREQUFrRjtBQUNsRiw0REFBc0U7QUFDdEUsc0RBQWtJO0FBQ2xJLHdDQUFxQztBQUVyQzs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxTQUFnQixnQkFBZ0IsQ0FBQyxHQUFvQixFQUFFLFFBQWlCLEVBQUUsTUFBNEIsRUFBRSxJQUErQztJQUV0SixNQUFNLGdCQUFnQixHQUFHLFFBQVEsSUFBSSxZQUFZLENBQUM7SUFDbEQsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDdEUsTUFBTSxxREFBMkUsRUFBM0UsRUFBRSxpQkFBaUIsT0FBd0QsRUFBdEQsZ0RBQXNELENBQUM7SUFFbEYsTUFBTSxxQkFBcUIsR0FBRyxvQ0FBdUIsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUM7SUFFbEcsT0FBTyxLQUFLLFVBQVUsZUFBZSxDQUFDLEdBQVksRUFBRSxHQUFhLEVBQUUsSUFBa0I7UUFFcEYsSUFBSTtZQUVILE1BQU0sV0FBVyxHQUFHLGdDQUFrQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBRTVDLE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxXQUFXLEVBQUUsY0FBYyxFQUFFLFlBQVksQ0FBQyxDQUFDO1lBRWhHLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztZQUN6RCxxQ0FBdUIsQ0FBQyxHQUFHLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixDQUFDLENBQUM7WUFFN0QsSUFBSSxFQUFFLENBQUM7U0FFUDtRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ2YsV0FBSSxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztTQUNqQztJQUVGLENBQUMsQ0FBQztBQUVILENBQUM7QUEzQkQsNENBMkJDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILFNBQVMsNEJBQTRCLENBQUMsR0FBb0IsRUFBRSxHQUFZLEVBQUUsZ0JBQXVDO0lBRWhILE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO0lBRWxDLEdBQUcsQ0FBQyxJQUFJLENBQUMsNEJBQXdCLEVBQUUsZ0NBQWdDLGdCQUFnQixDQUFDLFFBQVEsSUFBSSxTQUFTLE1BQU0sR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFFM0gsSUFBSSxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUU7UUFFOUIsT0FBTyxDQUFDLElBQUksR0FBRztZQUNkLFFBQVEsRUFBRSxnQkFBZ0IsQ0FBQyxRQUFRO1NBQ25DLENBQUM7UUFFRixJQUFJLDRDQUFpQyxDQUFDLGdCQUFnQixDQUFDLEVBQUU7WUFDeEQsT0FBTyxDQUFDLElBQUksQ0FBQyxjQUFjLEdBQUcsZ0JBQWdCLENBQUMsUUFBUyxDQUFDLGNBQWMsQ0FBQztTQUN4RTtRQUVELEdBQUcsQ0FBQyxJQUFJLENBQUMseUJBQXFCLEVBQUUsNkJBQTZCLGdCQUFnQixDQUFDLFFBQVEsTUFBTSxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztLQUN4RztJQUVELE9BQU8sQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQztJQUM1QyxHQUFHLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztBQUV2QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoYykgMjAxOSwgRmluYW5jaWFsRm9yY2UuY29tLCBpbmNcbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0IG1vZGlmaWNhdGlvbixcbiAqICAgYXJlIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucyBhcmUgbWV0OlxuICpcbiAqIC0gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLFxuICogICAgICB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyLlxuICogLSBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsXG4gKiAgICAgIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlIGRvY3VtZW50YXRpb25cbiAqICAgICAgYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uXG4gKiAtIE5laXRoZXIgdGhlIG5hbWUgb2YgdGhlIEZpbmFuY2lhbEZvcmNlLmNvbSwgaW5jIG5vciB0aGUgbmFtZXMgb2YgaXRzIGNvbnRyaWJ1dG9yc1xuICogICAgICBtYXkgYmUgdXNlZCB0byBlbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmUgd2l0aG91dFxuICogICAgICBzcGVjaWZpYyBwcmlvciB3cml0dGVuIHBlcm1pc3Npb24uXG4gKlxuICogVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUyBcIkFTIElTXCIgQU5EXG4gKiAgQU5ZIEVYUFJFU1MgT1IgSU1QTElFRCBXQVJSQU5USUVTLCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgVEhFIElNUExJRUQgV0FSUkFOVElFU1xuICogIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMXG4gKiAgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1IgQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsXG4gKiAgRVhFTVBMQVJZLCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgKElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBQUk9DVVJFTUVOVCBPRiBTVUJTVElUVVRFIEdPT0RTXG4gKiAgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIgQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZXG4gKiAgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpXG4gKiAgQVJJU0lORyBJTiBBTlkgV0FZIE9VVCBPRiBUSEUgVVNFIE9GIFRISVMgU09GVFdBUkUsIEVWRU4gSUYgQURWSVNFRCBPRiBUSEUgUE9TU0lCSUxJVFkgT0YgU1VDSCBEQU1BR0UuXG4gKi9cblxuLyoqXG4gKiBAbW9kdWxlIG1pZGRsZXdhcmUvdG9rZW5JbnRyb3NwZWN0aW9uXG4gKi9cblxuaW1wb3J0IHsgTmV4dEZ1bmN0aW9uLCBSZXF1ZXN0LCBSZXF1ZXN0SGFuZGxlciwgUmVzcG9uc2UgfSBmcm9tICdAZmluYW5jaWFsZm9yY2VkZXYvb3JpenVydSc7XG5cbmltcG9ydCB7IEVWRU5UX1RPS0VOX0lOVFJPU1BFQ1RFRCwgRVZFTlRfVE9LRU5fVkFMSURBVEVEIH0gZnJvbSAnLi4vLi4nO1xuaW1wb3J0IHsgSW50cm9zcGVjdGlvbk9wdGlvbnMsIEludHJvc3BlY3Rpb25QYXJhbXMsIEludHJvc3BlY3Rpb25SZXNwb25zZSB9IGZyb20gJy4uL2NsaWVudC9vYXV0aDInO1xuaW1wb3J0IHsgaXNTYWxlc2ZvcmNlSW50cm9zcGVjdGlvblJlc3BvbnNlIH0gZnJvbSAnLi4vY2xpZW50L3NhbGVzZm9yY2UvaWRlbnRpdHknO1xuaW1wb3J0IHsgY3JlYXRlVG9rZW5JbnRyb3NwZWN0b3IgfSBmcm9tICcuLi9pbnRyb3NwZWN0aW9uL2ludHJvc3BlY3QnO1xuaW1wb3J0IHsgREVGQVVMVF9NSURETEVXQVJFX09QVElPTlMsIGV4dHJhY3RBY2Nlc3NUb2tlbiwgTWlkZGxld2FyZU9wdGlvbnMsIHNldEFjY2Vzc1Rva2VuT25SZXF1ZXN0IH0gZnJvbSAnLi9jb21tb24vYWNjZXNzVG9rZW4nO1xuaW1wb3J0IHsgZmFpbCB9IGZyb20gJy4vY29tbW9uL2ZhaWwnO1xuXG4vKipcbiAqIFJldHVybnMgYW4gZXhwcmVzcyBtaWRkbGV3YXJlIHRoYXQgaW50cm9zcGVjdHMgdGhlIE9wZW5JRCBDb25uZWN0IGFjY2VzcyB0b2tlblxuICogcGFzc2VkIGluIGFuIEhUVFAgQXV0aG9yaXphdGlvbiBoZWFkZXIuXG4gKlxuICogSWYgc3VjY2Vzc2Z1bCwgdGhlIHRva2VuIGluZm9ybWF0aW9uIGFuZCB0aGUgdXNlciBvYmplY3QgYXJlIHNldCBvbiB0aGUgcmVxdWVzdCBvYmplY3QuXG4gKlxuICogQGZpcmVzIEVWRU5UX1RPS0VOX0lOVFJPU1BFQ1RFRCwgRVZFTlRfREVOSUVEXG4gKiBAcGFyYW0gYXBwIFRoZSBPcml6dXJ1IHNlcnZlciBpbnN0YW5jZS5cbiAqIEBwYXJhbSBbcHJvdmlkZXJdIFRoZSBuYW1lIG9mIHRoZSBhdXRoIHByb3ZpZGVyLiBEZWZhdWx0cyB0byAnc2FsZXNmb3JjZScuXG4gKiBAcGFyYW0gW3BhcmFtc10gVGhlIHRva2VuIGludHJvc3BlY3Rpb24gbWlkZGxld2FyZSBwYXJhbWV0ZXJzLlxuICogQHBhcmFtIFtvcHRzXSBUaGUgb3B0aW9uYWwgcGFyYW1ldGVycyB1c2VkIHdoZW4gaW50cm9zcGVjdGluZyB0b2tlbnMuXG4gKiBAcmV0dXJucyBBbiBleHByZXNzIG1pZGRsZXdhcmUgdGhhdCBpbnRyb3NwZWN0cyBhbiBhY2Nlc3MgdG9rZW4uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVNaWRkbGV3YXJlKGFwcDogT3JpenVydS5JU2VydmVyLCBwcm92aWRlcj86IHN0cmluZywgcGFyYW1zPzogSW50cm9zcGVjdGlvblBhcmFtcywgb3B0cz86IE1pZGRsZXdhcmVPcHRpb25zICYgSW50cm9zcGVjdGlvbk9wdGlvbnMpOiBSZXF1ZXN0SGFuZGxlciB7XG5cblx0Y29uc3QgaW50ZXJuYWxQcm92aWRlciA9IHByb3ZpZGVyIHx8ICdzYWxlc2ZvcmNlJztcblx0Y29uc3QgaW50ZXJuYWxQYXJhbXMgPSBwYXJhbXMgfHwgYXBwLm9wdGlvbnMub3BlbmlkW2ludGVybmFsUHJvdmlkZXJdO1xuXHRjb25zdCB7IHNldFRva2VuT25Db250ZXh0LCAuLi5ncmFudE9wdGlvbnMgfSA9IG9wdHMgfHwgREVGQVVMVF9NSURETEVXQVJFX09QVElPTlM7XG5cblx0Y29uc3QgaW50cm9zcGVjdEFjY2Vzc1Rva2VuID0gY3JlYXRlVG9rZW5JbnRyb3NwZWN0b3IoYXBwLm9wdGlvbnMuYXV0aFByb3ZpZGVyW2ludGVybmFsUHJvdmlkZXJdKTtcblxuXHRyZXR1cm4gYXN5bmMgZnVuY3Rpb24gaW50cm9zcGVjdFRva2VuKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKSB7XG5cblx0XHR0cnkge1xuXG5cdFx0XHRjb25zdCBhY2Nlc3NUb2tlbiA9IGV4dHJhY3RBY2Nlc3NUb2tlbihyZXEpO1xuXG5cdFx0XHRjb25zdCB0b2tlbkluZm9ybWF0aW9uID0gYXdhaXQgaW50cm9zcGVjdEFjY2Vzc1Rva2VuKGFjY2Vzc1Rva2VuLCBpbnRlcm5hbFBhcmFtcywgZ3JhbnRPcHRpb25zKTtcblxuXHRcdFx0c2V0VG9rZW5JbmZvcm1hdGlvbk9uUmVxdWVzdChhcHAsIHJlcSwgdG9rZW5JbmZvcm1hdGlvbik7XG5cdFx0XHRzZXRBY2Nlc3NUb2tlbk9uUmVxdWVzdChyZXEsIGFjY2Vzc1Rva2VuLCBzZXRUb2tlbk9uQ29udGV4dCk7XG5cblx0XHRcdG5leHQoKTtcblxuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRmYWlsKGFwcCwgZXJyb3IsIHJlcSwgcmVzLCBuZXh0KTtcblx0XHR9XG5cblx0fTtcblxufVxuXG4vKipcbiAqIFNldHMgdGhlIHRva2VuIGluZm9ybWF0aW9uIG9uIHRoZSBPcml6dXJ1IGNvbnRleHQuXG4gKlxuICogSXQgYWxzbyBzZXRzIHRoZSB1c2VyIG9uIHRoZSByZXF1ZXN0IGlmIHRoZSB1c2VybmFtZSBpcyBjb250YWluZWQgaW4gdGhlIGludHJvc3BlY3Rpb25cbiAqIHJlc3BvbnNlLlxuICpcbiAqIEBmaXJlcyBFVkVOVF9UT0tFTl9JTlRST1NQRUNURUQsIEVWRU5UX1RPS0VOX1ZBTElEQVRFRFxuICogQHBhcmFtIGFwcCBUaGUgT3JpenVydSBzZXJ2ZXIgaW5zdGFuY2UuXG4gKiBAcGFyYW0gcmVxIFRoZSBIVFRQIHJlcXVlc3QuXG4gKiBAcGFyYW0gdG9rZW5JbmZvcm1hdGlvbiBUaGUgdG9rZW4gaW5mb3JtYXRpb24gdG8gc2V0IG9uIHRoZSByZXF1ZXN0LlxuICovXG5mdW5jdGlvbiBzZXRUb2tlbkluZm9ybWF0aW9uT25SZXF1ZXN0KGFwcDogT3JpenVydS5JU2VydmVyLCByZXE6IFJlcXVlc3QsIHRva2VuSW5mb3JtYXRpb246IEludHJvc3BlY3Rpb25SZXNwb25zZSkge1xuXG5cdGNvbnN0IG9yaXp1cnUgPSByZXEub3JpenVydSB8fCB7fTtcblxuXHRhcHAuZW1pdChFVkVOVF9UT0tFTl9JTlRST1NQRUNURUQsIGBUb2tlbiBpbnRyb3NwZWN0ZWQgZm9yIHVzZXIgKCR7dG9rZW5JbmZvcm1hdGlvbi51c2VybmFtZSB8fCAndW5rbm93bid9KSBbJHtyZXEuaXB9XS5gKTtcblxuXHRpZiAodG9rZW5JbmZvcm1hdGlvbi51c2VybmFtZSkge1xuXG5cdFx0b3JpenVydS51c2VyID0ge1xuXHRcdFx0dXNlcm5hbWU6IHRva2VuSW5mb3JtYXRpb24udXNlcm5hbWVcblx0XHR9O1xuXG5cdFx0aWYgKGlzU2FsZXNmb3JjZUludHJvc3BlY3Rpb25SZXNwb25zZSh0b2tlbkluZm9ybWF0aW9uKSkge1xuXHRcdFx0b3JpenVydS51c2VyLm9yZ2FuaXphdGlvbklkID0gdG9rZW5JbmZvcm1hdGlvbi51c2VySW5mbyEub3JnYW5pemF0aW9uSWQ7XG5cdFx0fVxuXG5cdFx0YXBwLmVtaXQoRVZFTlRfVE9LRU5fVkFMSURBVEVELCBgVG9rZW4gdmFsaWRhdGVkIGZvciB1c2VyICgke3Rva2VuSW5mb3JtYXRpb24udXNlcm5hbWV9KSBbJHtyZXEuaXB9XS5gKTtcblx0fVxuXG5cdG9yaXp1cnUudG9rZW5JbmZvcm1hdGlvbiA9IHRva2VuSW5mb3JtYXRpb247XG5cdHJlcS5vcml6dXJ1ID0gb3JpenVydTtcblxufVxuIl19