const CookieJar = require('tough-cookie').CookieJar, ToughCookie = require('tough-cookie').Cookie, PostmanCookie = require('postman-collection').Cookie, PostmanCookieList = require('postman-collection').CookieList, Url = require('postman-collection').Url, FUNCTION = 'function', OBJECT = 'object', STRING = 'string', /** * Convert PostmanCookie Cookie instance to ToughCookie instance. * * @private * @param {PostmanCookie} cookie - Postman Cookie instance * @returns {ToughCookie} Tough Cookie instance */ serialize = function (cookie) { if (!cookie) { return; } return ToughCookie.fromJSON({ key: cookie.name || cookie.key, value: cookie.value, expires: cookie.expires, maxAge: cookie.maxAge, domain: cookie.domain, path: cookie.path, secure: cookie.secure, httpOnly: cookie.httpOnly, hostOnly: cookie.hostOnly, extensions: cookie.extensions }); }, /** * Convert Tough Cookie instance to Electron Cookie instance. * * @private * @param {ToughCookie} cookie - Tough Cookie instance * @returns {ElectronCookie} Electron Cookie instance */ deserialize = function (cookie) { if (!cookie) { return; } return new PostmanCookie({ name: cookie.key, value: cookie.value, expires: cookie.expires === 'Infinity' ? null : cookie.expires, maxAge: cookie.maxAge, domain: cookie.domain, path: cookie.path, secure: cookie.secure, httpOnly: cookie.httpOnly, hostOnly: cookie.hostOnly, extensions: cookie.extensions }); }, /** * Sanitize url object or string to Postman Url instance. * * @private * @param {Object|String} url - * @returns {Url|Null} */ sanitizeURL = function (url) { if (Url.isUrl(url)) { return url; } if (url && typeof url === STRING) { return new Url(url); } return null; }, /** * Executes a provided function once for each array element. * * @note not completely asynchronous, don't compare with async.each * * @private * @param {Array} items - Array of items to iterate over * @param {Function} fn - An async function to apply to each item in items * @param {Function} cb - A callback which is called when all iteratee functions have finished, * or an error occurs */ forEachWithCallback = function (items, fn, cb) { !cb && (cb = function () { /* (ಠ_ಠ) */ }); if (!(Array.isArray(items) && fn)) { return cb(); } var index = 0, totalItems = items.length, next = function (err) { if (err || index >= totalItems) { return cb(err); } try { fn.call(items, items[index++], next); } catch (error) { return cb(error); } }; if (!totalItems) { return cb(); } next(); }, /** * Helper function to handle callback. * * @private * @param {Function} callback - Callback function * @param {Error|String} err - Error or Error message * @param {*} result - */ callbackHandler = function (callback, err, result) { if (typeof callback !== FUNCTION) { return; } if (err) { return callback(err instanceof Error ? err : new Error(err)); } callback(null, result); }, /** * Helper function to fetch a cookie with given name. * * @private * @todo add CookieJar~getCookie to avoid this * * @param {CookieJar} jar - ToughCookie jar instance * @param {String} url - Url string * @param {String} name - Cookie name * @param {Function} callback - Callback function */ getCookie = function (jar, url, name, callback) { jar.getCookies(url, function (err, cookies) { var i, ii; if (err) { return callback(err); } if (!(cookies && cookies.length)) { return callback(null, null); } for (i = 0, ii = cookies.length; i < ii; i++) { if (cookies[i].key === name) { return callback(null, cookies[i]); } } callback(null, null); }); }; class PostmanCookieJar { /** * @param {Object} cookieStore - */ constructor (cookieStore) { this.store = cookieStore; this.jar = new CookieJar(cookieStore, { rejectPublicSuffixes: false, looseMode: true }); } /** * Get the cookie value with the given name. * * @param {String} url - * @param {String} name - * @param {Function} callback - */ get (url, name, callback) { url = sanitizeURL(url); if (!url) { throw new TypeError('CookieJar.get() requires a valid url'); } if (typeof callback !== FUNCTION) { throw new TypeError('CookieJar.get() requires a callback function'); } if (typeof name !== STRING) { throw new TypeError('CookieJar.get() requires cookie name to be a string'); } getCookie(this.jar, url.toString(true), name, function (err, cookie) { if (err || !cookie) { return callbackHandler(callback, err, null); } cookie = deserialize(cookie); return callbackHandler(callback, null, cookie.valueOf()); }); } /** * Get all the cookies for the given URL. * * @param {String} url - * @param {Object} [options] - * @param {Function} callback - */ getAll (url, options, callback) { url = sanitizeURL(url); if (!url) { throw new TypeError('CookieJar.getAll() requires a valid url'); } if (typeof options === FUNCTION && !callback) { callback = options; options = {}; } if (typeof callback !== FUNCTION) { throw new TypeError('CookieJar.getAll() requires a callback function'); } if (typeof options !== OBJECT) { throw new TypeError('CookieJar.getAll() requires options to be an object'); } options = { // return HttpOnly cookies by default http: Object.hasOwnProperty.call(options, 'http') ? Boolean(options.http) : true, // if undefined, auto-detect from url secure: Object.hasOwnProperty.call(options, 'secure') ? Boolean(options.secure) : undefined }; this.jar.getCookies(url.toString(true), options, function (err, cookies) { if (err) { return callbackHandler(callback, err); } callbackHandler(callback, null, new PostmanCookieList(null, cookies && cookies.map(deserialize))); }); } /** * Set or update a cookie. * * @param {String} url - * @param {String|Object} name - * @param {String|Function} [value] - * @param {Function} [callback] - */ set (url, name, value, callback) { url = sanitizeURL(url); if (!url) { throw new TypeError('CookieJar.set() requires a valid url'); } if (typeof value === FUNCTION && !callback) { callback = value; value = null; } var cookie; // @todo avoid else-if to reduce cyclomatic complexity if (name && value) { cookie = serialize({ name, value }); } else if (typeof name === OBJECT) { cookie = serialize(name); } else if (typeof name === STRING) { cookie = name; } else { throw new TypeError('CookieJar.set() requires a valid set cookie arguments'); } this.jar.setCookie(cookie, url.toString(true), function (err, cookie) { if (err) { return callbackHandler(callback, err); } callbackHandler(callback, null, deserialize(cookie)); }); } /** * Remove single cookie with the given name. * * @param {String} url - * @param {String} name - * @param {Function} [callback] - */ unset (url, name, callback) { url = sanitizeURL(url); if (!url) { throw new TypeError('CookieJar.unset() requires a valid url'); } if (typeof name !== STRING) { throw new TypeError('CookieJar.unset() requires cookie name to be a string'); } var store = this.store; getCookie(this.jar, url.toString(true), name, function (err, cookie) { if (err || !cookie) { return callbackHandler(callback, err); } store.removeCookie(cookie.domain, cookie.path, cookie.key, function (err) { callbackHandler(callback, err); }); }); } /** * Remove all the cookies for the given URL. * * @param {String} url - * @param {Function} [callback] - */ clear (url, callback) { url = sanitizeURL(url); if (!url) { throw new TypeError('CookieJar.clear() requires a valid url'); } var store = this.store; this.jar.getCookies(url.toString(true), function (err, cookies) { if (err || !cookies) { return callbackHandler(callback, err); } forEachWithCallback(cookies, function (cookie, next) { store.removeCookie(cookie.domain, cookie.path, cookie.key, next); }, function (err) { callbackHandler(callback, err); }); }); } } module.exports = PostmanCookieJar;