feat(#968): cookie support

This commit is contained in:
Anoop M D 2023-11-20 13:41:47 +05:30
parent 9f535aeba7
commit e1a96e0f23
8 changed files with 194 additions and 7 deletions

42
package-lock.json generated
View File

@ -17745,6 +17745,7 @@
"node-machine-id": "^1.1.12",
"qs": "^6.11.0",
"socks-proxy-agent": "^8.0.2",
"tough-cookie": "^4.1.3",
"uuid": "^9.0.0",
"vm2": "^3.9.13",
"yup": "^0.32.11"
@ -17905,6 +17906,28 @@
"js-yaml": "bin/js-yaml.js"
}
},
"packages/bruno-electron/node_modules/tough-cookie": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"packages/bruno-electron/node_modules/tough-cookie/node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"engines": {
"node": ">= 4.0.0"
}
},
"packages/bruno-electron/node_modules/uuid": {
"version": "9.0.0",
"license": "MIT",
@ -22828,6 +22851,7 @@
"node-machine-id": "^1.1.12",
"qs": "^6.11.0",
"socks-proxy-agent": "^8.0.2",
"tough-cookie": "*",
"uuid": "^9.0.0",
"vm2": "^3.9.13",
"yup": "^0.32.11"
@ -22926,6 +22950,24 @@
"argparse": "^2.0.1"
}
},
"tough-cookie": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"requires": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"dependencies": {
"universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="
}
}
},
"uuid": {
"version": "9.0.0"
}

View File

@ -0,0 +1,30 @@
import React from 'react';
import { useSelector } from 'react-redux';
import Modal from 'components/Modal';
const CollectionProperties = ({ onClose }) => {
const cookies = useSelector((state) => state.app.cookies) || [];
return (
<Modal size="md" title="Cookies" hideFooter={true} handleCancel={onClose}>
<table className="w-full border-collapse" style={{ marginTop: '-1rem' }}>
<thead>
<tr>
<th className="py-2 px-2 text-left">Domain</th>
<th className="py-2 px-2 text-left">Cookie</th>
</tr>
</thead>
<tbody>
{cookies.map((cookie) => (
<tr key={cookie.id}>
<td className="py-2 px-2">{cookie.domain}</td>
<td className="py-2 px-2 break-all">{cookie.cookieString}</td>
</tr>
))}
</tbody>
</table>
</Modal>
);
};
export default CollectionProperties;

View File

@ -3,10 +3,11 @@ import Collections from './Collections';
import StyledWrapper from './StyledWrapper';
import GitHubButton from 'react-github-btn';
import Preferences from 'components/Preferences';
import Cookies from 'components/Cookies';
import { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { IconSettings } from '@tabler/icons';
import { IconSettings, IconCookie } from '@tabler/icons';
import { updateLeftSidebarWidth, updateIsDragging, showPreferences } from 'providers/ReduxStore/slices/app';
import { useTheme } from 'providers/Theme';
@ -18,6 +19,7 @@ const Sidebar = () => {
const preferencesOpen = useSelector((state) => state.app.showPreferences);
const [asideWidth, setAsideWidth] = useState(leftSidebarWidth);
const [cookiesOpen, setCookiesOpen] = useState(false);
const { storedTheme } = useTheme();
@ -79,6 +81,7 @@ const Sidebar = () => {
<aside>
<div className="flex flex-row h-screen w-full">
{preferencesOpen && <Preferences onClose={() => dispatch(showPreferences(false))} />}
{cookiesOpen && <Cookies onClose={() => setCookiesOpen(false)} />}
<div className="flex flex-col w-full" style={{ width: asideWidth }}>
<div className="flex flex-col flex-grow">
@ -91,19 +94,26 @@ const Sidebar = () => {
<IconSettings
size={18}
strokeWidth={1.5}
className="mr-2 hover:text-gray-700"
className="mr-2 hover:text-gray-700"
onClick={() => dispatch(showPreferences(true))}
/>
<IconCookie
size={18}
strokeWidth={1.5}
className="mr-2 hover:text-gray-700"
onClick={() => setCookiesOpen(true)}
/>
</div>
<div className="pl-1" style={{ position: 'relative', top: '3px' }}>
<GitHubButton
{/* This will get moved to home page */}
{/* <GitHubButton
href="https://github.com/usebruno/bruno"
data-color-scheme={storedTheme}
data-show-count="true"
aria-label="Star usebruno/bruno on GitHub"
>
Star
</GitHubButton>
</GitHubButton> */}
</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.1.1</div>
</div>

View File

@ -14,7 +14,7 @@ import {
runFolderEvent,
brunoConfigUpdateEvent
} from 'providers/ReduxStore/slices/collections';
import { showPreferences, updatePreferences } from 'providers/ReduxStore/slices/app';
import { showPreferences, updatePreferences, updateCookies } from 'providers/ReduxStore/slices/app';
import toast from 'react-hot-toast';
import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions';
import { isElectron } from 'utils/common/platform';
@ -135,6 +135,10 @@ const useIpcEvents = () => {
dispatch(updatePreferences(val));
});
const removeCookieUpdateListener = ipcRenderer.on('main:cookies-update', (val) => {
dispatch(updateCookies(val));
});
return () => {
removeCollectionTreeUpdateListener();
removeOpenCollectionListener();
@ -149,6 +153,7 @@ const useIpcEvents = () => {
removeConfigUpdatesListener();
showPreferencesListener();
removePreferencesUpdatesListener();
removeCookieUpdateListener();
};
}, [isElectron]);
};

View File

@ -16,7 +16,8 @@ const initialState = {
font: {
codeFont: 'default'
}
}
},
cookies: []
};
export const appSlice = createSlice({
@ -46,6 +47,10 @@ export const appSlice = createSlice({
},
updatePreferences: (state, action) => {
state.preferences = action.payload;
},
updateCookies: (state, action) => {
state.cookies = action.payload;
console.log(state.cookies);
}
}
});
@ -58,7 +63,8 @@ export const {
showHomePage,
hideHomePage,
showPreferences,
updatePreferences
updatePreferences,
updateCookies
} = appSlice.actions;
export const savePreferences = (preferences) => (dispatch, getState) => {

View File

@ -50,6 +50,7 @@
"node-machine-id": "^1.1.12",
"qs": "^6.11.0",
"socks-proxy-agent": "^8.0.2",
"tough-cookie": "^4.1.3",
"uuid": "^9.0.0",
"vm2": "^3.9.13",
"yup": "^0.32.11"

View File

@ -28,6 +28,7 @@ const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-he
const { addDigestInterceptor } = require('./digestauth-helper');
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util');
const { chooseFileToSave, writeBinaryFile } = require('../../utils/filesystem');
const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require('../../utils/cookies');
// override the default escape function to prevent escaping
Mustache.escape = function (value) {
@ -181,6 +182,12 @@ const configureRequest = async (
request.timeout = preferencesUtil.getRequestTimeout();
// add cookies to request
const cookieString = getCookieStringForUrl(request.url);
if (cookieString && typeof cookieString === 'string' && cookieString.length) {
request.headers['cookie'] = cookieString;
}
return axiosInstance;
};
@ -439,6 +446,22 @@ const registerNetworkIpc = (mainWindow) => {
const { data, dataBuffer } = parseDataFromResponse(response);
response.data = data;
// save cookies
let setCookieHeaders = [];
if (response.headers['set-cookie']) {
setCookieHeaders = Array.isArray(response.headers['set-cookie'])
? response.headers['set-cookie']
: [response.headers['set-cookie']];
for (let setCookieHeader of setCookieHeaders) {
addCookieToJar(setCookieHeader, request.url);
}
}
// send domain cookies to renderer
const domainsWithCookies = await getDomainsWithCookies();
mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookies)));
await runPostResponse(
request,
response,

View File

@ -0,0 +1,70 @@
const { Cookie, CookieJar } = require('tough-cookie');
const each = require('lodash/each');
const cookieJar = new CookieJar();
const addCookieToJar = (setCookieHeader, requestUrl) => {
const cookie = Cookie.parse(setCookieHeader, { loose: true });
cookieJar.setCookieSync(cookie, requestUrl);
};
const getCookiesForUrl = (url) => {
return cookieJar.getCookiesSync(url);
};
const getCookieStringForUrl = (url) => {
const cookies = getCookiesForUrl(url);
if (!Array.isArray(cookies) || !cookies.length) {
return '';
}
const validCookies = cookies.filter((cookie) => !cookie.expires || cookie.expires > Date.now());
return validCookies.map((cookie) => cookie.cookieString()).join('; ');
};
const getDomainsWithCookies = () => {
return new Promise((resolve, reject) => {
const domainCookieMap = {};
cookieJar.store.getAllCookies((err, cookies) => {
if (err) {
return reject(err);
}
cookies.forEach((cookie) => {
if (!domainCookieMap[cookie.domain]) {
domainCookieMap[cookie.domain] = [cookie];
} else {
domainCookieMap[cookie.domain].push(cookie);
}
});
const domains = Object.keys(domainCookieMap);
const domainsWithCookies = [];
each(domains, (domain) => {
const cookies = domainCookieMap[domain];
const validCookies = cookies.filter((cookie) => !cookie.expires || cookie.expires > Date.now());
if (validCookies.length) {
domainsWithCookies.push({
domain,
cookies: validCookies,
cookieString: validCookies.map((cookie) => cookie.cookieString()).join('; ')
});
}
});
resolve(domainsWithCookies);
});
});
};
module.exports = {
addCookieToJar,
getCookiesForUrl,
getCookieStringForUrl,
getDomainsWithCookies
};